尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Harness工程实战:从零构建最小可运行的AI Agent框架

Harness工程实战:从零构建最小可运行的AI Agent框架 最近后台一直有读者在问一类问题网上突然冒出来很多“Harness 工程”教程从 DeepSeek Harness 到 Codex Harness再到各种 Multi-Agent 实战视频为什么大家都在讲这个词有人说它是新一代 AI Agent 开发的核心也有人说它不过是把函数调用包装了一层到底该不该学我的判断很明确Harness 不是某个具体工具也不是某个模型厂商的专属名词它是 AI 应用从“能跑通 Demo”走向“能上线”的关键工程层。你花一周看完 60 集视频如果没有亲手写过一次 Harness 的主循环大概率只是记住了一些名词。这篇文章不打算复刻任何一套视频而是把 Harness、Multi-Agent、Sandbox、Skill 这条技术线拆开带你写一个最小可运行的 Harness 示例并讲清楚它在大模型应用开发里到底处于什么位置。读完你会得到三样东西第一能准确说清 Harness 和 Agent 的区别第二本地跑通一个包含技能注册、沙箱执行、控制循环的最小框架第三知道后续往真实大模型、多智能体方向扩展时优先补哪些工程能力。全程不需要 API Key 也能运行适合第一次接触 Harness 工程的开发者。1. Harness 工程真正解决了什么问题很多人刚接触 Agent 项目时都会产生一个错觉模型能搞定一切。用户说“帮我查一下上海天气再算一个表达式的值”模型确实能理解这句话但理解之后呢它需要先去调用天气查询接口拿到结构化数据再写一段 Python 代码去计算最后把结果整理成自然语言。如果这些动作都靠开发者在代码里写死写十个功能点还能忍受写到一百个时系统就会变成一个巨大的 if-else 工厂。传统做法大概长这样开发者在代码里为每个功能写一个 handler在主流程里根据用户意图做一轮轮 if-else 分发工具执行结果用临时变量保存多轮对话时把历史消息和工具结果手动拼回 prompt。这种模式在三个工具以内是能工作的。一旦工具数量超过十个、需要多步决策、还要接入多角色 Agent 协作代码就会被状态管理和消息拼接拖垮。你今天加了一个工具明天要在三个分支里同步修改模型某个步骤返回了非预期格式你根本不知道是模型选错了工具还是工具返回的数据结构变了。Harness 工程解决的正是这一层问题。它把“模型决策”和“工具执行”拆开再通过一套控制循环把它们接起来模型只负责根据任务和上下文决定下一步动作包括调用哪个技能、传什么参数、什么时候结束真正的执行放到受限的 Sandbox 里技能注册中心负责管理所有可执行能力。系统复杂度从“一堆 if-else”变成了“一个可编排的循环”。这意味着一个很重要的变化新增能力不再需要改主流程只需要注册一个新的 Skill。排查问题时每一步决策和执行都有结构化记录而不是靠人肉读日志。这正是 Harness 工程在大模型应用开发里越来越被重视的原因。2. Harness、Agent、SandBox、Skill 到底是什么关系网络上搜索“harness和agent区别”的人很多说明大部分开发者被这些名词绕晕过。这里先给一个比较直观的分层方式Agent你想要的最终结果是一个能感知环境、做出决策、采取行动的系统。Harness承载 Agent 的工程外壳负责控制循环、状态管理、工具调度和日志追踪。SkillAgent 可以调用的最小能力单元相当于给 Agent 预装的“岗位技能包”。SandBox技能执行的隔离环境防止不可信代码或模型误操作影响宿主系统。用一个现实中好理解的类比Agent 是“员工”Harness 是“项目管理制度”Skill 是“员工掌握的技能清单”Sandbox 是“带隔离玻璃的操作间”。员工能力再强没有流程和操作间你也不敢让他直接在生产环境乱动。概念一句话定位举例Agent目标系统闭环智能体能自主完成数据分析任务的助手Harness工程外壳控制 Agent 运行执行循环、状态管理、调度逻辑Skill能力单元供 Agent 调用查询天气、执行代码、查数据库SandBox隔离环境保证执行安全Docker 容器、受限 subprocess这四者的关系是Harness 是骨架Agent 是骨架之上跑起来的业务闭环Skill 是闭环中可复用的动作Sandbox 是所有动作执行的边界。2.1 Agent 是目标不是实现如果你已经熟悉大模型基础调用可能会觉得 Agent 没那么神秘无非是大模型加工具调用加多轮记忆。这个理解没有错但它把 Agent 只当成了一组代码功能。在工程视角下Agent 应该是一个闭环系统感知输入、形成计划、调用工具、观察结果、修正计划。你写的是这个闭环而不只是一个能回答问题的聊天机器人。闭环的关键是“观察结果”这一步。很多简单 Demo 里模型生成了一串工具调用开发者把结果直接打印给用户看没有把工具结果再次交给模型。这在单轮场景下没问题但一旦任务需要连续调用多个工具模型就看不到中间结果后续决策就没有依据。Agent 的完整闭环必然要求“执行结果回到模型上下文”。2.2 Harness 是控制框架Harness 负责搭建这个闭环的骨架。典型 Harness 会包含四部分规划器决定下一步动作执行器调用具体 Skill记忆模块保存历史决策和结果安全策略决定哪些操作允许、哪些必须人工审批。很多开源 Agent 框架里的 agent loop本质就是 Harness 的一部分。结合社区里 DeepSeek Harness、Codex Harness 的相关讨论来看这些工具团队在公开资料中强调的 Harness普遍都包含“控制循环 工具协议 沙箱”的组合。所以不要把它理解成某个 SDK它更像一套工程范式不同厂商只是用不同方式实现了它。2.3 SandBox 是隔离执行环境模型生成代码之后直接在宿主机上执行是生产环境大忌。你无法预知模型会生成什么样的代码尤其当任务来自第三方输入时恶意代码风险是真实存在的。Sandbox 把所有不可信执行放进受限环境比如临时目录加超时控制或者 Docker 容器限制网络和内存。演示项目用 subprocess 可以真实项目至少上容器级隔离。2.4 Skill 是需要注册、可被调度的能力模块Skill 不只是“写一个函数”它需要有元信息名字、描述、参数说明、返回值说明。因为大模型并不知道你的代码库里有哪些函数它只能根据 Skill 的语义描述来决定什么时候调用、怎么调用。Skill 注册中心本质上是在给模型建立一张“能力地图”模型通过这张地图知道系统里有什么工具、每个工具能做什么、参数怎么传。这也是 Skill 和普通函数的区别所在。普通函数是给程序员用的Skill 是给模型用的。所以 Skill 必须可以被模型通过自然语言理解注册信息里描述写得越精确模型调用的准确率越高。3. 为什么 Harness 工程是 AI 应用开发的分水岭当前 AI 大模型应用开发的热度还在持续上升大量开发者正在从“学习大模型 API”转向“学习大模型应用工程”。这个转变里Harness 就是分水岭你会写 prompt、会调 function calling只能算入门你能把工具调用、上下文管理、沙箱隔离、多步规划组织成一个稳定系统才叫工程化。3.1 可控性真正上线后你会发现模型输出的不确定性是最大的敌人。同一个任务跑十次模型可能给出十种不同的工具调用序列。Harness 通过把决策点收敛到 Planner、把执行收敛到 Skill、把风险操作收敛到 Sandbox 和授权策略让系统在“模型行为漂移”时仍然有兜底。没有这种分层时一旦模型调用了一个不该调用的工具或者把参数传错了问题会直接暴露到生产环境。有了 Harness决策和执行被拆开你可以在决策层加规则过滤在执行层加权限校验双重保险。3.2 可观测性没有 Harness 时工具调用和模型输出混在日志里很难定位“是模型选错工具还是工具返回格式不对”。有了 Harness每次决策、每次执行、每次返回都可以结构化记录。哪个 Skill 被调了几次、平均耗时多少、失败率多少、哪一步 token 消耗最大这些数据都能统计出来。可观测性直接决定了系统能不能在真实业务里长期维护。一个只能“跑起来”的 Agent 和一个可以“调优”的 Agent差别就在日志粒度上。3.3 可扩展性新增一个能力不需要改主流程只需要注册一个新的 Skill。扩展到多个 Agent 时每个 Agent 共享同一套 Sandbox 和安全策略。这也是为什么 Multi-Agent 系统基本都会有一个中心化的 Harness 或编排器——没有这个编排层多个 Agent 之间的通信、权限、资源隔离会迅速失控。另外提一句在很多垂直行业方案里模型的地位反而没那么突出。比如农业大模型类项目真正复杂的是安全接入土壤传感器、气象数据、灌溉控制这些外部系统。这恰好是 Harness 层要解决的事模型负责决策Harness 负责让决策安全地触达真实世界。4. 环境准备与前置条件4.1 基础环境本文的示例代码依赖很少只要满足以下条件即可操作系统Windows、macOS、Linux 均可Python 版本3.9 及以上建议创建虚拟环境避免污染全局 Python 环境演示部分不需要 API Key不需要额外安装第三方库。准备命令如下mkdir agent_harness_demo cd agent_harness_demo python -m venv venv source venv/bin/activate # Windows 系统执行 venv\Scripts\activate4.2 项目目录结构我们将创建一个最小的 Harness 系统目录结构如下agent_harness_demo/ ├── main.py └── harness/ ├── __init__.py ├── core.py ├── skills.py └── sandbox.py其中 core.py 存放 Harness 主循环skills.py 存放技能注册中心sandbox.py 存放沙箱执行器main.py 负责把各部分组装起来并运行一次示例任务。5. 完整示例写一个最小 Harness 系统为了让没有 API Key 的同学也能跑通我先用固定决策序列的 DemoPlanner 代替真实模型。等你理解了框架流程之后再把 Planner 替换成真实大模型的 function calling。这样拆开的好处是能把“Harness 工程本身的问题”和“模型调用的问题”分开排查也更容易理解每个组件各自的职责。5.1 技能注册中心harness/skills.py# 文件路径harness/skills.py class SkillRegistry: 技能注册中心管理所有可被 Agent 调用的能力。 def __init__(self): self._skills {} def register(self, name, description, handler): self._skills[name] { description: description, handler: handler, } def list_skills(self): return [ {name: name, description: info[description]} for name, info in self._skills.items() ] def execute(self, name, payload, sandboxNone): skill self._skills.get(name) if skill is None: raise KeyError(fSkill not found: {name}) return skill[handler](payload, sandboxsandbox)这里的核心设计是Skill 必须有名字、描述、处理函数三个要素。描述字段是给未来大模型 Planner 看的名字是模型返回的 action 里要携带的标识处理函数才是真正干活的代码。这样设计的目的是把“模型能感知的能力清单”和“代码里实际存在的函数”解耦。5.2 沙箱执行器harness/sandbox.py# 文件路径harness/sandbox.py import os import subprocess import tempfile import textwrap class Sandbox: 极简沙箱用临时目录 超时控制来隔离不可信代码执行。 真实项目建议使用 Docker、gVisor、Firecracker 等更完整的隔离方案。 def __init__(self, timeout5): self.timeout timeout def execute_python(self, code): with tempfile.TemporaryDirectory() as tmpdir: script_path os.path.join(tmpdir, skill_script.py) with open(script_path, w, encodingutf-8) as f: f.write(textwrap.dedent(code)) try: result subprocess.run( [python, script_path], capture_outputTrue, textTrue, timeoutself.timeout, ) return { stdout: result.stdout, stderr: result.stderr, returncode: result.returncode, } except subprocess.TimeoutExpired: return { stdout: , stderr: fexecute timeout, limit{self.timeout}s, returncode: -1, }沙箱的核心意图有两个临时目录隔离和超时控制。模型生成的代码被写进临时目录里的独立脚本执行完成后目录自动清理宿主项目不会留下垃圾文件。超时控制防止模型生成的代码出现死循环避免把整个服务拖垮。需要注意这个 Sandbox 只是教学演示它不是安全边界。真实生产环境应该使用容器级隔离限制网络、CPU、内存、磁盘并且禁止挂载生产数据目录。5.3 Harness 主循环harness/core.py# 文件路径harness/core.py class Harness: Harness 主循环负责按 Planner 的决策调度 Skill并维护历史记录。 def __init__(self, planner, skills, sandboxNone, max_rounds5): self.planner planner self.skills skills self.sandbox sandbox self.max_rounds max_rounds self.history [] def run(self, task): for round_index in range(1, self.max_rounds 1): print(f[R{round_index}] 规划中 ...) decision self.planner.plan(task, self.history) action decision.get(action) if action finish: self.history.append({action: finish}) return { status: ok, result: decision.get(result), history: self.history, } if action call_skill: skill_name decision.get(skill) payload decision.get(payload, {}) print(f[R{round_index}] 调用技能: {skill_name}, 参数: {payload}) result self.skills.execute(skill_name, payload, sandboxself.sandbox) self.history.append( {round: round_index, skill: skill_name, result: result} ) return {status: max_rounds_exceeded, history: self.history}Harness 主循环是系统最核心的部分。它做的事情很简单向 Planner 要一个决策判断是结束还是调用技能调用完把结果记入 history然后进入下一轮。关键工程点在于所有决策结果都写进了 history后续 Planner 可以基于历史决策做出更合理的规划。max_rounds 是兜底机制防止 Agent 陷入无限循环。5.4 组装入口main.py# 文件路径main.py from harness.core import Harness from harness.sandbox import Sandbox from harness.skills import SkillRegistry def run_python(payload, sandboxNone): Skill在沙箱中执行一段 Python 代码。 code payload.get(code, ) return sandbox.execute_python(code) def get_weather(payload, sandboxNone): Skill查询城市天气。这里返回模拟数据真实项目应接入天气服务 API。 city payload.get(city, unknown) return {city: city, weather: sunny, temperature: 25} class DemoPlanner: 演示用 Planner按固定顺序返回决策用于验证 Harness 框架本身。 真实项目应替换为支持 function calling 的大模型 Planner。 def __init__(self, decision_sequence): self.decision_sequence decision_sequence self.step 0 def plan(self, task, history): if self.step len(self.decision_sequence): decision self.decision_sequence[self.step] self.step 1 return decision return {action: finish, result: {status: done}} def main(): sandbox Sandbox(timeout5) skills SkillRegistry() skills.register( nameget_weather, description查询指定城市的天气, handlerget_weather, ) skills.register( namerun_python, description在沙箱中执行 Python 代码, handlerrun_python, ) planner DemoPlanner( decision_sequence[ {action: call_skill, skill: get_weather, payload: {city: Shanghai}}, {action: call_skill, skill: run_python, payload: {code: print(1 2 * 3)}}, {action: finish, result: {status: ok, summary: task completed}}, ] ) harness Harness(plannerplanner, skillsskills, sandboxsandbox, max_rounds5) result harness.run(查询上海天气并计算 1 2 * 3) print([Result], result) if __name__ __main__: main()main.py 把前面三个组件组装成了完整系统。执行逻辑是先注册两个 Skill然后让 Planner 依次返回“查天气”“执行 Python 代码”“结束”三个决策Harness 按对应顺序调度。这个示例虽然简单但完整体现了 Harness 的骨架注册能力、控制循环、历史记录、结束判定。6. 运行结果与效果验证6.1 运行命令在虚拟环境激活后执行python main.py6.2 预期输出[R1] 规划中 ... [R1] 调用技能: get_weather, 参数: {city: Shanghai} [R2] 规划中 ... [R2] 调用技能: run_python, 参数: {code: print(1 2 * 3)} [R3] 规划中 ... [Result] {status: ok, result: {status: ok, summary: task completed}, history: [...]}从输出里可以看到Harness 一共执行了三轮第一轮调用 get_weather第二轮调用 run_python第三轮结束。history 中会保存每次技能执行后的完整结果包括沙箱的 stdout、stderr 和 returncode。6.3 如何判断框架跑通判断标准有三点两个 Skill 都被成功调用没有出现 Skill not found 异常run_python 的沙箱返回结果里包含 stdout 字段值为7附近的内容整个流程在 max_rounds 之内正常结束返回值是 statusok而不是 max_rounds_exceeded。6.4 如何把 DemoPlanner 替换成真实大模型理解了框架之后就可以把固定序列的 DemoPlanner 替换成真实大模型。下面是一个思路示意以 OpenAI 风格的 function calling 为例# 替换 DemoPlanner 的示意代码以 OpenAI 风格 function calling 为例 # 注意不同 SDK 的写法会有差异请以官方文档为准。 import json import openai class LLMPlanner: def __init__(self, api_key, modelgpt-4o-mini): self.client openai.OpenAI(api_keyapi_key) self.model model def plan(self, task, history): tools [ { type: function, function: { name: get_weather, description: 查询指定城市的天气, parameters: { type: object, properties: {city: {type: string}}, required: [city], }, }, }, { type: function, function: { name: run_python, description: 在沙箱中执行 Python 代码, parameters: { type: object, properties: {code: {type: string}}, required: [code], }, }, }, ] response self.client.chat.completions.create( modelself.model, messageshistory [{role: user, content: task}], toolstools, tool_choiceauto, ) message response.choices[0].message if message.tool_calls: tool_call message.tool_calls[0] return { action: call_skill, skill: tool_call.function.name, payload: json.loads(tool_call.function.arguments), } return {action: finish, result: {text: message.content}}替换时有个关键点要处理模型完成技能调用后Skil 的执行结果必须以 tool message 的形式写回上下文否则下一轮模型看不到上一轮工具的结果。这个闭环在真实工程里往往比框架本身更复杂也是很多 Agent 项目效果不稳定的根因之一。7. Harness 工程常见问题与排查方法问题现象可能原因排查方式解决方案Harness 报 Skill not foundPlanner 返回的技能名不在注册中心查看决策日志和已注册技能列表检查模型返回的 function name 与注册名严格一致沙箱执行超时模型生成的代码存在死循环或长时间等待查看 stderr 中的 timeout 信息调小 timeout真实环境使用容器 CPU、内存配额多轮后效果明显变差上下文历史过长关键信息被稀释查看每次请求的 token 用量对历史做摘要压缩或滚动裁剪模型反复调用同一个技能没有把工具结果正确写回上下文检查 history 中是否包含工具结果把技能执行结果以结构化消息追加到历史多 Agent 协作时互相等待任务依赖关系没有建模缺少超时机制查看各 Agent 的调用链路耗时给每个子任务设置超时和失败重试策略模型生成了危险操作代码沙箱配置过松宿主机目录暴露检查沙箱挂载目录和网络权限禁止挂载生产数据目录高危操作走人工审批7.1 Skill not found 是最常见的接入问题这个问题在真实项目中出现的频率最高。原因通常是模型返回的工具名和代码里注册的名字不一致比如多了空格、大小写不同、或者注册名是复数而模型返回了单数。排查思路是打印已注册 Skill 清单直接和模型返回的 name 做字符串对比。7.2 上下文历史管理是调优重点很多 Agent 跑几轮后效果变差不是模型变笨了而是历史太长。每次技能结果都完整保存几轮之后消息数量就非常可观。实际项目里一般会做三件事截断过长的工具返回、摘要化旧历史、只保留最近几轮完整消息。7.3 不要迷信模型的安全能力模型本身不具备安全判断能力。它不知道自己生成的代码会造成什么影响因此沙箱和权限策略不能依靠模型自觉。凡是涉及数据库、支付、生产环境变更的操作都应该有独立的审批机制。8. Harness 工程最佳实践8.1 安全边界优先于功能丰富度做 Harness 工程时最容易犯的错误是先加功能后补安全。正确顺序是先确定边界哪些 Skill 允许模型直接调用哪些必须走审批沙箱里能不能出网宿主机的哪些目录可以挂载。边界确定之后再往里加功能。高危操作的默认策略应该是拒绝而不是放行。尤其在数据库类操作上宁可先实现只读查询也不要把写入权限直接交给模型。8.2 控制循环尽量保持简单第一个版本不要做复杂的状态机不要引入多级路由。把决策统一表示成“action skill payload”三要素先让循环稳定运行再逐步增加复杂逻辑。很多团队一上来就设计十几个状态最终调试成本远高于收益。8.3 Skill 的注册信息要标准化Skill 的描述直接决定了模型能不能正确调用它。建议每个 Skill 的注册信息包含以下要素唯一名字使用小写加下划线例如 get_weather、query_user_db一句话描述说明这个技能能做什么、不能做什么参数说明逐字段描述类型和含义输出结构说明返回值长什么样。描述不清的 Skill 很容易被模型误调用。比如一个执行删除操作的 Skill如果在描述里没有写清楚“危险操作需要审批”模型可能在处理排序需求时误选它。8.4 多 Agent 协作的前提是单 Agent 足够稳Multi-Agent 不是银弹。如果单个 Agent 的 Harness 循环都经常超时、调用错 Skill那么多 Agent 只会放大这些问题。建议顺序是先跑通单 Agent连续执行几十个代表性任务统计成功率再考虑多智能体协作。多 Agent 架构下公共 Harness 负责全局调度各 Agent 只保留自己的 Skill 集和上下文。跨 Agent 通信要么通过显式消息传递要么通过共享任务队列不要依赖隐形的全局状态。8.5 日志、追踪与评测要一起设计Harness 的好处之一就是可以把决策和执行过程结构化记录下来。每个日志条目至少包含round、decision、skill_name、payload、result、耗时。有了这些数据才能回答“系统哪里最薄弱”这个问题。评测同样重要。建议准备一个任务集每条任务标注期望调用链路和期望结果每次修改框架后跑一遍统计工具调用准确率、任务完成率、平均轮数。垂直行业场景尤其需要这类评测数据例如农业大模型项目把天气、土壤、灌溉等外部系统接入 Harness 后只有靠评测集才能判断模型在真实数据源接入后是否稳定。8.6 生产环境部署要注意资源隔离本地演示可以用 subprocess生产环境建议使用容器级隔离。每个 Agent 或每个会话独立容器限制 CPU、内存、网络、磁盘。模型生成的代码不能默认拥有宿主机文件系统访问权限也不能默认拥有外网访问权限。9. 总结与学习路径Harness 工程不是一个神秘的算法也不是某个模型的专属能力。它是一套把大模型从“会聊天”变成“能干活”的工程框架核心包括控制循环、技能注册、沙箱隔离、历史管理四个部分。Agent 是你要实现的业务闭环Skill 是能力单元Sandbox 是安全边界Harness 则是把这三者组织起来的骨架。如果你现在准备开始学习建议不要先从视频开始而是先把本文的最小示例跑通。跑通之后你再回来看任何 Harness 相关教程都能很快对应上“控制循环、Skill 注册、沙箱执行”这些概念。下一步就是把 DemoPlanner 替换成真实大模型的 function calling把一个真实工具接入 Skill 注册中心例如天气接口、数据库查询、文件处理等跑通一个带真实工具的 Agent。有了单 Agent 的基础再往 Multi-Agent 方向扩展时会顺利很多。多智能体系统本质上就是多个 Agent 共享同一个 Harness 编排层各自维护独立的 Skill 和上下文。到这个阶段重点关注任务编排、上下文隔离、权限分级和评测体系建设。最后提醒一句无论你的 Agent 业务跑得多顺生产上线之前一定先把安全边界补上。沙箱、超时、人工审批、最小权限这些工程能力才是 Harness 工程真正值钱的地方。建议收藏本文按文中顺序从最小示例开始跑通再逐步迭代成你自己的 Agent 框架。
返回列表