研发团队 · 科普分享

Harness 是什么
从一段代码开始

跟着一个 agent 任务跑完整轮,看清楚每一步谁在动。

万能的明明 · 2026.05
翻页 · Space 下一页
02 / 18

开场 · 一个脑筋急转弯

把大象放进冰箱需要几步?

把大象放进冰箱 - SOP
① 大象 + 冰箱 如何放进去? ② 打开冰箱 ③ 把大象放进去 ④ 关上 · 完毕 提示:把图保存到 ./assets/elephant-fridge-sop.png 替换此占位
3

① 打开冰箱
② 放大象
③ 关冰箱

那么……

请问,把长颈鹿放进冰箱里——需要几步?

03 / 18

开场

你有没有想过这件事

刚才你脱口而出"4 步"的时候——已经在依赖上下文了。LLM 也是同样的工作方式。换个真实点的问题再看一遍——

你给 Claude 说 "读 a.txt 写摘要到 b.txt"——它怎么做到的?
它真的读了你的文件,还是装的?
读完之后,它怎么知道下一步要写文件?
中间这一切,是谁在控制?
Harness
今天的目标 —— 30 分钟后,你能用一张白纸画出整个过程。
04 / 18

第一章 · 拆开 Agent

旧的定义 vs 新的定义

EARLY LANGCHAIN ERA

Agent = LLM + Tools + Memory + Planning + Action + ...

CALCULATOR CALENDAR CODE INTERPRETER SEARCH MORE TOOLS SHORT TERM MEMORY LONG TERM MEMORY REFLECTION SELF - CRITICS CHAIN OF THOUGHTS SUB GOAL DECOMPOSITION AGENT TOOLS MEMORY PLANNING ACTION

看着很全 —— 但写代码时 不知道谁负责跑循环

2026 MAINSTREAM

Agent = LLM + Harness

LLM 脑子 · 决定做什么 + Harness 手脚 · 真的去做 Loop · Tools · Memory Permission · Hooks Subagent

原本散落在 4 大分支 + 一堆子组件里的事,
全部收拢到 Harness 这一层

→ 公式 Agent = Model + Harness 由 LangChain 团队 @Vtrivedy10 推动定型

05 / 18

第一章 · 拆开 Agent

为什么 2026 年突然都在讲 Harness?

2023~2024
Prompt Engineering
调一句好 prompt
2025
Context Engineering
把对的上下文喂给模型
2026
Harness Engineering
设计模型的运行时
证据 · 1

模型进入"高原期"

2024 觉得模型笨;2025 觉得自己笨;
2026 模型智力都 OK
差距不在脑子,在手脚

证据 · 2

DeepMind 实验

固定模型不动,只换 Harness——
性能产生巨大差异
证明 Harness 是战略级资产

业内引子

某创业者花 3 个月调 Prompt,任务完成率提了 20%;后来花 2 周搭 Harness,完成率从 35% → 82%评论区:"方向错了"。

06 / 18

第一章 · 拆开 Agent

Harness 干的四件事

STEP 1 攒上下文 messages[] STEP 2 调模型 下一步干啥? STEP 3 调工具 真的执行 STEP 4 回灌结果 继续 / 退出 stop_reason = tool_use → 回到 STEP 1 (模型回复 + tool_result 都 append 到 messages) stop_reason = end_turn → 完成
RECORD THIS

模型本身不能动文件、网络、数据库。模型只会输出"我想调 read_file"——真正动手的是 harness

07 / 18

第二章 · 50 行代码看一个 Harness

这就是一个完整的 Harness

def run(user_message):
    messages = [{"role": "user", "content": user_message}]

    while True:
        response = client.messages.create(
            model="claude-sonnet-4-6",
            tools=tools,
            messages=messages,
        )
        messages.append({"role": "assistant",
                         "content": response.content})

        if response.stop_reason != "tool_use":
            return response.content[0].text   # 模型说完了

        # 模型要调工具,依次执行
        tool_results = []
        for block in response.content:
            if block.type == "tool_use":
                result = execute_tool(block.name, block.input)
                tool_results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": result,
                })
        messages.append({"role": "user",
                         "content": tool_results})
区块 1 · 上下文

messages[]

全部对话历史都装在这个数组里。

  • 初始 = [{role:user, content:问题}]
  • 模型回复后 → append assistant
  • 工具执行后 → append user(tool_result)
  • 每轮调 API 都把整个数组传进去
区块 2 · 循环

while True

