开篇:什么是 Agent?什么是 Harness?

2026 年初,Mitchell Hashimoto(HashiCorp 联合创始人)在一篇博客中提出了 “Engineer the Harness” 的概念。随后几天内,OpenAI 发布了 Harness Engineering 实践报告,LangChain 发表了 The Anatomy of an Agent Harness,Martin Fowler 也跟进了分析文章。“Harness Engineering” 一夜之间成了 AI 工程领域的热词。

但热度归热度,很多人对 Agent 和 Harness 的关系仍然模糊。这篇文章先把概念理清,再用一个真实项目带你看它们是怎么落地的。

Agent:动态自主决策的系统

先说 Agent。Agent 不是"调 API 的脚本",也不仅仅是一个 ReAct 循环。

按照 Anthropic 在 Building Effective Agents 中的定义,Agent 的核心属性是 LLM 动态自主决策——模型自己决定下一步做什么、调用哪些工具、什么时候结束,整个过程由环境反馈驱动,而不是由预定义的代码路径控制。

这和 Workflow 形成对比。Workflow 是预定义的编排——你在代码里写好"先做 A、再做 B、如果 X 就做 C"。Agent 则是把决策权交给 LLM,让它根据当前状态自主规划执行路径。

ReAct(Reasoning + Acting)是 Agent 的一种经典实现模式,但不是唯一的。Agent 还可以包括规划(Planning)、子 Agent 委派、记忆检索、验证循环等更复杂的行为。Harrison Chase(LangChain CEO)在 2025 年提出的 “Deep Agent” 概念,就是指那些拥有复杂基础设施支撑的 Agent。

一句话:Agent 的本质是"LLM 拿着工具自主干活"。

Harness:让 Agent 可靠工作的一切工程手段

再说 Harness。

Hashimoto 的原始定义很务实:每当你发现 Agent 犯了一个错,你就工程化一个方案让它再也不犯这个错。 这些方案的总和,就是 Harness。

但"Harness 到底是什么"这个问题,业界目前有两种理解:

视角一:Harness 在 Agent 内部(LangChain)

LangChain 在 The Anatomy of an Agent Harness 中给出的公式:

Agent = Model + Harness

“A harness is every piece of code, configuration, and execution logic that isn’t the model itself.”

在这个框架里,Harness 是 Agent 的内部组成部分——模型之外、Agent 之内的一切基础设施。裸模型不是 Agent,加上 Harness 之后才变成 Agent。

Agent(LangChain 视角)

Harness(模型之外的一切)

Prompt & 工具配置

记忆与上下文管理

编排逻辑

验证循环

🧠 Model
LLM 推理核心

视角二:Harness 在 Agent 外部(OpenAI / Hashimoto)

OpenAI 在 Harness Engineering 和 Hashimoto 的原文中,Agent 本身已经是一个完整的实体(模型 + 推理循环),Harness 是围绕 Agent 的外部环境——AGENTS.md 文档、Lint 规则、沙箱、自检脚本、模块边界约束等。

Harness(Agent 外部的环境)

Agent(已有的完整实体)

🧠 Model

推理循环 + 工具调用

AGENTS.md
规则文档

Lint / 测试
自动验证

沙箱 / 执行环境

模块边界
约束与防护

两种视角的本质区别
LangChain OpenAI / Hashimoto
Harness 位置 Agent 内部,包裹 Model Agent 外部,包裹 Agent
Agent 是什么 Model + Harness 的组合体 已有的完整工具(如 Codex、Claude Code)
典型场景 从零构建一个 Agent 应用 使用现有 Agent 产品,工程化其运行环境
Harness 的例子 Prompt 模板、状态管理、RAG 管线 AGENTS.md、Lint 规则、沙箱、自检脚本

这两种视角并不矛盾,只是站在不同的抽象层:

  • 如果你在构建 Agent(比如用 LangChain/LangGraph 开发),你会自然地把 Harness 理解为 Agent 内部的基础设施——因为你在搭建模型周围的一切。
  • 如果你在使用 Agent(比如用 Codex、Claude Code 写代码),你会自然地把 Harness 理解为 Agent 外部的环境——因为 Agent 对你来说是个黑盒,你能控制的是它运行的环境。

核心共识是一致的:Harness 是所有让 LLM 可靠工作的非模型推理部分。 区别只在于你把"Agent"的边界画在哪里。

