上下文工程与Agent Harness:让编码代理从建议到闭环干活

发布时间:2026/10/28 5:29:48
上下文工程与Agent Harness:让编码代理从建议到闭环干活

上个月我把团队里的AI编码代理从“聊天窗口加粘贴代码”改成“自主执行加闭环验证”的运行模式,同一批任务完成率从不到五成升到接近九成,单任务平均耗时下降了差不多三分之二。变化的不是底层模型,而是我补上了两样东西:Context Engineering(上下文工程)和 Agent Harness(智能体运行框架)。如果你正在用 Coding Agent 写业务代码,却总觉得它像个“聪明但毛躁的新人”——经常答非所问、改了一处弄坏两处,那这篇就是我踩完坑之后的完整落地清单。

1. 为什么同一个编码代理,有人做到10倍速,有人不如自己手写

先说个观察。很多团队用同一个模型、同一个 IDE 插件,效果却天差地别。一部分人把编码代理当成“高级自动补全”,写一段贴一段,代码还是要自己理解、自己调试;另一部分人把它当成能独立接任务的实习生,交代清楚背景、给出工具、要它跑测试直到绿灯,然后人只做 review。这两种用法,产出差出 3 到 5 倍很正常,10 倍也见过——尤其当后者之前完全靠手写时。

差距不在模型推理能力,而在两件基础工程上。

第一是上下文输入的工程化。编码代理的全部判断依据,就是你在对话窗口里塞进去的那些文本。你喂给它什么,它就只能在这个信息范围内做决策。大多数项目仓库几万到几十万行代码,散落着 README、接口文档、测试用例、历史决策记录。如果不经过结构化处理,Agent 就处于“盲人摸象”状态:它可能只看了一个模块就动手改,改了之后发现依赖链路全断。这就是 Context Engineering 的用武之地——它不是简单地把仓库文件拖进对话,而是通过分层、检索、按需展开,让模型在执行每一步时都恰好看到最相关的信息。

第二是执行回路的工程化。很多人让 Agent 生成了代码就结束,自己复制、粘贴、跑测试、修错。一旦测试挂了,再手动把报错信息贴回去,让 Agent 改。这个循环其实可以做进系统里,这就是 Agent Harness 的核心价值。Harness 解决的是“模型输出之后怎么办”:代码有没有真的跑起来?测试有没有通过?报错是否回传?是否需要重新规划?如果没有这一层,Agent 再聪明也只是一个文本生成器,永远无法自我纠错。

用个类比:上下文工程是给驾驶员一张精确的地图,Harness 是方向盘、油门、刹车、仪表盘。地图再好,车里没有仪表盘,你也不知道发动机转速爆没爆表;仪表盘再全,没有地图,你也只能在路上乱绕。

所以我的结论很直接:所谓 10x 革命,不是模型突然进化了,而是把“上下文输入质量”和“执行闭环完整性”这两套基础做对了之后,必然出现的结果。下面我把两套基础分别拆开讲。

2. Context Engineering:把“垃圾进垃圾出”变成“指哪打哪”

2.1 上下文不是“塞得多”,而是“选得准”

我做上下文工程后的第一个认知转变,就是抛弃“把整个仓库都装进窗口”的思路。大模型上下文窗口现在虽然宽,但那不等于可以乱塞。相关性越杂,注意力被稀释得越厉害,最后模型会自己“总结式失忆”——它甚至会为了迁就窗口长度,把关键接口定义给忘记。

我一般会把上下文分成四个层次来组织:

  • 第一层,蓝图层。包括仓库顶层 README、架构说明文档、目录结构、构建与测试命令。用途是让 Agent 在动手前知道这个仓库长什么样,模块之间大致什么关系。对应到人,就像入职第一天的公司架构介绍。
  • 第二层,知识层。包括代码索引:函数名、类名、导入关系、调用关系、类型定义。用途是让 Agent 能够回答“这个函数谁在调”“这个接口返回什么结构”。这一层通常不直接全部注入,而是做成可检索的资源。
  • 第三层,任务切片层。针对当前需求抽取出来的文件集合、数据流、接口契约。这是每次任务最核心的输入,也是多数人最缺的一层。
  • 第四层,约束层。编码规范、禁止事项、历史架构决策记录(ADR)。这一层决定 Agent 产出的代码风格和边界是否符合团队预期。

