Harness Engineering:给AI智能体搭建安全高效的运行环境
发布时间:2026/10/1 13:44:09来源:尧图网络
第一次听到 Harness Engineering 这个词是在一次技术评审会上。当时我们团队正为一个 AI 编程智能体项目内部代号就叫 CodeBuddy接入公司的代码仓库、测试环境和发布流水线结果发现真正难的不是模型选型而是怎么把这个智能体的工作环境搭得既像一个人一样顺手又不会越界闯祸。后来我们慢慢把这件事总结成一句话给 AI 配一间办公室而这间办公室的设计与建造过程就是 Harness Engineering。如果你不了解这个概念我先用大白话解释一下。Harness 在英文里的原意是马具、挽具也就是把马和车连接起来的那套装置。在 AI 工程领域Harness 指的就是连接大模型与外部世界的整套运行环境——包括模型怎么接入、上下文怎么管理、工具怎么调用、权限怎么控制、人和 AI 怎么配合、出了问题怎么追溯。Harness Engineering 则是设计、搭建并持续维护这套装置的全部工程实践。这篇文章适合谁看只要是正在做 AI 应用、AI Agent、AI 编程助手或者打算把大模型接进真实业务系统的人都会用得上。我会按六个模块展开接入与权限、上下文与记忆、工具与执行、规划与协作、人机协同、观测与评估。每个模块都会讲清为什么需要它以及我们是怎么实现的最后给出一套 CodeBuddy 从零落地的完整路径和踩坑记录。1. 先从办公室这个比喻说起Harness Engineering 到底是什么把 AI 放进一个空地上它什么都干不成。它需要工位、电脑、文件柜、会议室、主管、前台和监控。同样一个 LLM 如果只有裸的对话接口也完成不了真实项目任务。它需要一套完整的办公环境知道自己的能力边界工位、过去发生了什么文件柜、能调用什么工具电脑和工具箱、遇到大活怎么拆解主管和会议室、什么节点必须请示前台和审批窗以及出了问题怎么回溯考勤和监控。这六个部分就是 Harness Engineering 的六大模块模块办公室角色对应工程能力接入与权限门禁/工牌模型接入、身份认证、权限边界上下文与记忆工位/文件柜系统提示、短期上下文、长期记忆工具与执行工具箱/实操间工具注册、函数调用、沙箱执行规划与协作主管/会议室任务分解、多 Agent 编排、状态机人机协同前台/审批窗关键节点确认、人工干预、反馈回传观测与评估考勤/监控室日志追踪、指标评估、回归与审计我见过很多团队一上来就死磕 prompt觉得只要把提示词写得足够花哨AI 就能稳定输出。但实际跑一段时间你会发现Prompt Engineering 解决的是怎么说而 Harness Engineering 解决的是怎么干活。一个任务最终能不能成往往取决于 60% 的 harness 设计和 40% 的模型能力。举个最简单的例子同样是写一个函数模型能力决定它写得好不好但工具层决定它能不能真的把代码写进文件、能不能跑测试、能不能在出错时回退——后面这些才是真实项目里最花时间的地方。我们内部还有一个更形象的比喻Prompt 是开会时说什么Harness 是公司长什么样。你可以在会上把需求讲得很清楚但如果公司没有门禁、没有文件柜、没有能干的工具箱这个人再聪明也交不出活。下面我们就按这个公司的设计蓝图一间房一间房地搭。2. 门禁与工牌接入、身份与权限模块2.1 模型接入不是一个 API Key 那么简单很多人以为接入模型就是配一个 API Key填进环境变量就完事。实际一旦进入工程化你会发现至少要回答这些问题用哪个模型当主脑不同任务要不要路由到不同模型Key 怎么管理本地开发、CI、生产各用什么凭据请求超时、熔断、降级策略是什么成本预算怎么控制拿我们 CodeBuddy 项目举例模型接入层大致长这样# harness.yaml —— 模型接入与路由配置节选 model: primary: provider: deepseek-v3 max_tokens: 8192 temperature: 0.2 fallback: provider: deepseek-r1 max_tokens: 8192 temperature: 0.2 router: default: primary code_review: r1 # 代码审查场景走推理模型 semantic_search: embedding_model # 检索场景走 embedding这样设计有三个好处。第一Key 统一在运行时注入不会散落在各台机器和代码库里。第二不同任务可以分流到更合适的模型省钱也省时间。第三主模型失败时自动切到备用通道避免整个任务中断。很多人忽略第三点实际跑起来你就知道生产环境里模型接口超时是家常便饭没有 fallback 的话一次超时就能让整条流水线堵半天。这段配置里我想重点提醒的是别在高频路径上让 Agent 自由选择模型。看起来让模型自己选择最合适的模型很智能真跑起来你会发现不同模型的上下文格式、工具调用习惯、返回风格千差万别最后调试成本远高于省下的那点钱。最稳的做法是默认一条主链路只有极少数特殊场景比如代码评审再显式路由过去。2.2 权限设计工牌能进哪扇门门禁最容易被忽略也最容易出大事。一个 AI 智能体如果拥有和你一样的全量权限它干活确实方便但一旦被注入恶意指令比如读了某个 README 后照着里面的建议执行或者误操作删除了生产数据后果非常严重。我们给 CodeBuddy 设置的权限边界遵循最小权限 按环境分级的原则本地开发环境允许读写项目目录、执行构建和测试命令CI 环境只读代码 写报告目录生产环境禁止直接操作只能生成正式的审批请求敏感操作git push、部署、删除文件、支付等必须触发人工确认。权限本身还做成可审计的。我们在工具调用层加了一个 authorize() 中间件每个 tool call 进来先查一遍策略表# permission.py —— 工具调用前的权限校验简化版 def authorize(tool_name: str, env: str, payload: dict) - bool: rules { execute_command: {dev: True, ci: [safe_cmds], prod: False}, read_file: {dev: True, ci: True, prod: [whitelist]}, write_file: {dev: True, ci: [report_dir], prod: False}, deploy: {dev: False, ci: False, prod: require_human}, git_push: {dev: False, ci: False, prod: require_human}, } allowed rules[tool_name].get(env, False) if allowed require_human: return human_approval_queue.enqueue(tool_name, payload) return allowed踩坑记录最初我们觉得AI 需要灵活所以权限尽量放开于是在生产环境也允许执行命令。真跑一次才发现AI 在调试时会出于好奇心去执行各种命令有些真的就误伤了测试数据。后来加了权限中间件这类事故基本消失了换来的是偶尔要人工点一下确认。这个成本完全值得——毕竟你招一个实习生也不会第一天就把生产库的口令直接给他。3. 工位与文件柜上下文与记忆模块3.1 系统提示词就是办公室里的员工手册每个 AI Agent 开工前都需要一份员工手册也就是 system prompt。但真正做好它远不止写几句你是我的 AI 助手那么简单。我们建议手册里至少包含五块内容手册章节对应内容目的角色与目标我是谁在哪个项目里服务谁定位工作规范任务如何拆解先规划后执行约束行为环境说明仓库结构、构建命令、测试命令减少试错边界条款什么不能做、什么必须请示安全兜底协作约定与人类/其他 Agent 的协调方式打通协作我们给 CodeBuddy 的 system prompt 里有一个特别关键的设计强迫它一开始就把任务拆成 todo list并且每个 todo 后面都标注做完之后怎么验证。比如不写优化数据库查询而写把订单列表查询从 N1 改为 join并跑 test_orders.py 通过。这个设计治好了 AI 最常见的毛病——闷头干完一大坨最后发现方向从一开始就错了。3.2 短期上下文就是工位桌面要防止爆炸Context window 现在越做越大128K 甚至 1M 的模型都出来了。但真以为可以随便往里塞东西就会遇到上下文飘移模型读到后面忘了前面或者被无关信息干扰出错率明显上升。这就像把桌子堆满文件人反而找不到当前该看的那一份。我们的做法是桌面收纳法桌面只放当前该干的活。每次任务开始时先做一次上下文清理把上次任务的日志、中间文件、无关讨论全部归档只保留当前真正需要的东西完整的项目说明书如果 Agent 是第一次接触这个仓库当前 ticket / issue 的描述相关代码文件片段按需从仓库检索不一股脑全塞上一次会话的 summary而不是原始对话记录。检索式接入很好用。我们在 CodeBuddy 里接了一个轻量向量库本地用 SQLite embeddings让 Agent 按需拉开文件柜取资料。实测下来一次中等规模项目的编程任务上下文消耗从原来的约 200K tokens 降到了 60K 左右。成本降了一大截任务完成率反而稳中有升——因为模型终于不用在噪音里找信号了。3.3 长期记忆怎么做才不会变成垃圾堆长期记忆是很多人都会做但很容易做成把什么都存下来。要记住一个原则记忆不是存档是决策支持。我们给 CodeBuddy 的长期记忆分了三个抽屉项目知识库架构决策、接口规格、领域术语来源是文档和人工整理经验教训每次踩坑后沉淀的教训比如不要直接改 migration 文件用户偏好代码风格、命名习惯、常用库版本号等。每个抽屉都有写入门槛。尤其是经验教训必须经过人工或评估模块评审通过才能入库。否则 AI 会把自己偶然的错误当成金科玉律越存越歪。有一次它因为一次单元测试超时就永久记住了不要跑完整测试差点带偏后面好几天的所有任务。那条记录是后来人工清理掉的。从那以后我们所有教训入库都加了一道是不是一个稳定规律的审查。4. 工具箱与实操间工具集与执行沙箱4.1 工具不是越多越好在办公室里给员工 100 个工具不如给他 10 个常用且顺手的工具。工具多了模型的选择就难选择难就更容易出错。我们在 CodeBuddy 里给编程智能体用的工具常年保持在十来个read_file / write_file / edit_filelist_dir / searchbash沙箱内执行命令run_tests专用测试入口git_status / git_diff / git_commit但不开放 pushask_human遇到歧义时提问不必要的工具我们甚至会刻意关掉。比如发送邮件发消息通知这类工具让智能体在工作流里去发消息十有八九会出乌龙。更合理的做法是把这些通知动作放到人机协同阶段由人工或者固定流程来触发。4.2 Function Calling 的接口设计工具接口设计的关键是让填表一样明确。每个工具都要有功能描述、参数定义、返回结构、失败语义。一个常见的不太好的设计是让 Agent 传自由格式的 JSON 字符串结果就是你整天在解析各种各样的写法解析逻辑越写越肥。我们用 JSON Schema 定义工具签名比如 edit_file{ name: edit_file, description: 对指定文件做精确内容的替换。用于局部修改不适合整体重写。, input_schema: { type: object, properties: { file_path: {type: string}, old_content: {type: string, description: 需要被替换的原内容片段}, new_content: {type: string, description: 新内容片段} }, required: [file_path, old_content, new_content] } }特别注意 description 的写法。LLM 对工具的选择高度依赖 description我们甚至在描述里写反例不要用 edit_file 做大型重构那种情况请先和用户讨论方案。这一句看着不起眼实测能减少大量顽固错误——模型不会因为能做就该做你得明确告诉它什么场景不该用。4.3 沙箱工具最危险的其实是执行比模型选型更让人头秃的一环是 AI 生成代码并执行。执行环境必须沙箱化这是我们用血泪教训换来的结论。所谓沙箱拆开看就三点网络隔离或白名单文件系统虚拟化Agent 只能看到项目目录实际写入落到指定目录资源受限CPU、内存、超时、命令数。在实现上我们早期用 Docker每个任务一个容器后来为了速度换成了轻量进程隔离加容器兜底。现在 CodeBuddy 跑测试命令统一走 run_tests 这个专用入口。这个入口内部会先在临时目录构建再抓取环境变量限制网络跑完后把日志回传。这样一来就算 Agent 在 bash 里瞎折腾也碰不到开发环境。有人问为什么不干脆不开 bash答案很简单真实任务里AI 需要查依赖版本、看进程列表、统计日志这些灵活操作没法全部封装成固定工具。所以正确思路不是禁用执行而是把它关进笼子里给它一个可以随便折腾但不影响外界的实操间。5. 主管与会议室任务规划与多 Agent 协作5.1 单 Agent 也要有任务状态机如果说前面是办公室的硬件这一节就是办公室的流程制度。一个 Agent 干活时需要一个显式的状态机防止它在思考、执行、验证之间乱跳。我们给 CodeBuddy 设计了四态循环planning拆分任务、定 todo、写验证方式acting逐个执行工具每调一次工具更新 todochecking跑测试、静态检查、取证reflecting根据检查结果修订计划或者宣布完成。关键是 todo 必须写成可验证的动作而不是愿望。比如优化数据库查询这种 todo执行完你根本没法判断做没做改成把订单列表查询从 N1 改为 join并跑 test_orders.py 通过模型执行起来就有明确的完成标准人也容易审核。这一点怎么强调都不过分我见过太多 Agent 任务失败根源都在 todo 写得像口号。5.2 多 Agent 协作主管、经理、员工当任务大到一个人做不完比如跨模块重构、根因排查可以开会议室用多个 Agent 分角色协作。我们试过两种模式各有优劣。模式一Manager-Workers 树状结构。Manager 负责规划和拆解WorkerA 处理前端模块WorkerB 处理后端模块各自执行后把结果交回 Manager 收口校验。优势是职责清晰、上下文天然隔离劣势是 Manager 需要很强的全局判断力拆解得偏了整条线就偏了。模式二多智能体评审模式。一个 Agent 写实现另一个 Agent 专门挑毛病代码评审、安全审计、异常场景测试。优势是能在早期发现单视角盲区劣势是上下文成本翻倍评审 Agent 还容易过度保守把什么改动都打回去。我们的原则是默认单 Agent只在任务规模大、失败代价高的场景才上下多 Agent。每个 Agent 都是办公室里的一个脑袋开会开销不小电费也不低。5.3 一个复杂任务的完整走查拿 CodeBuddy 处理实现一个带缓存的用户详情接口这个任务来演示一遍状态机planning 阶段读接口文档和现有 service 层拆成 5 个 todo包括加 Redis 依赖 - 写缓存装饰器 - 改 service - 写单测 - 跑全部测试。acting 阶段逐项执行。中途发现原有 service 依赖了一个旧配置项于是它主动调 read_file 去查配置然后回头更新 todo把更新配置说明加进去。checking 阶段跑单测发现缓存命中场景覆盖率不够补了测试再跑。reflecting 阶段自己生成一段 release note 和一页排查要点然后停在 ask_human请人工确认是否合并。整个流程下来人工只介入了一次——就是最后的合并确认。如果没有状态机这一层大概率它会一口气 push 代码甚至顺手改掉生产配置那可能就是事故了。6. 前台与监控室人机协同与可观测性6.1 不是所有环节都要 AI 自己拍板办公室必须有人而且人在关键节点要能拦住 AI。这个人工确认不应该是打断而是设计好的节奏。我们在 CodeBuddy 里把需要确认的节点收敛为三类高风险动作写生产数据、删除、部署、任何影响多人协作的操作方向性决策重构方案、技术选型、接口设计变更语义模糊处需求有歧义、无法确定边界时宁可多问一句。实现上很简单就是让 Agent 在对应动作前调用 ask_human 工具并且把它显式做成权限的一部分。注意确认消息不能只是让 Agent 自己生成一份我很靠谱的总结否则人会盲目点同意。我们要求确认消息必须同时列出将要做什么、影响面、回滚方案。这个约束写在 system prompt 里再配合后面的日志能有效防止 AI 把错误包装得很合理。6.2 日志与追踪给每个决定都留证据没有可观测性的 Agent 系统是不能上生产的。我们要求每次任务运行都产出一份办公室巡检报告所有 model call 的耗时与 token 数、所有 tool call 的入参与返回、每次 planning/reflecting 的状态流转、每个人工确认的决策与理由。技术上最简单的方式就是结构化日志。把关键事件统一打到 JSON 日志流里跑完任务后用脚本生成 HTML 报告。有一次排查AI 为什么改了不该改的文件就是靠日志回放定位的它在一次 tool call 的 payload 里带上了错误路径而那个路径来自前一个任务遗留的工作目录。没有日志这个 bug 根本无从查起。6.3 评估体系考勤不只是为了扣钱评估模块回答三个问题任务完成了吗完成得好不好花的代价值不值我们给 CodeBuddy 搭了一个很朴素的评估矩阵维度指标通过线完成度验收测试通过率、需求点覆盖数全部关键点通过质量静态检查告警数、评审打分、覆盖率变化无新增 critical 告警效率任务耗时、token 消耗、工具调用次数与人工基线可比安全越权次数、危险命令数、人工干预次数越权次数为 0评估数据会倒灌回两个地方。一是 Agent 的经验教训库——只有评估通过的教训才允许入库。二是我们的复盘报告每周看一次看看哪些任务类型总失败然后去修对应的工具、权限或状态机配置。这一层是最容易被砍掉的但我强烈建议别砍没有评估前面五个模块都是在裸奔。7. 用 CodeBuddy 从零搭一套办公室完整落地路径与踩坑记录7.1 别一上来就搞大而全的航空母舰我见过不少团队一听说 Harness Engineering 有六大模块就猛冲猛打先接五个模型再上四十个工具然后做多 Agent搞一堆 metrics 面板。结果系统复杂到没人敢改项目还没上线就先被自己绊倒了。我们自己的落地路径是从一间房起步第一周只做模型接入 权限 三个核心工具读、写、执行测试第二周加 todo 状态机和人工确认节点第三周补结构化日志和每周报告第四周根据真实失败案例再决定要不要上多 Agent、扩展记忆。这套路径的核心判断标准是每加一个模块都必须有真实的失败案例或瓶颈支撑否则不加。没有业绩压力的模块纯属给自己找麻烦。7.2 完整案例让 CodeBuddy 修一个生产环境偶现超时问题这里给一个端到端示例方便大家理解六大模块是怎么一起工作的。背景订单服务在生产环境偶发 502排查成本高。我们把这个任务交给 CodeBuddy门禁与权限赋予读日志、执行压测脚本、读代码的权限禁止写生产配置。上下文与记忆注入服务架构图和最近三天的日志摘要从经验库调取一条偶现问题先查线程池耗尽的教训。工具与执行它依次用 search、read_file、bash跑压测50 并发发现线程池核心线程数设置过小高峰时请求排队积压。规划与协作todo 拆成压测复现 - 定位线程池参数 - 评估修改方案 - 提交代码并跑回归每一步都带验证命令。人机协同到修改核心配置节点弹确认窗它附上了影响面高峰 QPS 下线程数变化和回滚方案。观测与评估全程结构化日志改完跑 100 并发压测完成度、质量、效率指标全部按报告模板输出。人工最终只点了两次同意一次是确认配置修改一次是确认合并 PR。这就是六大模块协同工作的样子。7.3 我们踩过的四个坑和对应的解法上下文过载最初把整个 monorepo 塞给 Agent它越到后面越失忆。解法是检索式接入只给当前任务相关的文件。工具万能化给了 Agent 全部 shell 权限它开始乱试命令甚至清掉了测试环境目录。解法是最小权限 专用入口 容器兜底。人不看就跑自动确认让 Agent 在错误路径上一路狂奔。解法是所有高风险动作强制人工确认且确认消息必须包含影响面和回滚方案。记忆污染Agent 把偶发错误当经验入库后来带偏后续任务。解法是经验教训必须通过评估验证才允许进入长期库。7.4 关于下一阶段的几点个人体会说实话Harness Engineering 到现在还在快速演化远远没到一个标准答案。我自己最大的感受是它不是一个配置完就完事的静态工程而是一个持续迭代的系统。每次加工具、改权限、调状态机本质上都是重新装修那间办公室。装修没有终点因为业务在变、模型在变、踩过的坑也在变。如果让我给新手一个最实在的建议那就是先把最小的闭环跑通——一个 Agent、一个工具、一个确认点、一份日志——然后再往外扩。办公室可以慢慢装修但第一间能开灯能干活的房间越早落成越好。上面讲的六大模块本质上是六张检查清单而不是六堵墙。先拿它们对齐思路再按自己项目的实际情况取舍比照抄任何模板都管用。
网站建设高端定制企业官网