Advanced Context Engineering for Coding Agents 不是一个可以安装运行的工具,而是 HumanLayer 团队持续更新的一组文章、实验和方法论。
这些材料试图回答一个很现实的问题:
为什么 Coding Agent 在新项目和小改动上表现很好,一进入大型、已有代码库,就容易写出方向错误、难以维护的代码?
作者最初把答案归结为上下文管理,并提出了“频繁且有意地压缩上下文”。随着实践深入,他又进一步意识到:上下文工程只能提高 Agent 的表现,不能消除模型在系统设计和长期维护上的局限。要真正把 Agent 用在复杂工程中,人仍然需要参与需求、架构、程序设计和实现节奏的判断。
Table of contents
Open Table of contents
从“模型不够聪明”转向“上下文没有组织好”
Coding Agent 可以把每次行动理解成一个近似无状态的过程:
当前上下文 → 模型推理 → 下一步行动
在不重新训练模型的前提下,我们能直接控制的,主要就是输入给模型的上下文。
复杂任务的问题是,上下文很快会被各种中间信息占满:
- 搜索文件的过程;
- 读取大量代码;
- 追踪调用链;
- 修改文件产生的 diff;
- 测试与构建日志;
- 工具返回的大段 JSON;
- 已经失败的尝试。
这些内容在当时可能有用,却不一定都值得长期保留。如果一直在同一个会话里边搜索、边讨论、边修改,真正重要的信息会逐渐被噪声淹没。
作者认为,一个好的上下文应同时满足四个条件:
- 正确:没有建立在错误理解之上。
- 完整:包含完成当前阶段所需的信息。
- 精简:不被无关搜索记录和日志占满。
- 方向一致:仍然沿着正确的目标推进。
其中,错误信息的危害最大,其次是缺失信息,最后才是上下文过长。单纯减少 token 并不是目的,真正的目标是提高上下文中的有效信息密度。
频繁且有意地压缩上下文
很多人已经在无意中使用“上下文压缩”:当对话变长或 Agent 开始偏离方向时,让它把当前进展写入 progress.md,然后开启一个新会话继续工作。
HumanLayer 把这个行为进一步变成了完整的工作流,称为 Frequent Intentional Compaction。
它不是等上下文快用完时才总结一次,而是主动把复杂任务拆成多个阶段,让每个阶段都产生结构化的 Markdown 工件:
Research → Plan → Implement
每个阶段都使用相对干净的上下文,上一阶段的结论经过压缩和人工确认后,再作为下一阶段的输入。
作者给出的经验值是:处理复杂任务时,尽量让上下文使用率保持在约 40%–60%,不要等到窗口接近极限才开始整理。这个数字不是硬性规则,它表达的是一种工作习惯:在上下文质量开始下降前主动切换阶段。
Research:先理解代码,再决定怎么改
Research 阶段不写实现代码,重点是把问题放回现有系统中理解。
一份有效的研究文档至少应该说明:
- 问题发生在哪条调用链上;
- 哪些文件和模块与问题有关;
- 数据如何进入、转换和输出;
- 当前行为为什么会出现;
- 代码库已经使用了哪些设计模式;
- 已有测试如何覆盖相关功能;
- 哪些看似可行的修改会破坏现有约定。
这个阶段可以大量使用子 Agent。子 Agent 的价值不是扮演“架构师”“测试工程师”等角色,而是使用独立的上下文完成搜索、阅读和总结,再把结论返回给主 Agent。
这样,主上下文不需要保存所有 grep、文件读取和试错过程,只需要保留经过整理的系统理解。
研究阶段也最容易产生高杠杆错误。错误地理解一个函数,只会影响一个局部判断;错误地理解整个系统的边界,则可能让后续计划和实现全部建立在错误方向上。
因此,研究文档不能直接当作真相。人需要检查它是否遗漏关键路径、是否误读现有行为,以及问题是否真的应该在 Agent 建议的位置解决。
Plan:把隐含的实现决定提前暴露
计划不是简单列出“修改后端、补充测试、更新文档”。真正有用的计划应该具体到:
- 修改哪些文件;
- 每个文件为什么需要修改;
- 新增或调整哪些类型与接口;
- 调用关系会发生什么变化;
- 每一步完成后如何验证;
- 哪些回归风险需要测试覆盖。
研究之后再做计划,与直接让 Agent 根据需求生成计划,往往会得到明显不同的结果。
没有研究支撑的计划可能也能“修好问题”,但更容易选择错误的修改层级,或者使用不符合代码库惯例的测试方式。基于研究的计划更有机会在正确的模块解决问题,并复用已有抽象。
计划的另一个作用,是把原本要到代码审查阶段才发现的分歧提前暴露出来。文件结构、函数边界、错误处理方式和测试策略,在计划阶段调整只需要修改几段文字;等 Agent 已经生成几千行代码,再改变这些决定,成本会高得多。
Implement:按阶段实现,而不是一次生成全部代码
实现阶段按照计划逐步推进,每完成一个阶段就运行对应验证。
对于复杂任务,可以在每个阶段完成后,把当前状态重新压缩回计划文件:
计划中的阶段
→ 实现
→ 测试
→ 人工检查
→ 更新状态
→ 开启下一阶段
这样,即使中途更换会话或 Agent,也不需要依赖已经膨胀的聊天历史恢复现场。
只有实现阶段需要独立 worktree。研究和计划主要产生 Markdown 文档,可以在主分支上完成;真正修改代码时,再用 worktree 隔离并行方案或不同实现。
人应该审查哪里
这套方法最重要的观点,并不是必须遵循 Research、Plan、Implement 三个固定步骤,而是重新安排人的注意力。
一条错误代码通常只造成局部问题,但一条错误计划可能生成几百行错误代码;一条错误研究结论,则可能让整个实现方向偏离。
错误的系统理解
↓
错误的实现计划
↓
大量方向一致但整体错误的代码
因此,人应该优先检查杠杆最高的环节:
- Agent 是否真正理解了用户问题?
- 是否找到正确的系统边界?
- 修改是否发生在合理层级?
- 接口和数据模型是否清晰?
- 测试能否验证真实行为?
- 实现顺序是否允许尽早发现错误?
作者认为,认真审查 200 行研究和计划,往往比最后艰难地阅读 2,000 行 AI 代码更有效。
这并不意味着代码审查不再重要。代码审查除了发现错误,还承担团队认知同步的作用:让成员知道系统发生了什么变化,以及为什么这样变化。AI 提高代码产量之后,这种认知同步反而更加重要。
后来的反思:上下文工程还不够
在后续文章 Why Software Factories Fail 中,作者开始反思一种更激进的设想:
只要增加足够多的 Agent 循环、自动评审、测试和 lint,就可以让软件工厂在无人监督的情况下持续工作。
问题在于,测试很擅长回答“程序有没有满足某个可观察行为”,却不擅长判断:
- 系统边界是否合理;
- 抽象是否便于未来扩展;
- 一次修改是否会传播到过多模块;
- 代码是否正在积累重复和隐性耦合;
- 下一位工程师能否轻松理解和修改它。
测试结果通常几秒就能得到,而糟糕架构的代价可能几个月以后才会出现。那时,一个原本只需要修改一处的需求,可能不得不同时调整十几个文件。
自动评审和更多 token 可以提高下限,抓住明显错误,但不一定能提高系统设计的上限。可维护性暂时没有一个快速、可靠、可以大规模用于训练和验证的判定器。
因此,作者后来把早期的 Research、Plan、Implement 扩展成四个更贴近工程决策的阶段:
- Product Requirements;
- System Architecture;
- Program Design;
- Vertical Slices。
Product Requirements:先确认为什么要做
产品审查需要固定两个问题:
- 用户真正遇到了什么问题?
- 上线后如何判断这件事值得做?
成功标准最好是用户结果,例如完成某个流程所需时间缩短,或者某类支持工单明显减少;也可以是错误率、延迟等技术指标。
这个阶段应尽量停留在用户体验层面,不要过早陷入实现细节。对于界面功能,粗糙但可交互的 HTML 原型,常常比几段抽象描述更容易让团队达成一致。
并非所有任务都需要产品审查。文案调整、一次性脚本和复现明确的小 bug,可以直接交给 Agent。完整流程适用于理解错误代价较高的任务。
System Architecture:明确系统边界和数据流
架构阶段描述功能如何进入现有系统:
- 哪些组件参与;
- 请求和事件如何流动;
- 接口契约是什么;
- 数据模型怎样变化;
- 哪些服务拥有相应职责。
流程图、时序图和接口草案可以帮助对齐,但架构正确并不等于实现代码自然会正确。架构图通常停留在模块层面,模型仍然可能在模块内部生成难以维护的程序结构。
Program Design:深入到代码形状
Program Design 是作者认为最容易被忽略的一层。
在真正写代码前,还需要继续明确:
- 文件树会怎样变化;
- 新增哪些类型;
- 关键方法的签名是什么;
- 调用栈如何变化;
- 控制流从哪里进入;
- 错误在哪一层转换和处理。
作者推荐使用轻量的伪代码,而不是制作过度精细的图。
调用链可以写成:
entrypoint
runCommand
+ handleCreateResource
+ ResourceClient.create(input)
+ POST /resources
+ renderResult
- legacyCreateFlow
文件结构可以写成:
src
└── resource
├── resource-client.ts # 新增:封装接口调用
├── resource-client.test.ts # 新增:验证数据映射
└── resource-route.ts # 修改:接入创建流程
类型和函数签名也应该提前写清楚。它们不需要成为完整代码,但应该暴露最重要的程序设计决定。
这些内容由 Agent 起草,人负责讨论和修正。很多原本要在代码审查时隐式做出的判断,可以在写代码前以更低成本完成。
Vertical Slices:用可运行的切片推进
Coding Agent 很容易生成横向计划:
数据库迁移
→ Service
→ API
→ 前端
→ 最后统一测试
这种方式在很长一段时间里都无法真正体验功能。如果早期方向错误,等到能够运行时,可能已经生成大量代码。
纵向切片更强调尽快打通一个可以观察的最小路径:
先定义 API,并返回 Mock 数据
→ 用 curl 验证
→ 前端接入 Mock API,并在浏览器中调整
→ 接入 Service
→ 接入数据库
→ 补充业务规则和异常处理
每个阶段都有可以触摸和验证的结果。一次只让 Agent 完成一到三个切片,检查 100–200 行代码后及时纠偏,比最后面对一个数千行的大型 PR 更可控。
这与传统工程中的 tracer bullet 思路接近:先打通一条真实路径,再逐步扩展宽度和完整性。
Benchmark 暴露出的长期维护问题
仓库还记录了使用 SlopCodeBench 评估 Coding Agent 的实验。
传统 benchmark 往往一次性给出完整需求,而 SlopCodeBench 会分多个 checkpoint 逐渐增加要求。模型需要持续修改同一个代码库,并保证前面已经实现的行为不发生回归。这种形式更接近真实软件维护。
作者在一个包含 17 个 checkpoint 的小规模实验中观察到:
- Opus 5 严格通过 4 个 checkpoint;
- Opus 4.8 和 Sonnet 5 分别通过 1 个;
- 没有模型无缺陷地完成任意一个完整挑战;
- 随着需求继续增加,代码量、复杂度和重复普遍上升。
这个实验样本很小,不能据此全面评价模型能力,但它提供了一个方向性信号:模型可以在单次任务中表现很好,却可能在连续需求下不断积累早期设计缺陷。
更值得注意的是,代码复杂度、函数数量和 lint 规则只能作为代理指标。它们可以重复计算,却不能单独回答“这个代码库未来是否容易演进”。这也是完全无人软件工厂当前最难解决的问题之一。
一套更适合实际使用的流程
综合这些文章,可以得到一套按任务复杂度分级的工作方式。
小任务
适用于文案调整、明确的小 bug、一次性脚本:
定位问题 → 修改 → 测试
不需要为了流程而制造文档。
中等任务
涉及多个文件,但系统边界相对清楚:
简短研究
→ 一份包含系统设计和实现步骤的计划
→ 分阶段实现与验证
大型任务
涉及新功能、核心重构、并发、性能或跨模块变化:
产品目标
→ 代码库研究
→ 系统架构
→ 程序设计
→ 纵向切片计划
→ 分阶段实现
→ 每阶段测试和人工审查
每完成一个阶段,就把经过确认的结论压缩成下一阶段需要的上下文。不要让下一阶段重新经历全部搜索过程,也不要把未经验证的猜测当作事实继续传递。
这套方法真正解决了什么
Advanced Context Engineering 并不是一组“神奇提示词”,也不能保证 Agent 自动解决所有复杂任务。
它解决的是三个更具体的问题:
1. 控制上下文质量
让每个阶段只保留完成当前工作所需的高密度信息,减少历史搜索、日志和失败尝试带来的噪声。
2. 把人工判断放在高杠杆位置
人在系统理解、需求、架构和程序设计阶段纠正一次,可以避免后续大量返工。
3. 让错误尽早暴露
通过纵向切片和阶段性验证,不让 Agent 一次生成几千行代码以后才第一次运行系统。
它最终追求的不是让 Agent 写出更多代码,而是让人和 Agent 形成一种更可靠的工程协作方式:
人负责目标、边界、判断和验收;Agent 负责搜索、整理、生成和执行。上下文则是两者之间最重要的接口。