模型 → 工具 → 模型,转到模型说"够了"。

  • client.messages.create()
  • append 模型回复到 messages
  • 看 stop_reason 决定分岔
  • 调工具 → append tool_result
  • 回到 ①
区块 3 · 出口

stop_reason

模型告诉 harness:我要调工具 / 我说完了。

  • ≠ tool_usereturn 文本,循环结束
  • = tool_use → 遍历 content:
  • └ 找出所有 tool_use 块
  • execute_tool() 真的执行
  • └ 结果包成 tool_result 回灌
08 / 18

第三章 · 跟着代码跑一遍

第 1 轮:模型说"我要读文件"

USER INPUT
读 a.txt,写摘要到 b.txt
模型返回
# response.content
[
  {"type": "text",
   "text": "好的,我先读 a.txt"},
  {"type": "tool_use",
   "name": "read_file",
   "input": {"path": "a.txt"}}
]
stop_reason = "tool_use"
HARNESS 干的两件事
1

把模型这段话 append 进 messages(保住模型的"记忆")

2

真的执行 open("a.txt").read()
结果再 append 进 messages

此时 messages 长这样
1USER"读 a.txt,写摘要到 b.txt"
2ASSISTANT"好的,我先读 a.txt" + tool_use(read_file)
3USER · tool_result"今天天气..."(文件内容)
09 / 18

第三章 · 跟着代码跑一遍

第 2 轮:模型说"我要写文件"

承上一轮
此时 messages 已有 3 条,模型已经读到了 a.txt 内容
模型返回
# response.content
[
  {"type": "text",
   "text": "好,我来写摘要"},
  {"type": "tool_use",
   "name": "write_file",
   "input": {
     "path": "b.txt",
     "content": "今天天气..."
   }}
]
stop_reason = "tool_use"
HARNESS 干的两件事
1

把模型这段话 append 进 messages

2

真的执行 open("b.txt","w").write(...)
结果("ok")再 append 进 messages

此时 messages 长这样(5 条)
1USER"读 a.txt,写摘要到 b.txt"
2ASSISTANT"好的,我先读 a.txt" + tool_use(read_file)
3USER · tool_result"今天天气..."(a.txt 内容)
4ASSISTANT"好,我来写摘要" + tool_use(write_file)
5USER · tool_result"ok"(b.txt 已写入)
10 / 18

第三章 · 跟着代码跑一遍

第 3 轮:模型说"完成了"

承上一轮
此时 messages 已有 5 条b.txt 已经写好
模型返回
# response.content
[
  {"type": "text",
   "text": "已完成。摘要内容是..."}
]
stop_reason = "end_turn"

没有 tool_use 块——模型不再调工具

HARNESS 干的事
1

append 模型最后一条话进 messages

2

stop_reason ≠ tool_use
return text,循环退出

最终 messages(6 条)
1-5前 5 条读 + 写过程(已折叠)
6ASSISTANT"已完成。摘要内容是..."(最终答复)
整个任务的账
调模型 3 次
messages 1 → 6
实际执行工具 2 次
口诀

只有 tool_use 让循环继续,其他都让它退出

11 / 18

第四章 · 现实里没那么简单

Harness 的其他细节 — 完整形态

把 4 件事放到生产环境里,每一步都会再"长出"一层周边能力——这就是真实的 Harness 全貌。

[ LAYER 4 · CONTEXT MANAGEMENT ] File-System Memory:CLAUDE.md / USER.md / MEMORY.md → 注入 messages 前缀 | Compaction:撑爆时压缩 | Session:跨会话持久化 stop_reason = tool_use → 回到 STEP 1 (模型回复 + tool_result 都 append 到 messages) [ L6 · HOOKS ] HOOK · 模型前 PreModelCall 参数改写 / 阻断 HOOK · 模型后 / 工具前 PostModelCall 结果改写 / 统计埋点 PreToolUse 参数改写 / 阻断 HOOK · 工具后 PostToolUse 结果改写 / 统计埋点 STEP 1 攒上下文 messages[] STEP 2 调模型 STEP 3 调工具 STEP 4 回灌结果 end_turn 完成 [ L2 · LOOP ] [ L2 · MESSAGES 细节 ] role: user / assistant 4 种 block: · text · tool_use · tool_result · image stop_reason 7 种: end_turn / tool_use max_tokens / refusal ... 三句口诀: 每轮必 append assistant 调工具必 append tool_result [ L1 · MODEL API CLIENT ] Prompt Caching cache_control: ephemeral → input → 10%、延迟 1/3 Streaming(SSE) 边生成边推送,可中断 8 种事件类型 Retry / Backoff 4xx 不重试(除 429) 5xx 指数退避 SDK max_retries=3 够用 [ L3 · TOOLS + L5 + L7 ] Tool Schema JSON Schema 3 种工具来源: · 进程函数 / 子进程 · MCP 协议(外部) · Subagent (Task tool) → 嵌套主循环,作为工具调用 Permission (L5) Allow / Ask / Deny + Auto Deny 永远胜出 Hooks 已挂在主流程箭头处 ↑ [ L2 · STOP_REASON 分支 ] 看 stop_reason 决定下一步: end_turn → return text,循环退出 tool_use → 回到 STEP 1,继续转 max_tokens → 重试 / 扩 max_tokens context_exceeded → 触发 Compaction refusal / pause / stop_seq → 业务侧降级 / 保存状态 7 LAYERS [L1] API Client [L2] Loop ★ [L3] Tools [L4] Context [L5] Permission [L6] Hooks ↑ [L7] Subagent ↑ 在 L3 里

