从 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_candidatesread_source_runupdate_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 还要说明 sideEffectreplayPolicy:无副作用的读取可以在条件满足时复用,上传、发信和外部写入不能自动重放。

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

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

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

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

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

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

日志列表适合回答"成功了吗"。Run Trace 要回答"经过了哪些阶段、为什么慢、失败在哪里、恢复从哪里开始"。见字通过 rootRunIdworkflowRunIdstageId 把 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 在真实业务里 可控、可追踪、可恢复。希望这篇实战复盘,能帮你少走几步弯路。欢迎留言交流你的实践心得。

相关推荐
她的男孩2 小时前
一个开源低代码框架的协作 SPI 是怎么设计的 ForgeAdmin 拆解 + 实战接入新平台
后端·算法·架构
蓝山6162 小时前
Python 字典(Dictionary)完全指南:从入门到实战
后端
foggyprojects2 小时前
在 DeepSeek Harness 里跑通 Foggy:从安装插件到第一次问数
后端
不甘先生2 小时前
Go 中 type、方法与指针接收者:从 str_name.Name() 看懂 Go 的类型系统
开发语言·后端·golang
拒绝内耗。2 小时前
程序员学 AI(六):Tools 与 Tool Calling——AI 为什么可以调用外部能力
人工智能·ai编程
粥里有勺糖2 小时前
视野修炼-技术周刊第132期 | 一些有趣的组件
前端·github·aigc
xierui1231233 小时前
Agent记忆不是聊天记录:用三层存储构建可追溯的 AI 工作流
数据库·人工智能·aigc·软件工程
杨杨杨大侠3 小时前
推理模型和 MoE 到底有什么区别?从一道打折题讲到专家分工
aigc·openai·ai编程
摇滚侠4 小时前
《SpringBoot 3:入门与应用实战》第 14 章 打包与部署 Spring Boot 应用打包 阅读笔记 41
spring boot·笔记·后端