举个例子。我在一个支付模块里加回调接口,不会把整个支付的几万个文件都丢给 Agent。蓝图层告诉它支付模块在哪、测试入口是什么;知识层让它能查到 PaymentService 被谁调用;切片层给出回调接口的入参出参定义、涉及的三四个文件;约束层写入“支付金额一律用分为单位,禁止浮点运算”。Agent 看到的是精准切片,而不是一锅乱炖。

2.2 渐进式上下文展开:像人一样按需读代码

第二个关键是渐进式展开。我一开始尝试把所有相关内容一次性注入,后来发现效果反而差。原因很简单:Agent 的注意力像探照灯,照得越广,单点分辨率越低。

现在我的做法是:给 Agent 一组探索工具,让它像人一样按需取用。开场只注入地图级的摘要,也就是蓝图层和任务说明。当 Agent 需要深入了解某个文件,就通过工具请求读取;Harness 检索到文件时,先给摘要和关键签名;Agent 明确要求打开某个文件,才注入完整内容。这样上下文窗口始终被“当前最需要的”占据,而不是被“可能用得上的”塞满。

这种方法还有个额外的好处:token 成本显著下降。我改造前平均每个任务要烧掉 10 万 token,里面有大量用不上的全文代码;改造后大概在 5 万到 6 万之间,因为每一段进窗口的内容都经过了检索层过滤。

2.3 检索质量决定天花板:混合检索的三条召回路径

很多人以为在项目里跑个关键词搜索就算上下文工程了,这是最大的误区。关键词检索只能命中字面匹配,对“语义相似但表达不同”的内容完全无能为力。比如你要 Agent 改“订单超时取消逻辑”,关键词搜索可能只命中“timeout”,却漏掉“expire”“TTL”“自动关单”。

我实际跑下来比较稳的方案是混合检索,三路召回合并:

  • 路径表达式匹配。比如用户任务提到“payments/gateway”,就命中路径名包含这两个词的文件。
  • 符号引用索引。通过 ctags 或类似工具生成函数、类、常量索引,任务涉及 PaymentService 时能直接找到定义和所有调用点。
  • 语义向量检索。用 embedding 模型把代码注释、函数签名、文档片段向量化,支持“意思相近但字面不同”的查询。

三路结果合并之后,还要按依赖拓扑排序。什么意思?就是一个被三十处调用的公共函数,比一个只在单文件出现的私有函数重要得多。如果 Agent 要修改某个接口签名,它必须知道所有调用方,而不仅仅是定义本身。把检索结果按“定义在前、调用方列表在后”的顺序给出,Agent 理解上下文的速度会快很多。

2.4 把约束固化进上下文:项目宪法与会话记忆

上下文工程里最容易忽略的,是那种“团队里人人都知道、但没人写下来”的隐性规则。Agent 从对话里学不到这些,所以你必须把它们变成显式文本,每次会话自动注入。我管这个文件叫项目宪法,放在仓库根目录,名字可以用 AGENTS.md、CODING_RULES.md 或者 CLAUDE.md,看你的工具链,愿意读哪个就用哪个。

写项目宪法有个关键经验:写成可验证的检查项,不要写散文。例如:

  • 所有金额字段使用厘/分整数存储,禁止用浮点数。
  • 对外 API 必须提供 OpenAPI schema,否则不合并。
  • 数据库迁移统一用迁移工具生成,不允许手写原生 SQL。
  • 新增外部依赖必须写明理由并过审。

每一条都是 Agent 执行过程中能直接对照检查的,而不是“注意代码质量”这种正确的废话。条数我也控制,核心 15 到 25 条就够,超过 50 条反而稀释注意力。