这就是"7 层 Harness"——把这些细节系统化的产物。Anthropic 官方 Claude Agent SDK 把这 7 层全部实现了,所以你写 agent 不用从头造

12 / 18

第四章 · 产品落地深挖

工具系统 — 内置 + MCP + 失败处理

工具是 Harness 和真实世界的接口。3 个产品里马上要面对的问题:怎么定义、太多怎么办、失败怎么办。

问题 ①

怎么定义?

本质就是 JSON Schema——告诉模型工具叫什么、要什么参数。

3 种来源:

  • · 进程内函数最简单
  • · 子进程命令Bash / Edit
  • · MCP 协议外部进程,可复用
问题 ②

太多怎么办?

超过 30 个,模型选错率明显上升。

4 个策略:

  • · 合并抽象
  • · 渐进披露
  • · Subagent 隔离
  • · 命名优化

优先合并,不是堆砌

问题 ③

失败怎么办?

不要抛异常——把错误包成 is_error: true,让模型自己决定。

3 道防线:

  • · 事前 PreToolUse hook 阻断
  • · 事中 is_error 让模型自决
  • · 事后 输出截断 + 错误改写

让模型看得懂,不回传 stack trace。

产品落地三条铁律:工具数 ≤ 30 · JSON Schema 写清楚 · 失败包成 is_error 让模型自决

13 / 18

第四章 · 现实里没那么简单

一行代码,省 70% 的钱

# 给长前缀加一行:
{
  "type": "text",
  "text": "...一段长背景...",
  "cache_control": {"type": "ephemeral"}
}
INPUT TOKEN 价格
10%

命中后降到原来的

首 TOKEN 延迟
1/3

大约降到

100 轮任务节省
90%+

是常态

不开 caching
100%
开 caching
23%

20 轮任务实测:节省约 77%

改动量:每个长前缀 block 加一行 | 风险:几乎为零
这是 agent 工程里 ROI 最高的单点优化。

14 / 18

第四章 · 产品落地深挖

记忆体系 — 三大 Harness 框架对比

Claude Code、OpenClaw、Hermes Agent 三大框架的"记忆体系"做法各有取舍——选型时要先看记忆怎么管。

OFFICIAL

Claude Code

Anthropic 官方 · 长会话 + 深度开发

分层文件记忆

  • · CLAUDE.md项目级
  • · USER.md用户级
  • · MEMORY.md跨会话

特点:启动自动加载,前缀稳定 → Caching 命中率最高

OPEN-SOURCE

OpenClaw

开源 · 可换存储后端

插件式记忆源

  • · 文件同 Claude Code 风格
  • · 数据库Postgres / SQLite
  • · 向量库语义检索 / RAG

特点:记忆 provider 可换,能接入企业现有数据栈

LIGHTWEIGHT

Hermes Agent

NousResearch · 任务级 / 短会话

任务内 context

  • · 单层不分项目 / 用户
  • · 任务结束即清默认不持久
  • · Subagent 横向并发核心能力

特点:记忆极轻,跨会话靠业务方自己持久化

选型口诀 · 长会话深开发 → Claude Code · 企业自托管 → OpenClaw · 横向并发短任务 → Hermes

15 / 18

第五章 · 对我们的启发

CLAUDE.md 的魔力

—— 先从团队约定的一份 CLAUDE 标准开始

