Tager: Harness
Table of Contents
Tager: Harness
Agent Harness:让模型在真实系统中可靠工作
1. 核心范式:Agent = Model + Harness
一个生产级 Agent 不是"一个更聪明的模型",而是两部分组成的复合系统:
组成 |
角色类比 |
职责 |
本质特征 |
|---|---|---|---|
Model |
CPU |
阅读上下文、推理、决定下一步动作 |
无状态、无外设、受上下文窗口限制 |
Harness |
操作系统 |
上下文加载、工具编排、权限隔离、记忆管理、会话生命周期、持久化、观测、重试、人类介入 |
确定性代码,是模型之外的全部执行支架 |
剥离 Harness,模型本身读不了代码库、跑不了测试、也无法在跨越数天的异步任务里保持记忆连贯。它只能生成 token。
核心循环是 ReAct(Reason + Act) :模型读上下文 → 决定动作 → Harness 通过沙箱/API 执行 → 把观察结果(Observation)反哺给模型 → 循环直到任务完成。
工程启示:评估 Agent 能力应当评估「模型 + Harness 配置」整体,而非单看基座模型。一个设计精良的 Harness 能托住弱模型的底线,也能在强模型出现"上下文腐烂"或逻辑幻觉时,通过强制验证循环把它重新锚定回真实任务。
参考架构
把 控制平面(Harness) 与 执行/计算环境 分离,是安全、耐久、可扩展的基础——避免把高价值凭证直接暴露到模型生成代码的运行环境里,同时支持快照、重放与容器失效后的恢复。
暂时无法在飞书文档外展示此内容
下面分两条主线展开:内部(内稳定) 和 外界(外部影响) 。
2. 内部:内稳定——让模型长时间不跑偏
长时 Agent 的第一个硬问题不是"会不会调用工具",而是"会不会在第十轮后忘掉自己在干什么"。内稳定主要由 Context + State + Retry + 模型原生特性 四块支撑。
2.1 Context 管理与 Compact 压缩
上下文窗口是有限且昂贵的资源。原始工具输出(几千行日志、全量查询结果)会迅速填满窗口,导致信噪比骤降——这就是 上下文腐烂(Context Rot) ,会直接削弱推理能力与任务完成率。
生产级 Harness 摒弃"把所有历史无脑追加"的策略,改用动态上下文管理:
- Compact(压缩) :窗口使用接近阈值时,后台智能总结并卸载较旧上下文,把核心发现提炼为高密度结构化记忆块(JSON/YAML),作为持久化提示注入下一轮。直接让 API 抛 context-limit 错误,在生产里不可接受。
- Tool-Call Offloading(工具输出卸载) :对庞大的工具输出做动态截断,上下文里只保留头部和尾部 token(提供执行状态),完整结果写入(虚拟)文件系统,模型需要时按需读取,保持核心推理流纯净。
关键认知:上下文窗口只是模型的工作记忆,不应承担系统完整状态仓库的角色。仅靠 compaction 不够——没有良好的任务分解和阶段性收尾,模型会留下半完成状态,下一轮只能靠猜。
2.2 目标漂移(Goal Drift)的监测与治理
模型长时间无监督自治时,即便初始提示再清晰,行为与推理也会逐渐偏移。这是多智能体系统崩溃的核心诱因之一。
漂移类型 |
表现机制 |
检测指标 |
治理策略 |
|---|---|---|---|
语义漂移 |
长上下文中倾向模式匹配,被先前噪声/对抗 token/自身冗余文本带偏,压倒初始系统提示 |
与目标的余弦相似度衰减、目标遵循度分数下降 |
情景记忆巩固(定期修剪+总结上下文)、用跨模型验证代替自我评估 |
协调漂移 |
多代理协作中共识协议崩溃,或"幻觉放大"——把某代理的错误推断固化为群体记忆 |
代理间一致性指数 |
动态路由、向高稳定性锚点代理注入基准示例强制纠偏 |
任务漂移 |
被某个挑战性子问题吸引,过度消耗资源,迷失最高层宏观目标 |
任务完成边界突破、API 调用频率异常峰值 |
“计划-执行-反思"架构,强制反思器验证当前输出是否服务于顶层目标 |
治理目标漂移最有效的手段不是写更长的系统提示,而是把目标拆进确定性结构:
- 目标重申(Goal Anchoring) :每一步强制重申顶层目标。
- 生成者与评估者分离:避免模型对自身输出盲目自信。
- 步数硬限制:在漂移越过临界点前强制阻断并触发修正。
- 结构化承载:把目标拆进 计划对象、阶段产物、检查点、状态机、可回放日志。
2.3 持久化(Persistence)与可恢复执行(Resumeable)
企业级 Agent 正从"即时请求-响应助手"演变为长期运行的后台软件实体:可能暂停数天等人审批合同,或休眠等待 IT 系统配置硬件。保持连接常开或阻塞工作线程都不可行。
机制:图执行的每个超步(super-step)基于全局唯一标识(如 thread_id)把完整状态快照(checkpoint)写入持久层(如 PostgreSQL)。由此催生两种关键模式:
- 事件驱动休眠:执行到
interrupt() 或需外部事件时,进程主动释放计算资源并写入状态机;当 Webhook 回调到达或人类提交审批(Command(resume=...)),Harness 从持久层**水化(hydrate)**先前状态,精确地从中断节点唤醒模型继续执行。 - 睡眠时间计算(Sleep-time Compute) :利用用户离线的空闲时间执行耗时的文献综述、代码分析、故障排查,把"思考成果"持久化,待用户上线直接呈现。
持久化至少应保存五类对象:
组件 |
建议持久化内容 |
目的 |
|---|---|---|
会话状态 |
目标、阶段、上次完成节点、用户偏好 |
resume 与分支恢复 |
工具调用日志 |
工具名、归一化参数、结果摘要、幂等键 |
去重、审计、补偿 |
环境快照 |
工作目录、生成文件、安装依赖、输出目录 |
中断后继续跑 |
记忆层 |
长期偏好、项目约定、历史决策 |
减少再解释成本 |
审计轨迹 |
模型调用、审批、手工接管、策略命中 |
合规与事后复盘 |
2.4 LLM 特性支持与 Retry 机制
Harness 不应假设模型一次就对,也不应把所有错误都丢给"再问一遍”。正确做法是区分错误类型:
错误类型 |
是否可自动重试 |
推荐措施 |
|---|---|---|
429 / 限流 / 短暂 5xx |
可以 |
指数退避 + jitter |
结构化输出解析失败 |
条件可重试 |
优先用约束解码/结构化输出;失败时把具体错误反馈给模型做局部修复 |
工具超时 / 网络抖动 |
条件可重试 |
仅对无副作用或幂等工具自动重试 |
不可逆副作用失败 |
不应盲重试 |
需要幂等键、补偿事务或人工确认 |
目标 / 计划错误 |
不建议直接重试 |
回到 planner,重做任务分解 |
可以把"模型原生特性"理解为 Harness 的降噪器:
- Function Calling:把动作参数从自然语言里解耦出来。
- 结构化输出(Structured Output) :通过约束解码(constrained decoding)保证 schema 合规,减少"无效 JSON / 缺字段 / 类型错乱"的解析重试。LLM 生成畸形 JSON 或不存在的参数时,执行钩子捕获异常,构建含具体错误与修正指导的提示反馈给模型,触发内部 Retry,全程无需人类介入。
- 指令层级(Instruction Hierarchy) :降低把外部内容当命令执行的概率(详见 Safety 篇)。
- Compaction:延长有效上下文寿命,但不能替代状态设计。
3. 外界:外部影响——工具的副作用怎么管
模型自身是封闭的;Harness 通过 工具(Tool) 和 环境(Env) 赋予它感知与改造世界的能力。但赋予行动力的同时引入了副作用(Side Effects) ,这要求极严密的权限生命周期管理。
3.1 Tool 与 Env
工具可以是内联函数、数据库、第三方 API,甚至另一个 Agent。常见形态包括 hosted tools、function tools、agents-as-tools、MCP、文件读写、命令执行、Web、subagents 等。
一旦工具连接真实环境,副作用就成了一等问题:写/删文件、发邮件、下单、付款、创建工单、写数据库、浏览器点击、跨系统同步……对这些动作,Harness 不能只看"能不能做",还要判断"是否已经做过、是否应该现在做、是否需要人类盖章"。
3.2 副作用管理:执行钩子、干跑、幂等
在"模型意图"与"实际执行"之间必须建立防火墙:
- 执行钩子(Execution Hooks) :不是模型能阅读的软性指南,而是由确定性代码编写的强制闸门。调用外部系统前做强制类型校验,拒绝非法的参数幻觉。
- 干跑模式(Dry-run) :真正提交前先计算并展示"我将要做什么",为自动化校验或人类审查提供缓冲,避免错误在长链路里复合放大。
- 幂等键(Idempotency Key) :对工具调用以
tool + 归一化参数 生成键并记录,防止重试/崩溃恢复/并发子代理导致重复发信、重复扣费、重复写库。
3.3 Permission 控制与 Human Confirm
涉及高风险、高价值或不可逆操作(生产库修改、大额转账)时,仅靠逻辑校验不够,必须引入 Human-in-the-Loop(HITL) 。
一条铁律:永远不要让具有非确定性的语言模型自主决定"何时需要人类审批"。 审批节点必须由 Harness 的状态机预先硬编码。
运行到预定义边界时,Harness 强制剥夺模型执行权,把任务状态挂起为 PENDING_APPROVAL,向人类控制台推送含上下文的审批负载。人类可批准、拒绝、甚至直接修改中间变量;处理完毕后,Harness 把修正信息合并入上下文并恢复执行。这保证了权限流转的可预测、可审计。
生产级 Permission 模型建议至少分四层:
- 资源边界:文件根目录、网络白名单、凭证作用域。
- 工具边界:允许哪些工具、哪些参数模式。
- 动作边界:哪些动作可自动、哪些必须确认。
- 组织边界:谁能配置工具、谁能查看审计、谁能升级权限。
4. 关键组件清单
一个生产 Harness 的最小可用组件集:
组件 |
作用 |
失效后果 |
|---|---|---|
Orchestrator |
驱动 plan-act-observe 循环与子代理编排 |
流程散乱、无法恢复 |
Context Manager |
构建工作上下文、compaction、记忆召回 |
目标漂移、上下文膨胀 |
Session / State Store |
保存状态、日志、断点、标识 |
crash 后丢进度 |
Tool Router |
把模型动作映射到工具与环境 |
参数错配、工具滥用 |
Policy Engine |
权限判断、风险分级、审批升级 |
高风险动作越权 |
Sandbox / 隔离环境 |
运行代码与文件操作 |
主机/个人数据暴露 |
Observability / Audit |
追踪模型、工具、审批、成本 |
无法复盘和合规 |
Retry / Idempotency Layer |
处理瞬时错误与去重 |
重复副作用、双写 |
5. 主流实现对比
实现 |
权限模型 |
持久化策略 |
沙箱/隔离 |
特点 |
|---|---|---|---|---|
OpenAI Agents SDK |
工具级 needs_approval,HITL pause/resume |
to_state()、session、sandbox snapshots、resumable state |
原生 Sandbox,支持多家 provider |
状态/审批/沙箱一体化强 |
Anthropic Claude Agent SDK / Claude Code |
默认只读、显式授权、allowlist、 acceptEdits、hooks 可控 |
会话恢复、memory 文件、compaction、checkpointing |
/sandbox文件系统与网络隔离,工作目录写限制 |
coding agent 体验成熟,上下文管理原生强 |
LangGraph + Deep Agents |
interrupts + 人工审查,策略自定义 |
checkpointer + stores, thread_id恢复 |
Deep Agents 支持 sandbox backends |
编排灵活,适合自定义状态机与长流程 |
Google ADK |
Tool Confirmation、ApprovalTool、自定义安全工具 |
sessions / state / TTL / long-running resume |
Agent Sandbox、GKE Agent Sandbox |
多语言、长流程与企业治理完善 |
这张表最重要的结论不是"谁最强",而是四家都在收敛到同一种架构:状态外置、审批内建、工具标准化、沙箱隔离、可恢复执行。差异主要在默认体验与企业治理深度。
6. 统一的 Run Loop(伪代码)
把前面所有要素收进一个循环:
def run_agent(task, state_id):
state = load_state(state_id) # 持久化恢复
while not state.done:
ctx = build_context(
goal=state.goal,
short_term=state.recent_history,
long_term=recall_memory(state),
workspace=mount_workspace(state),
)
if near_context_limit(ctx):
ctx = compact(ctx) # 保留目标/计划/关键证据/未完成事项
reply = model.generate(
ctx,
tool_schemas=tool_registry.schemas(),
output_schema=PlannerOrAction, # 结构化输出
)
if reply.type == "plan_update":
state.plan = merge_plan(state.plan, reply.plan)
persist(state); continue
if reply.type == "tool_call":
risk = policy_engine.classify(reply.tool, reply.args, state) # 风险分级
if risk.requires_human:
persist(state, pending=reply)
pause_for_approval(state_id, reply) # 硬编码审批点,挂起等待
return
token = make_idempotency_key(reply.tool, reply.args, state)
result = safe_execute(reply.tool, reply.args,
sandbox=select_sandbox(reply.tool),
idempotency_key=token)
if result.retryable:
backoff_retry(reply, token) # 仅幂等动作重试
else:
state.recent_history.append(result)
persist(state); continue
if reply.type == "final":
state.output = reply.output
state.done = True
persist(state)
return state.output
这个流程把 上下文构建、compaction、结构化输出、风险分级、沙箱执行、幂等重试、暂停恢复 统一进一个 run loop,基本对应当前主流框架的共同抽象。