从 Demo 到生产:Agent Harness 如何把 AI 真正“接”进项目

模型负责生成下一步,框架负责组织链路,而 Agent Harness 负责让这次运行在真实项目里可控、可追踪、可恢复。

很多团队已经用上了 LLM、LangChain、LangGraph 或自研 Agent 链路。Demo 阶段通常很顺:模型读一段文本,调用一个搜索工具,再返回一份结果。

真正接进项目以后,问题就变了。AI 不只要回答问题,还要读取本地项目里的配置和业务对象,调用内部服务,写回草稿或工单,并接受权限、预算、审计、人工确认和失败恢复。

本文把 Agent Harness 当作一个工程概念来使用:它是围绕一次 Agent Run 建立的应用侧运行时,负责接入项目上下文、授权工具能力、保存运行状态、记录 Trace,并在失败后安全恢复。它不替代 LangChain,也不等同于模型厂商提供的 API。

可以先用一句话记住它们的分工:模型负责生成下一步,模型框架负责组织模型侧链路,Harness 负责让这次运行在真实项目里可控、可追踪、可恢复。

见字是一个把内容采集、选题、写作、审稿和排版串成工作流的公众号内容工作台。最近这次改造,我没有把它改造成一个"万能 Agent",而是围绕一次真实任务,补上了项目上下文、能力授权、运行快照、检查点和 Trace 这些运行时基础设施。

一、先想清楚:Harness 到底管哪一层?

假设用户发起一个任务:"采集本周 AI 热点,并整理成一份可供编辑继续处理的候选清单。"

这次任务至少会经历几层运行逻辑:

  1. Workflow 决定先采集、再过滤、再整理,明确业务阶段和顺序。
  2. Skill 规定某个阶段需要什么输入、输出什么结果、允许使用哪些能力。
  3. Agent Run 负责模型与工具之间的多轮执行,决定什么时候继续推理、什么时候调用工具。
  4. Capability 是项目真正允许 Agent 执行的动作,例如读取来源、查询候选或更新阶段状态。
  5. Checkpoint 保存可以继续执行的状态,避免失败后只能重新发送一遍 Prompt。
  6. Trace 记录这次任务经过了哪些阶段、调用了什么模型和工具,以及哪里耗时或失败。

因此,Agent Harness 不是给 Prompt 外面再包一层名字,而是把一次 Agent 运行变成项目可以管理的对象。

二、为什么只接入模型框架还不够

模型框架主要解决"模型怎么调用、工具怎么编排、消息怎么组织"。但它通常不会自动知道当前项目有哪些领域对象可以读取、哪个用户可以调用某项能力、这次运行使用了什么版本,以及失败后应该从哪个阶段继续。

以 Responses API 这类新一代模型接口为例,它们降低了模型侧多轮响应、工具调用和流式事件的接入成本,也可以由平台托管部分对话状态。但这不等于平台替应用完成了本地项目的授权、领域上下文、幂等、Checkpoint、审计和恢复。

这也是见字改造时最重要的判断:模型 API 解决的是"怎么让模型继续工作",Agent Harness 解决的是"这次工作在我的项目里能做什么、留下什么证据,以及出错后怎么办"。

三、划清 Agent Harness 的职责边界

Harness 负责哪一层

LLM SDK 负责发消息和接收模型响应。LangChain 一类框架可以组织 Prompt、工具和链路,也可以提供部分状态和追踪能力。这里的关键不是谁"能不能做",而是谁拥有最终控制权。

在见字的架构里,Harness 管的是一次应用运行:任务从哪里开始,用哪个 Skill 和模型,能访问哪些项目资源,工具是否被授权,状态保存在哪里,失败如何恢复,以及 Workflow、模型、工具和产物怎样串起来。

复制代码
业务请求
  → Workflow:决定业务顺序
  → Harness:创建 Run,加载 Skill 和项目上下文
      → Model Runtime:LLM / LangChain / 自研 Agent
      → Capability Gateway:代码、数据库、文件、内部服务
      → Policy:权限、预算、门禁、人工确认
      → State:Checkpoint、幂等、恢复
      → Trace:事件、模型、工具、产物

更容易落地的组合是:保留现有的 LangChain Chain、Agent Executor 或自研模型循环,把它们接成 Model Runtime,再由 Harness 统一接管项目上下文、能力授权和运行记录。

别把整个仓库塞给模型

把整个仓库放进 Prompt,或者给 Agent 一个万能文件系统工具,看起来接入很快,实际上没有建立项目语义。

本地项目应该暴露经过权限和领域规则整理的上下文。比如,"当前候选稿有哪些事实来源"比"读取整个 articles 目录"更接近业务问题;"查询这次采集任务的失败来源"比"执行任意 SQL"更容易审计;"更新草稿的第 3 阶段状态"比"任意写文件"更容易回滚。

