news 2026/11/4 17:54:20

Agent Harness 12组件拆解:从后厨类比到工程落地

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Agent Harness 12组件拆解:从后厨类比到工程落地

上个月帮一个朋友 review 他写的 LLM Agent 项目,代码里塞了三十多个 if-else 来切换工具调用、角色、上下文拼接。看着像一张摊开的意大利面。问题不是模型不行,而是整个执行过程没有一个像样的Agent Harness。你只有 Agent,没有让 Agent 运转起来的“骨架”。这篇文章我从一个后厨类比的视角,把手里的 Agent Harness 拆成 12 个组件,顺便解释每个组件为什么存在、怎么用、哪些可以一开始就砍掉。适合正在设计多步骤 AI Agent、或者想把一堆 prompt 组织成工程化系统的开发者看。

1. 先把概念拎清:Agent 是演员,Harness 是舞台

很多人在网上搜“harness 和 agent 区别”,搜完还是懵。我直接用一句大白话总结:Agent 是那个负责“思考”的演员,Harness 是整个舞台的机械系统。演员台词再好,没有灯光、提词器、后台调度、安全网,也没法完成一出完整的话剧。Agent Harness 就是承载 Agent 运行的一套外围框架,负责接收任务、编排流程、调用工具、管理记忆、记录日志、控制权限。

从实现上讲,Agent 是一个循环:把用户请求拼进 prompt,调大模型,拿结果,判断是否需要调用工具,再继续调模型。而 Agent Harness 是这个循环之外的“操作环境”。

对比项AgentAgent Harness
核心职责推理、决策、生成回复编排、状态、工具、通信、治理
控制粒度单个循环内的输入输出跨步骤、跨多 Agent 的流程
典型问题怎么让模型“想”得更准怎么让系统“跑”得更稳
实现产物prompt + 模型 API 调用调度器、记忆库、事件总线、观测组件
单独使用效果能完成简单单步任务能承载复杂、可恢复、可观测的 Agent 应用

所以当你说“我做了个 Agent”,你大概率只是写了 prompt 和模型调用;当你说“我搞了一个 Agent Harness”,你是在定义这个 Agent 如何被创建、执行、暂停、恢复、追踪和治理。后者才是工程,前者是实验。

一个反直觉的事实是:调试复杂 Agent 时,瓶颈往往不在大模型的“智商”,而在 Harness 的“组织能力”。任务解析含糊、工具调用结果没有归档、断点续跑做不到、日志里看不见中间步骤——这些问题用再强的模型也补不回来。

2. 用一个“晚宴后厨”的类比,从 12 个问题倒推出 12 个组件

如果说一个复杂的 Agent 系统是一场晚宴,那么 Agent Harness 就是后厨。顾客不会看到厨师怎么备料、怎么协调、怎么应急,但后厨的每个环节直接决定菜能不能稳定端上去。

我不主张凭空列组件清单,而是先列出“运行一个复杂的 Agent 时必须回答的 12 个问题”,再把问题映射到后厨角色,最后抽象成组件。

  1. 用户到底让我干什么?
  2. 先干什么、后干什么?
  3. 谁来实际动手干?
  4. 用什么口径、格式、限制去干?
  5. 不会的事情怎么调用外部能力?
  6. 干到一半,上一步的细节忘了怎么办?
  7. 下次还想用上次沉淀的信息怎么办?
  8. 多角色/多步骤之间怎么传话?
  9. 进行到哪一步了,中断后能不能接着干?
  10. 结果靠不靠谱,要不要校验?
  11. 出了问题,怎么定位是哪一步错了?
  12. 是不是什么操作都允许 Agent 执行?

对应到后厨,就是:菜单、总厨、灶台、菜谱、备料台、手边冰箱、档案室、对讲机、台账本、试菜员、监控屏、保险柜。再抽象成工程组件,正好是下面这张映射表。