为什么需要 Harness Engineering?

因为 Agent 在真实场景中不够可靠。

Demo 阶段,裸 Agent 表现惊艳——给它几个工具,写几句 prompt,它就能跑起来。但一旦上生产环境,问题就来了:

1. 可靠性问题。 Agent 的行为是概率性的。同样的输入,它可能走不同的推理路径,调用不同的工具,产出不同的结果。在 Demo 里这叫"灵活",在生产环境里这叫"不可控"。用户说"确认",Agent 有时理解成确认操作,有时理解成闲聊——你能接受 95% 的成功率吗?在交易、医疗、运维这些场景里,答案是不能。

2. 成本与延迟问题。 每次 LLM 调用都有时间和金钱成本。很多请求根本不需要 LLM 介入——状态查询、确认操作、格式转换——但如果所有逻辑都塞在 Agent 里,每条消息都要跑一遍完整的推理循环。

3. 可观测性问题。 Agent 是个黑盒。它为什么调了这个工具?为什么选了这个参数?出错时怎么定位?没有 Harness 提供的日志、追踪、审计机制,调试 Agent 就像蒙着眼睛修车。

4. 上下文管理问题。 模型有上下文窗口限制。随着对话变长、知识增多,如何决定哪些信息送给模型、哪些丢弃、什么时候做摘要?这些不是模型能自己解决的。

5. 交互与环境问题。 模型只能输入和输出文本,但实际场景中 Agent 需要读写文件、执行命令、操作浏览器、调用外部 API。Codex 要和 PC 上的代码仓库交互,Claude Code 要在终端执行命令并解析输出,Devin 要操控浏览器和 IDE。如何把模型的文本输出转化为真实世界的操作?如何把环境的反馈转化为模型能理解的输入?这整个交互层——沙箱、文件系统访问、命令执行管线、流式 UI 反馈——都是 Harness 要解决的。

6. 安全与边界问题。 Agent 能调用工具意味着它能产生副作用——写数据库、发消息、执行代码。如何限制它的能力边界?如何防止 prompt injection 导致的越权操作?这些需要在 Agent 之外建立防护。

这些问题的共同点是:靠调 prompt 解决不了。 你可以在 prompt 里写"请不要调用危险工具",但这是概率性的约束——模型可能遵循,也可能不遵循。

Hashimoto 的实践经验一针见血:每次 Agent 犯错,不要去改 prompt 碰运气,而是工程化一个确定性的方案。

这就是 Harness Engineering 的核心:构建可靠、可扩展、可复现的基础设施,让我们能真正"看清"模型的能力边界和行为特征,而不是凭感觉判断模型好不好。它是模型开发闭环中不可或缺的一环。


理解了这些概念之后,我们来看一个真实项目是怎么做的。PAI 是一个从零构建的 Agent 应用,所以它天然属于 LangChain 视角——我们在搭建模型周围的一切基础设施,Harness 是 Agent 的内部组成部分。

本文使用的代码仓库:Lsogod/personal-ai-pai,如果对你有帮助欢迎点个 Star

项目简介:PAI 是一个基于 LangChain / LangGraph 构建的个人 AI 助理,支持智能记账、日程提醒、长期记忆和多端同步(Web + 微信小程序)。

分支说明

  • main — 多节点路由架构(Router → 专业领域节点)
  • feat/single-agent — 单 Agent + 全工具集架构(本文基于此分支)

前置阅读:如果你对工具系统还不熟悉,建议先看 从真实项目拆解 Agent 工具系统

下面这张图是 PAI 的 Harness 全貌——对照上面的七个组件,看看一个真实项目中每个组件对应了什么:

🔧 PAI 的 Harness

短路命中

短路未命中

交付完整上下文

🤖 Model(LLM 推理核心)

调用

结果

GPT-4o / Claude
ReAct 自主决策循环

28 个 @tool
记账·日程·记忆·技能…

System Prompt & 工具配置
角色设定 + toolsets.py 工具注册

持久化存储
Redis Checkpointer + PostgreSQL

记忆与检索
长期记忆 + 对话历史

上下文管理
render_conversation_context()
16 轮窗口 + 超窗口摘要

编排逻辑
StateGraph 路由 + 短路机制

验证循环
图片预分析 + pending 确认流

可观测性
审计钩子 + 流式双通道

直接返回
零 LLM 调用


