首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >在 30 万行祖传代码上跑 Coding Agent

在 30 万行祖传代码上跑 Coding Agent

作者头像
陆业聪
发布2026-06-04 13:22:17
发布2026-06-04 13:22:17
620
举报

📰 科技要闻

Nvidia 发布 Vera CPU,专为 AI 智能体工作负载打造:N 家这次把"为 Agent 加速"写到了 Slogan 里,意味着 Coding Agent 这种"长上下文 + 高频工具调用"的工作负载已经被认作主流。

Nvidia RTX Spark 发布,号称史上最高效 PC 芯片:本地跑代码模型的门槛在降,这对我们这种"代码不能往云上传"的团队是大利好。

礼来靶向药物 Retevmo 显著降低肺癌复发风险(财经):医药股关注度回升,提醒一句,AI 编程工具正在把医疗算法工程师的迭代节奏抬高一档。

伊朗冲突推高能源价格(财经):美国家庭多花 450 美元/年,跨国云服务的电费成本也会传导,自建小集群跑模型反而更划算了。

起因:一个"祖传 30 万行"项目的 PR 评审灾难

说实话,我刚开始也不信 AI 工具能在老项目上跑得好。

一个朋友接手的是一个有八年历史的 Android 主工程,主仓 30 万+ 行 Kotlin/Java 混合代码,有四套并存的网络层(OkHttp 自研封装、Retrofit、Volley 残留、还有一个不知道谁写的 HttpURLConnection 工具类),再加上 RxJava 1/2/3、LiveData、Flow、协程混着用。新来的同学入职第一周写出来的代码,merge 上去之后测试跑红,是常态。

三月份朋友做了个实验:让一位刚来三周的同学全程用 Claude Code 写一个"账单详情页"需求,按我们正常的工作流提 PR,看会发生什么。结果是这样的:

❌ 翻车现场 → 代码用了 Retrofit,但同模块的其他接口都用我们自研的 NetClient;用了 LiveData,但 ViewModel 基类是 Flow;用了 Glide,但项目早就全切到 Coil 了。功能跑得起来,评审过不了。

问题不在 AI,在于它根本不知道这个项目的"潜规则"。这事儿让我开始认真思考一个问题:在一个有大量历史包袱的真实项目上,怎么把 Coding Agent 跑成"团队的人",而不是"外包来的临时工"?

第一刀:上下文不是"喂得越多越好"

我最早犯的错误是直接把 README、架构文档、项目规范全塞进 system prompt,觉得"上下文越全越好"。结果 Claude 反而越来越笨——它会莫名其妙地引用文档里某条早就废弃的规范,或者忽略当前任务最关键的那个约束。

不要把上下文当"百度网盘"

我后来反过来做:把项目知识切片成按需注入的小模块,每一片都有明确的触发条件。在仓库根目录放一个 .agent/ 目录,结构长这样:

📁 .agent/ 📄 conventions.md — 通用规范(必读) 📁 layers/ 📄 network.md — 网络层用什么 📄 image.md — 图片加载用什么 📄 di.md — 依赖注入怎么写 📄 navigation.md — 跳转路由方案 📁 forbidden/ 📄 deprecated_apis.md — 禁用清单 📁 examples/ 📄 viewmodel.kt — 标准 ViewModel 📄 repository.kt — 标准 Repository

关键的诀窍是:每个 .md 控制在 80 行以内,开头一句话写明什么时候要读这个文件。比如:

代码语言:javascript
复制
# network.md
# 触发条件: 涉及 HTTP/接口/请求/
#   下载/上传 时必读## 唯一允许的方式// 通过 NetClient 走全局拦截链
val api = NetClient.create(
BillApi::class.java
)
val resp = api.fetchBill(id)## 严禁直接
- new Retrofit.Builder()
- OkHttpClient() 自建实例
- HttpURLConnection## 错误码统一处理
- 401: ApiException.Auth
- 5xx: ApiException.Server
- 业务错: ApiException.Biz

