
最近翻 Multi-Agent 相关项目时“Harness”这个词出现的频率越来越高。B站、GitHub、技术社区里DeepSeek Harness、Codex Harness、Harness Engineering 这些概念被反复提及不少教程甚至用“六十集全量讲解”“七天从小白到大神”来吸引注意力。但很多开发者的第一反应其实是这又是一个换皮新词它和 Agent 到底有什么区别我为什么要学它先给一个明确判断如果你目前只写“一个 Python 脚本 一个大模型 API 几段 Prompt”的小工具Harness 确实不是刚需。但一旦你要做多 Agent 协作要让 AI 调用外部命令要上生产环境并接受别人 review 代码Harness 就是决定项目能否稳定维护的分水岭。它解决的不是“模型能不能回答”而是“AI 应用能不能像正规软件工程一样被控制、被审计、被回滚”。这篇文章不会尝试复述六十集视频的全部内容而是提炼一条最精简的 Harness 工程学习路径先搞懂 Harness 为什么出现再拆开 Agent、SandBox、Skill 这几个核心概念最后用一套可以在本地直接跑通的最小工程代码把 Multi-Agent 流水线串起来。读完你会得到三个东西一个能直接运行的最小 Harness 工程骨架一份判断 Harness 相关报错和异常的思路一套在真实项目里使用 Harness 的工程建议。1. Harness 工程到底解决什么问题先从一个真实场景说起。假设你正在做一个“文档自动审校”功能大模型读取一篇技术文档找出其中疑似不合格的句子然后给出修改建议。单 Agent 版本很容易写几行代码就能跑通。但是产品经理很快会加需求文档需要先拆分章节不同章节分配给不同的审校 Agent审校 Agent 还需要调用一个内部的拼写检查脚本最后要有一个汇总 Agent 输出报告。这个时候问题就来了。状态放在哪里多个 Agent 之间怎么传数据如果某个 Agent 调用外部命令它有没有权限删除文件一次运行失败后怎么知道是哪一步出了错如果把大模型换掉整个流程还能不能复现这些问题已经不再是“怎么设计 Prompt”能够解决的而是一个软件工程问题。Harness 就是在这里介入的。Harness 工程可以理解成一套“AI 应用运行控制框架”它把一次 AI 任务的输入、模型调用、工具执行、结果校验、日志、权限、失败重试全部纳入统一的运行环境。没有它Agent 是一堆松散的函数有了它Agent 是在一个可监控、可限制、可恢复的“驾驶舱”里工作。所以 Harness 工程真正降低的不是模型调用成本而是三类软件工程成本可控成本每个 Agent 能做什么、不能做什么不再依赖模型自觉而是由 Harness 的权限边界决定。可观测成本每次运行的输入输出、每个 Skill 的执行情况、每次沙箱命令的结果都有日志。可复用成本模型调用、文本处理、命令执行等原子能力抽成 Skill不同 Agent 可以共享。如果你已经在做 Multi-Agent 应用并且开始被“结果不稳定、问题定位难、代码不可维护”困扰那么 Harness 就是下一步该补的课。2. Harness、Agent、SandBox、Skill 的概念与区别很多文章把这四个词混在一起讲导致新手越看越乱。这里我用工程类比把它们拆开。2.1 Agent决策和执行者Agent 是具备自主决策和执行能力的程序模块。它接收一个任务对象根据输入决定“接下来调用哪个模型能力、哪个工具”然后把结果写到输出对象里。Agent 可以简单到只做一次文本分类也可以复杂到包含多轮推理循环。关键认知Agent 解决的是“做什么、怎么做”但它没有义务保证“这次运行是否合法、是否能复现”。2.2 Skill可复用的原子能力Skill 指的是可以被多个 Agent 复用的原子能力比如“统计文本字数”“查找关键词”“调用拼音纠错接口”“读取指定数据库表”。Skill 本身不感知业务流程它只对外提供一个稳定的输入输出接口。为什么需要 Skill如果没有这个抽象层每个 Agent 都会自己写一套文本处理代码很快就出现重复实现和参数不一致。把能力抽成 Skill 之后Agent 只关心“调用哪个 Skill”而不关心 Skill 内部怎么实现。2.3 SandBox隔离执行环境SandBox 是执行外部命令或不可信代码时的隔离环境。它限制 Agent 能访问哪些资源、能执行哪些命令、能读写哪些目录从而避免模型生成的命令直接操作到真实系统。注意一点SandBox 不等于“安全”。它更多是 Harness 工程里的一道护栏。生产环境中的沙箱需要依赖容器、虚拟机或独立进程来实现而不是一个简单的判断开关。2.4 Harness连接模型与工程的控制壳Harness 是包裹在 Agent、Skill、SandBox 之外的“工程壳”。它定义了任务如何创建、Agent 之间如何传递数据、Skill 如何注册、SandBox 如何启用、日志如何输出。Harness 和 Agent 的区别是最容易被问到的。简单说Agent 是工具箱里的工人Harness 是工地管理制度。工人可以自己选工具、按顺序干活但管理制度规定了他能进哪个区域、必须戴什么安全帽、出了问题怎么追责。概念核心职责抽象层级类比Agent决策和执行任务业务逻辑层工人Skill提供可复用的原子能力能力层专用工具SandBox限制执行边界隔离层安全围栏Harness控制流程、权限、日志、恢复工程控制层工地管理制度3. 为什么 Multi-Agent 开发需要 Harness 而不是简单函数调用传统软件工程中一次业务请求的主控逻辑是开发人员写死的先调 A 接口再调 B 工具最后写响应。只要没有故障执行路径是确定的。Multi-Agent 应用改变了这一点。主控逻辑的一部分被交给了模型模型会根据输入动态决定调用哪个工具、哪条分支。这带来灵活性的同时也带来了不确定性同一段输入两次运行可能走不同的分支模型可能生成一个你从未预期的工具调用某个 Agent 的输出格式可能发生变化。Harness 工程就是在这种背景下出现的。它做的事情是把“模型自主决策”重新放回工程约束的笼子里。具体来说Harness 提供了四层约束第一结构约束。所有 Agent 之间传递的数据使用统一 Task 对象而不是随意往全局变量里塞值。这样每个节点的输入输出都是可检查的。第二权限约束。Agent 调用外部命令、访问网络、读写文件之前必须先经过 SandBox。Harness 可以在配置层面决定哪些命令允许执行哪些必须拒绝。第三流程约束。Multi-Agent 的下一步动作不再完全由模型自由发挥而是由 Harness 编排器根据当前步骤结果决定。模型在限定的分支里做选择而不是无限发散。第四可观测约束。每一步运行都记录 trace_id所有 Skill 调用和命令执行都有日志。出了问题时可以按链路回溯。没有这层约束Multi-Agent 应用很容易出现“demo 能跑、上线就崩”的情况。因为 demo 只需要展示正常路径而生产系统必须面对异常分支、权限失控、模型输出漂移这些问题。这也是为什么社区近期讨论的 DeepSeek Harness、Codex Harness 等概念本质上都不是在讨论某个神秘技术而是在讨论“如何给 AI Agent 应用加上标准化的工程壳”。4. 环境准备与工程结构这一节开始进入实操。先说明本文的示例不依赖任何特定大模型只使用 Python 标准库目的是用最小成本讲清楚 Harness 的核心结构。如果你之后要接云端大模型 API再安装对应 SDK 即可。4.1 环境要求Python 3.10 或更高版本。如果系统版本较旧请至少保证 Python 3.8 以上并把 dataclass 特性准备好。一个独立的虚拟环境避免污染全局 Python 环境。不需要 GPU不需要额外数据库。如果后续要接入云端大模型通常需要一个 API Key但从环境变量读取不要硬编码到代码里。4.2 创建项目目录建议命名为harness_demo目录结构如下harness_demo/ ├── harness.py # Harness 核心组件 ├── skills.py # Skill 实现 ├── main.py # 主流程编排 └── config └── app.json # Harness 配置在终端里执行mkdir harness_demo cd harness_demo python -m venv .venv source .venv/bin/activate如果你使用的是 Windows PowerShell激活命令改为.venv\Scripts\activate本示例只用标准库所以不需要 pip install 任何额外包。目录里的config/app.json是示例配置后面的代码会用到其中部分字段。5. 核心流程拆解从 Task 到 SandBox在写完整代码之前先理解 Harness 工程四条核心链路。这一步非常关键因为后面所有代码都是在实现这四条链路。5.1 Task统一的数据载体Task 对象是 Harness 工程中跨 Agent 传递数据的标准结构。它至少包含任务 ID、输入数据、输出数据和元信息四部分。为什么不能用普通 dict 代替dict 确实很方便但到后期你会发现没人知道某个 key 是谁写入的、value 是什么类型、某个 Agent 改没改过别人的数据。Task 把输入和输出明确分开还允许通过 meta 记录运行过程信息这是工程化最基本的一步。5.2 SkillRegistry技能注册中心SkillRegistry 是一个注册表Agent 不直接依赖某个函数而是通过名字查找技能。这样带来的好处是替换技能实现版本时不需要修改 Agent 代码多个 Agent 可以共享同一套能力技能的调用情况可以被统一埋点记录。5.3 BaseAgentAgent 的接口契约BaseAgent 定义了一个 run 方法输入 Task输出 Task。每个具体 Agent 只需要实现自己的 run 逻辑。这个接口约束保证了 Multi-Agent 流水线中的每个节点都可以被单独测试、单独替换。5.4 Sandbox外部命令执行护栏Sandbox 是命令执行的统一出口。在实际 Harness 工程中Agent 不应直接使用 subprocess 调用系统命令而是统一走 Sandbox。Sandbox 内部可以判断开关状态、执行超时、记录日志。本示例给出一个教学级实现生产环境请使用容器或独立进程做真正隔离。6. 完整示例一个带 Skill 的 Multi-Agent 审校流水线现在把上面的设计落到代码里。示例场景是“文档审校流水线”先从原文中拆出文本片段再对片段做关键词质检最后汇总报告。为了可读性三个核心组件放在同一个文件里。6.1 Harness 核心组件文件路径harness_demo/harness.py# harness_demo/harness.py from abc import ABC, abstractmethod from dataclasses import dataclass, field from typing import Any, Callable, Dict, List dataclass class Task: 任务对象整个 Harness 内部传递数据的唯一载体。 task_id: str input_data: Dict[str, Any] output: Dict[str, Any] field(default_factorydict) meta: Dict[str, Any] field(default_factorydict) class SkillRegistry: 技能注册表把可复用的原子能力集中管理。 def __init__(self): self._skills: Dict[str, Callable] {} def register(self, name: str, func: Callable) - None: self._skills[name] func def execute(self, name: str, **kwargs) - Any: if name not in self._skills: raise KeyError(fSkill not found: {name}) return self._skills[name](**kwargs) def list_skills(self) - List[str]: return list(self._skills.keys()) class BaseAgent(ABC): Agent 基类流水线中的每个节点都继承它。 def __init__(self, name: str, role: str, registry: SkillRegistry): self.name name self.role role self.registry registry abstractmethod def run(self, task: Task) - Task: 处理输入把结果写回 task.output。 pass class Sandbox: 最小沙箱仅用于教学演示真实项目应使用容器或独立进程隔离。 def __init__(self, enabled: bool True, timeout: int 10): self.enabled enabled self.timeout timeout def execute(self, command: str) - str: if not self.enabled: raise RuntimeError(Sandbox is disabled. Refuse to run external command.) import subprocess result subprocess.run( command, shellTrue, capture_outputTrue, textTrue, timeoutself.timeout, ) return result.stdout.strip()这个文件里的代码有两处需要重点说明。第一SkillRegistry.execute通过名字查找函数。如果技能未注册直接抛KeyError这个异常会在完整代码中让我们快速定位“技能没注册”的问题。第二Sandbox.execute是教学实现它只是用subprocess执行命令。所以在注释里我特别强调真实项目必须做成进程级隔离或容器级隔离。这段代码更重要的是展示“所有命令执行必须经过统一出口”的设计思想。6.2 Skill 定义文件路径harness_demo/skills.py# harness_demo/skills.py def upper_text(text: str) - str: 把文本转成大写。 return text.upper() def count_words(text: str) - int: 统计文本单词数。 return len(text.split()) def find_keyword(text: str, keyword: str) - bool: 检查文本是否包含指定关键词。 return keyword in text这三个 Skill 都比较简单但已经足够演示“注册 - 调用”的完整流程。真实的 Skill 可以是一个复杂的模型调用封装、数据库查询函数或者内部 HTTP 接口。6.3 Multi-Agent 主流程文件路径harness_demo/main.py# harness_demo/main.py from harness import Task, SkillRegistry, BaseAgent, Sandbox from skills import count_words, find_keyword, upper_text # 1. 注册 Skill registry SkillRegistry() registry.register(count_words, count_words) registry.register(find_keyword, find_keyword) registry.register(upper_text, upper_text) class ExtractAgent(BaseAgent): 第一环节从原始文档中抽取待审校的文本片段。 def run(self, task: Task) - Task: raw_text task.input_data.get(document, ) fragments [ fragment.strip() for fragment in raw_text.split(.) if fragment.strip() ] task.output[fragments] fragments task.meta[fragment_count] len(fragments) return task class CheckAgent(BaseAgent): 第二环节对每个片段做关键词检查模拟模型质检。 def run(self, task: Task) - Task: keyword task.input_data.get(keyword, TODO) problems [] for fragment in task.output.get(fragments, []): if not self.registry.execute( find_keyword, textfragment, keywordkeyword ): problems.append(fragment) task.output[problems] problems return task class SummaryAgent(BaseAgent): 第三环节汇总结果输出最终报告。 def run(self, task: Task) - Task: problems task.output.get(problems, []) task.output[summary] { fragment_count: task.meta.get(fragment_count, 0), problem_count: len(problems), level: pass if len(problems) 0 else need_review, } return task def build_pipeline(): 构建 Multi-Agent 流水线。 return [ ExtractAgent(extract-agent, 抽取器, registry), CheckAgent(check-agent, 质检器, registry), SummaryAgent(summary-agent, 汇总器, registry), ] def run_pipeline(agents, task: Task) - Task: 按顺序执行 Agent前一个 Agent 的输出作为后一个的输入。 for agent in agents: print(f[{agent.role}] {agent.name} 正在处理 {task.task_id}) task agent.run(task) return task if __name__ __main__: demo_task Task( task_idtask-001, input_data{ document: Harness工程是AI应用开发的关键。TODO补充沙箱配置。Skill是可复用能力。, keyword: TODO, }, ) agents build_pipeline() final_task run_pipeline(agents, demo_task) print(结果, final_task.output[summary]) # 演示 Sandbox 命令执行 sandbox Sandbox(enabledTrue, timeout5) print(沙箱执行结果, sandbox.execute(echo hello-harness))这段代码的核心逻辑可以分成四步看。第一步注册 Skill。registry是所有 Agent 共享的技能中心CheckAgent并不直接调用find_keyword函数而是通过self.registry.execute(find_keyword, ...)来调用。第二步按顺序执行 Agent。run_pipeline函数按列表顺序把同一个Task传给三个 Agent。ExtractAgent负责拆解文本CheckAgent从 Task.output 中读取 fragments再把有问题的片段写回。SummaryAgent最终汇总。第三步体会数据传递方向。注意每个 Agent 都是修改同一个 Task 对象前一个 Agent 写入的 output 字段后一个 Agent 可以读取。这就是 Harness 工程里最基础的“结构化流水线”。第四步Sandbox 演示。主流程最后实例化一个 Sandbox 并执行echo hello-harness。在实际项目中这里应该是一个经过权限校验的命令执行入口。7. 运行结果与验证方式在harness_demo目录下执行python main.py预期输出[抽取器] extract-agent 正在处理 task-001 [质检器] check-agent 正在处理 task-001 [汇总器] summary-agent 正在处理 task-001 结果 {fragment_count: 3, problem_count: 1, level: need_review} 沙箱执行结果 hello-harness如何判断运行成功关键看三处。第一fragment_count应该等于 3说明ExtractAgent成功把文档按句号拆成了三部分。第二problem_count等于 1因为只有第二句包含TODO关键词CheckAgent的逻辑正确。第三沙箱执行结果输出hello-harness说明命令走通了沙箱出口。如果失败第一步看 Python 回溯堆栈。最常见的错误包括模块找不到、当前目录不在 Python 路径里、虚拟环境未激活。按下面顺序排查# 确认当前目录 pwd # 确认 Python 版本 python --version # 直接运行脚本 python -m main把这段最小流程跑通之后你就可以把文本处理逻辑替换成真正的大模型 API 调用把CheckAgent的规则判断换成“让模型判断片段是否合规”从而得到一个更接近生产形态的 Harness 工程。8. 常见问题与排查思路这一节整理 Harness 工程学习中最常见的五个问题。每一个问题都来自实际项目中的典型场景而不是凭空想象。问题现象可能原因排查方式解决方案启动时报 ModuleNotFoundError未激活虚拟环境或依赖未安装检查python -m pip list是否包含对应包激活虚拟环境按依赖清单安装运行提示 Skill not found技能名称拼写不一致或注册顺序晚于调用在调用前打印registry.list_skills()统一技能命名启动时先注册全部 Skill沙箱提示 disabled no sandbox配置中沙箱开关为 false或代码绕过了沙箱直接执行命令检查配置文件里sandbox.enabled的值搜索代码里的 subprocess 调用所有外部命令统一走 Sandbox默认关闭不信任命令Multi-Agent 运行很久不结束某个 Agent 内部出现循环或模型调用超时查看每个 Agent 的开始和结束日志确认卡在哪一步为每个 Agent 增加最大步数和执行超时超时后走 fallback结果不稳定同一次输入两次输出不一致模型输出格式不稳定或 Agent 依赖了未排序的数据结构打印模型原始输出与解析结果增加输出 schema 校验解析失败时进入重试或人工处理日志太多问题不好定位没有统一 trace_id无法串联同一任务的日志检查日志中是否包含 task_id在 Task 创建时生成唯一 ID所有日志统一带上该 ID这里特别说一下disabled no sandbox这类报错。它通常不是某一个框架的标准报错而是沙箱开关被关闭时的提示。出现这个信息真正的排查方向不是“如何绕过沙箱”而是“为什么你的运行环境没有启用沙箱”。是配置里没开还是当前环境不被允许执行外部命令还是安全策略默认拒绝。绕过沙箱是最危险的做法。9. 最佳实践与工程建议跑通最小示例只是第一步。真正要在项目里使用 Harness 工程下面几条建议值得直接进团队规范。9.1 使用 Task 对象传递数据不要堆全局变量Multi-Agent 项目一旦超过两三个 Agent全局变量就会变成维护灾难。你很难追踪某个字段是什么时候被谁写入的也容易在并发场景下出数据竞争。统一使用 Task 对象并在每次 Agent 执行前后打印核心字段是成本最低的排查手段。9.2 所有外部命令必须经过 Sandbox且默认拒绝如果你的 Agent 会执行 Python 脚本、Shell 命令或调用内部工具不要直接写 subprocess。先定义一个 Sandbox 接口然后在接口内部做命令白名单、超时、日志记录。默认情况下未显式放行的命令应该被拒绝而不是默认允许。9.3 Skill 尽量“小、纯、可测试”Skill 设计得越小越好。一个 Skill 只做一件明确的事输入输出都是基础类型或简单的数据结构。这样你可以为每个 Skill 单独写单元测试。如果一个 Skill 内部既调数据库又调大模型还改文件一旦出错定位成本会非常高。9.4 给模型调用设置超时和重试把大模型调用接进 Harness 后一定要考虑超时。模型服务偶尔变慢某个 Agent 阻塞会导致整个流水线卡死。比较稳妥的做法是每个模型调用设置独立的超时时间超时后先重试一次仍然失败则把异常信息写进 Task.meta并走降级分支。9.5 生产环境控制权限与审计如果 Harness 工程部署在服务器上并且 Agent 能接触生产数据请务必遵守最小权限原则。具体来说沙箱进程使用独立操作系统用户不共用应用主账号。数据库账号只授予任务所需的最小读写权限。所有 Agent 执行的关键操作写审计日志。涉及删除、修改配置等敏感操作先经过测试环境验证再制定回滚方案。不要相信模型永远会按预设路线走。Harness 的价值恰恰在于即使模型走了一条意外路径你的权限边界、执行沙箱和审计日志仍然能拦住风险。9.6 评估集先行不少 Multi-Agent 项目“跑起来容易改起来崩溃”的主要原因是没有评估集。建议在写第一个 Harness 流水线时同时准备 20 到 50 条典型输入每条输入都标注期望输出。每次修改 Agent 逻辑或更换模型后都跑一遍评估集对比成功率。这一步会让你的工程迭代速度明显提升。10. 总结与后续学习方向Harness 工程不是一个炫技词汇它是 AI 应用从小工具走向可维护系统过程中必然出现的工程层。把视角拉高一点传统的软件工程控制的是代码逻辑而 Harness 工程要控制的不只是代码还有模型的决策过程和工具执行过程。Multi-Agent 的下一步不是堆更多的 Agent而是把现有的 Agent 放进一套可观测、有边界、可回滚的 Harness 里。如果你正打算学习这一方向我的建议是从本文这个最小 demo 开始先把 Task、SkillRegistry、BaseAgent、Sandbox 四个概念串起来跑通一次完整的流水线再逐步把它替换成你真实项目里的模型调用和工具链。B站上那些几十集的教程可以作为备查资料但真正重要的是在本地亲手跑通一个最小闭环哪怕它只有三四十行代码。能稳定复现的小工程比一百集看过即忘的视频更能说明问题。下一步可以继续深入的方向有三个第一把示例里的 Sandbox 换成真正的容器隔离方案并补充命令白名单策略第二为 Task 增加版本号和链路追踪完善运行日志第三接入一个大模型 API把 CheckAgent 的规则判断改成模型语义判断让流水线真正具备“智能”。每走一步你都会更清楚地感受到 Harness 工程与普通脚本开发之间的差距。