Agent Harness AI学习笔记05
Agent Harness 学习笔记:包在模型外面的那层「脚手架」
写作主线:记住一个核心等式——Agent = Model + Harness。模型提供「原始智能」,而 Harness(运行支架/挽具) 是包在模型外面的一整套软件脚手架:控制循环、工具、上下文管理、护栏、状态恢复与可观测。模型越强,越要靠 harness 把这份智能稳定、安全、可复现地变成能干活的 Agent。 每个知识点讲 是什么 → 为什么这么设计 → 怎么实现,并用一个最小可运行 harness 把概念落到代码(Python + LangGraph,
deepseek-v4-flash实测,配套agent_harness_demo.ipynb)。
学习笔记的配套代码: https://github.com/LT-IENG/AI-Agent-Study-Notes
目录
- 0. 一个核心等式
- 1. 什么是 Agent Harness
- [2. Harness 的组成解剖](#2-harness 的组成解剖)
- 3. 三条顶层设计哲学(Anthropic)
- 4. ACI:给 Agent 设计工具接口(SWE-agent 四原则)
- 5. The Loop:控制循环与终止
- 6. Context Engineering:harness 的核心战场
- 7. Guardrails 与 Recovery:让错误不扩散、可恢复
- 8. 动手:用 LangGraph 搭一个最小 Harness
- 9. 主流 Harness 对照与开源参考
- 10. 设计 Checklist 与常见反模式
- 11. 参考资料
0. 一个核心等式
Agent(能可靠干活的智能体) = Model(原始智能) + Harness(运行支架)
很多人以为「Agent 效果不好 = 模型不够强」,于是只换模型。但工程实践反复证明:同一个模型,套不同的 harness,表现可以天差地别。Anthropic 在《Harnessing Claude’s Intelligence》中给出的一组数据最能说明问题:
- 仅给 Claude 3.5 Sonnet 配上两个它本来就熟悉的通用工具(bash + 编辑器),SWE-bench Verified 就达到 49%——提升主要来自 harness 而非更大的模型;
- 在 BrowseComp 上,让模型自己过滤工具输出,Opus 4.6 从 45.3% → 61.6%;
- 用「技能渐进披露 + 子代理 + 上下文编辑」让模型自管上下文,再提升约 +2.8%;
- 用「上下文压缩 + 外部记忆文件夹」让模型自持上下文,BrowseComp-Plus 从 60.4% → 67.2%。
这些提升没有一个来自「换更强的模型」,全部来自 harness 的设计。这就是为什么要专门学它。
1. 什么是 Agent Harness
1.1 定义
Harness 是包在语言模型外面、把「单轮文本补全」变成「能自主完成多步任务的 Agent」的全部软件机制。它不产生智能,它决定模型的智能在什么约束下、以什么节奏、用什么工具、带着什么上下文、出错时怎么办。
一个直观的分层:
flowchart TD
U[用户任务] --> H
subgraph H[Agent Harness 运行支架]
direction TB
L[The Loop 控制循环:思考-行动-观察,何时停]
T[Tools / ACI 工具与 Agent-计算机接口]
C[Context Engineering 上下文:注入/裁剪/压缩/分层]
G[Guardrails 护栏:权限/确认/校验/错误抑制]
M[State & Recovery 状态、检查点、恢复]
O[Observability 可观测:轨迹/日志/评测]
L --- T --- C --- G --- M --- O
end
H --> MDL[Model 大模型:唯一的“智能”来源]
MDL --> H
1.2 Harness 与「框架」「Prompt」的区别
| 概念 | 是什么 | 关系 |
|---|---|---|
| Prompt | 一段静态指令 | 只是 harness 里「上下文注入」的一小部分 |
| 框架(LangGraph 等) | 实现 harness 的工具/库 | 框架给你零件,harness 是你用零件搭出的整套运行机制 |
| Harness | 模型运行时所处的完整受控环境 | 包含循环、工具、上下文、护栏、状态、观测的整体设计 |
换句话说:LangGraph 是建材,Harness 是建筑设计。用同一个框架,可以搭出好 harness,也可以搭出坏 harness。
1.3 为什么「模型变强,harness 反而更重要」
模型越强、自主步数越多,两个问题越突出:① 自主行动的破坏力变大(一条命令、一次写库不可逆);② 上下文成为稀缺资源,长任务必然超出窗口。harness 正是用来约束破坏力、并持续为模型「保鲜」上下文的。能力边界由模型决定,可靠性边界由 harness 决定。
2. Harness 的组成解剖
下面六件事,是评估或搭建任何 Agent(Claude Code、Cursor、Aider、SWE-agent、你自己的 LangGraph 应用)时都该逐项对照的清单。
2.1 The Loop(控制循环)
驱动「模型决策 → 执行动作 → 观察结果 → 再决策」的主循环,以及什么时候必须停(任务完成 / 步数到顶 / 预算耗尽 / 卡住 / 需要人)。对应 LangGraph 里的「agent 节点 ↔ tools 节点 + 条件边 + recursion_limit」。
2.2 Tools / ACI(工具与 Agent-计算机接口)
模型能调用的动作集合,以及每个动作的名字、参数、说明、返回格式。这套「Agent 看世界、动手操作世界」的接口叫 ACI(Agent-Computer Interface),是 GUI/CLI 之后专门为 Agent 设计的第三类接口(第 4 章详谈)。
2.3 Context Engineering(上下文工程)
决定每一步往模型上下文里放什么、放在哪、何时删/压缩/外置:系统提示、工具清单、历史消息、检索资料、中间产物、长期记忆。它是当前决定 Agent 长任务成败的核心战场(第 6 章)。
2.4 Guardrails(护栏)
在动作真正作用于真实世界前进行约束:工具白名单、只读/危险分级、参数校验、难逆动作的人工确认、lint/类型检查等「在错误传播前就拦下」的机制(第 7 章)。
2.5 State & Recovery(状态与恢复)
记录 Agent 走到哪了(检查点)、崩了能从最近一步续跑、必要时能回滚/重放。对应 LangGraph 的 Checkpointer、thread、时间旅行。
2.6 Observability(可观测)
把每一步的输入/输出/工具调用/耗时/token/决策理由完整记录,让非确定的 Agent「可调试、可评测、可回归」。对应 LangSmith / 结构化 trace。
flowchart LR
A[一次模型调用] -->|决定动作| B{Guardrails 校验}
B -->|通过| C[执行工具]
B -->|危险/越权| H[暂停: 人工确认]
C --> D[观察结果]
D --> E[Context: 裁剪/压缩后写回]
E --> F{Loop: 完成?}
F -->|否, 步数/预算内| A
F -->|是| Z[输出]
C -.每步.-> S[(State 检查点)]
A -.全程.-> O[Trace 日志]
3. 三条顶层设计哲学(Anthropic)
Anthropic 在《Harnessing Claude’s Intelligence》中把好 harness 的设计归纳为三条,它们是后续所有具体做法的「总纲」。
3.1 哲学一:Lean on the model——尽量「倚靠模型」,用它本来就会的通用工具
是什么:优先给模型少量、通用、它在预训练中早已见过无数次的工具(典型就是 bash + 文件编辑器),而不是为每个业务动作造一个高度定制的专用工具。
为什么:模型对通用工具的用法已经「内化」,不需要冗长说明、不易误用;通用工具能组合出无穷操作,灵活性最高;定制工具越多,模型的选择负担和出错面越大。Claude 3.5 Sonnet 仅靠 bash+editor 就在 SWE-bench Verified 拿到 49%,正是「倚靠模型」的证据。
怎么做:能用一个通用执行器 + 清晰文件系统抽象解决的,就别造几十个按钮式工具;工具要贴近模型熟悉的表达(命令行、文本编辑、HTTP)。
3.2 哲学二:Strip down——不断问「我能停止做什么」,把编排权交还给模型
是什么:与其在 harness 里写死大量流程、过滤、拼接逻辑,不如做减法,让模型自己编排、自己管理信息。三个层次:
- 让模型自己过滤工具输出:不要把海量原始结果硬塞进上下文,给它「查看/搜索/翻页」的能力,由它按需取。BrowseComp 上 Opus 4.6 因此 45.3%→61.6%。
- 让模型自管上下文:技能(Skills)渐进披露(用到才加载,而不是一次全塞)、用**子代理(subagents)**隔离各自的上下文、允许模型编辑自己的上下文(context editing)。
- 让模型自持上下文:长任务用压缩(compaction)总结早期过程、用外部记忆文件夹/文件存放暂时不用但之后要取回的东西。BrowseComp-Plus 60.4%→67.2%。
为什么:harness 写死的规则永远追不上任务的多样性;模型最清楚「此刻它需要什么信息」。harness 应提供机制(能过滤、能外置、能压缩),而不是替它做所有决定。
反面案例是「上下文焦虑(context anxiety)」:因为怕模型漏信息,就把所有文档、历史、工具说明一次性全塞进去,结果关键信息被淹没、注意力被稀释,效果反而下降。
3.3 哲学三:Carefully set boundaries——在关键处精心设边界
「倚靠模型、做减法」不等于放任不管;在高杠杆、难逆转、影响成本与安全的地方要精心设界:
- 提示缓存布局:static-first, dynamic-last。把不变的内容(长系统提示、工具定义、技能文档)放在上下文最前面以命中 prompt 缓存,把每步变化的动态内容放最后;被缓存的 token 成本可低至约 10%。
- 工具定义放进缓存前缀(它们几乎不变),且不要在任务中途切换模型(会破坏缓存与行为一致性)。
- 用声明式工具(declarative tools)承担 UX、可观测与安全边界:把「确认弹窗、参数表单、审计日志、权限校验」沉淀在工具层,而不是靠提示词叮嘱。
- 难逆动作必须人工确认;编辑前做 staleness check(文件是否已被改动),避免覆盖新变更;「自动模式(auto-mode)」下对动作做二次安全判定。
三条哲学的关系:默认倚靠模型、尽量做减法(放权),同时在安全/成本/一致性的关键节点设硬边界(收口)。放权与收口的平衡,就是 harness 设计的艺术。
4. ACI:给 Agent 设计工具接口(SWE-agent 四原则)
SWE-agent(arXiv:2405.15793)最早系统提出 ACI(Agent-Computer Interface) 概念:既然人有 GUI、命令行有 CLI,Agent 也需要专门为它设计的操作接口。同一模型在好/坏 ACI 下解题率差距巨大。四条可直接落地的原则:
4.1 动作要简单:参数少、文档短而明确
每个工具一句话说清「做什么、何时用、每个参数什么格式」。参数越少越好;需要模型猜格式的工具一定会频繁出错。工具 docstring 就是 ACI 的「界面文案」。
4.2 动作要高效:别把一个高阶操作拆成十几回合
如果完成一件常事需要模型连续做十几次低阶调用,既慢又容易在中间走偏。应提供组合度高、一步到位的动作(例如「搜索并带上下文返回」「按范围编辑」),用更少回合达成目标。回合数本身就是成本和错误来源。
4.3 反馈要充分但简洁:给「看得懂、能纠错」的观察
- 返回约百行级、带行号的窗口而非整个巨型文件,既够定位又不撑爆上下文;
- 空输出也要明确反馈:命令成功但无输出时,必须回一句类似「ran successfully(已成功执行,无输出)」——否则模型会误以为命令失败而反复重试或走偏。
4.4 用护栏抑制错误传播
在动作链中插入自动校验(如语法 lint、类型检查、测试),错误在下一步之前就被拦下并反馈给模型自纠。SWE-agent 统计:51.7% 的成功轨迹至少被这类护栏拦截纠正过一次。这说明「让模型一路裸奔」和「每步带校验」是两种可靠性量级。
flowchart LR
subgraph 差的ACI
a1[参数繁多/靠猜] --> a2[动作碎:十几步] --> a3[输出巨大或空] --> a4[错误一路传播]
end
subgraph 好的ACI
b1[参数少/文档清] --> b2[高阶一步到位] --> b3[带行号窗口/空输出也回执] --> b4[lint护栏即时自纠]
end
5. The Loop:控制循环与终止
5.1 最小循环骨架
剥掉所有框架,Agent 就是一个带预算和终止条件的 while 循环:
# 伪代码:Agent 主循环的最小内核
state = {"messages": [user_task], "steps": 0}
while state["steps"] < MAX_STEPS: # ① 步数预算(防死循环烧钱)
ai = llm.bind_tools(tools).invoke(state["messages"])
state["messages"].append(ai)
if not ai.tool_calls: # ② 终止条件:模型不再调工具 = 认为任务完成
return ai.content
for call in ai.tool_calls:
ok, guard_msg = guardrail_check(call) # ③ 动作前护栏
if not ok:
obs = f"[被拦截] {guard_msg}" # 拦截结果也作为观察反馈给模型自纠
elif is_dangerous(call):
obs = await_human_approval(call) # ④ 难逆动作:暂停等人
else:
obs = run_with_timeout(call) # ⑤ 超时/异常兜底,绝不让单次动作崩掉整个循环
state["messages"].append(ToolMessage(obs, tool_call_id=call["id"]))
checkpoint(state) # ⑥ 每步存档,可恢复/回放
state["steps"] += 1
raise BudgetExceeded("超过步数预算仍未完成") # ⑦ 明确失败,而不是无限转下去
5.2 为什么「终止」比「启动」更重要
新手关注怎么让 Agent 动起来,老手关注怎么让它正确地停。必须显式处理这几种停止:正常完成(模型不再请求工具)、步数/时间/token 预算到顶、连续失败/无进展(检测原地打转)、需要外部输入(等人)、不可恢复错误。没有明确终止策略的 Agent 要么死循环烧钱,要么在错误方向上越走越远。
5.3 「错误是流程的一部分」(Thinking in LangGraph)
LangGraph 的设计观很值得借鉴:不要用 try/except 把错误当异常「吞掉或炸掉」,而要把错误当成图里的一种正常状态来路由——工具报错 → 生成一个带错误信息的观察 → 沿条件边回到「重新决策」节点。这样自纠错是被显式建模的,而不是藏在黑盒里。这与 Text2SQL 笔记里「执行失败→带着报错重写 SQL」的自纠错闭环是同一个思想。
6. Context Engineering:harness 的核心战场
Agent 的长任务失败,绝大多数不是「模型不会」,而是「该看的信息没在上下文里、没用的信息占满了上下文」。上下文工程就是系统性地管理「模型每一步能看到什么」。
6.1 上下文从哪里来、到哪里去
flowchart TD
subgraph 注入[每步注入什么]
S[系统提示/角色/规则] --> CTX[当前上下文窗口]
TD[工具定义] --> CTX
H[近期消息历史] --> CTX
R[按需检索到的资料] --> CTX
MM[从长期记忆取回的内容] --> CTX
end
CTX -->|窗口有限| MGMT{上下文管理}
MGMT -->|裁剪| TR[丢弃无关/冗余]
MGMT -->|压缩| CP[compaction:把早期过程总结成摘要]
MGMT -->|外置| EXT[写入外部记忆文件/库,用时再取]
MGMT -->|隔离| SUB[子代理:各自独立上下文,只回传结论]
6.2 四个关键手段
- 渐进披露(Progressive Disclosure):工具说明、技能文档、长规则不要一次全塞;先给索引/摘要,模型需要时再加载细节。Skills「先给名字和一句话说明,调用时才展开全文」就是典型。
- 压缩(Compaction):当历史接近窗口上限,把较早的多步过程总结成一段摘要保留要点、释放空间,让任务能继续而不「失忆」。注意保留:当前目标、已做决定、关键中间产物、待办。
- 外置记忆(Offload / Memory folder):把「现在不用、稍后可能要」的细节写到外部文件或存储,上下文里只留「去哪取」的指针,需要时再读回。这让有效记忆突破单窗口大小。
- 子代理隔离(Subagents / Context isolation):让专职子代理在自己的上下文里处理信息密集的子任务(如读 20 个文件),只把最终结论交回主代理,避免原始噪声污染主上下文。
6.3 布局也影响成本与稳定性:static-first, dynamic-last
- 不变内容前置:系统提示、工具定义、技能文档放在最前,构成可被 prompt 缓存的稳定前缀(缓存命中的 token 成本可低至约 10%);
- 变化内容后置:最新对话、工具结果等动态部分放最后;
- 不要在任务中途换模型/改前缀,否则缓存失效、行为也可能漂移。
6.4 上下文预算(Token Budget)要显式管理
把上下文窗口当成一个有容量的预算池来规划:预留输出 token、给工具结果设上限、为历史设保留窗口、逼近上限时触发压缩。好的 harness 会主动监控 token 使用并在阈值处行动,而不是等 API 报「context length exceeded」。
7. Guardrails 与 Recovery:让错误不扩散、可恢复
7.1 错误处理的四级降级策略
对动作失败,不要一出错就整体崩溃,按代价从低到高逐级处理:
flowchart LR
E[动作出错] --> R1[① Retry 重试<br/>瞬时错误:退避重试,限次数]
R1 -->|仍失败| R2[② Degrade 降级<br/>换工具/换策略/缩小范围]
R2 -->|仍失败| R3[③ Skip 跳过<br/>标记并继续其他可做部分]
R3 -->|影响主目标| R4[④ Human 上报<br/>带着上下文请人介入]
- Retry:网络抖动、限流等瞬时错误,指数退避重试,必须有次数上限。
- Degrade:换一条路径(大模型超时换小模型、搜索失败换关键词、整文件读取失败换分段读)。
- Skip:非关键子任务失败就记录并跳过,保证主流程推进。
- Human:只有当失败影响主目标且机器无法自救时,带着完整上下文升级给人(对应 LangGraph 的
interrupt)。
7.2 动作前护栏(事前)优于事后补救
| 护栏类型 | 做法 |
|---|---|
| 权限分级 | 只读动作自动放行;写/删/执行/外发等动作分级,危险动作需确认 |
| 白名单 | 工具、可访问路径、可执行命令用白名单而非黑名单 |
| 参数校验 | 调用前用 schema/类型/范围校验参数,非法参数直接打回模型 |
| 难逆确认 | 删除、覆盖、付款、发消息等不可逆动作,interrupt 暂停等人批 |
| 并发/陈旧检查 | 编辑前检查对象是否已被改动(staleness check),防止覆盖 |
| 自动校验 | lint / 类型检查 / 单测在动作链中即时拦截(SWE-agent 51.7% 轨迹受益) |
7.3 State & Recovery:检查点是恢复与回放的前提
- 每一步(每个 super-step)存检查点:消息、已用工具、中间产物、步数;
- 崩溃续跑:从最近检查点恢复,而不是从头再来;
- 时间旅行/重放:回到任一历史检查点,换个决策分支重跑,用于调试和「如果当时这样做会怎样」;
- 副作用幂等:因为恢复可能重放某些步骤,动作(尤其写操作)要设计成幂等或可补偿。
7.4 Observability:没有 trace 就无法改进
为每一步记录结构化轨迹:时间、节点/工具名、输入摘要、输出摘要、耗时、token、成功与否、决策依据。非确定系统只能靠「可复盘」来定位「为什么走偏」,并据此沉淀评测集做回归(对应 LangSmith)。
8. 动手:用 LangGraph 搭一个最小 Harness
概念最终要落到代码。下面把前面六件套逐一映射到 LangGraph 机制,并用一个「文件/计算小助手」演示。完整可逐格运行版见配套 agent_harness_demo.ipynb。
8.1 六件套到代码机制的映射
| Harness 部件 | 本示例的实现 |
|---|---|
| The Loop + 终止 | agent ↔ tools 两节点 + 条件边;State 里记 steps,到 MAX_STEPS 强制结束 |
| Tools / ACI | @tool 定义,docstring 写清用途;read_file 返回带行号的有限窗口 |
| Guardrails | 工具白名单;delete_file 标记为危险动作,用 interrupt 暂停等人确认 |
| Context Engineering | trim_messages 保留系统提示 + 最近 N 条,逼近预算时裁剪 |
| State & Recovery | InMemorySaver 检查点 + thread_id,可查看历史、断点续跑 |
| Observability | 每个节点写入结构化 trace 列表(动作/结果/耗时) |
8.2 关键代码骨架
import time, operator
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import interrupt, Command
from langchain_core.messages import ToolMessage, SystemMessage
from langchain_core.messages import trim_messages
MAX_STEPS = 5
DANGEROUS = {"delete_file"} # 危险动作白名单(需人工确认)
class HarnessState(TypedDict):
messages: Annotated[list, add_messages]
steps: int
trace: Annotated[list, operator.add]
def _manage_context(msgs): # 上下文工程:保留 system + 最近若干条
return trim_messages(msgs, max_tokens=1200, strategy="last",
token_counter=lambda m: len(str(m)),
include_system=True)
def agent_node(state):
msgs = _manage_context(state["messages"])
ai = llm.bind_tools(tools).invoke(msgs)
return {"messages": [ai], "steps": state.get("steps", 0) + 1,
"trace": [{"step": state.get("steps", 0), "at": "agent",
"tool_calls": getattr(ai, "tool_calls", [])}]}
def tools_node(state):
outs, trace = [], []
for tc in state["messages"][-1].tool_calls:
t0 = time.time()
if tc["name"] not in TOOL_REGISTRY: # 护栏:工具白名单
obs, status = "[拦截] 未知工具", "blocked"
elif tc["name"] in DANGEROUS: # 护栏:危险动作人工确认
decision = interrupt({"require_approval": tc}) # 暂停,状态自动存档
obs, status = (do_run(tc), "approved") if decision == "yes" else ("已取消", "rejected")
else:
try:
obs, status = TOOL_REGISTRY[tc["name"]].invoke(tc["args"]), "ok" # 错误当流程:异常转观察
except Exception as e:
obs, status = f"[工具报错] {type(e).__name__}: {e}", "error"
outs.append(ToolMessage(str(obs), tool_call_id=tc["id"]))
trace.append({"tool": tc["name"], "status": status, "ms": int((time.time()-t0)*1000)})
return {"messages": outs, "trace": trace}
def route(state):
if state.get("steps", 0) >= MAX_STEPS: # 终止:步数预算
return END
return "tools" if state["messages"][-1].tool_calls else END
g = StateGraph(HarnessState)
g.add_node("agent", agent_node); g.add_node("tools", tools_node)
g.add_edge(START, "agent")
g.add_conditional_edges("agent", route, {"tools": "tools", END: END})
g.add_edge("tools", "agent")
harness = g.compile(checkpointer=InMemorySaver()) # 状态与恢复
危险动作的「暂停—审批—恢复」:
cfg = {"configurable": {"thread_id": "task-1"}}
harness.invoke({"messages": [("user", "把 temp.txt 删除")], "steps": 0, "trace": []}, cfg)
# 运行到 delete_file 时停在 interrupt;外部查看待审批动作后:
harness.invoke(Command(resume="yes"), cfg) # "yes"执行 / 其他取消
print(harness.get_state(cfg).values["trace"]) # 可观测:完整结构化轨迹
这个不到百行的循环,就是 Claude Code / Cursor 这类产品 harness 的「教学微缩版」:换更强的模型只是换 llm,而可靠性来自循环预算、白名单、人工确认、上下文裁剪、检查点和 trace 这些 harness 部件。
9. 主流 Harness 对照与开源参考
| 系统 | Harness 设计要点 |
|---|---|
| Claude Code(Anthropic 官方 CLI/SDK) | 倚靠模型:主打 bash+编辑器通用工具;Skills 渐进披露、subagents 隔离上下文、compaction 压缩、难逆动作确认、prompt 缓存 static-first |
| SWE-agent | ACI 概念提出者;定制简洁的搜索/编辑动作、带行号窗口、空输出回执、lint 护栏(51.7% 轨迹被纠错) |
| Cursor / Aider | 面向代码编辑的 harness:diff 式编辑、编辑前陈旧检查、仓库级上下文检索与裁剪 |
| OpenHands(原 OpenDevin) | 开源通用 Agent 运行时:浏览器/终端/编辑器多工具 + 事件流状态 + 安全确认 |
| LangGraph / LangChain | 用 StateGraph/Checkpointer/interrupt/middleware 让你自己搭 harness(本笔记代码路线) |
观察共性:通用工具 + 受控循环 + 上下文管理 + 危险动作确认 + 检查点 + 可观测,几乎是所有优秀 Agent 产品的「最大公约数」——这正印证了第 2 章的六件套清单。
10. 设计 Checklist 与常见反模式
10.1 搭一个 Agent 前/后逐项过一遍
循环与终止
- 是否有步数/时间/token 预算上限?模型能否明确「放弃」?
- 「任务完成」如何判定?是否会把工具空转误当完成?
工具 / ACI
- 每个工具是否一句话说清用途、参数最少、格式无歧义?
- 是否优先用通用、模型熟悉的工具,而非堆砌定制按钮?
- 常事能否一两步完成?返回是否「够定位又不撑爆上下文」?空输出有无回执?
上下文工程
- 是否渐进披露(用到才加载)而非一次全塞?
- 是否有裁剪 / 压缩 / 外置记忆 / 子代理隔离?token 预算是否被监控?
- 稳定前缀是否前置以命中缓存、任务中是否避免换模型?
护栏与恢复
- 工具/路径/命令是否白名单?参数是否校验?
- 删除/写入/外发/付款等难逆动作是否人工确认?编辑前是否做陈旧检查?
- 错误是否走「重试→降级→跳过→人」四级,而不是直接崩?
- 是否每步检查点、可续跑/回放?写操作是否幂等可补偿?
可观测
- 是否有结构化轨迹可复盘?是否有评测集做回归?
10.2 常见反模式(对照避坑)
- 上下文焦虑式堆砌:把所有资料/工具文档一次塞满,关键信息被淹没。
- 黑盒 AgentExecutor 一把梭:没有预算、没有检查点、无法插入人工确认。
- 工具过碎:一个目标要十几次低阶调用,又慢又偏。
- 只靠 Prompt 叮嘱安全:「请不要删除文件」不是护栏,白名单和确认才是。
- 错误直接 raise 崩掉:把工具异常当程序异常终止,而非反馈给模型自纠。
- 空输出无回执:命令成功但无输出时模型误判失败、反复重试。
- 无 trace 上线:Agent 走偏时无法复盘,只能靠猜。
- 只换模型不优化 harness:忽视了可靠性边界其实由 harness 决定。
11. 参考资料
- Anthropic,《Harnessing Claude’s Intelligence》(harness 定义与三条设计哲学、关键提升数据):https://claude.com/blog/harnessing-claudes-intelligence
- Yang et al., SWE-agent: Agent-Computer Interfaces Enable Automated Software Engineering, arXiv:2405.15793:https://arxiv.org/abs/2405.15793
- Anthropic Engineering,Building agents with the Claude Agent SDK:https://www.anthropic.com/engineering/building-agents-with-the-claude-agent-sdk
- Anthropic,Building Effective Agents(workflow vs agent、常见模式):https://www.anthropic.com/research/building-effective-agents
- LangGraph 官方文档(持久化 / interrupt / 多 Agent / Thinking in LangGraph):https://docs.langchain.com/oss/python/langgraph/
- SWE-agent 项目主页:https://swe-agent.com/ ;OpenHands:https://github.com/All-Hands-AI/OpenHands
配套动手材料(同目录):
agent_harness_demo.ipynb(裸模型 vs 加 harness、ACI 好坏对比、手写受控循环、护栏四级、上下文裁剪、用 LangGraph 组装带「预算+白名单+危险确认+检查点+trace」的最小 harness,已用deepseek-v4-flash逐格实测)、requirements.txt、.env.example。