一、Harness 的第一个职责:状态管理与路由

PAI 的 Harness 用 LangGraph 的 StateGraph 实现。整个图只有三个节点:

setup_stage < 3

setup_stage >= 3

Entry
入口

Onboarding
新用户引导

Agent
主推理节点

条件判断是一个纯逻辑函数:新用户的 setup_stage < 3,走引导流程;已完成设置的用户,进入 Agent。

def _route_setup(state: GraphState) -> str:
    """Pure logic gate — no LLM call."""
    if state.get("user_setup_stage", 0) < 3:
        return "onboarding"
    return "agent"

注释里的 “no LLM call” 是刻意写的——提醒每个读代码的人:这里不该有 LLM 调用。路由判断是 Harness 的活儿,不是 Agent 的活儿。

状态通过 Redis Checkpointer 持久化。断线重连、服务重启后,对话的 user_idconversation_idsetup_stage 这些数据不会丢。代码里做了优雅降级——Redis 连不上就回退到内存存储,开发环境不用为了跑一个 Agent 先搭 Redis。


二、Harness 最有价值的设计:短路机制

进入 Agent 节点之后,Harness 不会立即创建 Agent,而是先做一轮短路检查

什么是"短路"?举个真实场景:

用户发了一张小票图片 → Agent 识别出三笔消费,生成预览 → 用户回复"确认"

这个"确认"需要 LLM 来理解吗?不需要。Redis 里已经存了待确认状态(pending_ledger),用户的下一条消息只要命中这个状态,就直接交给专用处理器——插入数据库、清除 pending 状态、返回"已记账"。

整个过程零 LLM 调用。

✅ 有短路 ≈ 100ms

用户说确认

检测 pending

直接处理

返回

❌ 没有短路 ≈ 2-3 秒

用户说确认

Agent 推理

调工具

返回

节省的不仅是 token 成本,更关键的是延迟——一次 LLM 推理通常 1-3 秒,短路可以在毫秒级完成。

PAI 里有两类短路:

  • 待确认账单pending_ledger):小票 OCR 识别后等用户确认
  • 待确认日程pending_reminder_plan):日程计划生成后等用户确认

还有一个设计细节:如果用户这次发了新图片,即使有 pending 状态也不走短路。因为新图片意味着用户可能在发一张新的小票,而不是在确认上一张。这种边界条件的处理,就是 Harness 的价值——在 prompt 里写"如果有新图片就忽略 pending 状态",远不如一个 if not current_image_urls 可靠。


三、Harness 的第二个职责:上下文组装

短路检查通过后,Harness 开始为 Agent 准备"工作包"。这是 Harness 和 Agent 之间的交接面——Harness 把所有信息收集好、格式化好,打包成一个 System Prompt 交给 Agent。

这个工作包包含七层信息:

内容 来源
身份 用户昵称、AI 助手名称、角色设定 数据库
图片预分析 图片类型、OCR 文字、结构化摘要 预调用 analyze_image
工具使用原则 工具调用规范、错误处理策略 硬编码模板
行为约束 最大调用次数、安全边界 硬编码模板
会话上下文 最近 16 轮对话 + 超窗口摘要 GraphState.extra
长期记忆 用户偏好、习惯,带优先级标签 PostgreSQL
技能文档 用户启用的自定义技能说明 技能系统

图片预分析:为什么在 Harness 里做?

用户发了一张图片说"帮我记一下",Agent 需要先知道这是小票还是风景照,才能决定调 analyze_receipt 还是 analyze_image。如果不预分析,Agent 的第一轮推理就是盲猜,很可能浪费一轮工具调用。

Harness 在创建 Agent 之前,先调一次 analyze_image 做预分析,把结果注入 System Prompt。Agent 拿到的上下文已经包含了"这是一张超市小票,有 3 笔商品"这样的结论,第一轮就能直接调用正确的工具。

省掉一轮工具调用 = 省掉一次 LLM 推理。 这种优化只能在 Harness 层做,因为 Agent 还没创建。

上下文渲染的关键设计

会话上下文由 render_conversation_context() 渲染,它支持灵活配置——包不包含助手消息、包不包含长期记忆、上下文窗口多大。这些决策由 Harness 做出,Agent 完全不感知。

Agent 的认知边界 = System Prompt 的内容。Harness 决定了这个边界。

