什么是Harness Engineering?——最小的Agent Harness

什么是Harness Engineering?——最小的Agent Harness Harness Engineering 背景介绍1. 概念起源Harness套具/挽具 这个词最早来自领域含义马具控制马的装备让马的力量被正确引导电气工程测试套具Test Harness- 连接被测系统与测试设备的中间层软件测试自动测试框架负责执行测试用例并收集结果在 AI Agent 领域Harness 沿用了同样的隐喻Harness 控制 AI 能力的挽具让 AI 的输出被正确引导和执行2. 发展脉络阶段一纯 API 调用2022-2023用户 → LLM API → 返回文本 → 结束模型直接输出结果无法执行真实操作应用场景有限阶段二工具调用 Function Calling2023用户 → LLM API → 决定调用工具 → Harness 执行 → 返回结果 → 结束模型开始具备调用工具的能力但缺乏循环和状态管理阶段三完整 Agent Harness2023-至今用户 → [模型思考 → Harness 执行 → 模型思考 → Harness 执行 → ...] → 完成循环直到任务完成完整的上下文管理多工具协作错误恢复机制为什么需要 Harness核心问题模型不是万能的模型能做的模型实际不能做的理解自然语言真正读取你的文件生成代码真正运行代码推理和规划真正访问互联网写回答真正修改数据库Harness 的价值 把说和做分离模型负责决策Harness 负责执行。模型只负责思考和决策Harness 负责真正执行、管理上下文、控制循环。┌─────────────────────────────────────────────────────────────┐ │ Harness │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 工具注册表 (TOOL_FUNCTIONS) │ │ │ │ - read_file() - calculate() - ... │ │ │ └─────────────────────────────────────────────────────┘ │ │ ▲ │ │ │ 调用工具 │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Agent Loop (循环) │ │ │ │ 1. 发消息给模型 │ │ │ │ 2. 模型决定: 调用工具 or 给出答案 │ │ │ │ 3. 如果调用工具 → Harness 执行 → 返回结果 │ │ │ │ 4. 循环回到第1步 │ │ │ └─────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌───────────────┐ │ AI Model │ │ (Claude) │ │ 思考和决策 │ └───────────────┘以下是代码示例 最小 Agent Harness 示例 核心思想:模型只负责思考和决定调用什么工具, harness 负责真正执行工具、管理上下文、控制循环。 import json from anthropic import Anthropic client Anthropic() # 需要设置 ANTHROPIC_API_KEY 环境变量 # ---------- 1. 工具定义 ---------- # harness 里真正干活的函数。模型自己不能读文件、不能算数, # 它只能请求harness 帮它做,这就是工具存在的意义。 def read_file(path: str) - str: 读取一个文本文件的内容。 try: with open(path, r, encodingutf-8) as f: return f.read() except Exception as e: return f读取失败: {e} def calculate(expression: str) - str: 计算一个数学表达式,比如 2 * (3 4)。 try: # 生产环境别用 eval,这里只是演示。用 ast 或专用库更安全。 result eval(expression, {__builtins__: {}}, {}) return str(result) except Exception as e: return f计算失败: {e} # 工具的注册表:把函数名映射到真正的 Python 函数 TOOL_FUNCTIONS { read_file: read_file, calculate: calculate, } # 工具的 schema:告诉模型有哪些工具、怎么调用、参数是什么。 # 这段描述写得好不好,直接影响模型用不用得对工具。 TOOL_SCHEMAS [ { name: read_file, description: 读取指定路径的文本文件内容, input_schema: { type: object, properties: { path: {type: string, description: 文件路径} }, required: [path], }, }, { name: calculate, description: 计算一个数学表达式并返回结果, input_schema: { type: object, properties: { expression: { type: string, description: 要计算的表达式,如 2 * (3 4), } }, required: [expression], }, }, ] # ---------- 2. Agent Loop ---------- # 这是 harness 的心脏。 def run_agent(user_task: str, max_turns: int 10) - str: # messages 就是上下文。harness 的核心工作之一 # 就是决定这个列表里放什么、怎么增长。 messages [{role: user, content: user_task}] for turn in range(max_turns): # (a) 把当前上下文 工具列表发给模型,让它决定下一步 response client.messages.create( modelclaude-sonnet-4-20250514, max_tokens1024, toolsTOOL_SCHEMAS, messagesmessages, ) # (b) 把模型这一轮的输出加回上下文(关键:保持对话连续) messages.append({role: assistant, content: response.content}) # (c) 判断模型是想调用工具,还是已经给出最终答案 if response.stop_reason ! tool_use: # 模型没有要求调工具 → 认为任务完成,返回文本 final_text .join( block.text for block in response.content if block.type text ) return final_text # (d) 模型要求调用一个或多个工具 → harness 真正执行它们 tool_results [] for block in response.content: if block.type tool_use: fn TOOL_FUNCTIONS[block.name] print(f [调用工具] {block.name}({block.input})) result fn(**block.input) # 真正执行 tool_results.append({ type: tool_result, tool_use_id: block.id, # 对应上模型的请求 id content: result, }) # (e) 把工具执行结果作为一条 user 消息喂回去,进入下一轮 messages.append({role: user, content: tool_results}) return 达到最大轮次,未完成任务。 # ---------- 3. 跑起来 ---------- if __name__ __main__: answer run_agent(帮我算一下 (128 256) * 3 等于多少) print(\n最终回答:, answer)1. 工具定义Lines 13-70TOOL_FUNCTIONS { read_file: read_file, calculate: calculate, }为什么需要工具模型本身不能真正读取文件、做计算它只能请求Harness 帮它做工具 模型与真实世界交互的桥梁TOOL_SCHEMAS [ { name: read_file, description: 读取指定路径的文本文件内容, input_schema: {...} }, ... ]Schema 的重要性 描述写得越好模型用工具越准确。2. Agent LoopLines 73-118这是 Harness 的心脏for turn in range(max_turns): # (a) 发消息给模型让它决定下一步 response client.messages.create(...) # (b) 把模型输出加回上下文 messages.append({role: assistant, content: response.content}) # (c) 判断是调用工具还是给出答案 if response.stop_reason ! tool_use: return final_text # 完成 # (d) Harness 真正执行工具 for block in response.content: if block.type tool_use: result fn(**block.input) # 执行 # (e) 把结果喂回去进入下一轮 messages.append({role: user, content: tool_results})3. 运行示例Lines 122-125answer run_agent(帮我算一下 (128 256) * 3 等于多少)执行流程 模型收到任务 → 我需要调用 calculate 工具 Harness 执行 → calculate((128 256) * 3) → 1152 模型收到结果 → 给出最终回答答案是 1152总结Harness Engineering 的兴起是 LLM 应用成熟的必然结果从玩具到生产力 - LLM 需要真正做事而不只是说话安全与可控 - 把危险操作隔离在 Harness 层可靠性保障 - 模型可能出错Harness 需要兜底工程化需求 - 可测试、可监控、可迭代