Harness Engineering的落地实践 摘要Harness Engineering 最近很火但我翻了数十篇文章大部分都在讲它的概念极少人去讲解它的落地实践。今天这篇文章我将大致讲解什么是 Harness Engineering并讲解一个采用 Harness Engineering 架构设计、基于 LangChain Deep Agents 框架实现的项目落地核心代码实现以及我们日常如何将这个概念运用到日常开发中。三个阶段的演进Prompt Engineering提示词工程主要优化单次交互的质量重点是一句提示词该怎么写模型才更容易给出想要的结果。Context Engineering上下文工程则动态构建知识、记忆、RAG解决模型看什么的问题减少幻觉、提高检索命中率。Harness Engineering驾驭工程重点构建整个运行环境解决模型怎么把长链路任务稳定做完的系统级问题。核心定义Harness 原意是马具、缰绳。放到 Agent 里它指除模型本身之外的所有东西——工具、记忆、规划、安全护栏、执行循环、状态管理。LangChain 团队将其精炼为公式Agent Model Harness模型提供思考能力Harness 负责让它真的能干活而且别出大事。传统 Agent 在应对长周期、多步骤任务时常面临上下文窗口爆炸、状态丢失、工具调用混乱、缺乏规划、无法从失败中恢复等工程难题。Harness Engineering 正是为了解决这些工程上的’稳’的问题而生的。模型决定能力的上限Harness 决定它能不能把能力变成可交付的结果。DeepAgents 中的七大能力模块在 DeepAgents 框架中Harness Engineering 被拆成一组可组合的能力模块模块核心能力Harness 价值Planningwrite_todos维护结构化任务清单将非结构化思考转为可追踪、可恢复的确定性工作流Virtual Filesystem读、写、编辑、搜索、执行文件对抗 Context Rot扩展有效上下文窗口Subagents主 Agent 委派专项任务给子 Agent上下文隔离 算力分配引入多线程和微服务架构Context Management内容卸载、摘要压缩、长期记忆自动执行上下文工程让模型注意力集中在核心决策Code Execution沙箱内执行 shell 命令Trust the LLM但隔离执行环境Human-in-the-Loopinterrupt_on在关键操作前暂停在高风险操作前插入确定性的人工控制点Skills渐进式披露按需加载领域知识知识模块化让 Agent 具备领域专长五个核心原理配置优于编码所有复杂能力被封装为可配置的中间件栈通过声明式方式组合而非编写底层控制流。开箱即用分钟级定制。对应第 1~7 节的所有模块配置中间件架构每个核心能力都是独立的AgentMiddleware在 Agent 生命周期的各阶段插入钩子动态注入工具、修改提示、管理上下文。对应第 1、3、5 节的 Planning、Subagents、Skills后端协议抽象统一的BackendProtocol抽象了文件存储和执行环境。内存、本地磁盘、云存储还是沙箱对 Agent 而言都是统一的ls、read、write接口。对应第 2 节的 Sandbox上下文工程自动化Harness 自动执行摘要压缩、内容卸载、按需加载等策略确保模型在任何时刻看到的都是最相关、最精炼的信息。对应第 6 节的 Context Engineering从智能到系统通过规划管理目标通过文件系统管理状态通过子 Agent 管理复杂度通过安全沙箱管理风险通过人在回路管理不确定性。最终将一个聪明的对话者转变为一个可靠的操作者。范式转变这套思路意味着开发范式的根本转变从前手动编写任务分解逻辑、管理上下文窗口、实现子 Agent 通信、构建安全机制。现在通过组合规划、文件系统、子 Agent、上下文管理和人工审批等能力可以快速搭建具备长任务执行基础的 Agent Harness真正进入生产前仍需围绕权限、幂等、审计、失败恢复、评测与观测补齐业务级保障。这不是另一套抽象名词而是一条具体实现路径。下面从一个真实项目看它怎样落地。1. Planning先把复杂任务拆成可追踪的待办简单任务不必每次都规划但只要目标包含调研、查询、生成报告、调用多个系统这类步骤就应该先把任务外置为结构化清单。这样做的重点不是给用户展示进度而是让 Agent 自己始终知道当前做到哪里、下一步是什么、哪些任务还没有完成。我们这里通过write_todos做长任务分步规划它将任务 ID、内容、状态、依赖关系和合并策略都变成明确字段。代码中的装饰器和导入路径取决于你的具体封装与框架版本。fromdeepagentsimporttool,ToolParametertool(namewrite_todos,description(创建并管理一个结构化的任务清单用于规划复杂目标。可以一次性创建完整列表或者增量更新现有列表。每一步执行完成后必须更新对应任务的状态。),parameters{todos:ToolParameter(typearray,description要应用的任务列表。如果提供空数组则清空所有任务。,items{type:object,properties:{id:{type:string,description:唯一标识符},content:{type:string,description:任务描述},status:{type:string,enum:[pending,in_progress,completed,cancelled],},depends_on:{type:array,items:{type:string},description:前置依赖的任务ID列表,},},required:[id,content,status],},),merge:ToolParameter(typeboolean,description是否与现有列表合并默认True否则全量替换,defaultTrue,),})defwrite_todos(todos:list[dict],merge:boolTrue)-str:# 实际逻辑将由框架自动接入这里只是占位函数体...这段代码里有三个很关键的点。第一status被限制为固定枚举值模型不能随手写一个快完成了之类的自然语言状态。第二depends_on让任务顺序可以被显式表达例如生成图表必须等待数据调研完成。第三merge让 Agent 可以增量更新计划而不是每做一步就丢掉已有任务。Harness 在这里真正做的是校验模型传来的结构是否合理并把更新后的清单写回运行时状态。计划一旦外置模型就不必把全部进度硬记在对话里中断后也能从最近状态继续。任务跑到最后一步时Agent 看到的应该是这样的状态而不是一长串失效的聊天记录Updated todo list to [ {content: 扫描技能目录并加载分析操作手册, status: completed}, {content: 收集产品规格与技术参数web_search ERP查询, status: completed}, {content: 市场价格调研爬取竞品B2B页面, status: completed}, {content: 供应商渠道对比supplier_query part_search, status: completed}, {content: 执行综合分析并生成图表, status: completed}, {content: 输出最终报告到 /analysis/ 目录, status: in_progress} ]这份清单不是任务完成后的汇报而是 Agent 执行过程中的工作内存。真正容易出问题的不是模型不会列计划而是计划只写在提示词里没有被工具、状态管理和验收流程约束住。2. Sandbox允许执行但不要把宿主机直接交出去Agent 要完成真实任务往往需要读写文件、执行命令、安装依赖甚至访问外部服务。我们可以用到 OpenSandbox 或 Cube Sandbox这一步不能省。沙箱的意义不只是防止删错文件。更重要的是把代码执行、依赖安装、临时文件和高风险工具放进受控环境让 Agent 有执行能力但没有无限制的系统权限。读操作、写操作、网络访问、密钥使用和生产资源都应该按最小权限拆开配置。对 Harness 来说安全不是在系统提示词里写一句请谨慎操作而是让危险动作在架构上根本没有越界的机会。下面是 OpenSandbox 的核心配置代码。OpensandboxBackend将其适配为 DeepAgents 的沙箱后端文件操作走BackendProtocolexecute则是沙箱后端提供的扩展能力。该适配包独立于 DeepAgents 主包生产使用时应锁定与 DeepAgents、OpenSandbox 兼容的版本。importosfromopensandbox.sync.sandboximportSandboxSyncfromopensandbox.sync.connection_configimportConnectionConfigSyncfromdatetimeimporttimedeltafromdeepagents_opensandboximportOpensandboxBackend# ---------- 方式一通过环境变量配置推荐生产环境 ----------# os.environ[OPEN_SANDBOX_API_KEY] your-secret-api-key# os.environ[OPEN_SANDBOX_DOMAIN] sandbox.example.com:8080# ---------- 方式二直接创建沙箱实例 ----------# use_server_proxyTrue 适用于 Server 在 Docker、Client 在宿主机的场景configConnectionConfigSync(use_server_proxyTrue)sandboxSandboxSync.create(python:3.11,# 指定 Docker 镜像timeouttimedelta(seconds600),# 沙箱最大存活时间ready_timeouttimedelta(seconds120),# 等待沙箱就绪的超时connection_configconfig,)# 将沙箱包装为 DeepAgents 后端Agent 通过 BackendProtocol 访问backendOpensandboxBackend(sandboxsandbox)print(fSandbox ID:{backend.id})# Agent 可以在沙箱内执行命令宿主机不受影响resultbackend.execute(pip install pandas python -c import pandas; print(pandas.__version__))print(fExit code:{result.exit_code})# 0print(fOutput:{result.output})# pandas 版本号# 错误命令也能被捕获不会影响宿主机resultbackend.execute(exit 42)print(fExit code:{result.exit_code})# 42# ---------- 清理 ----------sandbox.kill()sandbox.close()在实际项目中backend会被传入create_deep_agent的backend参数。对 Agent 来说它通过统一的文件工具操作后端当后端实现沙箱执行协议时才可以调用execute。实际隔离边界取决于你部署的 OpenSandbox 配置。这就是后端协议抽象的价值——切换沙箱、本地磁盘或云存储时业务 Agent 的调用方式可以保持一致。3. Subagents把专项任务隔离出去主 Agent 只收结论当一个任务涉及多个专业领域比如订单创建流程里既有物料调研、又有价格比对、又有合规检查如果全部塞给一个 Agent上下文会迅速膨胀工具列表也会越来越长模型注意力被稀释。Subagents 的思路类似微服务主 Agent 是编排者负责拆任务和收结果每个子 Agent 在独立的上下文中完成专项工作只返回压缩后的结论。这样主 Agent 的上下文窗口始终保持在可控范围。内联定义子 Agent最直接的方式是在create_deep_agent时通过subagents参数传入fromdeepagentsimportcreate_deep_agent# 定义一个物料调研子 Agentresearch_sub_agent{name:researcher,description:负责产品规格、技术参数和市场价格的调研返回结构化结论,model:anthropic:claude-haiku-4-5-20251001,# 子 Agent 可用更轻量的模型system_prompt:(你是一个专业的物料调研助手。根据给定的物料名称搜索产品规格、技术参数和市场价格。返回 JSON 格式的调研结论不要返回冗长的原始数据。),tools:[web_search,supplier_query,part_search],}# 定义一个合规检查子 Agentcompliance_sub_agent{name:compliance_checker,description:检查采购订单是否符合合规要求供应商资质、预算上限、审批层级,model:anthropic:claude-sonnet-4-20250514,system_prompt:(你是一个采购合规审核助手。检查订单的供应商资质、预算上限和审批层级是否满足要求。返回 pass/fail 及具体原因。),tools:[erp_query,budget_check],}# 将子 Agent 接入主 Agentagentcreate_deep_agent(modelanthropic:claude-sonnet-4-20250514,tools[order_create,order_update],system_promptSYSTEM_PROMPT,subagents[research_sub_agent,compliance_sub_agent],backendbackend,# 接入第 2 节的 OpenSandbox 后端checkpointerCHECKPOINTER,interrupt_on{order_create:{allowed_decisions:[approve,reject]},order_update:{allowed_decisions:[approve,reject]},},)YAML 配置子 Agent如果子 Agent 较多也可以用 YAML 文件管理当前 DeepAgents 不提供load_subagents需要由应用自己把 YAML 解析为SubAgent列表# subagents.yamlresearcher:description:负责产品规格、技术参数和市场价格的调研model:anthropic:claude-haiku-4-5-20251001system_prompt:|你是一个专业的物料调研助手。 根据给定的物料名称搜索产品规格、技术参数和市场价格。 返回 JSON 格式的调研结论。tools:-web_search-supplier_query-part_searchcompliance_checker:description:检查采购订单是否符合合规要求model:anthropic:claude-sonnet-4-20250514system_prompt:|你是一个采购合规审核助手。 检查供应商资质、预算上限和审批层级。 返回 pass/fail 及具体原因。tools:-erp_query-budget_checkimportyamlfromdeepagentsimportcreate_deep_agent TOOL_REGISTRY{web_search:web_search,supplier_query:supplier_query,part_search:part_search,erp_query:erp_query,budget_check:budget_check,}withopen(subagents.yaml,encodingutf-8)asf:raw_subagentsyaml.safe_load(f)subagents[{name:name,**config,tools:[TOOL_REGISTRY[tool_name]fortool_nameinconfig[tools]],}forname,configinraw_subagents.items()]agentcreate_deep_agent(modelanthropic:claude-sonnet-4-20250514,tools[order_create,order_update],system_promptSYSTEM_PROMPT,subagentssubagents,backendbackend,checkpointerCHECKPOINTER,)主 Agent 如何委派任务配置好子 Agent 后主 Agent 会自动获得一个task工具。当它判断某个子任务应该交给专家处理时会调用task并指定subagent_type主 Agent 接收帮我创建一张采购订单物料是 X → write_todos 拆解任务 → 调用 task(description调研物料X的规格和市场价格, subagent_typeresearcher) └─ researcher 子 Agent 在独立上下文中执行调研 └─ 返回结构化结论给主 Agent原始搜索结果不进入主上下文 → 调用 task(description检查此订单的合规性, subagent_typecompliance_checker) └─ compliance_checker 子 Agent 执行审核 └─ 如果发现违规触发 interrupt 等待人工确认 → 主 Agent 拿到两个结论继续执行 order_create这里的关键是上下文隔离researcher 搜索到的几十条原始网页数据、compliance_checker 查到的 ERP 记录都不会进入主 Agent 的上下文。主 Agent 只收到一句调研完成物料X规格如下…“和合规检查通过”。这就是 Subagents 对抗上下文膨胀的核心价值。4. Human-in-the-Loop信息不够时暂停不要瞎猜订单创建、文件修改、付费调用、生产数据更新这类场景最怕 Agent 缺字段还继续往下跑。正确做法是暂停展示已收集的信息和缺失字段等待人补充后从原状态恢复。fromlanggraph.typesimportinterruptimportjsondefrequest_missing_info(missing_fields:list[str],collected_data:dict)-str:当工具调用时发现必要字段缺失暂停执行并向用户展示 缺失字段等待人类补充输入后继续执行。 Args: missing_fields: 缺失的字段列表格式如 partId (物料ID, 必填) collected_data: 当前已收集到的数据 Returns: 用户补充的字段数据JSON 字符串 responseinterrupt({type:order_info_request,missing_fields:missing_fields,collected_data:collected_data,})returnjson.dumps(response,ensure_asciiFalse)这里的interrupt不是普通报错。它把当前任务停在一个可恢复点把缺失项和已收集的数据一起交给人。前提是 Agent 创建时传入了checkpointer用户补完partId后要用同一个thread_id和Command(resume...)恢复系统才会继续原任务而不是让用户从头再说一遍需求fromlanggraph.typesimportCommand config{configurable:{thread_id:order-20260723-001}}# 首次调用在 request_missing_info() 处暂停agent.invoke({messages:[{role:user,content:创建采购订单}]},configconfig)# 人工补齐字段后从同一个 checkpoint 恢复agent.invoke(Command(resume{partId:P-10086}),configconfig)下面的 YAML 则把人工确认放在订单创建和更新这两个关键节点上interrupt_on:order_create:allowed_decisions:[approve,reject]order_update:allowed_decisions:[approve,reject]人不必盯住每一步但必须控制不可逆或高风险的那一步。5. Skills把能力拆成模块按需加载这里体现了两个 Harness 原则渐进式披露。Agent 启动时只读取SKILL.md的元数据部分根据用户请求匹配到相关技能后才加载完整内容。这大幅减少了初始上下文的 Token 消耗。已配置/skills/order/作为技能来源后新增技能只需放进该目录无需再改 Agent 代码——这就是配置优于编码。角色边界。system_prompt不是写一篇百科全书而是明确告诉 Agent你是谁、在哪里运行、核心流程是什么。订单相关的详细规则不塞在全局 Prompt 里而是拆进技能目录按需加载。fromdeepagentsimportcreate_deep_agent agentcreate_deep_agent(modelanthropic:claude-sonnet-4-20250514,backendbackend,# /skills/order/ 下的每个子目录均可作为一个技能被扫描skills[/skills/order/],system_prompt(你是一个专业的 ERP 采购订单管理助手运行在隔离的 OpenSandbox 沙箱环境中。你的核心职责是理解订单操作需求 → 调用 MCP 工具执行操作 → 验证结果 → 返回确认。),)这几行配置把技能发现和职责边界写得很清楚订单相关的详细规则不需要塞在全局 Prompt 里Agent 匹配到订单任务后再去读取/skills/order/下的资料。这个项目的 skills 实现了自我进化自动下载、自动创建、功能测试与自动分配。6. Context EngineeringToken 爆了别硬塞一条复杂任务跑久了上下文一定会越来越大。检索原文、工具返回、运行日志、代码片段都堆在对话里模型真正需要记住的计划反而被淹没。这四种处理方式基本就是长任务的常用组合自动 Offloading一次读取内容过多时将大结果拆分或落到文件系统需要时再读取自动 Summarization上下文接近上限时把历史压缩为结构化摘要手动compact_conversation在适当节点主动收缩历史保留结论、约束、文件路径和未完成任务Isolation让多个子 Agent 在独立上下文中完成专项工作主 Agent 只接收压缩结果。下面是一个手动压缩上下文的示例。当前 DeepAgents 通过create_summarization_tool_middleware注册compact_conversation该工具不接收手写摘要而是由中间件生成摘要、更新状态并将原始历史按 backend 配置落盘fromdeepagentsimportcreate_deep_agentfromdeepagents.middleware.summarizationimport(create_summarization_tool_middleware,)modelanthropic:claude-sonnet-4-20250514agentcreate_deep_agent(modelmodel,backendbackend,middleware[create_summarization_tool_middleware(model,backend),],)Context Engineering 不是拼命扩大 Token而是管理模型注意力。一个好的 Harness 会让模型每一轮只看到此刻必须知道的内容而不是把整段历史原封不动再发一遍。7. Checkpoint人工介入后任务还能接着跑当 Agent 接入 SSE 事件流、异步子 Agent、用户偏好和多轮审批后内存里的状态远远不够。任务中断、服务重启、人工补字段后都需要 checkpoint 把它带回原来的执行位置。importosfromdeepagentsimportcreate_deep_agentfromlanggraph.store.memoryimportInMemoryStorefromlanggraph.checkpoint.mongodbimportMongoDBSaver# ---------- MongoDB 配置用于 Agent 短期记忆 / checkpoint ----------MONGODB_URIos.environ[MONGODB_URI]# ---------- 持久化存储 ----------STOREInMemoryStore()# 仅开发环境使用# MongoDB 持久化用于 Agent 对话状态 checkpointing。# checkpointer 的生命周期应覆盖 Agent 的调用和恢复过程。withMongoDBSaver.from_conn_string(MONGODB_URI)asCHECKPOINTER:agentcreate_deep_agent(modelanthropic:claude-sonnet-4-20250514,tools[order_create,order_update],backendbackend,checkpointerCHECKPOINTER,storeSTORE,)# 使用同一个 thread_id 调用 agent并在 interrupt 后以 Command(resume...) 恢复。这里要分清三类信息当前任务状态放 checkpoint会话中间结果放工作区或会话存储长期偏好可以沉淀到preferences.md或受控数据库。不要把三类东西混成一个记忆库否则很难排查权限、更新来源和过期数据。对应的项目目录可以保持简单/agent.md /prompts.py /persisted-skills /preferences.mdagent.md放总规则和索引prompts.py放可测试的提示词构造persisted-skills放经过审核的可复用技能preferences.md只记录允许跨会话复用的偏好。不要把一切都塞进agent.md控制在一两百行的规范通常更容易维护。8. 完整链路七层如何协同把上面的代码连起来看一次完整的订单创建流程是这样的用户帮我创建一张采购订单供应商是华为物料是... ↓ Agent 启动 → 加载 system_prompt → 匹配 /skills/order/ 技能渐进式披露 ↓ write_todos → 拆任务收集字段 → 调研物料 → 合规检查 → 创建订单 → 验证结果 ↓ task(subagent_typeresearcher) → 子 Agent 在独立上下文中调研物料规格和市场价格 ↓ task(subagent_typecompliance_checker) → 子 Agent 执行合规审核 ↓ 调用 order_create → 发现 partId 缺失 ↓ interrupt() 暂停 → CHECKPOINTER 把状态写入 MongoDB ↓ 人类补充 partId → 从 MongoDB checkpoint 恢复 ↓ Agent 拿到数据继续执行 → order_create 调用 MCP 工具 ↓ interrupt_on 触发 → 人类 approve ↓ 执行完成 → 验证结果 → 返回确认每一层各司其职system_prompt定义角色skills按需加载领域知识write_todos管理规划task委派子 Agent 隔离上下文interrupt()实现暂停MongoDBSaver保证状态可恢复OpenSandbox隔离执行环境。这就是 Harness Engineering 的配置优于编码——不是手写控制流而是组合模块。三、日常开发中如何运用 Harness 思路先从当前项目最容易失控的地方补起。1. agent.md 不要写成百科全书首页只放角色、边界、启动流程和目录索引控制在两百行以内。详细规则拆进 skills、prompts、验收规范等子目录按需加载。新增能力时改的是对应模块不是把主提示词越堆越长。2. 每接一个工具都补齐契约明确输入字段、权限范围、失败类型、幂等性和返回格式。特别是写操作必须定义执行成功后怎么验收——不能只返回一段自然语言说已完成。3. 把中间结果落盘调研原文、代码修改记录、测试输出、待办状态都应能被重新读取。不要只存在模型的短期上下文里。大文件写入工作区对话中只保留摘要和路径需要时再读取。4. 给 Agent 验收能力浏览器、日志查询、单元测试、接口检查都可以成为最终确认的一部分。Agent 调完工具不能只看返回一句success而要检查文件是否真的生成、接口状态是否符合预期、测试是否通过。Harness 的交付标准不是模型说我做完了而是系统能验证结果确实在。5. 高风险操作设人工闸门涉及删除、提交、付费、外发、生产数据时先展示影响范围再由人批准。人不需要盯住每一步但必须在风险真正发生前拥有控制权。一句话总结按这个顺序补多步骤任务就接一个结构化待办工具有写操作就加校验和审批工具结果很长就落到文件系统任务会跨会话或等待人工输入就加 checkpoint。结语本文讲解了一个基于 Harness 架构实现的 Agent 项目包含了 Planning、Sandbox、Subagents、Human-in-the-Loop、Skills、Context Engineering 和 Checkpoint 七个核心模块的代码实现。这种思维架构值得我们去学习、去运用到日常开发中。Harness Engineering 不替模型思考却把模型的思考接进一个可以规划、执行、校验和恢复的系统。它不替代 Prompt 或 Context而是把它们放进一个可持续运行的闭环里。Prompt 决定模型如何理解任务Context 决定模型看到什么Harness 决定它如何规划、执行、校验、恢复并最终完成交付。当 Agent 开始真正操作文件、调用系统、完成多步骤工作时工程重点就已经从怎样让它回答得更漂亮转向怎样让它可靠地做完而且出了问题能被看见、能被拦住、能继续恢复。这才是 Harness 最有价值的地方。项目参考【Harness Engineering 企业级多 Agent 协同项目实战Multi-AgentSandBox自我进化的 Skill人工介入-码士集团 AI 大模型】https://www.bilibili.com/video/BV1Cs7h6MEsX?p31vd_source0d5a4d32633eb45ba20dd7e06a8a4604