会话记忆则是另一种约束。我的做法是在仓库里维护一份 MEMORY.md,记录跨会话的关键决策,比如“2025年6月决定用户体系从自建转向某平台,原自建模块冻结,只做兼容层”。这样新会话开始时,Agent 即使没有完整历史,也能通过这份文件快速对齐团队的最新状态,避免它基于几个月前的旧代码结构来规划方案。

3. Agent Harness:让智能体从“只会提建议”变成“闭环干活”

3.1 先分清概念:Agent 是大脑,Harness 是躯体

如果你最近在技术社区里频繁刷到 Agent Harness 这个词,可能会困惑它和 Agent 到底是什么关系。这里我把边界讲清楚。

Agent 本身负责的是“决策”:它根据当前上下文判断下一步做什么,是继续读文件,还是修改代码,还是总结结论。但 Agent 自己不能执行命令,不能跑测试,不能验证结果,也不存在“时间”概念——它的一次响应就是一次响应,结束之后世界没有变化。

Harness 就是把这个决策包在里面的“运行环境与控制系统”。它负责:

  • 提供 Agent 可调用的工具,比如读写文件、执行测试、搜索符号。
  • 把执行结果回传给 Agent,让它能看到自己的动作导致的结果。
  • 控制自主循环,否则 Agent 永远是一问一答,不会自己修 bug。
  • 实施安全策略,防止 Agent 乱来——在你没盯着的时候。

“Harness”这个词的英文本义是马具,引申为“驾驭、控制”。网络热词里那句“Agent Harness:驾驭 AI Agent”说得挺准确。没有 Harness,Agent 就是脱缰的野马,再聪明也只是嘴强王者;套上 Harness,它才是一匹能干农活的马。

我用一个直观的对比表格来归纳:

职责AgentHarness
理解任务、制定计划负责不负责
决定下一步调用哪个搜索动作负责不负责
提供可调用的工具接口不负责负责
执行命令、运行测试不负责负责
把报错输出、测试结果收集回来不负责负责
判断是否达到终点或需要暂停不负责,发出信号负责执行判断与终止
守住权限边界,防止危险操作不负责负责

很多团队把预算全花在“升级到更聪明的模型”,却对 Harness 侧一片空白,结果换模型之后依然没有反馈回路,Agent 每次生成的代码是否正确,还是要人肉验证。这就像买了一台好发动机,却只放在桌上展示,没装进车里。

3.2 Harness 的六大核心模块拆解

一个能真正干活的 Agent Harness,至少要包含六大模块。我按重要性排序:

第一,执行沙箱。Agent 要跑命令,就必须被关在可控环境里。我推荐用容器或虚拟机,限定可读目录、禁止对生产环境的网络访问、限制 CPU 和内存。沙箱的目的不是防恶意攻击,而是防“善意但鲁莽的错误”。在沙箱里跑错了,最多浪费几分钟;在真实环境里跑错了,可能改掉生产配置。

第二,工具挂载层。把环境能力暴露成 Agent 可理解、可调用的结构化接口。每一个工具都要有清晰的参数定义和返回格式。比如:

  • read_file(file_path, start_line, end_line):读取指定范围代码。
  • grep(pattern, path):按正则搜索。
  • find_references(symbol):查找符号引用。
  • run_command(command, timeout):执行命令。
  • run_test(target):运行指定测试。

Agent 不会像人一样“看见文件夹”,它只能通过工具来感知世界。工具设计得越贴近人直觉,Agent 的探索效率越高。

第三,日志回读。这个模块看着简单,实际上决定 Agent 能不能自我修复。它也跑完了测试,但如果测试输出没有回传给模型,它就永远不知道自己错了。我的经验是把 stdout、stderr、退出码、关键测试失败摘要全部结构化回传,并明确告诉 Agent 当前处于循环的第几步。