打开 Claude Code 写一句话它就懂——背后是 harness 启动时偷偷做的三件事。CLAUDE.md 是项目的"长期记忆"

启动时做的三件事
① 扫 CLAUDE.md 项目根目录 项目说明 / 规则 ② 读 USER.md + MEMORY.md 个人偏好 + 跨会话 ③ 拼 塞进 messages[] 最前面 system: CLAUDE.md + USER.md user: 你刚刚问的问题 assistant: ...(之后会出现) 进入 while True 主循环
写 vs 不写 CLAUDE.md
维度 不写 写了
新会话 重新交代结构 开局即"懂"
输出 临时文件乱丢 按约定路径输出
长会话成本 反复塞背景 命中 Caching 10%
团队协作 各自上下文 git 共享一份

为什么这套设计成立:CLAUDE.md = harness 的 file-system memory,是 messages 的稳定前缀 → 命中缓存 → 写得长也几乎不要钱
研发同学的最小行动 → 自己代码仓库写一份 CLAUDE.md,5 行也行,提交到 git。

16 / 18

第五章 · 对我们的启发

不要焦虑

—— 软件工程是 Harness 的 Harness

讲完 Harness,最容易冒出一个念头:"我会不会被取代?" 回头看 30 年——每一次新工具出现,没让工程师消失,反而让工程师更值钱

1994
设计模式
驾驭对象
2002
DDD / Fowler
驾驭企业架构
2010
微服务
驾驭分布式
2017
DDIA
驾驭数据系统
2026
Harness
驾驭智能体
本质

每一代都在驾驭复杂性

方法论在变(设计模式 → 微服务 → Harness),但抽象 + 结构化的能力一脉相承。
学会驾驭新工具的工程师,永远值钱。

怎么做

把基础功用在 Harness 上

  • · 写 CLAUDE.md ← 文档能力
  • · 设计工具集 ← 模块化 + 接口
  • · 做 Permission ← 安全工程
  • · 定 Hooks ← AOP

工程师永远不会失业,但码农可能会失业。
码农是写代码的;工程师是设计并驾驭复杂系统的。Harness 让工程师变得更稀缺,不是更便宜。

17 / 18

第五章 · 对我们的启发

团队知识沉淀

—— 互相蒸馏,让团队更 NB

Harness 公开、模型公开、Skill 公开——真正不可复制的是团队领域知识。 业内多家公司已经把这件事工程化,我们也该跟上。

沉淀什么

6 类核心知识

· 需求 PRD
· 接口设计文档
· 概要设计文档
· 测试文档
· 会议纪要
· 业务知识

原则:不只是"写出来归档"——更要能被 Agent 检索到。

过去散落在飞书 / Confluence / 钉盘里的文档,对 LLM 是"看不见的"。沉淀的目的,是让它们成为团队 Agent 的"工作记忆"。

怎么用

向量化 + MCP 暴露

文档
向量化
MCP Server
团队 Agent

全队共用的知识 MCP,所有人写 agent 时都能直接调用——沉淀 = 复用

本质

全队共用的知识库 = 互相蒸馏,每个人的经验复利到全队

怎么保存 · 怎么建立机制

① 集中存储

独立 git 仓库,不寄生业务项目

② 模板规范

PRD / 接口 / 概要设计统一模板

③ 定期沉淀

每个项目交付时强制归档(CR 卡点)

④ 自动衰减

季度 lint,过期 / 矛盾自动降级或归档

18 / 18

收尾

行动清单 & Q&A

个人 · 这一周
1

抄一遍那 50 行最小骨架,跑通,亲手观察 stop_reason 怎么变

2

自己代码仓库写一份 CLAUDE.md,5 行也行,提交到 git

3

需要正经写 agent,选 Claude Agent SDK

团队 · 这一季度
1

每个项目根目录都有 CLAUDE.md

2

建一个独立的团队知识仓库(git 管理)

3

每周输出 ≥1 条 pitfall,每月跑一次 lint

下一场可讲
  • → MCP 协议详解
  • → Context Management 实战
  • → Permission Pipeline 与 Auto Mode
  • → 团队知识库怎么搭(5×5×3 落地)
参考资料
  • · Anthropic · Effective harnesses for long-running agents
  • · Claude Code Docs · How Claude Code works
  • · 腾讯云开发者《一文讲透如何构建 Harness》
  • · 腾讯技术工程《Harness 不是目的,知识才是护城河》
  • · Datawhale 黄佳《万字综述 Harness 革命》

凡此过往,皆为序章

谢谢大家 · Q&A