每个文件都是"做什么 / 不做什么 / 标准例子"三段式。Agent 会自己在需要的时候去 read 这些文件,不需要我塞进系统提示。

CLAUDE.md 是路标,不是百科全书

仓库根目录的 CLAUDE.md 我只写一件事:路标。把 Agent 引导到对的文档,而不是直接展开知识。整个文件不超过 50 行,长这样:

代码语言:javascript
复制
# 项目导航 (CLAUDE.md)## 你必须先读这个
.agent/conventions.md## 按需读
- 改网络: .agent/layers/
network.md
- 改图片加载: .agent/layers/
image.md
- 改 DI 注入:
.agent/layers/di.md
- 写 ViewModel: 参考
.agent/examples/
viewmodel.kt
- 涉及废弃 API: 必查
.agent/forbidden/
deprecated_apis.md## 改完代码必须运行
./gradlew
:feature:bill:lint
./gradlew
:feature:bill:test## 提交前必查
./scripts/
agent_precheck.sh

这个改动让 Agent 的 token 消耗下降了大约 40%,同时输出质量反而提升了。原因很朴素:少即是多,给它一个清晰的"找东西的地图",比把整本书摊在它面前管用得多。

第二刀:禁用清单是隐形武器

老项目最大的特点是:有大量"语法上完全合法、但团队约定不能用"的 API。这些东西光靠 prompt 提醒是没用的,Agent 写着写着就忘了。

我的做法是把这些规则编码成可执行的检查脚本。在 .agent/forbidden/deprecated_apis.md 里同时维护两份内容:

一份是给 Agent 看的语义说明

代码语言:javascript
复制
## 严禁使用清单### 网络
- ❌ Retrofit.Builder() 直接 new
  → ✅ 改用 NetClient.create()
  原因: 绕过了埋点和重试链### 图片
- ❌ Glide.with(ctx).load(url)
  → ✅ 改用 ImageLoader.load(url)
  原因: Glide 已下线,2024Q3### 异步
- ❌ Observable / Subscriber
  → ✅ 改用 Flow + suspend
  原因: RxJava 在新代码禁止### 跳转
- ❌ Intent + ComponentName
  → ✅ 改用 Router.go(uri)
  原因: 模块化路由统一管理

另一份是给 CI 看的正则规则,放在 .agent/forbidden/lint.yaml

代码语言:javascript
复制
rules:
- id: no_retrofit_builder
pattern: "Retrofit\\.Builder\\("
message: "用 NetClient.create"
severity: error- id: no_glide
pattern: "Glide\\.with\\("
message: "用 ImageLoader.load"
severity: error- id: no_rxjava
pattern:
"io\\.reactivex\\."
message: "新代码用 Flow"
severity: error- id: no_handler_postdelay
pattern:
"Handler\\(\\)\\.postDelayed"
message: "用 lifecycleScope"
severity: warn

脚本本身用 Python 写了 80 行,每次 Agent 执行 commit 之前必跑:

代码语言:javascript
复制
# scripts/agent_precheck.py
import re, yaml, sys
from pathlib import Pathdef scan(rules, files):
errors = []
for f in files:
text = f.read_text()
for r in rules:
if re.search(
r["pattern"], text
):
errors.append((
f, r["id"],
r["message"]
))
return errorsrules = yaml.safe_load(
Path(
".agent/forbidden/lint.yaml"
).read_text()
)["rules"]changed = [
Path(p) for p in sys.argv[1:]
if p.endswith(
(".kt", ".java")
)
]errs = scan(rules, changed)
if errs:
for f, rid, msg in errs:
print(
f"::error::{f}: "
f"[{rid}] {msg}"
)
sys.exit(1)

这一招的奇效在于:Agent 在执行 commit 时如果触发 precheck 失败,它会自动读取报错信息、修正代码、重新提交。整个过程不需要我介入。一开始我担心它会陷入死循环,实际跑下来 95% 的情况它能在两轮内自我修正完成,剩下 5% 是真的踩到架构问题,会主动停下来问我。

第三刀:Diff 评审 Agent —— 让 AI 看 AI 写的代码