后厨类比要解决的工程问题Agent Harness 组件
菜单/下单台用户意图转结构化任务Task Parser 任务解析器
总厨决定做菜顺序和派工Orchestrator 编排调度器
灶台真正执行的厨师Agent Runtime/Executor
菜谱控制行为范式的描述Prompt Manager 提示词管理器
备料台外部技能和函数调用Tool Invoker 工具调用器
手边冰箱当前对话上下文Context Manager 上下文管理器
档案室跨会话的长期记忆Long-term Memory 长期记忆库
对讲机组件间通信Event Bus 事件总线
台账本状态和断点保存State Store 状态存储
试菜员输出校验与兜底Guardrails 护栏组件
监控屏日志、追踪、指标Observability 可观测组件
保险柜权限、密钥、操作边界Security/Auth 安全组件

这 12 个问题不是拍脑袋,它们覆盖了 Agent 运行链路的四个平面:决策、执行、支撑、治理。每层 3 个组件,形成了紧凑的 3×4 结构。后厨类比的妙处在于:它天然把“流程协作”“资源管理”“异常兜底”这三个要素放在了一起,这正好是 Agent Harness 的核心命题。

3. 12 个组件逐个拆解:职责、接口和关键点

组件清单列出来了,但每个组件到底做什么、接口长什么样、什么时候必须做,这些决定你能不能把理论落地。我按四个平面展开讲。

3.1 决策链路:任务解析、编排调度、提示词管理

这一层负责告诉系统“用户要什么、按什么顺序做、用什么话术做”,是整个 Harness 的头部。

Task Parser 任务解析器,对应后厨的菜单。它不是简单的意图分类器,而是要把“帮我查一下附近有没有卖情人节玫瑰的店,顺便规划一条路线”解析成结构化的任务:intent=purchase_search, targets=["flower_shop", "route"], constraints=["valentine", "nearby"]。常见实现有两种:一种是直接让大模型输出 JSON,一种是结合规则做槽位抽取。我建议先让模型自由解析,再用 schema 校验,把不合格的重试一次。接口可以直接抽象成def parse(user_input: str) -> Task。

Orchestrator 编排调度器,对应总厨。它决定子任务按什么顺序执行,能不能并行,失败怎么重试。这里要区分两种风格:中心化编排和 ReAct 式自循环。中心化编排适合步骤明确的任务,比如“先搜索,再总结,再发邮件”;ReAct 自循环适合动态探索,比如“持续检索直到找到答案”。一个成熟的 Harness 通常两者都要支持,用有限状态机来描述步骤流转,而不是靠模型自由发挥。接口大概是def run(task: Task, context: Context) -> Result。

Prompt Manager 提示词管理器,对应菜谱。没有它,你会把 prompt 拼在代码里,换一个模型就崩。这个组件负责统一管理系统提示词、few-shot 示例、工具描述模板,并支持按组件或按任务类型渲染。关键点是变量隔离:只有通过render(task, context)才能注入动态内容,而不是到处用 f-string 直接拼接。否则 prompt 会变得不可测试、不可审计,出问题的时候根本分不清是模型问题还是模板问题。

这一层最容易犯的错是让 Orchestrator 直接改 prompt。编排器只应该决定流程走向,不该关心措辞。prompt 是 Prompt Manager 的地盘。

3.2 执行链路:Agent 运行器、工具调用器、上下文管理器

任务定好了,接下来要有人动手。这一层是离大模型最近的地方。

Agent Runtime/Executor,对应灶台上的厨师,也是很多人理解的“Agent”。它维护最内层的推理循环:把当前消息发给模型、解析模型输出、检测是否有工具调用、如果有就触发工具、把工具结果放回对话、再继续。要注意它和 Orchestrator 的区别:Orchestrator 管的是“多个步骤/多个 Agent 之间的宏观编排”,Agent Runtime 管的是“单个 Agent 内部的模型-工具循环”。接口核心是def step(messages: list) -> AgentUpdate,每次只走一步,把控制权交还给 Harness。这样你才能在外面做中断、暂停、人工审批。