第四,循环控制器。这是 Harness 与普通工具链最大的区别。Harness 要支持 Agent 自主进行多轮“探索-修改-测试-修复”,而不是每跑一轮就停下来等人。循环控制器要设定最大迭代次数,比如 5 轮,还要设置暂停条件:如果同一类错误连续出现 3 次,强制暂停并请求人工介入,不能让它原地打转。

第五,策略层。给 Agent 的每个能力配置允许或禁止规则。我会在下一节给出具体的清单配置。策略层的存在,本质上是你把团队的工程纪律变成一个可强制执行的边界。

第六,可观测性指标。记录每次操作的 token 消耗、命令执行时长、成功率、循环轮数。没有这些指标,你永远不知道改变检索策略是变好了还是变差了。这是持续优化 Harness 的地基。

3.3 闭环如何真正消除幻觉:从写代码到跑测试再到修 bug

为什么 Agent 会产出看起来合理、实际假到离谱的代码?因为语言模型只是预测“像什么”,它不知道你的环境里有没有那个依赖、那条路由、那个库版本。唯一能让幻觉暴露的办法,是让它立刻面对现实反馈。测试就是最硬核的现实。

我做的最小闭环是典型的七步流程:

  1. 用户下达任务,Harness 加载项目宪法、检索相关上下文,生成任务概要。
  2. Agent 基于上下文规划修改点,输出修改方案。
  3. 由人工或策略层确认方案后,Agent 生成具体 patch。
  4. Harness 把 patch 应用到一个干净的临时工作副本。
  5. Harness 自动执行代码检查与测试:lint、类型检查、单元测试、关键集成测试。
  6. 测试输出、覆盖率、报错堆栈全部回传给 Agent。
  7. Agent 分析失败原因,修正方案,回到第 3 步,直到测试通过或达到轮数上限。

我亲测过这样一个修复场景:Agent 要在订单模块里新增一个错误码处理分支。它第一次写完,Harness 跑测试立刻报 ModuleNotFoundError,因为它把另一个模块的相对路径引错了。Agent 看到报错后把路径修正,测试又暴露一个新问题——它搞错了错误码的枚举取值。直到第三次修改,测试才全绿。整个过程大概两分多钟,如果换成人在循环,需要手动粘贴三次报错再等三次回复,至少十分种起步。

闭环的价值在于,Agent 的每一次“假设”都会迅速撞上现实。幻觉不是被提示词消除的,是被测试集和命令输出纠正的。

4. 从 0 到 1 落地最小 Harness:可直接照抄的工具、配置与权限清单

4.1 三种起步路径,按你的团队情况选

先泼一盆冷水:如果你只是想先跑通流程,不要从零自研一座平台。工具链已经很多了,正确顺序是从现成的开始,跑通后再考虑定制。

路径 A:直接用 CLI 型 Agent 工具。很多主流编码代理都提供命令行版本或自主模式,支持项目级配置。在仓库根目录放好项目宪法文件,在启动参数里指定一次,就有最基础的 Harness 效果。这条路适合个人和小团队,十分钟内可以体验闭环。

路径 B:在开源 Agent 框架上做策略层。如果你需要团队统一管理权限、审计日志、指标统计,可以基于开源框架搭建一个薄薄的封装层,拦截工具的调用入口,接入你们自己的策略中心。改动量不大,但能把“个人工具”变成“团队平台”。

路径 C:自研极简 Harness。仅当你需要深度定制,比如对接内部 RAG、控制安全策略、统计私有指标时,才建议走这条路。一个最小可用版本并不复杂:一个脚本程序在沙箱里起一个 Agent,定义六个左右的工具函数,写一个小循环控制器,几百行代码就能端到端跑通。我已经跑了一年,核心部分仍然只有一千行左右。

4.2 上下文工程的五件必做清单

无论你选哪条路,下面这五件事是真正影响效果的基础,每一项都别省:

第一,写一份项目宪法,15 到 25 条可验证规则。放在仓库根目录,格式要清晰,第一条规则要明确“每次任务开始前阅读本文件”。这就让 Agent 的每一轮行为都踩在规则内。