前两刀解决"写得对"的问题,第三刀解决"写得好"的问题。

我在 PR 流程上加了一个独立的评审 Agent,它和写代码的 Agent 用的是不同的 system prompt 甚至不同的模型(实测 Claude Opus 写、Sonnet 评审,效果比同一模型自评好不少)。它做三件事:

PR 触发

评审 Agent 启动

检查 1 → Diff 是否符合 .agent/conventions.md

检查 2 → 是否触及 .agent/forbidden/ 列表

检查 3 → 给出"如果是我会怎么写"的建议

输出 review.md

阻断条件 → 出现 severity=blocker 直接拒绝合并

评审 Prompt 怎么写

评审 Agent 的 system prompt 是整个流程的灵魂,我打磨了大概十几个版本。最关键的一点是:不要让它自由发挥,要给它一个明确的输出 schema。

代码语言:javascript
复制
# 评审 Agent System Prompt你是这个 Android 项目的资深评审者。
你只做评审,不写新代码。## 评审范围
仅看 PR diff,不评审历史代码。
不在你 diff 里的问题不要管。## 输出严格 JSON
{
"summary": "一句话概括",
"issues": [
{
"file": "path",
"line": 42,
"severity":
"blocker|major|minor",
"category":
"convention|"
"forbidden|design",
"reason": "…",
"suggestion": "…"
}
]
}## blocker 触发条件
- 命中 forbidden 清单
- 引入新的网络/图片/路由
实例(绕过统一封装)
- 公共 API 改动但无单测
- main thread 跑 IO## 不要做的
- 不评论代码风格(lint 管)
- 不要求改动超出 diff 范围
- 不臆测意图,只看代码

关键设计:

1) 限定评审范围在 diff 内。你不告诉它,它就会顺着 import 去看历史代码,然后开始批评一些根本不是这个 PR 引入的问题。这种"附带伤害"会把 PR 评论搞得乱七八糟,作者收到一堆不相关的建议,体验很差。

2) JSON 输出。结构化输出能直接喂给后续 GitHub/Gitlab API 转成 inline comment,体验和真人评审一样。

3) 明确 blocker 边界。不要让模型自己判断什么严重什么不严重,它会乱来。把"什么情况必须 block"写成显式规则,剩下的都按 minor 处理。

真实数据

这套流程跑了三个月,统计了 247 个由 Agent 主笔的 PR,几个数据:

指标

改造前

改造后

一次合入率

23%

71%

触发 forbidden 规则次数

每 PR 平均 3.2

每 PR 平均 0.4

人工 review 用时

平均 18min

平均 6min

单 PR token 消耗

~120K

~70K

最让我意外的不是一次合入率,而是token 消耗下降。最初我以为 .agent/ 目录会让 Agent 多读很多文件,token 会涨;实际效果是它不再瞎逛代码库,路径从 5-8 跳的 random walk 变成 2-3 跳的精准定位,反而省了。

第四刀:让 Agent "做饭",不让它"端菜"

这一刀比较反直觉,我也是最近才想明白。

很多团队用 Coding Agent 的姿势是:Agent 写代码 → 人提交 → 人沟通 → 人合并。Agent 只负责"做",人负责"端"。

我反过来:Agent 写代码 + Agent 写 commit message + Agent 写 PR 描述 + Agent 自动回复评审意见。人只在两个时间点介入:定需求、合并。

commit message 模板化

commit message 我直接给一个 prompt 模板:

代码语言:javascript
复制
# commit message 模板<type>(<scope>): <subject><body, 不超过 5 行># 必填字段
- 改动原因(1 行)
- 影响范围(1 行)
- 是否需要回归测试# 例
feat(bill): 增加账单详情页- 原因: 需求 #BILL-1029
- 范围: feature/bill 模块
- 回归: 账单列表跳详情链路Co-authored-by:
Claude
<agent@team.local>