Tool Invoker 工具调用器,对应备料台。真实项目里工具签名五花八门,有些是 REST API,有些是 Python 函数,有些是数据库查询。Tool Invoker 要解决三件事:工具发现(有哪些工具可用)、参数转换(把模型给的 JSON 转成实际函数参数)、结果归一化(把返回结果统一包装成模型能理解的消息)。最容易被忽略的是错误透出。工具抛异常时,不能直接把堆栈丢给模型,而是要把“发生了什么、有什么替代方案”变成结构化的工具反馈。比如搜索超时,应该返回{"status": "timeout", "suggestion": "try_again"}。

Context Manager 上下文管理器,对应手边冰箱。所有需要进入模型上下文的信息都在这里汇聚,同时它也是 token 预算的守护者。需要考虑滚动窗口、自动摘要、丢弃不重要信息。很多人问“上下文管理器跟记忆组件有什么区别”,区别很简单:上下文管理器管的是“这一次推理要带什么”,长期记忆管的是“以后还能不能找回”。接口可以设计成def build_system_messages(task, state) -> list和def compact(messages) -> messages。

执行链路是出 bug 最多的地方。我见过最常见的 bug 是工具调用结果过长导致上下文爆炸,模型开始重复已经说过的内容。解决方案不是调大 token 上限,而是让 Context Manager 学习摘要压缩。

3.3 支撑链路:事件总线、状态存储、长期记忆

这三兄弟不直接参与推理,但少了它们,Harness 只能跑单机单线程,连中途续跑都做不到。

Event Bus 事件总线,对应后厨的对讲机。组件间需要解耦通信,比如工具执行完发一个ToolFinished事件,编排器监听后决定下一步,观测组件同时监听来做日志记录。实现上可以用一个简单的发布订阅类,不需要一上来就上消息队列。事件应当是不可变的,包含时间戳、事件类型、关联的run_id。比如:

@dataclass class Event: type: str # "tool.finished", "agent.step", "task.pending" run_id: str payload: dict timestamp: float

State Store 状态存储,对应台账本。Agent 每执行一步,都要把状态持久化:当前在哪个节点、已经收集了哪些信息、哪些工具调用成功过、有没有待人工确认的东西。为什么需要单独的组件?因为内存中的对象一旦进程重启就全没了。生产级 Harness 至少要把 state 写入 Redis 或 Postgres。接口就两个:save(run_id, state)和load(run_id)。有了它,你才能实现“任务中断后恢复”,而不是从头再来。

Long-term Memory 长期记忆库,对应档案室。这里存的是结构化知识、历史会话摘要、用户偏好。常见的实现是向量库(存语义检索的 chunk)加关系表(存事实)。不要把它和 Context Manager 搞混:长期记忆不是每轮都全文塞给模型,而是先通过检索把相关片段取出来,交给 Context Manager 做组装。长期记忆的录入也必须有策略,不能把乱七八糟的内容都写入,要有评分、去重和过期机制。

这一层设计好了,Agent 才是真正的“可规模化系统”。否则换个节点就跑不了,改个参数就丢上下文。

3.4 治理链路:护栏、可观测性、安全权限

执行完后厨还要有人把关。没有治理层的 Harness 只能跑 Demo,不能跑业务。

Guardrails 护栏组件,对应试菜员。它在 Agent 的输入侧和输出侧做双向检查。输入侧防止用户注入恶意 prompt;输出侧校验结果格式、敏感信息、是否符合规范。注意护栏不是简单字符串过滤,它还可以做结构化验证:比如要求 Agent 输出 JSON,护栏先尝试解析 JSON,解析失败就让模型修正一次。也能检查工具调用参数是否在白名单内,防止 Agent 去调用删除类操作。这个组件放在 Agent Runtime 的外围,作为每个 step 之后的拦截点。

Observability 可观测组件,对应监控屏。复杂 Agent 的调试难度是几何级上升的,没有 trace 的话,你根本不知道是模型回答错了,还是工具返回错了,还是编排顺序错了。这个组件至少要记录:每次模型请求的 prompt 摘要、返回 token 数、耗时、工具调用的输入输出、各步骤的事件流。如果团队有条件,把事件总线里的所有事件都接到这个组件,就能还原一次完整运行的“时间线”。这一步做到位,线上问题处理效率能提升一个量级。