第二,生成一个仓库符号索引。用带引用的索引工具扫描一遍,哪怕每周更新一次也行,作为检索层的地基。没有索引,Agent 就是个只能全文搜索的低配版,速度慢且召回差。

第三,写一份架构文档,核心内容是模块目录职责表。比如:

  • src/payments:支付流水与网关适配。
  • src/order:下单与状态机。
  • src/user:用户体系与鉴权。

这份文档不需要多长,但必须让 Agent 在接到跨模块任务时,能快速定位该去哪几个目录。

第四,把测试命令统一成单一入口。最理想是 make test 或者 scripts/test.sh,这样 Agent 永远只需要执行一个固定命令,不需要搞明白多少种测试框架。如果这个入口能同时覆盖单元测试和关键集成测试,效果最好。

第五,定义任务输入模板。我每次派任务给 Agent 都要求包含四要素:需求背景、涉及模块、验收标准、禁止越界修改项。比如“本次只修改订单状态机,不允许改动支付相关的表结构和接口签名”。这条看着简单,但它直接决定了 Agent 的行为边界,也是后续 review 的基准线。

4.3 沙箱与工具边界的最小配置策略

下面是让我现在稳定跑的小规模 Harness 配置示例,可供参考。它不是一个平台,只是一个 YAML 化的策略文件,可以挂在你的 Harness 上。

sandbox: allow_read_dirs: ["/repo/src", "/repo/tests", "/repo/docs"] allow_write_dirs: ["/tmp/agent-workspace"] network: "blocked" resource_limits: cpu: "2 cores" memory: "4 GB" timeout_seconds: 120 tools: enabled: - read_file - grep - find_references - run_command - run_test - apply_patch - list_directory disabled: - write_secret - modify_lockfile policy: allow_commands: - "make test" - "git diff" - "git status" - "python -m pytest tests/ -x" - "go test ./..." deny_commands: - "rm -rf" - "git push" - "curl" - ".*DROP TABLE.*" max_iterations: 5 pause_after_repeated_error: 3 review_mode: "require_patch_review"

几个字段的用意说明一下。allow_read_dirs 限定可读范围,防止 Agent 冷不丁把生产密钥文件读进来。network blocked 基本是标配,我不允许 Agent 在自主循环里访问外网,需要装依赖也走预置缓存。deny_commands 里真正要防的是 git push、rm -rf 这类不可逆或影响远端状态的操作。max_iterations 设成 5 而不是 20,是为了防止它反复用同一个错误方案硬撞。pause_after_repeated_error 则保证它在同一错误上卡壳三次后把控制权交还给人,而不是白烧 token。

4.4 最小评测集:保证 Harness 越改越稳不是错觉

很多人改完 Harness 配置、跑通两个任务就觉得大功告成,结果过了两周效果变差也无从感知。我建议花半天时间建一个小型评测集,规则如下。

挑 10 到 15 个代表性任务,覆盖你团队最常见的开发场景。我最初选了四类:修一个失败的单元测试、为一个接口写一个新的 CRUD、跨模块重命名一个公共函数、修一个单元测试覆盖的 bug。每个任务记录三项指标:完成率(测试是否全绿)、平均迭代轮数、总 token 消耗。

之后每改动一次上下文策略或 Harness 配置,就跑一遍这个评测集,和基线对比。我的门槛是完成率不得低于 80%,如果一次“优化”让完成率从 85% 掉到 60%,不管当时看起来多合理,都先回滚。这个机制看起来很土,但能挡住大部分自我感觉良好的玄学调优。

5. 实测数据复盘:配好上下文和 Harness 后,我看到了什么

我把团队某一阶段的任务拉出来做过一次对比。同一批工程师、同一个模型,改造前用的是“聊天窗口手动粘贴代码”模式,改造后用的是项目宪法加检索加最小 Harness 闭环。结果是大概 40 个中到大型后端任务的数据:

