如何利用AI快速理解复杂项目
本文转载自知乎Up主笙囧同学的如何利用cursor快速理解复杂代码工程? - 知乎
去年我试着读 vLLM 的源码。
起因是我在用 vLLM 做推理服务的时候遇到了一个性能问题,想看看它的调度器到底是怎么工作的。我clone了仓库,打开文件树,看到了大概几百个Python 文件、一堆 C++ 扩展、还有 CUDA kernel。
我盯着屏幕看了十分钟,关掉了编辑器,去泡了杯咖啡。
那种感觉你一定体会过——就像你站在一座陌生城市的市中心,四周全是高楼,每栋楼里都有几十层,你知道你要找的东西就在某一栋楼的某一层的某个房间里,但你连东南西北都分不清。
后来我开始用 Cursor 来辅助读源码。摸索了一段时间之后,确实找到了一些有效的方法。
不是那种”让AI帮你读完所有代码然后给你一份总结”的方法——那种方法我试过,没用,AI给你的总结跟你自己看README得到的信息差不多。
我要说的方法更像是把Cursor当成一个对这个代码库了如指掌的向导,你问它路,它带你走,但路是你自己走的,风景是你自己看的。
为什么直接让AI”总结整个项目”没用
这是很多人的第一反应。打开Cursor,把整个项目索引了,然后问:”帮我介绍一下这个项目的整体架构。”
AI会给你一段话。听起来头头是道。什么”这个项目采用了分层架构,核心模块包括 XXX、YYY、ZZZ,它们之间通过 XXX 方式通信”。
你读完觉得”嗯嗯好有道理”。
然后呢?
然后你发现自己并没有真正理解任何东西。你知道了几个模块的名字,但你不知道数据是怎么从入口流到出口的。你不知道某个关键的设计决策背后的原 因。你不知道当一个请求进来的时候,代码到底在做什么。
因为理解不是信息的传递,是认知结构的构建。 AI把架构信息告诉你,相当于给了你一张地图。但地图不是领土。你没有亲自走过那些路,地图上的每一个 标记对你来说都只是一个符号,而不是一段体验。
这就是为什么”让AI帮你读代码”作为一个整体策略是行不通的。
但”在你自己读代码的过程中,让AI帮你解决具体的障碍”——这个策略是非常有效的。
两者的区别是什么?是主动权在谁手里。前者你是被动的信息接收者,后者你是主动的探索者。AI的角色从”替你读”变成了”帮你读”。
我的方法:从一个问题开始
读任何一个复杂项目,我现在都不会从”了解整体架构”开始。
我会从一个具体的问题开始。
读 vLLM 的时候,我的问题是:”一个推理请求从进入系统到返回结果,经历了哪些步骤?”
读 FastAPI 的时候,我的问题是:”当我写了一个 @app.get('/users') ,框架是怎么把这个装饰器变成一个能处理HTTP请求的东西的?”
读 Redis 的时候,我的问题是:”当我执行 SET key value 的时候,这个命令从被接收到数据被写入内存,中间经过了什么?”
一个好的问题就是你的故事线。 你不需要理解整个项目——你需要沿着一个问题,把它涉及到的代码路径从头到尾走一遍。
走完一条路径之后,你对项目的理解就不再是零了。你有了一个”锚点”。然后你可以从这个锚点出发,问第二个问题,走第二条路径。两条路径之间一定 会有交叉点——那些交叉点就是项目的核心模块。
几条路径走完,项目的骨架自然就在你脑子里了。
这个方法不需要AI也能做。但有Cursor的帮助,效率会高非常多。
第一步:找到入口
每一条故事线都需要一个入口。对于大部分项目来说,入口就是用户最先接触到的那个界面。
如果是一个 Web 框架,入口就是你创建 app、注册路由的地方。
如果是一个推理引擎,入口就是你调用 model.generate() 的地方。
如果是一个数据库,入口就是它接收客户端连接和命令的地方。
怎么用 Cursor 找入口? 直接问它:
|
|
注意最后那句”不需要解释细节”。这很重要。
你在这个阶段只需要一个起点,不需要AI给你长篇大论。如果你让AI一次性解释太多,你会淹没在信息里,反而找不到方向。
Cursor 会告诉你类似这样的信息:”入口在 vllm/entrypoints/llm.py 的 LLM.generate() 方法”。
好。打开那个文件。开始读。
第二步:沿着调用链往下走
打开入口文件之后,你开始读代码。很快你会遇到第一个障碍——你看到了一个函数调用,但你不知道那个函数做了什么、在哪个文件里。
这是Cursor最有价值的使用场景之一。
选中那个函数调用,问Cursor:
|
|
注意:一两句话。 你不需要AI给你逐行解释那个函数的实现。你现在只需要知道”它大概做了什么”以及”它在哪”。
这就像你在陌生城市里走路,遇到一个路口,你不需要知道每条岔路通向哪里的完整地图。你只需要知道”这条路大概通往火车站”和”那条路大概通往商 业区”,然后选择跟你目标相关的那条走下去。
你会沿着调用链一路走下去。入口函数调用了 A,A 调用了 B,B 调用了 C……
你不需要走完所有的分支。你只需要走跟你的问题相关的那条主线。
中间遇到不影响主线理解的分支——比如日志记录、参数校验、缓存检查——直接跳过。怎么判断一个分支是不是主线?问 Cursor:
|
|
Cursor会告诉你:”第47行的 self._run_engine() 是核心调用,上面的都是参数处理和校验,可以先跳过。”
然后你跳到第47行,继续往下走。
第三步:遇到不懂的技术栈时
这是你提到的第二个痛点——项目里用了你不熟悉的语言或技术。
比如你在读一个 Python 项目,突然发现核心的计算部分是用 C++ 写的 CUDA 扩展。你不懂 CUDA。
传统的做法是:先去学 CUDA,学个几天几周,然后回来接着读。
有了 Cursor 你可以换一种做法:不学 CUDA 本身,只理解那段 CUDA 代码在做什么。
选中那段 C++/CUDA 代码,问 Cursor:
|
|
最后那句”用 Python 的思维方式类比”很关键。它告诉 Cursor 用你已有的知识框架来解释新东西。
Cursor 可能会说:”这段 CUDA kernel 本质上在做的事情相当于 Python 里的:对一个大矩阵的每一行做 softmax,然后跟另一个矩阵做矩阵乘法。只不过它把这个计算拆分到了GPU的几千个线程上并行执行。”
你不需要理解它是怎么拆分的、线程是怎么同步的——那些是CUDA的实现细节。你需要理解的是它在计算层面做了什么。 有了这个理解,你就能把这个 CUDA函数当成一个”黑盒”,知道它的输入输出,然后继续沿着主线往下走。
同样的方法适用于你遇到的任何不熟悉的技术栈:
-
不懂 Rust?让 Cursor 用你懂的语言类比解释
-
不懂某个框架的特定 API?让 Cursor 解释它的作用而不是用法
-
不懂某个设计模式?让 Cursor 解释它在这个项目里解决了什么问题
关键原则是:你的目标不是学会那个技术栈,而是理解代码的逻辑流。两者需要的知识量差了一个数量级。
第四步:画地图
走完一条主线之后,你需要把你走过的路记下来。
我的做法是在一个单独的markdown文件里,边读边记。格式很简单:
|
|
这份笔记不是给别人看的,是给你自己看的。 所以不需要写得很正式,用你自己能理解的语言就好。
你可能觉得”我直接记在脑子里不就行了”。不行。复杂项目的调用链太长了,你走到第五层的时候大概率已经忘了第二层的细节。写下来才能在脑子里维 持一张完整的地图。
而且这份笔记还有一个用处——当你后续探索第二条故事线的时候,你可以把新的路径叠加到同一份地图上,看到不同路径之间的交叉点。那些交叉点往往 就是项目最核心的模块。
第五步:在关键节点上深入
走完主线之后,你已经有了一个粗粒度的理解。接下来你可以选择在某些你特别感兴趣的节点上深入。
比如我走完vLLM的主线之后,对调度器特别感兴趣——因为它是vLLM性能优势的核心。
这时候我会打开调度器的代码,让Cursor帮我做更细粒度的解读:
|
|
注意这次我要求”详细解释”了——因为这是我选择深入的节点,值得花时间理解细节。
在主线探索阶段,你的提问策略应该是”概括+定位”——告诉我大概做了什么、在哪里。
在深入阶段,你的提问策略变成”细节+原因”——具体是怎么实现的、为什么这么设计。
这个节奏很重要。如果你从一开始就事事追问细节,你会迷失在细节里走不动。如果你全程只停留在概括层面,你最终得到的理解是浅薄的。
先粗后细,先主线后分支。 像画画一样——先画骨架,再填细节。
一些容易踩的坑
坑一:一次问太多。
不要发这种消息:
|
|
这种问法得到的回答一定是又长又泛的废话。不是 Cursor 不行,是你的问题太大了,任何人面对这种问题都只能给你一个泛泛的回答。
好的提问是窄的、具体的、有明确边界的。
-
“这个函数的第三个参数是干什么用的?”
-
“这个类为什么要继承那个基类而不是直接实现?”
-
“数据从这个函数出来之后,下一步去了哪里?”
每个问题只解决一个困惑。解决完了再问下一个。你的理解就是这样一个困惑一个困惑地拼起来的。
坑二:完全信任Cursor的回答。
Cursor 有时候会瞎说。尤其是当你问的问题涉及到跨文件的复杂逻辑时,它可能会给你一个听起来很合理但实际上是错的解释。
怎么办?交叉验证。
Cursor 告诉你”这个函数会调用XXX”——你自己跳过去看一眼,确认它确实调用了。
Cursor 告诉你”这个变量在YYY文件里被初始化”——你自己搜一下那个变量名,确认它说的位置是对的。
不需要每一句话都验证,但关键节点一定要自己确认。 尤其是那些会影响你对整体架构理解的判断——比如”模块A和模块B是通过消息队列通信的”—— 这种结论你最好自己看到代码里的证据。
养成这个习惯之后,你会发现大部分时候 Cursor 是对的,但偶尔它确实会犯错。那些”偶尔”的错误如果你没有验证就接受了,可能会导致你对整个项目的理解建立在一个错误的基础上。
坑三:试图一次读完。
复杂的开源项目不是一个下午能读完的。vLLM 的代码我前前后后读了大概两周,中间穿插着其他工作。
不要给自己压力说”今天一定要把这个项目读完”。你今天读明白了一条主线,就是实实在在的进展。明天再读一条。
每次读完记得写笔记。 你下次再回来的时候,看一眼笔记就能快速恢复上下文,不用从头开始。
坑四:只读不跑。
这可能是最大的坑。
光看代码你能理解的东西是有限的。把项目跑起来,加几个断点或者打几行日志,亲眼看到数据怎么流动的——这个过程给你的理解深度是光看代码的好几 倍。
你可以让 Cursor 帮你做这件事:
|
|
不需要搭建完整的开发环境。只要能跑通一个最小的例子,能让你在关键位置加断点看到数据,就够了。
跑起来之后,在你走过的主线上的关键函数入口加个断点或者日志。然后触发一个请求,看实际的执行路径是不是跟你读代码时理解的一致。
经常会有惊喜。 你以为数据走的是路径A,实际上走的是路径B——因为有一个条件分支你读代码时忽略了。这种”预期和现实的偏差”本身就是最好的学习 机会。
针对不同类型项目的策略差异
不同类型的项目,”故事线”的选择方式不一样。简单说几个常见的:
Web框架类(FastAPI、Express、Gin等):
最好的故事线是”一个HTTP请求从进入到返回的完整生命周期”。从监听端口→接收请求→路由匹配→中间件执行→handler调用→响应返回。这条线走 完,你就理解了框架的骨架。
数据库/存储引擎类(Redis、LevelDB、SQLite等):
最好的故事线是”一个写操作从接收到持久化的完整路径”。从命令解析→数据结构操作→内存写入→持久化到磁盘。然后再走一条”读操作”的路径。两 条路径交叉的地方就是核心的数据结构和存储引擎。
AI推理/训练框架类(vLLM、DeepSpeed、PyTorch等):
最好的故事线是”一个前向传播从输入到输出的完整路径”。从数据预处理→模型加载→计算执行→结果后处理。特别注意计算是在哪里从Python进入 C++/CUDA的——那个边界通常是理解性能优化的关键。
编译器/解释器类(CPython、V8、GCC等):
最好的故事线是”一段源代码从文本到被执行的完整路径”。从词法分析→语法分析→AST→中间表示→优化→代码生成/执行。每一步的输入和输出是什 么,格式是什么。
不管哪种类型的项目,核心思路都是一样的:找到一条从入口到出口的主线,沿着它走,遇到障碍时让 Cursor 帮你清除障碍。
一个完整的实战流程示例
最后把整个流程用一个具体的例子串一遍。假设你想理解 FastAPI 是怎么工作的。
起手。 把 FastAPI 的仓库 clone 下来,用 Cursor 打开。
|
|
Cursor 告诉你在 fastapi/applications.py 里。打开它。
沿主线走。 你看到 app.get 实际上调用了 self.router.add_api_route() 。你不知道这个函数做了什么。选中它,问 Cursor:
|
|
Cursor告诉你它在 fastapi/routing.py 里,作用是”把你的函数包装成一个 APIRoute 对象,注册到路由表里”。
你跳到 routing.py ,看到了 APIRoute 这个类。你好奇路由匹配是怎么做的——当一个请求进来时,框架怎么知道该调用哪个 handler?
|
|
Cursor 告诉你 FastAPI 底层用的是 Starlette 的路由系统。实际的匹配逻辑在 Starlette 的 Router.__call__ 里。
遇到技术栈边界。 你可能不熟悉 Starlette。没关系:
|
|
Cursor 会解释清楚边界。你决定要不要跨过这个边界深入到 Starlette 里去。也许现在不需要——你只需要知道”路由匹配这件事 Starlette 替你做了”就行, 你更感兴趣的是 FastAPI 自己加了什么东西。
记笔记。 走完这条主线之后你写下:
|
|
选择深入点。 你对依赖注入特别好奇。开始第二轮探索,专门走Depends的解析流程。
循环。 每一轮探索都让你的地图更完整、更细致。三四轮之后,你对FastAPI的理解就已经远超大部分使用者了。
回到最开始的问题。
读复杂代码工程最大的难点从来不是”代码太多看不完”。代码多不可怕,可怕的是你不知道该看哪里。
Cursor 最大的价值不是替你读代码,而是在你迷路的时候告诉你该往哪走。
但走路这件事,得你自己来。
每一行你亲自读过的代码、每一个你亲自想明白的逻辑、每一次你的预期和实际运行结果对不上然后你搞清楚了为什么——这些时刻构成的理解,是任何AI 总结都给不了你的。
Cursor 是一个极好的向导。但向导的价值是带你去你自己想去的地方,不是替你走完全程然后给你讲一遍沿途的风景。
风景得自己看。看过的才是你的