ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

冷启动遗留系统:用四层上下文让AI读懂旧代码

冷启动遗留系统:用四层上下文让AI读懂旧代码 我接手过一个跑在生产环境里快十年的老系统Java 加 JSP还有一堆没人敢删的 shell 脚本和配置文件。第一次想让它被 AI 看懂的时候我把几个核心类丢进去问这个方法的业务含义是什么它给出的回答流畅、自信然后完全是错的。那次之后我才意识到一件事AI 读新代码很强读旧代码几乎是半瞎的。原因不在模型而在于旧项目长年累月堆积出来的信息断层——命名混乱、结构塌陷、业务规则只存在于某些人的脑子里。所以这篇东西想聊的就是冷启动一个旧项目时怎么用一套可复现的方法把 AI 从看得见字但看不懂事变成能回答、能定位、能改的状态。适合谁看适合接手遗留系统的开发、需要快速摸清历史包袱的维护者也适合想把 AI 真正用进日常工作而不是当玩具的人。整个过程不需要什么特殊工具一台能跑代码的机器、一个能读文件的 AI 助手加上一点耐心就够了。1. 先搞清楚旧项目为什么会让 AI看不懂1.1 旧项目的三类信息断层新项目的代码和业务是同步长出来的变量名基本能自解释分层也还清晰。旧项目不是这样它是被一次次需求、一次次救火、一次次人员流动磨出来的。磨到最后代码里会出现三类非常典型的断层。第一类是命名断层。你会在代码里看到doIt()、tmp2、dataList1、a1、processFlag这种东西甚至有直接拿拼音首字母当变量名的。这类命名对人来说都费劲对 AI 更费劲因为模型理解代码高度依赖符号语义。当所有符号都失去语义模型就只能靠调用位置去猜猜错的概率非常高。第二类是结构断层。典型特征是单个文件几千行一个方法几百行逻辑全塞在一起没有 service 层、没有边界。AI 做代码分析靠的是分块 关系文件一大它要么被截断要么只能看到局部看不到全局。你在一个 8000 行的类里问它这个改动会影响哪些地方它几乎无法回答因为影响面散落在它没读到的部分。第三类是上下文断层也是最致命的一类。业务规则不在代码里。比如这个字段为 0 的时候表示未审核但历史数据里也有 -1 表示未审核这种信息只存在于某些人的记忆或者某份早就不知道去哪的需求文档里。AI 没有任何渠道知道这件事它只能看到代码里那个if (status 0)然后给你一个看似合理的解释。这三类断层叠加在一起就是冷启动旧项目最难受的地方你问的问题越接近业务本质AI 的答案就越不可靠。1.2 AI 读代码的真实能力边界很多人对 AI 读代码的期待是把仓库丢进去它全懂这个期待不现实。它的真实边界大概是这几条。它能做得很好的局部代码解释、单文件内的逻辑梳理、根据一段代码写测试、把一段老语法翻译成新语法、找出明显的空指针和资源泄漏。这些事情不依赖全局理解只依赖它眼前的那段文本。它做得一般的跨文件调用链追踪、影响面分析、这个功能在哪里实现的这类检索型问题。做一般的原因不是它笨而是它拿到的东西不完整——检索召回不准或者上下文塞不下。它做不好的运行时的行为、依赖外部系统的副作用、以及所有不在代码里而在人脑里的隐性规则。把这三档区分清楚你的期望就对了。你要做的不是让 AI 变强而是把它的输入补全——把那些它拿不到的东西用人能写、机器能读的方式补进上下文里。这也是后面所有方法的出发点。2. 冷启动前的准备先给项目做一次分诊2.1 判断这个项目值不值得投入不是每个旧项目都值得你花几天时间去整理上下文。先做一次快速分诊用三个问题判断。第一个问题它还在跑吗还要改吗。如果一个系统只是挂着不动、未来半年没有任何需求那你的目标应该是看得懂就行不需要精细整理把入口文件和关键配置交给 AI能回答基本问题就收工。第二个问题核心业务逻辑集中还是分散。集中在一个模块里的整理成本低一天能搞定散落在十几个模块互相引用里的就得先做调用链切片成本高一截。第三个问题有没有测试。有测试的项目AI 可以通过测试理解预期行为这是巨大的杠杆没有测试的项目你就得多花时间在用文档补预期上。我一般会用一个很粗暴的标准如果这个项目我预计要投入超过两周那就值得花半天到一天专门整理上下文如果只是看一眼直接用 AI 边读边问更划算。别把整理上下文本身变成一个拖垮进度的大工程。2.2 建立最小可读基线整理之前先确保项目处于可运行、可构建的状态。这一步看起来和 AI 无关其实关系很大。AI 判断代码对不对最可靠的方式是对照运行结果、对照编译错误、对照测试输出。如果项目连编译都过不了你给它的所有输出都没法验证你就只能凭感觉信或者不信这个循环非常低效。具体做法很朴素把依赖装上把数据库或者数据文件准备好让服务能在本地起来跑一遍现有的测试哪怕是几个。跑不起来的旧项目很常见那就退一步至少让核心模块能单独编译通过。这个基线建立起来之后你后面问 AI 的每个问题都有一个验证手段——让它在本地改一版编译一次看报不报错。我踩过的坑是跳过这一步直接让 AI 读代码并给出修改建议改完一编译一堆错误然后我又得回去问它为什么来回消耗的时间比搭环境还长。先把地基铺平后面才快。3. 核心方法把旧项目翻译成 AI 能吃下的四层结构这套方法是我自己磨出来的核心思路是分层补全每一层解决一类断层从粗到细做完一层就能回答一批问题不需要一次做完。3.1 第一层目录地图与入口清单这一层解决结构断层。目的是让 AI 知道这个项目大致长什么样哪些目录是核心、哪些是历史遗留、哪些根本不重要。没有这层AI 会把一个废弃的old_backup目录和核心业务目录同等对待召回一堆垃圾。做法是这样先扫一遍顶层目录人工判断每个目录的角色然后写一份精简的地图。地图不用详细一个目录一句话就够。关键是把入口标出来——程序的启动点、请求的入口、定时任务的入口、消息消费的入口。这些入口是后面所有调用链追踪的起点。如果项目结构比较乱可以用几条命令先拿到客观数据避免凭印象判断。# 统计各语言代码量和文件数 find . -type f -name *.java -not -path */target/* | wc -l find . -type f -name *.py -not -path */venv/* -exec wc -l {} | sort -rn | head -20 # 找出最大的文件通常是重灾区 find . -type f \( -name *.java -o -name *.py -o -name *.js \) -not -path */node_modules/* \ -exec wc -l {} | sort -rn | head -15 # 找出最近半年没人动的目录大概率可以标记为低优先级 find . -maxdepth 2 -type d -mtime 180拿到这些数据之后你的目录地图就有了事实依据而不是拍脑袋。3.2 第二层术语表把业务黑话翻译成代码符号这一层解决命名断层和部分上下文断层是我认为投入产出比最高的一步。几乎每个旧项目都有一套自己的黑话。可能是业务术语比如结算单对账批次冻结额度也可能是历史遗留的奇怪缩写比如gmt其实是某个业务状态、qk是某种渠道。这些东西开发者心里有数但 AI 完全没有。你要做的是把它们整理成一张表三列业务说法、代码里的符号、一句话解释。这张表后面直接塞进 AI 的上下文效果立竿见影——原本它看到qkFlag会瞎猜现在它能准确说出这是渠道标识。整理这张表有个技巧不要对着代码硬想而是拿着代码里的高频名词去问同事或者翻历史文档。哪些词出现频率高、哪些词让你困惑优先整理它们。我一般会先跑一遍词频统计把出现次数最多的一批标识符挑出来。# 抓取驼峰和下划线标识符粗略看高频词 grep -rhoE [A-Za-z_][A-Za-z0-9_]{3,} src/ \ | sort | uniq -c | sort -rn | head -60这份表不需要一次做完先写十个最关键的够用了。3.3 第三层调用链切片从入口往下切这一层解决结构断层里最难的部分——跨文件理解。做法是挑出你最关心的几个业务场景从入口开始把这条链路上涉及的文件和方法挑出来做成一个切片包。比如用户下单这个场景链路可能是Controller 的createOrder→ Service 的validate→reserveStock→createPayment→ 落库。你把这条链路上的每个方法所在的文件都列出来标注它的角色校验、扣减、支付、持久化然后在这份切片里问 AI。为什么这么做因为 AI 处理大仓库的方式和你不一样它不是真的一次读完而是靠检索和分块。你主动把一条链路裁出来等于替它做了最关键的召回工作它的回答质量会直接上一个台阶。切片包的形式可以很简单一个 Markdown 文件列清楚场景、入口、链路文件清单和每步职责。关键是链路要准确最好你自己先跟着代码走一遍或者用 IDE 的调用层级功能确认一遍。3.4 第四层约束与潜规则文档这一层专门收留那些代码里看不出来但必须知道的东西。这是旧项目最有价值的资产也是最容易被忽略的。内容大概包括这几类数据状态的特殊取值比如 -1 代表什么、不能改的字段、有顺序要求的操作、和生产环境绑定的配置、以及团队约定俗成的规矩比如所有时间都用某个时区、金额都用整数分存储。每一条都写成一句话配上它在代码里的位置。写这层文档的时候有个原则只写如果不写AI会猜错的内容。不要把需求文档整段搬进来那会把上下文撑爆反而降低效果。我这边的实际经验是真正影响 AI 回答准确率的往往是那么七八条潜规则而不是几百页的需求。把这些关键点写清楚收益远超预期。这层文档写完你的上下文就基本够用了。4. 实操全流程从零到让 AI 准确回答业务问题前面讲的是思路这一节给一套可以直接照着做的流程。假设你刚接手一个项目打算用两个小时把 AI 的上下文搭起来。4.1 第一步生成项目档案先别急着问 AI先自己收集事实。跑几条命令拿到项目的客观画像语言构成、代码规模、最大的文件、入口在哪、依赖了哪些外部服务。# 语言构成 find . -type f -name *.java | wc -l find . -type f -name *.xml -not -path */target/* | wc -l # 入口找 main 方法和启动类 grep -rl public static void main --include*.java . grep -rl SpringBootApplication --include*.java . # 外部依赖看配置文件 grep -rhE (jdbc|redis|mq|amqp):// src/main/resources/ | sort -u这些输出自己看一遍你对项目的印象就具体多了后面写档案也不会瞎写。4.2 第二步写上下文包把前面四层的内容整合成几个文件放在项目根目录下一个专门的文件夹里比如.ai-context/。结构建议这样.ai-context/ 00-map.md # 目录地图与入口清单 01-glossary.md # 术语表 02-flows/ # 调用链切片一个场景一个文件 order-create.md settle-batch.md 03-rules.md # 约束与潜规则每个文件都尽量短能一句话说清就不要写两句话。00-map.md的模板大概是这样# 项目地图 ## 入口 - Web 入口src/main/java/.../OrderController.java - 定时任务src/main/java/.../job/SettleJob.java每天凌晨 2 点 - 消息消费src/main/java/.../mq/StockConsumer.java ## 目录角色 - service/核心业务逻辑改需求主要动这里 - dao/数据库访问老代码里的 SQL 拼在这 - legacy/废弃模块不要参考也不要改 - common/工具类通用但历史包袱多 ## 重点文件大且乱改动需谨慎 - OrderServiceImpl.java3200 行 - SettleHelper.java1800 行写完这几份文件你的准备就完成了大半。4.3 第三步设计提问模板上下文有了提问方式也很关键。我吃过最大的亏就是问得太宽比如帮我分析这个项目AI 给一堆泛泛而谈。有效的问法有几个特征绑定具体场景、要求引用文件、要求区分事实和推测。几个我常用的模板请只基于 .ai-context/ 和指定文件回答不要脑补。 问题订单创建时库存扣减和支付创建的顺序是什么 要求 1. 引用具体文件和行号 2. 如果信息不足明确说上下文里没有不要猜 3. 区分代码显示的事实和你的推断请阅读 02-flows/order-create.md 里列出的文件回答 如果我要在创建订单后增加一次风控校验最小改动点在哪 给出改动清单包括文件、方法、以及需要同步修改的地方。模板的核心是那句信息不足就说没有不要猜。旧项目里 AI 猜错的代价很大因为它猜得特别连贯你很容易被带偏。4.4 第四步用回读测试验证 AI 是否真的懂了AI 说自己懂了不算数。我一般会做一次三轮验证用三个问题测它。第一轮问一个你知道答案的事实型问题比如某个状态值的含义。答对了说明术语表和规则文档起作用了。第二轮问一个你没整理过的链路看它是老实说不知道还是开始编。老实说不知道说明边界控制得好开始编说明你的提问里缺约束要补。第三轮让它做一个小改动然后在本地编译验证。比如把某个日志级别改一下、给某个方法加个空值保护。改完能编译通过、逻辑合理说明它对这个文件的理解是到位的。三轮下来你就知道你的上下文包里哪一层还薄回去补哪一层就行。这个迭代过程通常两三轮就收敛了。5. 工具与环境一些实际选择上的考虑5.1 几类 AI 辅助方式的对比现在能用来读代码的 AI 工具形态差别挺大选错了会浪费很多时间。我按自己的使用体验做个对比。形态优势局限适合场景对话式 AI 手动粘贴灵活可控不依赖配置上下文有限粘贴麻烦单文件分析、语法转换编辑器内置补全与分析贴近编码现场响应快全局理解弱跨文件差日常写代码、局部重构能读整个仓库的助手召回强能跨文件回答大仓库索引慢隐私需评估冷启动梳理、调用链追踪本地部署的模型数据不出内网可定制部署和维护成本高有合规要求的场景我的建议是组合使用用能读仓库的工具做全局梳理和检索用编辑器内置的做日常编码两者不冲突。5.2 大仓库怎么处理才不让索引崩掉旧项目动辄几十万行直接把整个仓库喂给工具索引慢、召回也不准。有效的做法是先做过滤。构建产物、依赖目录、自动生成的代码、历史备份目录这些都要排除掉。大多数工具都支持配置文件指定忽略规则把规则写清楚索引量能降一大截召回质量反而上升。另外一个技巧是按模块分批索引。先索引你最关心的那一个模块把上下文包也做小一点回答质量会比全仓库一把梭好得多。等这个模块摸清了再扩到下一个。冷启动最忌讳贪多一次只啃一块效率最高。5.3 关于环境的一些零碎经验字体、终端、编辑器这些看似无关实际上对长时间读代码的体验影响很大。用等宽字体选一个你眼睛不累的配上合适的行高。终端里配好语法高亮读 diff 的时候会舒服很多。这些都是小事但你需要在这个项目上坐很久小事累积起来就是大差别。还有一点把项目跑起来的步骤写成脚本别每次都手动敲。冷启动阶段你会反复起停服务、跑测试、看日志一个make run或者一个 shell 脚本能省下大量重复劳动。6. 常见问题与排查技巧6.1 常见问题速查表整理一下我在冷启动过程中反复遇到的问题以及对应的处理方式。现象可能原因处理方式AI 回答流畅但内容错误上下文缺失模型在脑补提问里加信息不足就说没有补规则文档问跨文件问题答不上来召回不准或链路没整理做调用链切片主动给出文件清单回答被截断上下文超限缩小提问范围一次只问一个文件或一条链路改完编译不过依赖关系没被理解先让 AI 列出改动影响面再动手术语解释前后不一致术语表没覆盖到把高频词补进 glossary 后重新索引索引特别慢仓库太大未做过滤排除构建产物和依赖目录按模块分批6.2 几条踩过坑才总结出来的经验第一不要让 AI 一次理解整个项目。这是最常见的错误也是最容易导致挫败感的做法。正确的节奏是一个场景一个场景地啃每啃完一个就沉淀一份切片文档慢慢你手里就有了一整套可复用的上下文。第二把 AI 的回答当假设不当结论。特别是涉及业务规则的部分一定要用代码、数据或者同事的话去验证。旧项目里看起来对但其实错的解释最危险因为你会照着一个错误的模型改代码。第三规则文档宁少勿多。我一开始很兴奋把能想到的东西全写进去结果上下文被稀释关键信息反而被淹没了。后来精简到十几条最关键的效果好很多。第四改动前一定要让 AI 列出影响面。旧项目里方法之间往往是隐式耦合你改一个地方另一个地方就崩。让 AI 先说清楚这个改动会波及哪些文件即使它列得不全也能帮你避开几个大坑。第五定期回读验证。上下文会随着项目演进过期我大概每两周会把上下文包和代码对一遍把失效的部分更新掉。这个习惯让我的上下文包一直能用而不是用两周就废。7. 进阶让 AI 长期跟着这个项目走7.1 把上下文包当成项目资产维护上下文包一旦做起来就不该是一次性的。把它纳入项目的常规维护比如每次发布前顺手更新一遍目录地图每改动一个核心链路就更新对应的切片。这件事花不了几分钟但能让后面接手的人省很多事。我现在的做法是把.ai-context/和代码一起进版本管理改动它的时候也能看到历史。这样即使我离开这个项目下一个人接手时能直接站在这套上下文上继续。这比写一份辞藻华丽但没人看的交接文档实在得多。7.2 用 AI 做回归验证和知识补全除了读代码AI 在旧项目上还有两个高价值用法。一个是回归验证。你可以让 AI 基于现有代码生成一批回归用例尤其是针对那些边界条件复杂的逻辑。即使你不信任它生成的断言把这些用例跑一遍、看看哪些失败本身就能暴露一批历史遗留的隐藏问题。另一个是知识补全。旧项目里经常有一些方法没人知道为什么这么写注释也早没了。你可以让 AI 结合调用位置、上下文和历史提交信息给出一个最可能的解释然后拿去和同事确认。这种方式不保证准确但能帮你快速形成一个可以验证的假设比完全没头绪强。我自己最近还在试的一件事是让 AI 帮我维护一份变更风险清单把每次改动波及到的历史疑难代码记录进去时间久了就形成一份针对这个项目的专属知识库。用了几个月感觉方向是对的后面如果有新进展再分享。
返回列表