注意最后那个 Co-authored-by: Claude。这是 Git 标准字段,所有 Agent 写的提交我都要求带上。这事儿不是为了让 Claude 拿"功劳",而是为了溯源——三个月后某段代码出问题,我可以一行 grep 立刻知道:哦,这个是某个时期某版本 Agent 写的,结合那时候我的 prompt 模板、规则版本,能很快还原现场。

PR 描述里必须写"我没动什么"

这个习惯是我从一次事故里学到的。Agent 改了一个"看起来无关"的工具类,结果联调时全模块挂掉。复盘发现,它改这个工具类的原因是"代码风格统一"——是的,它觉得那个工具类风格不一致,"顺手"改了。

从那以后我在 PR 模板里强制要求一段:

⚠️ Out-of-scope changes

列出所有不是为了核心需求而改动的文件(重构、风格统一、注释补充等)。如果没有,写"none"。

这一段是强制 Agent 自我审视。Agent 在写这段的时候,往往会自己意识到:"咦我这次改得有点多了",然后回头把范围收窄。这就是经典的"外化的元认知"——把模型本来想偷懒省略的反思过程,强行 surface 出来。

几个反直觉的经验

1) 越老的项目,越值得上 Agent

很多人觉得 AI 适合写绿地项目(greenfield),祖传代码不合适。我的体会恰好相反:祖传代码才是 AI 的主场。原因是新项目没什么积累,规则少,AI 没什么发挥空间;老项目规则多、坑多、模式固定,反而非常适合让 AI 去"按规矩办事"。

人类工程师面对祖传代码常见的状态是沮丧+敷衍,不愿意去搞清楚那一堆历史包袱;AI 没有这种情绪,你给它一份禁用清单,它就老老实实按清单办。

2) Agent 的 "记忆"是负担,不是资产

这点和现在很多"长期记忆"产品的方向反着来,但我跑下来的真实感受就是:Coding Agent 的长上下文记忆,往往是污染源。它会记住你三天前那个早就废弃的方案,会反复问已经回答过的问题。

我现在的工作流是:每个任务开新会话,让 Agent 从 .agent/ 文件读一手信息,而不是依赖会话间的"记忆"。Stateless 的 Agent 反而更可靠。如果一定要持久化什么,持久化文件,不要持久化对话

3) 不要让 Agent 写"通用性代码"

Agent 一写"通用工具类"就容易过度设计——"为了将来扩展,我加了这个 sealed class…为了支持多类型,我做了 generic…"。停下来。

老项目里 90% 的代码不需要"未来扩展",需要的是能跑、能改、出 bug 时能看懂。我现在在 prompt 里加了一条:

不要为"将来可能的需求"写代码。只为当前需求写最小实现。如果将来真要扩展,那时候再重构一次比现在过度设计便宜十倍。

这条加上去以后,PR 平均行数下降了 30%,bug 率下降了 40%。

写在最后:工具是放大器

三个月跑下来我最大的感受是:Coding Agent 不是"另一个工程师",它是团队规范的放大器

如果你的团队规范是糊的,Agent 写出来的代码就是更大规模的糊;如果你的规范是清晰的、可执行的、有禁用清单和正向例子的,Agent 就能产出大量符合标准的代码,把工程师释放出来去做真正需要判断力的事。

工具的意义从来不是"代替你思考",而是"逼你把那些口耳相传的隐性规则,写成显性、可执行的规则"。这个过程本身就是团队工程能力的提升。

说回这周的新闻,Anthropic 估值反超 OpenAI、Nvidia 把 Vera 定位成"Agent 专用 CPU"——这两件事其实在说同一个趋势:行业在押注Coding Agent 不是临时玩具,而是接下来三五年开发流的核心设施。我们这些一线工程师能做的,是把自己的项目改造成一个"对 Agent 友好"的环境,让工具能真的帮上忙,而不是浪费在 fight 老代码上。

下一篇我会写《Android WebView 深度探索》系列的最后一篇——WebView 性能优化与稳定性治理:预热、复用池与崩溃防护,把整个系列收尾,敬请期待。

—— 一个还在和老代码搏斗的 Android 开发者

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-06-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 陆业聪 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档