09. 开发型智能体教材:从 learn-claude-code 理解 Harness
九、开发型 Agent 教材:从 learn-claude-code 理解 Harness¶
开发型 Agent 的核心架构与 Harness 最小物理配置¶
在软件工程自动化领域,开发型 Agent(Developer Agent)的工程实质是将非确定性的代码修改与测试任务,接入一个闭环的受控执行运行时(Controlled Execution Runtime)。其核心技术门槛并非代码生成本身,而是构建一套能够将真实物理环境(如文件系统、测试套件、编译器)的反馈实时注入决策树的 Harness(受控测试与执行框架)。
learn-claude-code 项目作为一个教学参考样本,其核心价值在于剥离了复杂应用框架的抽象干扰,显式地暴露出开发型 Harness 的最小骨架体系。它确立了开发型 Harness 不应依赖于概率性的长 prompt 编排,而必须构建在确定性的系统要素之上:目标定义、沙箱环境、共享状态与硬性停止条件。
该架构图展示了开发型 Agent 运行时与单轮文本生成的本质差异:单轮文本生成仅依赖“静态上下文 + 推理”,而开发型 Harness 则是“有状态的多步受控决策循环”。在该循环中,LLM 仅承担决策代理的角色,而 Harness 底座则负责物理环境暴露、动作安全执行、观察结果结构化回填及边界收敛控制。
Harness 运行时控制面与调度拓扑¶
在系统架构层面,Harness 不是“大语言模型加几个外部 API”的适配外壳,而是开发型 Agent 的运行时控制面。它把模型的非确定性判断,接入一组可审计、可恢复、可中断的工程边界里。任务能不能拆分、上下文能不能进入模型、命令能不能执行、结果能不能交付,都不应该由模型单独决定。
这条控制面可以按职责拆成七层。任务调度层把自然语言目标压成工作项、计划或后台任务;上下文装配层决定 rules、memory、plan、observation、tool result 的顺序和可见范围;执行环境层提供文件系统、命令、网络和依赖隔离;工具代理层负责 schema、ACL、超时、审计和错误归一化;Agent Loop 推动下一步动作;Verify Loop 用测试、lint、类型检查或人工 review 卡住交付;Eval Loop 把真实 trace 和 rollout 收集成回归样本。
这几层不是为了堆名词,而是为了避免模型同时扮演调度器、执行器、审计员和裁判。一个开发型 Agent 如果缺少 Harness,模型也许能生成 patch,但很难稳定回答这些更关键的问题:它为什么读这个文件,为什么跑这个命令,失败后为什么重试,哪个动作产生了副作用,最终结果经过了什么验证。
运行时受控循环与最小闭环实现¶
开发型 Harness 的第一阶段演进是实现基于 OODA 循环的最小闭环。在这一阶段,系统通过原子化的工具调用来响应物理环境的变化,避免引入高阶的状态图(State Graph)或多代理拓扑(Multi-Agent Topology)。
最小受控循环的物理抽象可简化为如下流程:
state = {
"goal": "修复 failing test,并补充相应的回归测试用例",
"messages": [],
"artifacts": [],
}
while True:
decision = model.decide(state)
if decision.type == "stop":
break
result = run_tool(decision.tool, decision.args)
state["messages"].append({"decision": decision, "result": result})
state["artifacts"].append(result.summary)
在上述受控循环中,系统的核心设计原则是职责彻底分离:
1. 决策代理(LLM):基于当前 state 快照,输出结构化的下一步执行指令(Decision)。
2. Harness 底座(宿主代码):拦截该指令,在真实的沙箱物理环境中执行动作(如读写文件、运行测试、解析错误堆栈),并将物理返回值格式化为 Observation 注入 state。
为了支持工具集的动态扩展并保持控制流的单一性,系统在调度层(Dispatch Layer)引入统一的注册表模式:
TOOLS = {
"read_file": read_file,
"search_code": search_code,
"run_command": run_command,
"run_tests": run_tests,
}
def run_tool(name, args):
handler = TOOLS[name]
return handler(**args)
通过这一层统一的代理转发,工具的扩展仅表现为注册表(Registry)的水平增长,从而保障了主控制流(Control Loop)的内聚性与稳定性。
长周期任务里,主决策循环能不能稳定,主要看四个控制点。第一是上下文顺序稳定。系统指令、项目规则、当前计划、最近观察、工具返回和压缩摘要要有固定装配顺序,不能每轮都让模型重新判断哪些材料重要。第二是工具异常要分类。schema 或参数错误可以回喂给模型自修;网络超时和限流应由宿主层退避重试;权限拒绝、危险写操作和业务硬冲突则应该中断或转人工确认。
第三是停止条件要在模型外侧生效。最大步数、重复命令检测、无进展检测、人工接管、失败收口都属于运行时责任。模型可以建议“我完成了”,但不能只靠这句话完成交付。第四是 hooks / middleware 要能在工具调用前后做拦截、审计和压缩。例如长日志不应原样回灌进上下文,而应折叠成错误类型、关键堆栈、文件行号和可复现命令。
长周期复杂任务的系统化收敛策略¶
随着任务复杂度的提升(例如:跨模块故障定位、重构及多阶段验证),单步受控循环容易因上下文发散而出现“决策漂移”。为保障长周期任务的稳定收敛,Harness 必须引入显式计划、子任务隔离、动态技能装配与上下文压缩机制。
1. 显式计划(Explicit Plan & Task Tracking)¶
为了防止 Agent 陷入无序的临场反应,必须将“脑内规划”转化为具有强类型、可被 Harness 调度引擎解析的显式任务表(Task Registry):
state["plan"] = [
{"id": 1, "task": "运行失败测试并记录报错", "status": "pending"},
{"id": 2, "task": "定位相关实现和调用链", "status": "pending"},
{"id": 3, "task": "决定是改实现还是补测试", "status": "pending"},
]
while True:
current = next_pending_item(state["plan"])
if current is None:
break
decision = model.decide(state, current)
result = run_tool(decision.tool, decision.args)
update_plan(state["plan"], current["id"], result)
write_back(state, result)
显式计划不仅为执行流提供了硬性边界约束,还为系统在出现异常时提供了局部重试、中断恢复或状态补偿(Compensating Transactions)的切入点。
2. 上下文隔离与子任务生命周期(Subtask Isolation & Handoff Boundaries)¶
在处理子任务(如独立分析测试夹具)时,若直接继承主会话的完整历史,将导致严重的上下文污染(Context Contamination)与推理开销飙升。Harness 通过生成独立的 Subagent 并赋予隔离的上下文空间来解决该问题:
subtask_input = {
"goal": "检查 test fixture 初始化是否导致失败",
"files": ["tests/order_test.py", "tests/fixtures/order.json"],
"constraints": ["不要修改主分支代码", "只输出判断依据和建议"],
}
sub_result = run_subagent(subtask_input)
state["artifacts"].append(sub_result.summary)
state["messages"].append({"subtask": sub_result})
Subagent 作为一个无状态、职责单一的临时执行单元,在专属的沙箱上下文中运行,其生命周期随子任务的结束而销毁,仅将归一化后的结果对象(Result Object)回灌至主状态机。
长程任务的多阶段移交(Handoff)不能直接传完整 transcript。原始历史里往往混着废弃假设、失败命令、用户插话和已经失效的上下文,直接交给下一个模型或 worker,会让接收方重新陷入旧噪声。更稳的交接包应该只保留可继续执行的状态: - 目标与停止条件:当前要完成什么,做到什么程度算完成。 - 约束:允许访问的目录、命令、网络、工具和禁止动作。 - 已验证事实:已经通过测试、构建、源码核验或人工确认的事实。 - 未决问题:仍需排查的分支、未验证假设和下一步候选动作。 - 工作区引用:相关文件、测试、产物、分支或 sandbox / worktree 标识。 - 输出契约:接收方最后必须返回什么结构,哪些内容需要证据或命令支撑。
handoff 的目标不是少传信息,而是把聊天历史压成可执行状态。尤其在跨模型切换时,交接包要能让新模型接着做事,而不是重新解读上一轮所有对话。
3. 动态技能装配与生命周期控制(Dynamic Skill Assembly Lifecycle)¶
为了避免冗长的常驻 Prompt 挤占宝贵的上下文窗口,Harness 将稳定的工程方法论(如“三阶段排障法”)抽象为“技能包”(Skills),仅在特定任务触发时,按需动态装配至决策上下文。
可复用的 Skills 框架要管住完整生命周期:发现、加载、注入、折叠、恢复和卸载。Skill 不应该长期常驻在主上下文里。加载过宽会占掉 token 预算,也容易让模型在不相关任务里套错方法;卸载过早又会让长任务中途丢掉约束。比较稳的做法是为每个 skill 绑定触发条件、适用范围、退出条件和最小保留摘要。
例如“源码审计 skill”只应在任务涉及代码事实核验时加载。进入普通文案润色阶段后,它可以折叠成几条边界规则,而不是继续把完整审计流程塞进上下文。Skill 和 sandbox 也要分开理解:Skill 是方法,sandbox 是执行环境。一个 skill 可以在不同 sandbox 中运行,同一个 sandbox 也可以承接多个不同 skill 触发的动作。
4. 增量上下文整理与分层衰减模型(Incremental Context Compaction & Layered Decay)¶
在长周期运行中,大量的中间调试输出与临时假说属于低价值噪声。Harness 必须部署上下文整理管线,在每个 OODA 循环结束时,提取“已确认事实”、“挂起疑问”与“当前工作分支状态”,实现上下文的无损压扁,防止推理链路由于历史垃圾数据造成的漂移。
上下文整理要分层做,不能只在 token 快爆时临时总结。L1 是局部折叠,处理一次工具返回、日志、编译输出或测试失败,只保留错误类型、文件行号、退出码、关键堆栈和可复现命令。L2 是会话压缩,把多轮观察整理成已确认事实、当前计划、废弃路径、挂起问题和下一步动作。L3 是长期沉淀,把已经稳定、可复用、来源明确的事实或经验提升到长期记忆或项目规则层。
压缩的好坏不看摘要写得像不像,而看压缩后能否继续执行任务。一个合格摘要至少要保住目标、当前分支、关键约束、失败原因、工作区引用和下一步动作。对开发型 Agent 来说,过度压缩和不压缩一样危险:前者会丢掉证据链,后者会让模型在旧噪声里反复打转。
多维记忆模型与在线自我改进¶
在开发型 Harness 中,记忆系统不再是简单的非结构化向量数据库,而是划分为四个明确层次的分级持久化状态网络:
- 多级存储体系的逻辑特征:
- 工作记忆(Working Memory):暂存当前 OODA 回合的原始 Observation 细节,具有强时效性与高降解率,属于短程临时数据。
- 动态会话摘要(Ephemeral Summary):存储 L2 压缩提取出的“已定位路径、执行进度 DAG、挂起疑点”等结构化状态。
- 只读长期记忆(Long-term Memory):落盘存储在多轮任务后被验证为正确的方法路线、系统偏好以及对项目本身架构的事实性积累。
-
静态硬规则(Hard System Rules):作为 Harness 顶层 System Prompt 的不可变前缀,包含底线权限范围(ACL)与停止条件,绝不允许被模型写入或改写。
-
冲突消除与写入准入审计控制: 当新事实和旧记忆冲突时,系统不能简单按时间覆盖。默认应同时看来源可信度、验证方式、时效性和作用域。测试通过、源码核验、用户明确确认这类来源权重更高;临时猜测、失败路径、模型自我总结这类内容不能直接晋升为长期记忆。更新也应尽量局部修订,只改发生变化的规则或事实片段,避免把整段经验重写到失真。
任何数据进入长期记忆前都应经过准入检查。临时调试信息、废弃计划、攻击性输入、一次性用户偏好都不应污染长期层。memory 的评估也不能只看“有没有召回”,还要看召回后是否真的提高任务成功率、是否降低冲突、是否减少人工纠错。
- 在线自我进化(Self-Improvement)的确定性边界:
开发型 Agent 的自我改进不是运行时让模型随手改自己的规则,而是把完成态轨迹送进离线评估管线。稳定的成功路径、常见故障修正和项目规则,可以在人工或自动审核后沉淀成
memory_rules、skill 或项目记忆。它们能改进后续任务的默认行为,但不能绕过权限、测试、审计和人工 review。
复杂多任务下的后台异步隔离与沙箱生命周期¶
当 Agent 系统从单会话交互演进为并发执行多项复杂任务时,传统的同步受控循环将面临 I/O 阻塞、执行现场冲突与并发安全等系统级挑战。必须在底层引入沙箱生命周期、任务持久化、异步后台执行及物理工作区隔离(Worktree Isolation)。
1. 沙箱虚拟化与状态持久化生命周期¶
物理沙箱不只是临时文件的暂存,它是隔离执行单元与底层操作系统攻击面的核心边界。物理沙箱必须具备确定性的生命周期管理(Lifecycle Management):
会话挂起时,系统不应把所有运行现场都强行保活。需要持久化的是任务进度、工作区引用、代码 diff、验证结果、审批状态和必要产物;可以丢弃或重建的是临时进程、无价值日志和可重新下载的依赖缓存。这样恢复时才能先还原“可继续执行的状态”,再按需拉起新的沙箱。
沙箱实现也应按风险分层,而不是一上来追求最重隔离:
| 隔离方式 | 主要解决的问题 | 代价与边界 | 典型场景 |
|---|---|---|---|
| Git Worktree | 隔离代码目录和并发修改现场 | 不提供系统级安全隔离 | 可信本地开发、多任务并发测试 |
| Container / Namespace | 隔离进程、文件系统、资源配额和依赖环境 | 仍共享宿主内核,需要控制挂载、网络和密钥 | 一般非特权任务、CI 式执行 |
| MicroVM / gVisor 等强隔离方案 | 缩小内核攻击面,增强多租户隔离 | 启动、镜像、快照和调度复杂度更高 | 不受信任代码、高风险命令、多租户平台 |
面试里不需要背冷启动数字,更重要的是讲清楚:你隔离的是工作区冲突、依赖污染、资源滥用,还是租户安全。不同目标对应的沙箱层级不同。
2. 任务持久化与生命周期管理¶
任务不再是内存中的临时状态,而必须通过结构化的元数据进行落盘持久化,支持跨进程恢复与状态回放:
task = {
"id": "task-123",
"goal": "修掉 failing test 并确认回归范围",
"status": "queued",
"owner": None,
"artifacts": [],
"depends_on": [],
}
if claim(task, worker="main-loop"):
task["status"] = "running"
result = execute(task)
persist(task, result)
3. 异步后台执行引擎¶
对于执行耗时极长的任务(如全量集成测试、构建静态索引、执行耗时编译),主循环不应进行同步阻塞等待。Harness 将其卸载至后台执行队列,通过事件驱动(Event-Driven)协议在任务完成时发送异步通知,主循环捕获事件后再执行 Checkpoint 状态恢复与下一步决策。
4. 工作区物理环境隔离(Git Worktree Isolation)¶
在并发修改同一个代码库时,若多个执行单元共享同一个工作目录,会导致文件读写冲突、未提交代码覆盖及测试现场污染。Harness 必须强制采用底层隔离策略,例如利用 Git Worktree 为每个并发任务分配专属的物理执行路径与独立的依赖追踪上下文:
worktree = create_worktree(base_repo, task_id=task["id"])
task["workspace"] = worktree.path
result = run_in_workspace(
workspace=worktree.path,
command="pytest tests/order_test.py -q",
)
collect_artifacts(task, result, workspace=worktree.path)
工作区隔离在物理层面划定了多任务执行边界,使开发型 Agent 在并发环境下更容易保持幂等和可复现。
开发型 Agent 评测工程、后训练对齐与大规模并行协作¶
1. 轨迹评测与多维奖励设计(Trajectory Evaluation & Reward Design)¶
评估一个开发型 Agent,不能只看最后文本或最终 patch。代码修改有真实副作用,模型也可能通过改测试、绕过失败路径、重复尝试等方式拿到看似正确的结果。因此,开发型 Agent 的评估核心在于轨迹评测(Trajectory Evaluation),即评估整个 Trace 路径:
轨迹评测至少要分三层。第一层是硬结果:编译、测试、lint、类型检查、目标用例是否通过。第二层是过程质量:是否读了相关文件、是否重复调用工具、是否越权、是否在失败后缩小问题而不是盲目重试。第三层是成本与交付:token、耗时、人工 review 退回率、回滚率、最终解释是否能让人复查。
如果要把这些信号用于后训练或离线调优,可以把它们拆成 reward signal,但不要只奖励最终通过。只看最终通过会鼓励绕过过程,只看解释质量又会鼓励写漂亮但不可验证的总结。更稳的指标组合是 task success、formal success、tool-call correctness、verification completeness、rollback rate、human rejection rate 和 cost。
2. 大模型后训练(Post-Training)与 Harness 控制面的解耦关系¶
在提升 Agent 能力时,需要明确划分模型底层优化(如 Prefill/Decode 优化、后训练)与 Harness 运行时的技术边界: - 服务吞吐优化(Prefill / Decode):旨在提升模型在服务层面的效率。例如通过 KV-Cache 复用、Continuous Batching 降低首 Token 延迟(TTFT),这解决的是通信效率问题,不能在逻辑层面解决工具调用边界溢出的问题。 - 模型后训练(Post-Training Alignment):通过 SFT(监督微调)与 RL(强化学习)重点优化模型底层的指令遵循能力(Instruction Following)、工具模式选择能力(Tool-Use Strategy)、自省验证(Self-verification)能力。 - Harness 控制面:后训练可以让模型更会遵循指令、更会选工具、更会自检,但权限审计、沙箱隔离、断点恢复、审批和回滚仍应由运行时承接。把安全和物理治理寄托于“大模型足够聪明”,会让系统边界变得不可审计。
3. Swarm 架构横向并发与大规模协作仲裁(Swarm Concurrency & Arbitrage)¶
Swarm 的价值主要在横向拆分。Deep Research、证据搜集、反证检查、跨文件影响面分析这类任务,天然可以拆成多个相对独立的 worker 并行推进。它带来的收益不只是速度,也包括视角分解、证据覆盖和交叉验证。
但 Swarm 不适合所有开发任务。强顺序、强副作用、强一致性的修改链,盲目并行会制造冲突。主 Harness 至少要管三件事:任务切分是否独立,worker 产出的证据是否保留来源,多个 worker 的结论冲突时由谁仲裁。涉及代码合并时,默认应通过外部状态机统一合并,而不是让 worker 之间用自然语言互相协商。
框架对比也应该围绕控制面,而不是围绕谁“更智能”。Claude Code 更适合讨论 rules、memory、tools、review 与 IDE 场景如何咬合;Codex 更适合观察 agent loop、沙箱执行、patch 生成和验证链如何组合;OpenClaw 这类个人化或扩展化框架更适合讨论 gateway、extension、personal skill 和外部能力接入。面试里比较这些框架时,先拆任务对象、控制面、沙箱、工具生态、记忆和评测,再说某个框架在哪几层更重,哪些层仍然需要业务系统自己补齐。
开发型 Harness 的系统演进与工程治理要义¶
1. Harness 组件演进对照表¶
以下矩阵总结了开发型 Harness 各核心组件的演进动因及其对主循环的系统级赋能:
| 核心组件 | 消除的非确定性与系统失控问题 | 引入后对主循环的核心系统级赋能 |
|---|---|---|
messages |
消除每轮观察历史的缺失,防止决策失忆 | 实现基于累计观察流(Observation Stream)的多步状态决策 |
tool registry |
消除硬编码在 Prompt 中的工具指令,防止意图越权 | 动作边界沉降至代码控制层,实现可注册、可审计的 ACL 管控 |
plan / todo |
消除仅存于 LLM 内存中的隐式规划,防止任务迷失 | 提供显式化的当前步骤、任务依赖图及断点恢复入口 |
subtask context |
消除子任务直接继承主历史造成的上下文污染与冗余推理 | 实现基于干净输入边界的子上下文隔离与无损产物回收 |
skill |
消除常驻于 Prompt 中的静态工程方法说明,释放上下文空间 | 实现工程知识与方法论的按需动态装配与按需卸载 |
memory / rules |
消除跨会话经验无法沉淀或旧经验污染新任务的问题 | 实现可检索、可更新、可审计的长期状态治理 |
summary / compacted |
消除长历史中的中间冗余噪声,防止长周期计算偏移 | 实现对关键事实与挂起状态的提炼,保障长周期收敛性 |
sandbox session |
消除执行现场、依赖、网络和文件系统边界不清造成的物理风险 | 实现冷启动、快照、恢复、回收和产物收集的环境生命周期管理 |
task |
消除因进程中断、会话挂起导致的计算进度丢失 | 实现任务的持久化存储、并发认领、冷启动恢复与操作审计 |
protocol message |
消除多执行单元间基于隐式自然语言交互的二义性 | 提供统一的消息信封格式,支持精准去重与状态版本控制 |
worktree binding |
消除并发代码修改与测试中的物理现场污染与冲突 | 实现物理执行目录的完全隔离,保障状态修改的幂等性 |
eval trace |
消除只看最终文本导致的不可归因优化 | 实现按轨迹、工具、验证和成本拆解的回归评测闭环 |
2. 生产级工程治理的必经之路¶
虽然教学参考项目展示了开发型 Harness 的系统生长顺序,但在构建工业级生产系统时,架构师必须清醒地认识到,生产环境的真正挑战在于构建严密的工程治理网络。以下核心治理要素在生产环境中绝不可缺席: - 沙箱安全与权限控制(Sandbox & ACL):执行不受信任的 LLM 生成代码或 Shell 命令时,必须将其约束在轻量级虚拟化环境(如 Docker、gVisor、Wasm)中,并在 API 网关层拦截特权调用。 - 人工介入审批(Human-in-the-Loop Approval):对于高副作用操作(如 Git Push、生产部署、涉及资金的物理变更),系统必须触发挂起中断(Interrupt),通过外部 Checkpoint 持久化当前状态,在人工确认后方可反序列化恢复执行(Resume)。 - 多维度审计与回放测试床(Audit Trail & Playback Sandbox):平台必须对 OODA 循环中的每一次 LLM 决策、工具调用、输入输出进行不可篡改的日志归档,提供物理回放能力,以便在决策发生偏航时执行精准的安全审计与 Red Teaming 红蓝对抗。 - 物理故障自愈与补偿事务(Fault Recovery & Compensating Transactions):针对网络抖动、类型解析失败、资源冲突等异常,系统需实现自愈状态机。在发生不可恢复的故障时,必须自动执行反向补偿事务(如回滚部分代码修改),保障物理环境的一致性。
开发型 Harness 的本质是一部逐步逼出来的系统演进史。在架构设计中,应当严格按照“最小闭环 -> 长周期收敛 -> 异步隔离 -> 生产级治理”的路径循序推进,使概率性的智能决策稳固地运行在确定性的分布式基础设施之上。