Harness 工程:驾驭 AI Agent 的工程化艺术
当 AI Agent 从玩具走向生产,你会发现:模型本身只占 20% 的工作量,剩下 80% 都是"如何驾驭它"。本文以「智能旅行助手」为例,聊聊什么是 Harness 工程,以及这套工程化基础设施如何让 Agent 可靠地跑起来。
一、什么是 Harness(驾驭层)?
1.1 从"调 API"到"驾驭 Agent"
早期做 AI 应用,核心就是一行 client.chat.completions.create(...)——模型是全部。
但当你试图构建一个真正可用的 Agent 系统,比如一个智能旅行助手,用户说"帮我规划 7 天云南行程,两个人,预算 1 万,帮我订机票酒店,再出一份费用清单"——你会发现问题远不止"调模型"这么简单:
- Agent 怎么知道当前用户是谁、偏好靠窗还是靠过道?
- 工具调了 200 次还没结果,怎么防止死循环?
- 上下文窗口炸了怎么办?
- 多个 Agent 之间怎么协作、怎么隔离?
- 沙箱挂了,用户就问不了"附近有什么好吃的"了?
- 用户喜欢海景房、素食这种偏好,怎么自动记住?
- 用户自己上传的"私人导游联系方式"技能包,怎么热插拔?
这些模型不解决但又必须解决的问题,催生了 Harness 这一层。
Harness(驾驭层) 是包裹在 AI Agent 外围的一整套工程化基础设施。它不是模型,不是提示词,而是让模型可靠地、可控地、可扩展地运行起来的所有工程机制的总和。
一句话概括:
模型提供智能,Harness 提供可靠性。
Harness 简史:从马具到测试工具到 AI 驾驭层
Harness 这个词本身,经历了一场跨越 60 年的语义迁移。
🐴 词源:马具(Harness)
Harness 的本意是"马具"——套在烈马身上的缰绳、马鞍和围栏。马本身有力气、能奔跑,但没有马具,骑手无法控制方向、无法制动、甚至可能被甩下来。马具不改变马的能力,但决定了马能不能被安全地使用。
这个隐喻精准地映射到了今天的 AI Agent:模型是烈马,Harness 是驾驭层。
🔧 1960s–1979:测试线束(Test Harness)
"Harness" 作为工程术语,最早出现在 NASA 阿波罗计划的飞行软件测试中。Margaret Hamilton 领导的 MIT 仪器实验室团队,在为登月飞船编写飞行软件时,需要一套基础设施来隔离测试各个模块——这套基础设施被称为"测试线束"(test harness),包括桩程序(stub)、驱动程序(driver)、测试脚本和数据夹具(fixture)。
1979 年,Glenford J. Myers 在其里程碑式的著作《The Art of Software Testing》中,正式系统化了 test harness 的概念,将其定义为"支撑自动化测试执行和验证的全部基础设施集合"。从此,test harness 成为软件工程中测试领域的标准术语。
📊 2010s:评估线束(Eval Harness)
随着机器学习的兴起,test harness 的概念被引入模型评估领域——用于标准化地跑 benchmark、收集指标、对比模型表现。这个阶段的"线束"主要关注的是"怎么科学地给模型打分"。
🤖 2025–2026:Agent 线束(Agent Harness)
"Harness" 从测试领域跳到 AI Agent 领域,关键的推动者是 Anthropic。
| 时间 | 事件 | 意义 |
|---|---|---|
| 2025 年 11 月 | Anthropic 发布《Effective harnesses for long-running agents》 | 首次将"harness"用于 AI Agent 语境,提出 initializer-agent + coding-agent 模式,解决 Agent 跨上下文窗口长时间运行的问题 |
| 2026 年 2 月 | Anthropic 发布《Building a C compiler with a team of parallel Claudes》 | 展示了多 Agent 并行的 harness 设计,多个 Claude 在统一驾驭层下协作 |
| 2026 年 3 月 | LangChain 发布《The Anatomy of an Agent Harness》 | 系统拆解 Agent Harness 的 11 个组件:编排循环、工具、记忆、上下文管理、文件系统、沙箱等 |
| 2026 年 3 月 | Anthropic 发布《Harness design for long-running application development》 | 提出三 Agent harness 架构:Planner → Generator → Evaluator,并开源配套代码库 |
| 2026 年 3 月 | arXiv 论文《Natural-Language Agent Harnesses》 | 学术界首次对 agent harness 进行形式化定义 |
| 2026 年 4 月 | LangChain 通过 harness engineering 将 DeepAgents 在 Terminal Bench 2.0 上的得分从 52.8% 提升到 66.5%,排名从第 30 名跃升至 Top 5 | 关键转折点——证明不换模型、只改 harness 就能大幅提升 Agent 表现 |
| 2026 年 4 月 | Martin Fowler 发表《Harness engineering for coding agent users》 | 将 agent harness 比喻为"控制论调速器"(cybernetic governor),结合前馈和反馈来调节代码库 |
| 2026 年 5 月 | 行业全面跟进,MongoDB、OpenAI、各路学者纷纷发表 harness engineering 相关文章 | Harness Engineering 正式成为 AI Agent 领域的核心工程范式 |
关键转折点:LangChain 的 Terminal Bench 实验
LangChain 团队的实验是 harness engineering 成为行业共识的标志性事件。他们没有换模型、没有改提示词,仅仅通过优化 harness 的六个方面——工具门控(tool gating)、自验证(self-verification)、追踪(tracing)、上下文优化、重试策略、终止规则——就把同一个 Agent 的表现提升了 13.7 个百分点。
这有力地证明了一个论断:
Agent 的可靠性,更多地由 harness 决定,而不是由模型决定。
这也解释了为什么 2026 年的 AI 工程界出现了"Harness Engineering"这个独立学科——它不是提示词工程的翻版,也不是传统的后端工程,而是专门解决"如何让 Agent 在真实世界中可靠运行"的工程实践。
1.2 Harness 的核心组成
一个完整的 Harness 工程体系通常包含以下层次:
┌──────────────────────────────────────────────────────────┐
│ 用户请求 / UI │
└────────────────────────┬─────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────┐
│ 中间件管线(Middleware Pipeline) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │上下文注入 │→│技能同步 │→│限流保护 │→│记忆更新 │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└────────────────────────┬─────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────┐
│ Agent 核心循环(Model ↔ Tools) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ LLM 调用 │←→│ 工具执行 │←→│ 状态管理 │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└────────────────────────┬─────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────┐
│ 后端抽象层(Backend) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │沙箱后端 │ │本地后端 │ │组合后端 │ │弹性后端 │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└────────────────────────┬─────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────┐
│ 持久化层(Store / Checkpoint) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │对话状态 │ │用户偏好 │ │技能存储 │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└──────────────────────────────────────────────────────────┘
上图展示了 Harness 的五层架构。下面逐一说明每一层的职责:
① 中间件管线(Middleware Pipeline)
中间件是 Harness 中最活跃的一层。它像流水线一样,在 Agent 执行的各个阶段插入自定义逻辑:
- 生命周期钩子:
before_agent(启动前注入上下文)、after_agent(回复后更新记忆)、wrap_model_call(每次 LLM 调用前后拦截修改) - 典型中间件:上下文注入、技能同步、记忆更新、摘要压缩、调用限流
中间件的核心价值是关注点分离——Agent 不需要"知道"怎么管理记忆,Harness 自动替它做。
② Agent 核心循环(Model ↔ Tools)
这一层是 Agent 的"大脑",由框架(如 LangGraph)驱动,负责:
- LLM 调用:把用户消息 + 系统提示词 + 工具描述发给模型,获取决策
- 工具执行:模型决定调用哪个工具(查航班、写文件、执行脚本),框架负责执行并把结果喂回模型
- 状态管理:维护对话历史、中间状态、检查点,支持暂停/恢复/回溯
核心循环本身不处理横切关注点——这些全交给中间件管线。
③ 后端抽象层(Backend)
Agent 需要执行代码、读写文件,但不同场景对执行环境的要求差异很大。后端抽象层提供统一的文件/命令接口,底层路由到不同的执行环境:
- 沙箱后端(OpenSandbox):代码在隔离容器中执行,安全但不一定可用
- 本地后端(LocalShell):直接在本机执行,快但无隔离
- 弹性后端(Resilient):沙箱优先,不可用时自动降级到本地
- 组合后端(Composite):按路径路由——用户偏好存本地,代码执行走沙箱
后端抽象层让 Agent 用同一套 API 操作文件,完全不感知底层是沙箱还是本地。
④ 持久化层(Store / Checkpoint)
Agent 的记忆不能只存在内存里——重启就丢了。持久化层负责把状态存到可靠的存储中:
- 对话状态(Checkpoint):每轮对话的完整快照,支持跨重启恢复、Human-in-the-Loop 暂停/继续
- 用户偏好(Store):长期记忆,如"喜欢靠窗座位、素食",按用户隔离存储
- 技能存储:用户自定义技能包(如私人导游联系方式),跨会话持久化
存储后端可以是 MongoDB、文件系统、内存——框架提供统一接口,具体实现可替换。
⑤ 声明式配置层(贯穿所有层)
除了上面四个"运行时"层,Harness 还有一个隐形的第五层:声明式配置。它贯穿所有层,用 YAML 文件定义:
- 子 Agent 是谁、能做什么(
name/description/system_prompt) - 每个子 Agent 用哪些工具(
tools,运行时动态解析) - 每个子 Agent 配哪些中间件(
middleware,差异化策略) - 安全边界和人工介入点(
interrupt_on)
声明式配置让新增一个 Agent 变成"写一个 YAML 文件",而不是"改一堆 Python 代码"。
接下来,我们用智能旅行助手的真实实现,逐一拆解每一层是怎么落地的。
二、中间件管线:Harness 的灵魂
2.1 为什么需要中间件?
回到那个旅行场景:用户说"帮我分析一下昆明、大理、丽江的酒店价格趋势,最好有对比图表"。
在行程规划师 Agent 开始工作之前,其实有很多"前置条件"需要准备好:
- 系统需要知道当前用户是谁、出行人数、预算上限(上下文注入)
- 数据分析技能包是否已同步到沙箱(技能同步)
- 用户之前偏好海景房、素食这些偏好需要加载(记忆恢复)
- 任务需要被拆解为可跟踪的步骤(Todo 管理)
Agent 完成回复之后,还有"后置动作":
- 提取对话中提到的目的地和酒店偏好,自动更新偏好文件(记忆更新)
- 统计本次调用消耗了多少 token(计量)
- 压缩过长的上下文——景点信息动辄几万字(摘要)
如果把这些逻辑全塞进 Agent 的提示词里,系统会迅速膨胀到不可维护。中间件就是把这些横切关注点(cross-cutting concerns)从业务逻辑中抽离出来的机制。
2.2 智能旅行助手的中间件栈
在主 Agent 的创建流程中,我们构建了一个 8 层中间件栈:
main_middleware = [
# 1. 自定义 Todo 管理(约束工作流程)
create_custom_todo_middleware(),
# 2. 用户上下文注入(身份识别)
ContextInjectionMiddleware(),
# 3. 预置技能同步(增量同步到沙箱)
SkillsSyncMiddleware(sandbox_backend),
# 4. 用户技能恢复(恢复用户创建的技能)
UserSkillsRestoreMiddleware(sandbox_backend, USER_SKILLS_DIR),
# 5. 上下文摘要(自动压缩 + 主动压缩工具)
build_summarization_middleware(backend, SUMMARY_MODEL),
# 6. 自动记忆更新(LLM 提取实体并持久化)
MemoryUpdateMiddleware(model=SUMMARY_MODEL),
# 7. 模型调用上限(防止无限循环)
ModelCallLimitMiddleware(run_limit=50),
# 8. 工具调用上限(防止资源耗尽)
ToolCallLimitMiddleware(run_limit=200),
]
每个中间件都实现了统一的钩子接口:
| 钩子 | 触发时机 | 典型用途 |
|---|---|---|
before_agent / abefore_agent |
Agent 开始运行前 | 注入上下文、同步文件 |
after_agent / aafter_agent |
Agent 完成回复后 | 自动记忆更新 |
wrap_model_call / awrap_model_call |
每次 LLM 调用前后 | 拦截/修改请求、动态过滤工具 |
执行时序如下:
用户请求
│
▼
[before_agent] ContextInjectionMiddleware → 注入 user_id、预算、出行人数
[before_agent] SkillsSyncMiddleware → 同步预置技能到沙箱
[before_agent] UserSkillsRestoreMiddleware → 恢复用户私人导游联系方式等技能
│
▼
┌─ Agent Loop ─────────────────────────────────┐
│ [wrap_model_call] CustomTodoMiddleware │
│ [wrap_model_call] SummarizationMiddleware │
│ ↓ │
│ LLM 调用 │
│ ↓ │
│ [工具调用] write_todos / compact_conversation │
└───────────────────────────────────────────────┘
│
▼
[after_agent] MemoryUpdateMiddleware → 提取"偏好海景房"等,更新偏好文件
[after_agent] 限流中间件统计调用次数
2.3 真实案例:上下文注入中间件
以 ContextInjectionMiddleware 为例,看看一个中间件长什么样:
class ContextInjectionMiddleware(AgentMiddleware):
"""将 runtime.context 中的用户信息注入到对话开头。"""
def before_agent(self, state, runtime):
ctx = getattr(runtime, "context", None)
if ctx is None:
return None
user_id = getattr(ctx, "user_id", None)
username = getattr(ctx, "username", None) or user_id
notice = (
f"【系统上下文】\n"
f"当前用户 user_id: {user_id}\n"
f"当前用户 username: {username}\n"
f"用户偏好文件路径: /memories/{user_id}/preferences.md\n"
f"\n请首先使用 read_file 读取上述偏好文件了解用户偏好。"
)
return {"messages": [SystemMessage(content=notice)]}
就这么简单的一段代码,解决了"Agent 不知道当前是谁在说话"的问题。当用户问"帮我订去昆明的机票"时,Agent 不需要调用任何工具就能知道用户身份,直接去读偏好文件——里面写着"偏好靠窗座位、素食、海景房"。
2.4 真实案例:自动记忆更新中间件
MemoryUpdateMiddleware 更复杂一些——它在 Agent 回复完成后自动触发,用 LLM 提取对话中的目的地和偏好信息,合并写入用户的偏好文件:
用户:"我下个月想去云南玩,喜欢安静的海边酒店,最好能提供素食"
↓ Agent 正常回复,规划了行程
↓ [after_agent 钩子触发]
↓ MemoryUpdateMiddleware 启动
↓ 1. 判断是否为有意义的旅行交互(跳过"你好""在吗")
↓ 2. 调用 SUMMARY_MODEL 提取实体
↓ → {"destinations": ["云南"], "preferences": ["海边酒店", "素食"]}
↓ 3. 读取 /memories/zhangsan/preferences.md
↓ 4. 合并新的偏好到 accommodation 和 diet 字段
↓ 5. 写回 store
↓
用户下次说"帮我订酒店"时,Agent 已经"记得"他要海景房和素食
Agent 不需要主动维护记忆——Harness 替它做了。
这就是 Harness 的哲学:让 Agent 专注于智能,让框架兜底可靠性。
三、后端抽象层:让 Agent"脚踏实地"
3.1 为什么需要后端抽象?
行程规划师 Agent 需要执行 Python 脚本做路线优化——安装 scipy、networkx 跑图论算法。但在企业环境中,你不能让 Agent 直接在宿主机上跑不可信的代码——太危险。你需要一个沙箱。
但沙箱又有问题:
- 启动慢,可能不可用
- 有些操作(如生成费用报告)不需要沙箱
- 用户偏好等持久化数据不该放在沙箱里
这就需要后端抽象——让 Agent 用统一的接口操作文件系统,底层路由到不同的执行环境。
3.2 智能旅行助手的四层后端架构
CompositeBackend(路径路由器)
├── /AGENTS.md, /docs/ → OpenSandbox(沙箱默认路由)
├── /memories/ → FilesystemBackend(本地持久化,按 user_id 隔离)
├── /persisted-skills/ → FilesystemBackend(本地持久化,私人导游联系方式等)
└── /download/ → FilesystemBackend(费用报告下载目录)
ResilientBackend(弹性后端)
├── 沙箱可用 → 沙箱执行(路线优化、数据分析)
└── 沙箱不可用 → 自动降级到本地执行(简单行程规划)
弹性后端(ResilientBackend)是一个有意思的设计:
class ResilientBackend(LocalShellBackend):
"""启动时不阻塞,沙箱在后台异步连接,不可用时自动降级。"""
def __init__(self, root_dir, *, sandbox_factory=None, **kwargs):
super().__init__(root_dir=root_dir, ...)
self._sandbox_backend = None
self._sandbox_lock = threading.Lock()
# 后台线程连接沙箱,不阻塞启动
if sandbox_factory is not None:
t = threading.Thread(target=self._connect_sandbox, args=(sandbox_factory,))
t.start()
def execute(self, command, *, timeout=None):
sb = self._sandbox_backend
if sb is not None:
try:
return sb.execute(command, timeout=timeout)
except Exception:
pass # 降级
return super().execute(command, timeout=timeout) # 本地执行
效果:系统启动时不卡在沙箱连接上;沙箱挂了,当地向导依然能在本地跑 API 查询推荐餐厅。用户几乎感知不到后端的切换。
3.3 组合后端:路径级路由
CompositeBackend 更进一步——它根据路径前缀把文件操作路由到不同的后端:
CompositeBackend(
default=sandbox_backend, # 默认走沙箱
routes={
"/memories/": FilesystemBackend(root_dir="/data/travel/memories"),
"/persisted-skills/": FilesystemBackend(root_dir="/data/travel/persisted-skills"),
"/download/": FilesystemBackend(root_dir="/data/travel/download"),
},
)
这样设计的好处:
| 路径 | 路由目标 | 设计原因 |
|---|---|---|
/AGENTS.md |
沙箱 | 智能体指引文件,需要与执行环境一致 |
/memories/ |
本地文件系统 | 用户旅行偏好需要持久化,不受沙箱生命周期影响 |
/persisted-skills/ |
本地文件系统 | 用户自定义技能需要跨会话持久化 |
/download/ |
本地文件系统 | 费用报告需要用户可直接下载 |
| 其他路径 | 沙箱 | 临时文件、脚本执行等 |
对 Agent 来说,它看到的是一个统一的文件系统;但在底层,不同路径的数据被存储在了最合适的位置。
四、多 Agent 编排:声明式子 Agent
4.1 问题:单 Agent 的困境
让一个 Agent 同时处理行程规划、机票预订、当地推荐、应急处理、费用报告——提示词会膨胀到上万字,工具集混杂着二十几个工具,模型在"什么都要懂"的重压下反而什么都做不精。
4.2 解法:YAML 声明式定义
Harness 工程的另一个重要体现是子 Agent 的声明式管理。每个子 Agent 是一份 YAML 文件,而不是代码:
# trip_planner.yaml
name: trip-planner
description: >
行程规划专家。负责目的地分析、路线规划、景点推荐和时间分配。
当用户需求涉及"规划行程"、"推荐景点"、"路线安排"、"几天怎么玩"时委派。
tools:
- scenic_spot_query
- distance_calculator
- weather_query
- generate_visualization
- web_search
skills:
- /skills/trip-planning/
system_prompt: |
你是旅行行程规划师,运行在隔离沙箱中。
核心职责:收集信息 → 路线优化 → 生成行程方案 → 返回结论。
运行时,loader.py 自动扫描 YAML 目录、校验必填字段、将工具名模式匹配为实际工具对象:
raw_configs = load_subagent_configs() # 扫描 *.yaml
subagents = resolve_subagent_tools( # 解析工具引用
raw_configs, available_tools, extra_middleware
)
新增一个子 Agent = 新建一个 YAML 文件。 不需要改代码、不需要重启服务。
4.3 五个专业子 Agent 的 Harness 差异化配置
不仅主 Agent 有中间件栈,每个子 Agent 也有自己专属的 Harness 配置:
| 子 Agent | 摘要中间件 | 模型调用上限 | 工具调用上限 | 后端 | 策略原因 |
|---|---|---|---|---|---|
| 🗺️ 行程规划师 | ✅ | 50 | 200 | 弹性后端 | 景点信息量大,需要反复查询对比、跑 Python 脚本 |
| ✈️ 机酒预订专家 | ❌ | 20 | 50 | 弹性后端 + HITL | 流程固定:查询→确认→下单,涉及支付需人工审批 |
| 🏮 当地向导 | ❌ | 20 | 50 | 本地后端 | 即问即答,流程简短直接 |
| 🆘 旅途应急员 | ❌ | 20 | 50 | 本地后端 | 应急处理讲究快速决策 |
| 📊 旅行报告官 | ✅ | 50 | 200 | 本地后端 | 需要汇总大量消费数据生成可视化 |
核心规律:分析类任务配宽松限制 + 摘要中间件;操作类任务配严格限制、不配摘要。既保证灵活性,又避免资源浪费。
这种分层 Harness 设计让每个子 Agent 都有最适合自己的约束和辅助机制。
五、技能系统:可插拔的能力扩展
5.1 技能 vs 工具
在 Harness 工程中,"技能"和"工具"是两个不同层次的概念:
| 维度 | 工具(Tool) | 技能(Skill) |
|---|---|---|
| 粒度 | 单个函数调用 | 一组文件 + 操作手册 + 脚本 |
| 定义 | Python 函数 / MCP 工具 | 目录(含 SKILL.md、脚本等) |
| 加载 | 静态注册到 Agent | 按需 read_file 渐进式加载 |
| 扩展 | 需要改代码 | 新增目录即可 |
比如"网页抓取"是一个技能——它包含一个 Python 脚本、一份使用说明(SKILL.md)、一个依赖清单。Agent 通过 read_file 读取 SKILL.md 后才知道怎么用这个技能。
5.2 技能同步中间件
SkillsSyncMiddleware 在每个 Agent 周期开始前,自动将本地 src/skills/ 下的技能文件增量同步到沙箱:
def _sync_files(self):
for skill_dir in local_skills_dir.iterdir():
for local_file in skill_dir.rglob("*"):
local_hash = hashlib.md5(local_content).hexdigest()
if self._last_hashes.get(cache_key) == local_hash:
continue # 未变化,跳过
# 对比沙箱文件,有变化则上传
self.backend.upload_files(files_to_upload)
检测到变化时,还会自动插入系统通知提醒 Agent 有新技能可用。
用户的私人技能(比如"私人导游联系方式列表")通过 UserSkillsRestoreMiddleware 在每次启动时恢复——这些技能按 user_id 隔离存储,不与其他用户共享。
六、记忆与状态管理:让 Agent"记住"
6.1 三层记忆体系
| 层次 | 存储内容 | 存储位置 | 生命周期 | 旅行场景示例 |
|---|---|---|---|---|
| 工作记忆 | 当前对话上下文 | LangGraph State | 单次对话 | 本轮讨论的景点和酒店 |
| 短期记忆 | 对话 Checkpoint | MongoDB | 跨重启 | 上次聊到一半的行程规划 |
| 长期记忆 | 用户偏好 | FilesystemBackend | 永久 | 靠窗座位、素食、海景房 |
6.2 自动上下文压缩
当对话超过 6 轮或上下文接近窗口上限时(行程规划场景尤其容易触发——景点信息动辄几万字),SummarizationMiddleware 自动触发压缩:
- 完整历史保存到
/conversation_history/ - 上下文中只保留摘要 + 最近几轮
Agent 也可以通过调用 compact_conversation 工具主动触发压缩——比如行程规划师在输出最终方案后,会主动压缩上下文,避免下一轮对话时"记着"几万字的历史景点列表。
七、安全护栏:Harness 的底线
Harness 工程的另一个关键职责是安全兜底。
7.1 调用次数限制
ModelCallLimitMiddleware(run_limit=50) # 最多 50 次 LLM 调用
ToolCallLimitMiddleware(run_limit=200) # 最多 200 次工具调用
防止 Agent 陷入死循环或无限工具调用——比如行程规划师在"查景点→查路线→查天气→再查景点..."的循环里停不下来。
7.2 安全边界(AGENTS.md)
通过上传到沙箱的 AGENTS.md 定义不可逾越的红线:
- 不修改
/AGENTS.md本身 - 不访问其他用户的
/memories/路径 - 订单创建/支付操作必须经过机酒预订专家,不得绕过
- 技能代码必须在沙箱内验证
7.3 Human-in-the-Loop
机票预订、酒店支付等涉及真金白银的操作,通过 interrupt_on 配置触发人工审批流程:
用户:"帮我订明天去昆明的机票"
↓
机酒预订专家查询航班 → 准备好订单详情
↓
[interrupt_on 触发] → 系统暂停执行
↓
用户确认:"确认,靠窗座位"
↓
真正执行下单
Agent 不能跳过这一步——这是 Harness 的硬性约束,不是提示词的"建议"。
八、Harness 工程的哲学
回顾整个智能旅行助手的实现,Harness 工程的核心哲学可以总结为三条:
8.1 关注点分离
Agent 负责"智能",Harness 负责"可靠性"。
行程规划师不需要操心"用户是谁""偏好怎么存""上下文超了怎么办"——这些都是 Harness 的事。Agent 只需要专注于分析景点、优化路线、给出方案。
8.2 声明式优于命令式
新增一个子 Agent = 写一份 YAML,不是一行代码。
子 Agent 的定义、工具绑定、中间件配置都是声明式的。这意味着:
- 业务人员可以参与 Agent 的配置(改 YAML 比改 Python 容易)
- 配置可以独立于代码迭代
- 系统更容易理解和审计
8.3 优雅降级
沙箱挂了,用户依然可以问"附近有什么好吃的"。
ResilientBackend 的设计体现了 Harness 工程的一个重要原则:永远有 Plan B。
- 沙箱不可用?→ 行程规划师降级为本地简单规划
- MCP 工具加载失败?→ 降级为空工具列表
- MongoDB 不可用?→ 降级到内存存储
每一层都有自己的降级方案,确保系统在任何情况下都能给出响应。
九、Harness 工程的本质
说了这么多,Harness 工程到底是什么?
回到最初的那个比喻:模型是一匹烈马,聪明但不可控。Harness 就是那套缰绳、马鞍和围栏——它不改变马的智能,但让马安全地、高效地为你工作。
在 AI Agent 从 demo 走向生产的过程中,Harness 工程是最容易被忽视、却又最决定成败的部分。
它不是一个框架、不是一个库,而是一种工程思维:
- 承认模型的不确定性,用工程手段兜底
- 承认系统的复杂性,用分层架构解耦
- 承认需求的多变性,用声明式配置应对
如果你正在构建一个 Agent 系统,不妨问自己:
"我的 Harness 够厚吗?"
不是要你堆砌功能,而是要你思考:那些模型不解决但又必须解决的问题,你交给谁了?
本文基于 DeepAgents 框架的智能旅行助手实践总结。项目使用 LangGraph 编排引擎、OpenSandbox 沙箱后端、MongoDB 对话持久化构建。
作者:Work Hard Work Smart
出处:http://www.cnblogs.com/linlf03/
欢迎任何形式的转载,未经作者同意,请保留此段声明!
浙公网安备 33010602011771号