Agent 不需要知道记忆是从 PostgreSQL 还是 Redis 来的,不需要知道摘要是怎么生成的,不需要知道上下文窗口是 16 轮还是 32 轮。它只看到一个已经渲染好的字符串。


四、Agent 层:自主推理与工具调用

上下文准备好后,Harness 才创建 Agent。在 PAI 里,Agent 用 create_agent() 创建,是一个标准的 ReAct 循环:

用户消息:帮我记一下午饭 35

LLM 推理:需要调用 ledger_insert

执行 ledger_insert(item=午饭, amount=35)

工具返回:已记录

LLM 推理:工具成功,可以回复了

已帮你记了一笔:午饭 35 元

每一轮 LLM 推理,模型会做一个判断:调工具,还是直接回复? 如果调工具,执行完工具后把结果喂回去,进入下一轮。如果直接回复,循环结束。硬限制 recursion_limit=12,同时在 System Prompt 里软约束"最多调用 6 次工具"——双重保险。

工具可见性:Harness 的权限控制

Agent 能看到哪些工具,不是 Agent 自己决定的,而是 Harness 通过 toolsets.py 在代码层面控制。

主 Agent 拥有全部 28 个工具。但短路处理器(ledger_manager_nodeschedule_manager_node)只能看到自己领域的工具子集——记账处理器看不到日程工具,日程处理器看不到记账工具。这些处理器不是 StateGraph 的独立节点,而是 main_agent_node 内部的短路分支——当检测到 pending 状态时,直接调用对应的处理器,跳过主 Agent 的创建。

这种隔离不依赖 prompt 约束(“请不要调用日程工具”),而是在代码层面根本不注册那些工具。比 prompt 约束可靠得多。


五、流式双通道:文本和工具事件的统一推送

Agent 推理不是等到全部结束再返回结果。PAI 用 astream_events 实现了实时流式推送,而且同时推送两种信息:

通道 内容 客户端表现
文本流 LLM 输出的每个 token 打字机效果,逐字显示回复
工具事件 工具调用的开始和结束 显示"正在记账…"“查询完成 ✓”

两个通道走的是同一个流,通过特殊前缀 \x00TOOL_EVENT: 区分。

客户端 SSE 流(单通道) Agent 客户端 SSE 流(单通道) Agent \x00TOOL_EVENT: ledger_insert 开始 显示「正在记账…」 \x00TOOL_EVENT: ledger_insert 完成 显示「记账完成 ✓」 已帮你记了一笔: 逐字显示文本 午饭 35 元 逐字显示文本

为什么不用单独的 WebSocket 通道?因为工具事件和文本流有严格的时序关系——用户需要先看到"正在查天气…",然后看到天气结果出现在回复文字里。如果走两个独立通道,时序对齐就变成了一个分布式时钟同步问题。走同一个流,顺序天然保证。

这个设计决策看起来不起眼,但在实际开发中省掉了大量的前端时序同步代码。


六、可观测性:不侵入业务代码的审计机制

每次工具调用都需要记录审计日志——谁调了什么工具、参数是什么、耗时多久、成功还是失败。但这个逻辑不应该写在每个工具函数里面。

PAI 用了闭包 + ContextVar 的模式:在 main_agent_node 入口创建审计钩子,通过 Python 的 ContextVar 注入到工具执行链中。工具代码完全不知道审计钩子的存在,它只是被调用、返回结果,审计在外层自动发生。

这个模式的好处是三重的:

  1. 工具代码保持纯净——不混入日志逻辑
  2. 生命周期可控——每个请求有自己的钩子实例,通过 ContextVar 做协程级隔离,不会跨请求泄漏
  3. 可插拔——测试时不设置钩子,审计逻辑就不执行

ContextVar 在这里至关重要。FastAPI 是异步架构,同一进程内可能有几十个请求并发执行。线程局部变量(threading.local)在异步场景下会互相污染,而 ContextVar 是协程安全的——每个协程有自己的上下文副本。


七、两种 Agent 模式:同一个 Harness 的不同 Agent

PAI 在两个分支上演进了两种完全不同的 Agent 模式。它们共享同一个 Harness。

模式 A:Router + 专用节点(main 分支)

每条消息先经过一个 Router(轻量 LLM 调用),分类成"记账"“日程”"闲聊"等意图,然后路由到对应的专用节点。每个节点只能看到自己领域的工具。