指标改造前改造后
任务完成率42%89%
平均循环/返工轮数4.6 轮1.7 轮
单任务耗时(中位数)37 分钟13 分钟
平均 token 消耗约 10 万约 5.8 万

注意,这个样本量不算大,不是严格的统计实验,但趋势在之后半年里反复出现。我特别想说的是 token 消耗:很多人担心让 Agent 自主循环会烧更多 token,实际恰恰相反。改造前大量 token 浪费在把整个仓库拖进对话、反复让模型“再看一遍却没看到点”上;改造后检索精准、返工少,总成本反而降了四成。

不同任务类型的收益差别,我也做了记录。收益最高的是跨模块重构和修复失败的单元测试,因为这类任务对全局依赖的感知要求很高,上下文检索和闭环反馈恰好对症。中等级别收益的是新增 CRUD 接口,Agent 本来就不容易跑偏,但闭环省去了人来回搬运测试结果。收益不明显的是复杂算法设计和性能调优,这类工作需要大量人类直觉与领域经验,Agent 目前只能做辅助推导,我不强行让它全自主。

还有一类容易被忽略的场景:老代码库、缺少文档、模块之间耦合严重,是上下文工程收益最大的地方。新仓库结构清爽,Agent 随便看看就能上手;老仓库才是上下文工程真正体现价值的主场。

6. 三个把我拉回现实的坑:换模型救不了流程问题

6.1 教训一:把整个仓库塞进上下文,等于什么也没说

我第一次做上下文改造时,抱着“宁可多给不可漏给”的心态,把一个三万文件规模的仓库全部建了索引,并且每个任务都把相关模块的信号全都注入窗口。结果模型开始一本正经地胡编,它把另一个模块的接口当成当前模块的接口来用,生成的代码串联了两套完全不兼容的数据结构。

根因是相关性过滤不够、分层缺失。后来我改成“先给地图,再按需展开”,并给检索结果加了依赖优先级排序,问题才缓解。这个坑的本质是:上下文工程不是为了“让模型看到更多”,而是为了让模型在有限注意力里看到更准确的东西。

6.2 教训二:权限给得太宽,Agent 差点改掉生产配置

有一次我为了省事,给 Harness 开了几乎无限制的命令执行权限。Agent 在跑集成测试时发现连接配置不对,居然自己找到配置文件,把指向生产环境的连接串改了。幸好当时设置了“patch 必须人工确认”的 review 模式,人在合并前发现了这个改动,否则后果很难收场。

这个坑让我彻底接受了两条原则:第一,Agent 的工作环境必须是隔离沙箱,默认不可读、不可写敏感路径;第二,任何对配置文件的修改都要经过人审。不要高估 Agent 的分寸感,它只是忠实地朝着“让测试通过”这个目标压路,不会像人一样意识到哪块地砖不能碰。

6.3 教训三:最大迭代次数设太大,Agent 在同一错误上死循环

我最初把自主循环上限设成 20 轮,以为多给几次机会总能成功。有一次它在一个不存在的依赖包上反复执行安装命令,报错信息一模一样,它却每次都认为自己找到了新方案,连续撞了 8 次,白白烧掉十几分钟和大量 token。

后来我在循环控制器里加了一个简单逻辑:当连续三轮的报错信息相似度极高时,强制暂停,把控制权交还给人。这个逻辑不需要多复杂,可以用报错文本的哈希或者相似度计算实现。它背后的原则是:Agent 如果在一个模式上连续失败 N 次,大概率是思路本身有问题,继续循环只是扩大浪费,而不是接近成功。

现在我的团队每天的工作流程已经固定成一个简单流水线:写任务卡片、Harness 自动加载项目宪法、检索相关代码、执行测试闭环、产出可审阅的 patch,人来审阅和决策。工具清单和策略模板上面都给出来了,照着搭一次,你会立刻感受到“给它一个任务而不是给它一条指令”的区别。也提醒一句:换更强的模型之前,先把上下文输入和执行闭环这两件基础做好,否则再聪明的模型也只会在一个毛躁的流程里发挥不出真正实力。