Security/Auth 安全组件,对应保险柜。包括密钥管理、用户身份校验、数据隔离、操作审计。很多人觉得“我就是个内部工具,不用管”,但一旦 Agent 可以调工具,它就有了实际影响力。必须做到:所有外部工具调用的凭证不过明文,工具执行前校验权限,关键操作(发邮件、删文件、改数据库)进入审计日志。安全组件要直接嵌入 Tool Invoker 和 API 入口,不能是事后补丁。

治理链路不是可选项。我见过太多项目立项时觉得麻烦,结果一上生产就出事:要么工具被 prompt 注入控制,要么出了问题完全无从查起。

4. 从后厨回到代码:一个 12 组件 Agent Harness 的最小骨架

光有概念不成,我给出一个可以直接参考的伪代码骨架。这不是某个框架的完整实现,而是告诉你 12 个组件如何咬合在一起。

先定义一个最小配置:

harness: components: task_parser: enabled: true max_retries: 1 orchestrator: strategy: fsm max_steps: 10 agent_runtime: model: chat-llm temperature: 0.2 tool_invoker: timeout_seconds: 15 retry: 1 context_manager: max_tokens: 4000 strategy: sliding_window long_term_memory: store: vector_store event_bus: enabled: true state_store: backend: redis guardrails: verification: json_schema observability: traces: true security: requires_auth: true

然后是运行时的主类,核心思路是让每个组件只做一件事,由 Harness 主循环把它们串起来:

class AgentHarness: def __init__(self, components): self.parser = components["task_parser"] self.orchestrator = components["orchestrator"] self.executor = components["agent_runtime"] self.tools = components["tool_invoker"] self.context = components["context_manager"] self.state = components["state_store"] self.events = components["event_bus"] self.guardrails = components["guardrails"] def run(self, user_input, run_id=None): run_id = run_id or create_run_id() task = self.parser.parse(user_input) plan = self.orchestrator.plan(task) self.state.save(run_id, {"plan": plan, "step": 0, "context": {}}) self.events.publish(Event("harness.start", run_id, {"task": task})) while not plan.is_finished(): step = plan.next_step() messages = self.context.build(step, self.state.load_context(run_id)) result = self.executor.execute(step, messages, self.tools) validated = self.guardrails.validate(result) if not validated.ok: result = self.executor.repair(result, validated.feedback) self.state.update(run_id, step, result) self.events.publish(Event("harness.step", run_id, {"step": step, "result": result})) plan.advance() self.events.publish(Event("harness.end", run_id, {})) return self.state.load(run_id).final_answer

一次请求的完整流转是这样的:用户输入进入 Harness,Task Parser 先翻译成结构化任务,Orchestrator 生成步骤计划,State Store 马上落账。然后进入循环,每一步先从长期记忆检索相关信息,交到 Context Manager 组装消息,Agent Runtime 调用模型,模型决定要用哪个工具,Tool Invoker 执行并把结果写回上下文,Guardrails 检查结果,状态更新,事件广播,直到计划完成。

这个骨架不是炫技,它体现了 Harness 的精髓:每个组件都位于固定的边界内,流程可暂停、可恢复、可追踪。在你自己的实现里,可以按需替换组件实现,比如把 Redis 换成内存,事件总线换成最简单的函数回调,只要保持接口稳定就行。

5. 我实际落地时的三个教训:组件不是越多越好

上面的 12 个组件是“完全体”,但真实项目里你不需要一上来就全部铺开。我踩过几个坑,说出来供你参考。

第一个教训:别为了用组件而拆组件。最初给一个内部运维工具做 Harness,我强行拆了 12 个组件,结果一大半时间花在定义接口和搬数据上。后来冷静下来,只保留 Task Parser、Orchestrator、Tool Invoker、State Store,其他都砍掉,开发速度快了三倍。如果任务只有三五个步骤,执行链路单一,那先砍掉长期记忆和事件总线,用函数的返回值传递结果就够了。加组件应该基于真实的失败模式,而不是基于架构理想。先让 Agent 跑起来,再根据痛点补组件。