建议分出 Context Provider 和 Capability 两层。Context Provider 只读地提供领域对象、允许访问的文件和 Workflow 状态,结果带对象 ID、来源和版本。Capability 提供最小的领域动作,例如 list_candidates、read_source_run、update_draft_stage,每个动作都有输入输出 Schema、资源范围和副作用声明。

如果 Tool 描述里出现"任意目录""任意 SQL""任意内部 API",大概率说明权限边界还没有建立,而不是完成了项目接入。

图:能力目录把"模型能调用什么"从 Prompt 中移到了项目的授权层。

四、四个关键概念,必须分清楚

改造中最容易踩的坑,就是把 Workflow、Skill、Agent、Tool 混为一谈。可以先用这张表建立一个最小心智模型:

概念 职责
Workflow 业务阶段和顺序,例如采集、选题、写作、审稿、排版
Skill 描述输入、输出、能力、预算和门禁的一张"技能卡"
Agent 一次多轮运行,包含消息历史、模型步骤和工具调用
Tool / Capability 执行具体动作,必须明确参数、权限、超时、输出和副作用

这四个概念的边界一旦清楚,后面设计数据库、接口和 Trace 都会简单很多。

一个内容生产 Workflow 可以先用 stage-skill 做确定性的事实整理,再用 prompt-skill 生成判断,只有需要多轮检索和修订时才启动 agent-skill。并不是每一步都值得承担 Agent 的不确定性。

这条经验也可以迁移到客服、代码分析和运营自动化等场景:确定性步骤尽量保持确定性,需要模型判断的地方再引入 Agent。

五、见字的三步接入:不颠覆,只缝合

第一步:加一层 Facade,而不是换掉框架

我的第一步是给现有的业务与模型之间加 Facade,而不是换模型框架。见字用 runSkill() 接住旧入口执行通用写作技能,内部继续复用 runConversationAgent,把 Prompt、Stage 和 Agent 三种运行方式收进同一个入口。旧路由、请求体、NDJSON 事件和页面字段仍由原 adapter 映射,用户看到的工作台和产物入口也不需要重写。

对 LangChain 项目来说,也可以先把 Chain 或 Agent Executor 包成 Model Runtime,让它向 Harness 报告模型步骤、Tool Call、结果和错误。迁移单位应该是一个真实 Workflow,而不是整个产品。

图:对使用者来说,运行时改造最终要落到两个地方:任务可以被发起和追踪,产物可以被查看和复核。

第二步:冻结一次运行------Snapshot 是 AI 运行的 Git 提交

改造第二步是冻结一次运行。见字在 Run 开始时生成 Snapshot,保存本次使用的消息、模型、工具目录、预算、阶段模型和门禁版本。它像 Git 提交,但保存的是 AI 运行实际拿到的输入和能力。

历史 Snapshot 可以帮助复现,却不能扩大今天的资源授权。也就是说,历史任务可以继续使用当时的技能和模型配置,但仍然要重新检查当前用户是否有权访问当前资源。

第三步:统一 Tool Broker------模型只管"要",项目决定"给"

改造第三步是统一 Tool Broker。模型可以提出"调用某项能力",项目决定"这次是否允许"。Tool Broker 负责校验能力、参数、路径、确认门禁、超时、取消和输出,并记录插件版本、耗时、摘要和 provenance,也就是这次结果来自哪个实现和哪个版本。工具 Manifest 还要说明 sideEffect 和 replayPolicy:无副作用的读取可以在条件满足时复用,上传、发信和外部写入不能自动重放。

这一步的关键不是把工具注册得更多,而是把每个工具的资源范围和副作用说清楚。工具数量越多,越需要一个统一的授权入口。

六、Checkpoint 恢复:不是"重新发一次 Prompt"

见字在 SQLite 中保存 Agent Step、Run Event、Checkpoint、恢复租约和工具幂等结果。恢复请求带 resumeFrom,先检查 Snapshot、能力和当前业务状态,再由业务层重建状态,最后原子占用租约,从明确的下一步继续。

同一个 Checkpoint 不能被两个任务同时恢复。无副作用且允许 reuse-result 的工具可以按幂等键复用;外部写操作默认不自动重放。

恢复不是"更聪明地重试",而是把下一步定义清楚:哪些动作已经完成,哪些动作可以复用,哪些动作必须重新确认。

七、Trace 才是真正的"慢在哪里"答案

