ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AI Agent Harness七个子系统:从0到1搭建真正能干活的智能体

AI Agent Harness七个子系统:从0到1搭建真正能干活的智能体 最近社区里聊 AI Agent 聊得特别热闹尤其是“从 0 到 1 搭建 AI Agent”“AI Agent 有哪些产品”这类问题几乎是人人都在问。但我连续被问得最多的一句话其实是模型接上了、工具也调通了为什么 Agent 一跑起来就像个只会聊天、不会干活的空壳这个问题十有八九出在 Harness 上。很多人以为 Harness 是什么新框架、新概念其实拆开看真正干活的 Harness 就 7 个子系统上下文状态、工具调用、记忆、规划编排、安全权限、可观测、运行载体。把这 7 块想清楚Agent 才真的能上生产、扛业务。这篇文章不打算讲花哨的概念就按我自己从 0 到 1 搭 Agent 的经验把这 7 个子系统逐个拆开揉碎再说说最小可用的 Harness 骨架怎么搭以及我在实操里踩过的坑。适合正在做 Agent 应用、想摆脱“demo 能跑、上线就挂”状态的朋友也适合刚入门、想搞明白 Agent 到底是怎么“干活”的新手。1. 为什么要把 Harness 单独拎出来讲1.1 Harness 不是模型也不是业务代码先说清楚一个容易混淆的事Harness 既不是某个大模型也不是你的业务逻辑。它更像一层“运行骨架”夹在模型和业务中间负责把模型的推理能力转换成一次一次可执行的动作再把执行结果变成模型能理解的输入。我见过不少人把 Agent 等于“大模型 工具函数”然后直接在业务代码里调模型、拼 prompt、解析返回跑通一两个用例就觉得完事了。等到真实场景一上问题就全冒出来多轮对话聊着聊着失忆了、工具返回了一堆垃圾结果模型看不懂、同一个操作反复执行、日志里完全查不到中间过程。这些都不是模型能力不够而是 Harness 太薄。你如果去搜 DeepSeek Harness、Harness Engineering 这些关键词会发现大家都在讨论类似的东西怎样把模型的能力稳定地固化成一个能干活的工作流。本质上就是把上面说的 7 个子系统都补上而不是让模型裸奔。1.2 用一个例子理解 Harness 的定位可以把 Harness 想象成汽车的底盘和电控系统。发动机再猛没有传动、转向、刹车、仪表盘这辆车也是废铁。模型就是发动机工具函数是车轮Harness 则是中间那套让整车能平稳跑起来的系统。还有一个更贴切的类比来自测试领域——Test Harness。做软件测试时你需要一套脚手架把被测模块包起来准备输入、执行、收结果、断言。Agent 的 Harness 干的是类似的事把模型包起来替它准备上下文、执行工具、收集反馈、控制流程最后返回一个可靠的结果。很多团队在搭 Agent 时只关心“选哪个模型”“prompt 怎么写”却把 Harness 当成可有可无的胶水。实际上Agent 能不能真正干活往往取决于 Harness 的完整性。1.3 缺失 Harness 的典型症状我总结了几类高频症状你如果中了两条以上基本可以判断是 Harness 有问题会话一长Agent 就“忘了”前面已经查到的订单信息反复追问用户。工具调用偶尔成功偶尔失败失败后整个流程直接卡死。模型发现了危险操作比如删数据、发邮件没有拦截就直接执行。线上出了 Bug但日志里只能看到最终回答中间调了哪些工具完全空白。并发一上来不同用户的状态互相串A 的订单跑到 B 的上下文里。这些症状靠换更强的大模型是解决不了的只能从 Harness 层面修。2. 7 个子系统的全景认知先建立一张整体地图2.1 完整清单Harness 是 7 块拼图我平时跟团队讲 Harness不是按模块化架构图那种讲法而是先给一张“口袋清单”。搭 Agent 时你不需要一次做全但脑子里必须知道这 7 块都在管什么上下文与状态管理给模型准备每次调用的输入维护会话状态机。工具注册与调用执行把业务能力暴露给模型处理调用的完整生命周期。记忆子系统短期对话记忆、长期业务记忆、程序性技能记忆。规划与编排决定下一步做什么、做到什么程度算完、失败怎么重试。安全与权限门禁拦截危险操作实现人审和审计。可观测与追踪记录每一步的输入输出、成本、耗时支持回放。运行载体与生命周期管理并发、限流、版本、优雅启停。你可以先把它当作一个检查清单。做 Demo 的时候可以只有 1 和 2但要是上生产7 块基本都是刚需。2.2 这 7 块之间是怎么协作的我用一个最简单的工作流串一遍用户丢来一句“帮我把今天新下的订单导成表格”。Harness 先由上下文管理把系统提示、用户消息、相关记忆、可用工具定义装进请求规划编排判断需要调“查询订单”和“生成表格”两个工具工具调用子系统依次执行把结果回填安全门禁发现“导出表格”是低风险操作直接放行可观测把每一步追踪信息落盘最后运行载体负责处理并发和超时。这个链路里任何一环断了Agent 都会出问题。比如上下文管理没把上一步工具结果放进去模型就不知道当前进度规划编排不设上限模型就可能在一个任务上无限循环。3. 核心子系统逐个拆开揉碎3.1 上下文与状态管理子系统Agent 的“工作台”大模型本身是无状态的每次调用都相当于一个新员工坐到工位上你必须把相关资料全部摆到桌面它才能干活。这个“桌面整理能力”就是上下文管理的责任。它至少要做三件事。第一拼装请求把 system prompt、历史对话、用户当前输入、检索到的知识、工具定义按合理顺序塞进模型上下文第二控制上下文长度超过模型窗口就做截断或摘要第三维护状态比如当前会话处于“等待确认”“工具执行中”“已完成”哪个阶段。这三件事看着简单实操里全是坑。最常见的坑是工具结果原样塞入。我见过一个查库存的工具返回了一大段 JSON里面还有几十个 debug 字段模型看完直接开始胡编乱造。正确做法是工具返回前先格式化只保留模型真正需要的字段甚至提前做好摘要。另一个坑是状态机缺失。没有状态管理的 Agent经常会收到过期的用户指令。比如用户说“先查一下订单再帮我把收货地址改了”Agent 查完订单后如果状态里还停留在“查询阶段”就可能反复执行查询而不是进入下一步“修改地址”。所以我会建议每个会话都维护一个显式的状态字段并在关键节点更新。3.2 工具注册与调用执行子系统Agent 的“手脚”工具调用是目前 Agent 干活的最高频方式。主流模型的 function calling 机制本质上不是让模型直接执行代码而是让模型输出一个“调用意图”真正去执行的是 Harness。这个子系统要处理的东西包括工具注册、参数校验、执行、超时、结果回填。工具注册就是把“这个工具叫什么、能干嘛、参数是什么样的”用统一的 Schema 描述出来。描述写得好不好直接影响模型能不能正确调用。我踩过的一个经验是工具说明里一定要写“人话”。比如一个工具叫 get_order_status说明里除了参数还要写清楚“这个接口只适合查询 30 天内的订单超过时间要去查历史订单库”。模型看过之后才不会被相似工具带偏。执行阶段Harness 要处理参数校验、并发控制、超时熔断。尤其超时必须有默认值否则模型在当前回合会一直卡住。工具异常也不能直接抛给用户要把异常转化成一条模型能理解的“工具结果”比如“查询超时请重试”或“订单号不存在请检查输入”。工具多了之后还会遇到“选错工具”的问题。我建议超过 20 个工具时引入一层语义索引先根据用户的意图检索出相关工具子集再丢给模型能显著降低调用干扰。3.3 记忆子系统Agent 的“存储硬盘”记忆不是简单的聊天记录我习惯把它分成三层看。第一层是短期会话记忆就是当前对话窗口里的内容。它的管理重点是长短太短了模型忘了前文太长了又占上下文窗口。实操里我会结合 token 预算比如超过一定长度就把早期内容做摘要压缩再拼回上下文。第二层是长期业务记忆跨会话保存用户偏好、项目背景、历史决策。落地方式通常是向量库加数据库把重要信息写成结构化的记录需要时通过语义检索召回。这里最大的坑是记忆污染——召回了一些过期或无关的信息模型反而被带偏。我的建议是每条记忆都带上时间戳和来源标签召回时优先按时间和业务域过滤。第三层是程序性记忆也就是常说的 Skill 或工作流模板。它保存的是一条完成任务的“操作套路”比如“生成周报的流程先拉项目数据再按模板排版最后推送到群”。程序性记忆的价值在于让 Agent 不用每次从零推理直接复用已验证的流程。很多人搜“deepseek harness 用 skill”“插件激活”其实就是在折腾这部分。3.4 规划与编排子系统Agent 的“小脑”这部分负责回答两个问题下一步做什么什么时候算结束最简单的规划就是 ReAct 模式的循环模型根据当前上下文输出一个动作Harness 执行结果回填再让模型决定下一步。这个循环看起来简单但必须加护栏否则会出现“反复横跳”。我常用三个护栏。第一是最大迭代次数比如 max_iterations10超过就强制终止并转人工第二是重复动作检测如果模型连续 3 次调用同一个工具且没有新信息产生就打断它第三是步骤预算按工具调用次数而不是轮次来控制成本上限。复杂一点的场景还会涉及分层编排。主管 Agent 负责拆任务子 Agent 负责执行结果再汇总给主管。这种结构灵活但调试难度高。我的经验是能明确写成固定流程的就尽量用 workflow 而不是靠模型自由发挥。模型适合处理“有随机性”的部分纯重复稳定的链路交给编排固定下来更稳妥。3.5 安全与权限门禁子系统Agent 的“刹车”Agent 一旦接上真实操作权限安全问题就成了头等大事。因为模型不是你它可能因为 prompt 注入、上下文误导或者单纯理解错误去执行一个破坏性操作。我的安全底线有这些危险操作必须人工审批删除、发邮件、转账、改配置这类不可逆动作Harness 要拦下来生成一个确认链接或二维码等用户点击确认后才继续。权限粒度最小化给 Agent 分配权限时不要直接给一个管理员账号尽量做到“按工具授权”“按数据范围授权”。代码执行要隔离如果 Agent 要写代码、跑脚本必须放进沙箱容器限制网络和文件系统访问。全程审计每个敏感操作记录谁发起的、用过什么参数、结果如何。漏掉安全子系统的代价我见过太多次了。最常见的是开发阶段为了方便权限放得很大上线后忘了收回去结果 Agent 在一次误操作里删掉了测试库的表。等出了事再补沙箱和审批成本远高于一开始就搭好。3.6 可观测与追踪子系统Agent 的“黑匣子”没有观测的 Agent 系统就像没有仪表盘的飞机飞起来全靠感觉。Agent 的运行链路比普通 API 长得多一步错后面全歪如果没有全程记录你根本不知道问题出在哪。我要求所有线上 Agent 至少记录四类信息每次模型调用的请求和响应特别是 tool call 的完整参数。工具执行的开始时间、耗时、返回结果摘要、状态码。token 消耗和成本估算按会话维度累加。一个 trace_id从用户进来一直到最终回复贯穿整个链路方便把日志串联起来回放。最好把这些日志输出成结构化的 JSON直接接到你的日志平台而不是只输出到控制台。我遇到过排查问题时需要还原现场但日志里只有模型最终回答、看不到中间工具调用的情况那一刻真是欲哭无泪。有了 trace你还可以做回归评估把历史问题整理成测试集每次改完 prompt 或工具跑一遍对比输出 diff能有效防止“修了 A 又坏了 B”。3.7 运行载体与生命周期管理子系统Agent 的“躯壳”最后这个子系统最容易被纯做应用的人忽略但它决定了 Agent 能不能稳定扛住线上流量。Agent 任务往往不是一次同步请求而是一个异步工作流。用户问一句“帮我写个周报”背后可能要调 5 个工具耗时几十秒甚至几分钟。这时候你的运行载体就不能是简单的 HTTP 请求-响应模型要有队列、任务、回调、超时重试。生命周期管理要处理的点包括并发隔离、限流、优雅停止、版本更新。并发隔离的意思是不同用户、不同会话的状态要彻底隔离不能互相污染。限流则是既保护模型 API也保护你自己的下游系统。版本更新尤其重要你迭代一版 prompt 或工具逻辑但正在执行中的旧会话怎么办我的做法是让 Harness 记录版本号新会话走新版本旧会话允许继续跑完而不是直接杀掉。长任务还要特别注意幂等。比如“提交订单”这类操作如果网络超时后 Harness 自动重试可能会重复下单。正确做法是给每次操作生成一个 request_id下游系统靠它去重。4. 实操搭一个最小可用 Harness 骨架4.1 先定边界Demo 和线上是不同的需求很多人一上来就想着上一个重框架结果被配置和概念淹没。我的建议是分两阶段先做一个单进程的最小骨架把主循环跑通再去完善线上需要的并发、观测、安全。下面这个骨架我默认你已经接好了一个支持 function calling 的模型 API业务工具也有现成函数。我们用 Python 的伪代码风格不绑定具体框架。4.2 核心代码骨架先定义一个简单的工具注册表# tools.py from dataclasses import dataclass from typing import Any, Callable dataclass class Tool: name: str description: str parameters_schema: dict handler: Callable[..., Any] need_approval: bool False _tools: dict[str, Tool] {} def register_tool(name, description, parameters_schema, need_approvalFalse): def decorator(fn): _tools[name] Tool( namename, descriptiondescription, parameters_schemaparameters_schema, handlerfn, need_approvalneed_approval, ) return fn return decorator然后是 Harness 主循环# harness.py from tools import _tools class Harness: def __init__(self, model, max_iterations10): self.model model self.max_iterations max_iterations def _build_messages(self, task, historyNone): # 这里应该拼 system prompt、历史、用户任务、工具定义 messages [{role: system, content: 你是业务助手...}] messages (history or []) messages.append({role: user, content: task}) return messages def _run_tool(self, name, arguments): tool _tools.get(name) if not tool: return {error: ftool {name} not found} if tool.need_approval: return {error: 需要人工审批} try: result tool.handler(**arguments) return {result: result} except Exception as e: return {error: str(e)} def run(self, task, historyNone): messages self._build_messages(task, history) tool_defs [ {name: t.name, description: t.description, parameters: t.parameters_schema} for t in _tools.values() ] for step in range(self.max_iterations): resp self.model.call(messagesmessages, toolstool_defs) if not resp.tool_calls: return resp.content for call in resp.tool_calls: tool_result self._run_tool(call.name, call.arguments) messages.append({ role: assistant, tool_calls: [call.to_dict()], }) messages.append({ role: tool, tool_call_id: call.id, content: str(tool_result), }) return 达到最大迭代次数请转人工这个骨架只有 40 行左右但已经把上下文管理、工具调用、规划循环三个核心子系统串起来了。你可以在它的基础上逐步加记忆、权限、观测。要注意的是真实生产里_build_messages要做上下文截断_run_tool要做超时和幂等run里要埋 trace 日志——这些后期都要补上。4.3 关键参数怎么定我给几个实操时常用到的初始值你可以按场景调整参数推荐初始值说明max_iterations10超过后强制转人工防止死循环max_tool_calls_per_iteration3单轮里最多执行几个工具避免模型一口气调十几个温度 temperature0干活场景用 0减少随机性上下文截断阈值模型窗口的 50%留一半空间给工具结果和系统提示工具超时10 秒下游接口慢时不要拖垮整个 Agent审批工具标记need_approvalTrue删除、发送、支付类工具默认开启这些参数不要照抄得根据你自己的工具延迟和任务复杂度调整。比如你的工具平均耗时 2 秒那 10 秒超时合理如果有个工具要跑 1 分钟你就得单独给它设置更长超时。4.4 跑通第一个“真正干活”的 Agent先用一个没有副作用的只读工具跑通比如查询订单状态。注册一个工具启动 Harness让它回答“订单 10086 现在到哪一步了”观察循环是否正常模型是否输出了正确的工具调用、工具结果是否被正确回填、最终回答是否基于工具结果。跑通之后再加第二个工具比如“生成订单摘要”让它完成一个多工具任务。这能帮你验证状态管理和上下文回填。第一次跑的时候最好打印每一步的 messages亲眼看到 tool call 和 tool result 是怎么交替的比看任何教程都直观。5. 常见问题与排坑实录5.1 工具结果被“吞”上下文被截断后失忆症状一次会话里调了 5 个工具后模型突然忘了第一个工具返回的关键信息开始让用户重新输入。我查这类问题时第一个动作是看上下文长度。很常见的情况是第一次工具结果很长后续输入把早期内容挤出了上下文窗口。解决思路有两个一是在回填工具结果时做精简提前用代码抽取关键字段而不是整段 JSON 塞回去二是对更早的历史做摘要压缩比如用模型把前三轮对话总结成三句话替换原始内容。5.2 插件或 Skill 加载失败激活不完整症状启动 Harness 时报类似failed to load plugins的错或者某个技能明明注册了模型就是调不出来。这种问题多半出在插件/技能清单和实际文件不一致上。我排查时会先做三件事看启动日志里的插件名确认哪些加载成功、哪些被跳过。检查命名冲突两个 Skill 用了同一个触发名或工具名后加载的会把先加载的覆盖掉。检查依赖缺失很多插件加载失败不是插件本身的问题而是它依赖的某个 Python 包没装。另一个容易被忽略的是 Skill 的触发条件写得太模糊。比如你写“处理订单”模型可能根本不知道什么时候该用它。我会在 Skill 描述里补充具体的使用场景甚至带上示例输入模型更容易命中。5.3 Agent 反复横跳同一个工具被连续调用症状模型在“查询订单”和“查询库存”之间来回调用就是不输出最终结论。这通常是因为上下文里缺少“你已经知道什么”的显式状态。模型每轮看到的虽然包含前一轮工具结果但如果结果被截断或不明显它就以为还没查过。我的解决办法是在 system prompt 里加一段由 Harness 动态生成的“当前进度摘要”每次工具回填后更新例如“已获取订单状态已获取库存数量等待生成最终答复”。模型看到明确的进度提示就不容易瞎绕。5.4 成本失控一次任务烧掉太多 token症状一个本来 3 步就能完成的任务模型绕了 15 步token 和费用翻了好几倍。成本失控的核心原因是目标不明确和护栏缺失。我通常会在 system prompt 里写明“完成任务后立即停止不要额外解释”“如果信息不足以回答直接说明缺少什么不要猜测”。同时打开第 4 节的步骤预算一旦工具调用次数超过阈值立即停止并把上下文回放给运维人员分析。5.5 问题速查表症状最可能的原因推荐排查方向会话一长就失忆上下文被截断检查 messages 长度和摘要逻辑工具调用失败后流程卡死缺少异常回填和重试在_run_tool里把异常转成可读结果模型反复调用同一工具缺少进度摘要动态维护“当前进度”并注入 prompt插件加载不全依赖缺失或命名冲突启动日志、插件清单、依赖检查高并发时状态串台会话隔离没做好检查状态存储是否按 session_id 隔离6. 最后分享几条实在经验先说工具选型。自研 Harness 还是用现成框架这个问题我经常被问。我的看法是前期别急着造轮子先用 LangGraph、CrewAI、Dify 这类开源或成熟的框架把业务跑通你会更清楚自己的系统到底卡在哪个子系统。但也不要框架崇拜等业务复杂度上来了该换成自己的 Harness 就果断换框架只是起点不是终点。再说工作量分配。很多人以为搭 Agent 的重头在模型选型和 prompt 调试实际上 Harness 会悄悄吃掉 30% 到 50% 的工时。上下文截断、工具异常、权限审批、trace 日志每一块都不难但都很琐碎。你要做好心理准备别把这些算成“额外工作”它们就是 Agent 的地基。我自己踩过最深的坑是先让 Agent 跑起来“看起来很智能”再回头补 Harness。结果业务侧已经开始依赖这个系统每次改上下文策略都像动心脏手术。如果你现在也在从 0 到 1 搭 Agent我真心建议反过来先把 7 个子系统的最小版本装好哪怕丑一点等骨架稳了再往里面加模型能力和业务逻辑。那把“真正干活”的钥匙不在模型手里而在 Harness 手里。
返回列表