第二个教训:State Store 和 Observability 最容易偷懒,但最不能偷懒。我第一次做完一个多步骤 Agent 后,没有做持久化,结果线上进程一重启,所有正在执行的任务全丢了。后来补了 Redis 状态存储,才发现恢复任务比自己想的重要。Observability 也是,以前以为打印点日志就行,直到一次线上事故无法还原发生了什么。现在我会在 Event Bus 发出所有事件时同步给观测组件,至少把run_id、步骤、关键 Prompt、工具返回记录完整。

第三个教训:事件总线不要往消息队列的方向做。你要是把每个事件都丢进 Kafka 再做异步消费,调试复杂度会急剧上升,因为事件顺序和时序会变得不可控。Harness 里的事件总线更多是进程内发布订阅,作用是解耦和审计。跨进程通信交给 State Store 和调度系统,不要让事件总线承担“模式匹配再触发逻辑”的重任。否则你会陷入循环事件、消息延迟、难以复现的 bug 泥潭。

还有个很实用的判断标准:当一个组件需要超过两周时间去实现时,先质疑它是不是伪需求。很多需求其实靠 prompt 调优就能解决,根本不需要拆出一个新组件。真正值得投入的往往是那些能让任务“可恢复”“可观测”的基础设施,而不是花哨的编排 DSL。

做个原型时,我建议的最小集合是:Task Parser + Agent Runtime + Tool Invoker + Context Manager。这四个组件能覆盖大多数 POC;等 POC 通过,再依次补上 State Store、Observability、Guardrails,最后按业务需要补长期记忆和事件总线。这样既不浪费前期开发时间,也能在关键节点接住复杂性。

Agent Harness 的 12 个组件不是一套需要全部照抄的模板,而是一张检查清单。把这张清单放在手边,每次设计 Agent 时过一遍,问自己:我的 Runner 遇到异常能不能恢复?我的调用路径能不能被追踪?我的 Agent 会不会执行超出边界的工具?这些问题想清楚,你的 Agent 工程化水平会和只调 prompt 的版本拉开明显距离。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/4 17:53:58

10万星AI Agent项目的软件工程密码:架构、测试与可靠性

聊开源项目的软件工程经验,如果只看代码本身,那基本是白聊。一个能冲到10万星的AI Agent项目,代码量动辄几十万行,靠的不是某个大佬的炫技,而是整套工程体系的胜利。我翻开这类项目的时候,第一感受是&#…

作者头像 李华
网站建设 2026/11/4 17:51:57

地面无人平台性能评价:内涵倒推的层次化指标树与量化方法

简介:这份PDF文献围绕地面无人作战平台(UGV)性能评价展开,面向无人装备研发、需求论证、试验评估人员及相关专业师生,针对当前缺乏统一评价标准、装备性能描述口径不一、又难以直接套用有人平台指标的问题,…

作者头像 李华
网站建设 2026/11/4 17:51:08

混沌工程如何提升大数据系统的容错能力

1. 混沌工程在大数据建模中的核心价值大数据系统天生具有复杂性——数据管道可能在任何环节断裂,计算节点随时可能崩溃,而业务部门往往要等到报表延迟或模型失效时才会发现问题。三年前我们团队就经历过一次惨痛教训:某个关键批处理作业因为依…

作者头像 李华
网站建设 2026/11/4 17:48:08

声音克隆工具VoiceStudio:3.3万Star背后的零样本克隆与部署实战

1. 声音克隆工具为什么突然火了1.1 从3.3万Star说起:一个工具出圈的底层逻辑GitHub上Star数能冲到3.3万的开源项目,基本都踩中了某个大众刚需。声音克隆这个赛道,过去两年一直不温不火,原因很简单——要么效果像机器人&#xff0c…

作者头像 李华