日志列表适合回答"成功了吗"。Run Trace 要回答"经过了哪些阶段、为什么慢、失败在哪里、恢复从哪里开始"。见字通过 rootRunId、workflowRunId、stageId 把 Workflow、Stage、Skill、Model、Tool、Checkpoint 和产物串起来。

下面这张图是项目中一条真实的编辑 Agent Run:16 个 span、1 个 Run、2 次模型调用、1 次工具调用,总耗时 13,361 ms。这个数字只是一次运行样本,不能当作性能基线;它的价值在于展示模型、工具和事件如何被放进同一条调用树和时间线。

图:Trace 让排查从"感觉模型很慢"变成"可以定位到具体阶段、模型调用或工具调用"。

如果是采集类 Workflow,还可以继续把来源状态、条目数、失败信息和质量过滤事件挂到同一条根 Run 下面。这样排查时不会只看到一个总耗时,而是能判断时间到底花在来源执行、模型处理、仓库过滤还是人工门禁上。

八、从 LLM 或 LangChain 迁移的通用六步

整个改造可以归纳为六步。每一步最好都配一个可验收的结果,而不是只完成代码拆分:

  1. 盘点本地边界:代码、数据库、文件、内部 API、外部服务和业务状态,先标记只读、内部写入、外部写入。
  2. 建立 Context 层:按领域对象和权限提供上下文,带来源、版本和对象 ID,不把整个仓库和整库数据塞给模型。
  3. 建立 Capability 层:每个动作定义 Schema、资源范围、超时、取消和副作用;框架 Tool 只是实现,不是授权层。
  4. 加 Harness Facade:保留现有 Chain、Agent Executor 或模型循环,把输入、Tool Call、结果和错误映射成统一 Run 事件。
  5. 补 Snapshot、Checkpoint 和幂等:分别回答"当时用的是什么""从哪里继续""哪些动作可以复用"。
  6. 先接 Trace,再做自动恢复:没有 Trace 时,恢复只是猜测;没有副作用策略时,自动恢复可能比失败更危险。

这六步的验收标准也很具体:Context 返回的是带来源和版本的业务对象;Capability 能说明调用者、资源范围和副作用;Facade 能让新旧入口共享同一套 Run 事件;Snapshot 能固定历史运行使用的配置;Checkpoint 能避免重复写入;Trace 能帮助团队在恢复、重试、人工接管和安全终止之间做出明确选择。

最容易走偏的地方也很明确:把 Harness 当作 LangChain 替代品,把所有步骤都变成 Agent,把任意文件或 SQL 暴露成万能 Tool,把 Retry 当 Resume,或者一开始就拆微服务。见字保持模块化单体,是因为当时要解决的是运行语义,不是部署形态。只要运行边界、状态和审计能够独立演进,就没有必要为了"看起来像平台"提前引入复杂的分布式部署。

九、验收四问:你接的到底是 Agent,还是"高级脚本"?

如果你也在把 LLM 或 LangChain 接进本地项目,可以用下面四个问题验收:

  1. 一次运行能否说清楚执行了什么?
  2. 每个 Skill 是否有输入、输出、能力、预算和门禁契约?
  3. 每个 Tool 是否经过授权、审计并声明副作用?
  4. 失败之后,系统能否明确恢复、人工接管或安全终止?

如果答案只能从某位同事的记忆里找,项目只是"接了一个 Agent"。

如果答案能从 Run、Snapshot、Capability、Checkpoint 和 Trace 中复核,AI 才真正接进了本地项目。

改造从来不是炫技,而是让 AI 在真实业务里 可控、可追踪、可恢复。希望这篇实战复盘,能帮你少走几步弯路。欢迎留言交流你的实践心得。

相关推荐
Java爱好狂.1 小时前
Java初学者如何一周速通SpringBoot!
java·spring boot·后端·spring·高并发·架构师·java面试
马良神笔3 小时前
2026 年Rag最近技术前沿总结
后端·python·django
程序猿DD3 小时前
同样10美元,OpenDesign和Command Code的DeepSeek V4.1 Flash额度到底有多少?
后端
Tangyuewei4 小时前
小米的 Coding Agent 开源了
人工智能·开源·ai编程
高频因子挖掘机4 小时前
A股量化从日线升级到5分钟线:数据量增加后最先暴露哪些工程问题?
后端·github·api
狼爷4 小时前
踩坑实录:OpenCode 端口 49374 占用之谜
aigc·ai编程
高频因子挖掘机4 小时前
历史行情返回顺序变了,为什么按行号计算指标很危险?
后端·github·api
Rocky Ding*5 小时前
深入解析GPT-6 Astra提示词工程:从Skills、AGENTS.md到可验证的Agent交付
论文阅读·人工智能·深度学习·机器学习·aigc·ai-native·提示词工程