记账

日程

闲聊

...

用户消息

Harness

Router
LLM 意图分类

ledger_manager
记账工具子集

schedule_manager
日程工具子集

chat_manager
通用工具子集

其他节点

适合场景:LLM 工具选择能力较弱时,通过工具隔离降低误调风险。每个节点的 prompt 更精简、更专注。代价是每条消息多一次 LLM 调用(路由),以及跨领域任务需要额外编排。

模式 B:单 Agent 全工具(feat/single-agent 分支,线上运行)

没有 Router,Agent 直接看到全部 28 个工具,自主决策调什么。

用户消息

Harness

短路检查

Agent
全部 28 个工具
自主决策

适合场景:LLM 足够强(GPT、Claude 等),在 20+ 工具的场景下依然能精准选择。零路由开销,跨领域任务一轮循环内链式完成。

怎么选?

这不是一个"哪个更好"的问题,而是你的 LLM 够不够强的问题。

模型工具选择能力强 → 单 Agent 更高效。模型容易"选错" → Router 隔离更安全。

关键是:两种模式切换的成本只在 Agent 层。 Harness 的状态管理、短路机制、上下文组装、审计日志——全部不变。这就是分层的价值。


八、总结:从 PAI 中提炼的 Harness Engineering 原则

回到 Hashimoto 的原始洞察:每次 Agent 犯错,就工程化一个方案让它再也不犯。 PAI 的每个 Harness 组件都是这个思路的体现:

1. 能用 if 解决的,不要用 LLM

短路机制是这个原则的极致体现。确认流程、状态检查、权限判断——确定性逻辑比 LLM 快 100 倍,且 100% 可靠。这不是"优化",而是通过确定性的基础设施明确划定了模型的介入边界——哪些事情需要模型,哪些根本不需要。

2. 控制 Model 的认知边界

Agent = Model + Harness,而 Model 能看到什么完全由 Harness 决定。System Prompt 的内容、工具的可见性、上下文的窗口大小——这些都是 Harness 的配置项,不是 Model 的选择。在代码层面不注册某个工具,比在 prompt 里写"请不要调这个工具"可靠得多。

3. 验证优于祈祷

图片预分析是一种验证——在 Model 推理之前先确认输入类型,避免盲猜。pending 确认流也是一种验证——用确定性的状态检查替代 LLM 对"确认"的语义理解。每一个验证环节都在让模型的行为变得更可观测、更可复现。

4. 可观测性是 Harness 的必备组件

你无法改进你无法观测的东西。审计钩子让每次工具调用都有迹可循,流式双通道让用户实时感知 Agent 的工作状态。这些不是锦上添花,而是 Harness 的基础能力。

5. Harness 是稳定的,Model 是可换的

PAI 在两个分支上运行着完全不同的 Agent 模式——Router 多节点 vs 单 Agent 全工具。切换的成本只在 Model 层,Harness 的状态管理、上下文组装、审计日志、流式推送全部不变。这就是 Agent = Model + Harness 这个公式的实际价值:Model 可以随时换,Harness 是你的长期资产。


写在最后

回到开篇的两种视角——无论你把 Harness 理解为 Agent 内部的基础设施(LangChain:Agent = Model + Harness),还是 Agent 外部的运行环境(OpenAI / Hashimoto),核心共识是一样的:

Harness Engineering = 构建可靠、可扩展、可复现的基础设施,让我们能真正"看清"模型的能力边界和行为特征。

PAI 作为一个从零构建的项目,更接近 LangChain 视角:我们在搭建模型周围的一切——状态管理、短路机制、上下文组装、工具可见性控制、流式推送、审计钩子。这些就是我们的 Harness,它和 Model 一起构成了 Agent。

但如果你是在使用 Codex、Claude Code 这类现有 Agent 产品,你做的事情本质也是 Harness Engineering——写 AGENTS.md、配 Lint 规则、搭沙箱环境、设计自检脚本——只不过你的 Harness 是在 Agent 外面,而不是里面。

不管边界画在哪里,Harness Engineering 的价值是不变的:它让你不再凭感觉判断"模型行不行",而是通过可观测的基础设施,清楚地看到模型在哪些场景下可靠、在哪些场景下需要约束,从而做出工程化的决策。

如果这篇文章对你有帮助,欢迎去 GitHub 仓库 点个 Star,感谢!!!

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