这几年做 Agent 工程,我最大的感受是:模型本身越来越聪明,真正决定项目能不能稳定上线的,反而是工程架构。Harness、Loop、Graph 这三个词,是 Agent 工程里绕不开的骨架:Harness 是给模型套上的工程外壳,Loop 是它不断感知、推理、行动的执行循环,Graph 则是把复杂任务拆解成一张可编排的流程图。如果你正在搭 Agent 项目、写 Agent 框架,或者只是想知道为什么别人的 Agent 能平稳跑几百轮、而自己的 Demo 一上生产就乱套,这篇文章会把三层架构的前因后果和落地细节拆开讲清楚,也会带上大量我在生产环境里踩过的坑。
全套架构其实不复杂,核心就一句话:模型负责思考,Harness 负责约束,Loop 负责推进,Graph 负责规划。下面一个个拆。
1. Harness:Agent 的工程外壳
1.1 先分清 Harness 和 Agent
Harness 和 Agent 的区别,是我被问得最多的问题。很多人以为 Harness 就是 Agent,或者觉得它只是个“启动器”,这两种理解都偏了。
用开车打比方:Agent 是驾驶员,负责看路、判断、打方向盘;Harness 是整辆车的驾驶舱,包含仪表盘、安全带、导航屏、刹车系统。没有 Harness,模型照样能单轮对话,但一旦进入多工具调用、多轮纠错、多用户并发的生产场景,就会失控——你没法看到它内部在干什么,也没法限制它调用危险工具,更没法在出错时快速回滚。
从代码角度看,Agent 是策略单元,它决定“下一步做什么”;Harness 是执行环境,它负责“让你能这么做,并且只这么做”。定义一个 Agent 只需要模型名、系统提示词和工具列表,定义一个 Harness 则需要上下文管理器、工具注册表、内存接口、日志管道、安全策略、生命周期钩子,以及一套能把这些东西串起来的启动流程。
我见过不少团队把 Agent 逻辑和 Harness 耦合在一起,结果换一个模型 provider 就要重写一半代码。正确做法是让 Agent 变成一个纯粹的函数输入输出,把工具注入、状态持久化、错误重试全部放到 Harness 层。这两层一分开,后面做测试和版本回滚都会轻松很多。
1.2 Harness 的核心组成
一个合格的 Harness,至少包含以下六块内容:
- 系统提示词管理:根据场景动态组装 system prompt,注入用户身份、当前时间、知识库摘要,而不是写死在代码里;
- 工具注册表:统一登记所有可调用工具的名称、描述、参数 Schema、执行函数;
- Skills / 插件加载器:允许把一组提示词和工具打包成一个 Skill,按需启用或停用;
- 记忆模块:提供短期上下文窗口和长期向量存储的访问接口;
- 安全围栏:在模型发出工具调用请求时做参数校验、命令白名单、敏感数据脱敏;
- 日志与追踪:记录每一步的模型输入输出、工具调用耗时、Token 用量。
其中工具注册表是最容易失控的部分。早期我直接在代码里写 if-else 分发工具调用,二十个工具之后就完全没法维护了。后来改成定义统一的Tool接口,每个工具只需要实现一个方法。
interface Tool { name: string; description: string; inputSchema: Record<string, unknown>; invoke(input: Record<string, unknown>, ctx: ExecutionContext): Promise<unknown>; }Harness 在启动时扫描所有已注册的工具,把name和description自动拼进发给模型的工具列表,模型返回工具调用请求后,再由 Harness 路由到对应实现。这样新增工具只需要往注册表里加一个实例,不用改任何业务逻辑。我见过的所有稳定运维的 Agent 项目,工具注册基本都是这种模式。
1.3 接口抽象与可插拔设计
把 Harness 设计成可插拔,不是为了炫技,而是为了让同一个 Agent 内核能跑在不同的运行底座上:本地命令行走一版,公司内网 HTTP 服务走一版,未来可能还要接进 IM 机器人。底座不同,但模型、工具、记忆这三样东西应该是完全复用的。
具体做法是三层依赖倒置:第一层定义接口(工具接口、模型接口、存储接口),第二层实现默认组件(OpenAI 兼容协议、本地 JSON 存储、内存日志),第三层才是装配入口,通过配置文件或启动参数切换实现。Rust 语言在这个场景里非常合适,一是内存安全让长驻服务的并发问题少很多,二是编译期就能把接口实现检查清楚,不会像 Python 那样跑到运行时才报缺方法。社区里已经有不少基于 Rust 的高性能 Agent Harness,启动延迟和并发承载能力都比脚本语言实现高一截。
这也就是最近常说的“Agent Anywhere”思路:Harness 是把 Agent 内核和运行环境解耦的关键,只要你愿意,同一个 Skill 包可以同时跑在服务器、命令行和本地桌面工具里。我自己的项目里,Harness 编译成一个二进制之后,直接拷贝到任何一台 Linux 机器上就能起服务,不需要装 Python 环境,也不需要考虑依赖冲突,省掉了一大半运维烦恼。
1.4 内网部署 DeepSeek Harness 的实操流程
很多团队想把 Harness 部署到内网服务器,尤其是用私有化模型服务的时候。这里有一个常见误区:以为把代码拷贝进去就行,实际上 Harness、模型服务、插件/Skills、依赖缓存这四样需要一起打包。
我部署社区常见的 DeepSeek Harness 到内网集群时,走的是这套流程:
- 在一台能联网的机器上下载 Harness 发行包和依赖,例如
deepseek-harness-linux-x64.tar.gz,解压后先跑一遍./harness version确认可执行; - 把模型服务地址改成内网地址,例如
http://10.x.x.x:8000/v1,在配置文件里关闭所谓的外部网络请求; - 把插件目录整体压缩,连同所有离线依赖打进部署包;
- 内网机器上执行
./harness init --config /etc/harness/config.yaml,启动后检查健康检查接口/health。
配置文件里最关键的几个字段是模型端点、并发线程数、日志输出目录、插件白名单。注意,内网环境经常没有外网源,所以插件依赖必须提前下载打包。我就是因为少打了一个插件依赖包,启动时直接报模块缺失,还排查了很久。
1.5 Skills 与插件加载机制
Skills 机制是 Harness 工程里最有价值的设计之一,Claude Agent Skills 那篇 First Principles Deep Dive 把这件事讲得很透:一个 Skill 不只是“一段提示词”,而是提示词、示例、工具白名单、校验规则和元数据的集合体。
在我这边,每个 Skill 长这样:
name: web-search-and-extract version: 1.3.0 description: 搜索网页并抽取正文内容 tools: - search_web - fetch_page - extract_text_chunks prompt: | 当你需要查找外部资料时,先使用 search_web 获取候选链接, 再用 fetch_page 和 extract_text_chunks 抓取正文,最后总结来源。这个文件放在插件目录下,Harness 启动时扫描所有skill.yaml,构建索引;用户对话时命中描述就会自动加载对应提示词和工具。由于 Skills 是配置化的,所以部署到内网服务器时只需要把整个插件目录打包分发即可,不用改任何业务代码。
2. Loop:Agent 的执行引擎
2.1 一个 Agent 是怎么“转圈”的
Harness 是外壳,Loop 就是引擎。现在社区流行的 Loop Engineering,本质上就是把“感知-推理-行动-评估”这个循环的每一步做得可控、可测、可观测。
一个典型的 Agent 调用循环是这样转的:
- 把用户请求和当前上下文拼接,发给模型;
- 模型返回文本回复,或者一个工具调用请求;
- Harness 校验工具参数,执行工具,把执行结果追加回上下文;
- 带着新上下文回到第 1 步,再次调用模型;
- 直到模型给出最终回答,或者触发终止条件。
听起来简单,但生产环境里真正难的是终止条件。没有终止条件的 Loop 就是失控的 Loop。我有一个项目早期上线时,Agent 在一次失败的工具调用后反复重试了十几轮,每一轮都在烧 Token,最后把预算烧空才停下来。从那以后,我在 Harness 里强制设定了两个参数:max_steps和max_tokens_per_run,分别限制最大循环轮数和单次任务的总 Token 消耗。
2.2 理解 Token:为什么循环会烧钱
Agent Token 一直是新手最不容易理解的概念。简单说,模型按 Token 计费,一个 Token 大约是一个汉字或半个英文单词,但每次请求不仅包括你的输入,还包括工具列表、系统提示词、历史对话和工具返回结果。
在 Loop 场景下,这些 Token 会随着轮数急剧膨胀。假设一轮消耗 2000 Token,20 轮就是 4 万,再加上工具返回值经常是几百行文本,一个复杂任务跑下来可能烧掉十几万 Token。所以纯按次数限制不够,还要按预算限制。
我采取的做法是三层护栏:
- 单步限制:每次模型调用最多输出
max_tokens,防止模型生成一大段无用废话; - 总步数限制:
max_steps控制在 10 到 30 之间; - 运行预算限制:Harness 实时累计当前任务的 Token 消耗,超过阈值后强制进入收尾模式,让模型只输出已收集的结果,不再发起新工具调用。
这样做以后,异常费用几乎消失了。想让成本更可控,还可以在 Harness 里加一层结果缓存,同样的搜索请求直接复用上次结果,省掉重复调用的 Token。
2.3 记忆:短期工作记忆与长期持久化
Agent 记忆是很多人的痛点。在 Loop 里,短期记忆就是上下文窗口,模型能看到的所有历史对话和工具返回都算。长期记忆则是跨会话的状态,通常存到向量数据库或者 Key-Value 存储里。
Harness 层要做的,是把这两类记忆统一封装。短期记忆由上下文管理器负责,超过窗口长度时做摘要压缩;长期记忆由记忆插件负责,定时把重要结论写入存储。这里有一个血泪教训:Agent 状态在序列化存储时,很容易因为对象互相引用而报循环引用错误。我遇到过.NET序列化agent_state时抛self referencing loop detected for property 'mem_memberinfo',排查半天才发现是记忆对象里既持有工具实例引用,工具实例又持有了 Agent 引用,形成了 A 指向 B、B 又指向 A 的环。解决办法很简单,序列化时只保留状态字段,不保留运行时的函数和实例引用,或者直接配置引用循环处理方式。
2.4 死循环与循环引用检测
死循环是 Loop 工程的大敌。一个 Loop 如果陷入“同一工具、同一参数、无限重试”的节奏,费用会像漏水一样哗哗流走。
我做了两件事来防死循环。第一,动作指纹去重:把“工具名 + 参数哈希”作为指纹记录下来,如果连续三次出现相同指纹,就直接中断循环并触发人工审批。第二,循环熔断:当重试次数超过阈值后,不再自动重试,而是把这个工具标记为当前任务不可用,让模型换一条执行路径。
有一个来自音视频处理的反直觉案例很能说服人:FFmpeg 的loop滤镜可以让图片流无限循环,但如果你在无限循环的输入上做编码,进程会一直吃内存,最后 OOM。Agent 的循环也一样,无限重试听起来是“再试一次也许能成功”,但生产环境里大概率是同样的失败原因,重试只是给账单加零。
2.5 重试不是无限重试:循环的熔断方案
正确重试模型是“指数退避 + 抖动”:第一次失败等 1 秒,第二次等 2 秒,第三次等 4 秒,最长不超过 30 秒,并且每次加重试偏移量,防止多个任务在同一时刻同时发起重试。这个策略是从分布式系统里搬来的,在 Agent 场景效果一样好。
还要注意,模型接口返回的 429、503 错误和工具内部逻辑错误要分开处理。网络类错误可以重试,业务类错误重试也没用。我通常给工具错误分级:可重试错误、可降级错误、不可恢复错误。只有第一类会进入重试逻辑,后两类直接反馈给模型,让它换方案或者终止任务。
2.6 代码回退:Loop 工程的最后保险
长驻 Agent 服务最怕改出线上问题,想回滚却找不到上一版。所以我在 Harness 项目里强制引入版本号和回滚机制:每次发布前用 Git Tag 标记,例如release-1.3.0,同时生成一份包含所有工具和插件哈希的部署清单。
有一次我更新了网页抓取工具的默认超时时间,结果大量任务因为抓取超时开始走重试分支,费用激增。发现问题后,我执行了一条回滚命令,把 Harness 回退到上一个 Tag,整个服务两分钟内就恢复了。那次之后我把“回滚时间小于 15 分钟”定为生产环境的硬性指标。回滚不只是代码回滚,插件配置、提示词模板、工具注册表全都需要纳入版本管理。
3. Graph:Agent 的流程骨架
3.1 为什么只有 Loop 不够
Loop 是线性的,一次只往前走一步。但真实业务往往有分支:一个任务需要并行调三个工具,一个分支失败后要走降级方案,某些环节需要人工确认后才能继续。这些用 Loop 硬写会很别扭,代码里全是 if-else 和状态标志位。
Graph 就是为了解决这个问题。把 Agent 的一次任务执行看作一张有向图,节点是操作,边是流转条件。Graph 和 Loop 不是二选一,而是上下层关系:Loop 是执行引擎,Graph 是规划蓝图。模型本身不会凭空知道下一步该去哪,它需要 Graph 提供全局路径。
3.2 Graph 的节点与边
我在生产里最常用的节点类型有这么几种:
- LLM 节点:调用模型做生成、摘要或判断;
- 工具节点:执行某个具体工具,比如搜索、写文件、查数据库;
- 条件节点:根据上一步结果决定走哪条边;
- 并行节点:同时触发多个子任务;
- 人工审批节点:挂起任务,等人确认后再继续;
- 结束节点:输出最终结果。
边则分为顺序边和条件边。条件边是最难设计的,因为模型判断本身有不确定性,判断错了就会把任务引向错误分支。我的经验是:条件节点的判断尽量用规则和结构化输出,而不是让模型自由发挥。比如让模型输出一个 JSON 格式的{"route": "approve" | "retry" | "abort"},再在代码里做映射,这样可测试性高很多。
3.3 条件路由、并行分支与人工审批
举一个我实际做过的调研报告 Agent 的例子。
开始的 Graph 是一条直线:搜索资料 → 抽取关键信息 → 生成报告。跑了一个月后发现问题:当搜索结果少于三个时,生成报告的质量很差。我把 Graph 改成了这样:
- 搜索节点执行搜索;
- 条件节点检查结果数量,如果太少,走“补充搜索”分支,换个关键词再搜一次;如果仍然不够,直接进入“下载源不足”提示路径;
- 资料充足时,并行启动三个工具节点:提炼摘要、抓取统计数据、翻译英文页面;
- 并行结果汇合后,进入人工审批节点,由运营确认数据来源可信;
- 审批通过进入生成节点,不通过则回到搜索分支重新采集。
加了 Graph 之后,整个流程的可控性完全变了。以前这些分支逻辑散落在代码里,现在一眼就能看出哪一步会失败、失败后走向哪里,新同事接手也容易理解。
3.4 从局部工具图到全局任务图
Graph 设计有一个非常重要的视角转换:从“局部工具图”上升到“全局任务图”。单个 Agent 内部,工具调用像一张小图,差不多是“搜索 → 打开链接 → 抽取 → 总结”这么几条边。但当你做多 Agent 协作或者复杂工作流时,需要把每个 Agent 当成一个子图,再把多个子图组成一张大图。
这个思路和图论里的商图思想很像:把一组关系紧密的节点压缩成一个抽象节点,用更粗的粒度观察整体结构。我在设计时经常先把局部工具图画出来,跑通后,再把“搜索-抓取-抽取”这一坨压缩成一个名叫“资料采集”的子图节点,然后只关心它和下游“报告生成”节点的关系。
甚至可以从局部到全局的图视角来做系统优化。脑功能网络研究里有一种方法,先从局部脑区连接构建起细粒度网络,再逐层聚合成全局连接图,目的就是兼顾细节和宏观结构。给 Agent 画图也是一样的道理:太细,维护成本爆炸;太粗,关键异常看不到。几轮迭代后,我习惯把一张任务图控制在 5 到 15 个节点之间,超过这个量就考虑拆分子图。
3.5 用 Graph Builder 把流程可视化
纯写 JSON 或 YAML 定义 Graph 很难看出问题,所以我都是先画图,再生成配置。现在主流的框架基本都提供可视化面板,类似 Snap 那种组件式 Graph Builder,左侧拖节点、右侧配参数、中间实时连线,导出成结构化配置。
可视化带来的第一个好处是发现遗漏。有一次我拖了一个并行节点,但忘了给其中一条边设置失败处理,直到可视化才发现,某条路径失败后会直接掉出整个 Graph,任务静默失败。这种问题写代码时极难发现,看图一眼就能看到问题所在。
我自己的习惯是:先用图形界面把流程画出来,评审通过后再落成配置。Graph 配置是 Harness 直接读取的,所以它会成为团队内部的“活文档”,比任何文档都准确。
3.6 三层架构合体后的执行顺序
Harness、Loop、Graph 三层合起来,一次任务执行的完整顺序是:
- Harness 接收用户请求,加载对应 Skill 和 Graph 定义;
- Graph 给出当前应该执行的节点;
- Loop 引擎进入循环:执行节点、收集结果、判断条件、流转到下一个节点;
- 每一步的 Token、耗时、工具结果都写进 Harness 的日志管道;
- 遇到 Graph 分支时,条件节点决定走哪条边;遇到终止节点时,Loop 退出。
简单来说,Graph 告诉 Loop “下一步去哪”,Loop 去执行,Harness 提供执行环境和安全约束。这套架构最直观的效果是:你不再需要靠“提示词硬控模型”来保证流程正确,架构本身就兜住了大部分边界情况。
4. 生产实践:从 Demo 到生产环境的完整落地
4.1 可观测性:让每一步都有迹可循
Agent 和普通接口最大的区别是,它内部的执行路径不固定,同一句话可能走完全不同分支。所以可观测性不是锦上添花,而是排查问题的生命线。
我在 Harness 里做的日志是结构化 JSON,每个任务带一个全局trace_id,每步循环带一个step_idx,每次工具调用记录tool_name、params_hash、start_time、duration_ms、status、token_usage。日志样例大致如下:
{ "trace_id": "0192f...", "step_idx": 7, "node_id": "search_web", "tool_name": "search_web", "status": "ok", "duration_ms": 120, "token_usage": 842, "params_hash": "a1b2c3" }有了这些字段,排查问题时只需要按trace_id一查,整个任务的每一步都看得清清楚楚,哪一步慢、哪一步报错、哪一步 Token 特别高,一目了然。如果没有 trace_id,在几十个并发任务里找一条异常链路,真的是大海捞针。
4.2 测试:用 Mock 工具和回归集对抗不确定性
模型输出有随机性,这给测试带来很大麻烦。我们不能像普通单元测试那样断言“返回结果等于某个值”,而只能断言“行为序列是否符合预期”。
我搭的测试体系分三层。第一层是工具级测试,用替身工具模拟真实工具返回,例如搜索工具固定返回三条结果,然后断言 Agent 是否走了正确分支。第二层是回归集测试,把历史用户问题攒成一个测试集,每次发布前跑一遍,统计工具调用序列和最终回答质量有没有明显退化。第三层是可重复性测试,固定随机种子和温度参数,确保同样问题在同版本下能复现相同工具调用路径,关键路径不一致就视为回归。
这套东西看起来繁琐,但救过我好几次。有一次改动系统提示词后,Agent 在遇到空搜索结果的场景时不再走降级分支,而是直接生成了一个胡编乱造的答案。如果没跑回归集,这个 bug 大概率要上线后被用户发现。
4.3 安全围栏:插件沙箱、权限与数据脱敏
Agent 安全不是一个空泛的话题,生产环境里每天都在面对:模型可能会被提示词注入攻击,工具调用参数可能带上越权路径,插件代码可能存在任意代码执行漏洞。
我设置了四层安全围栏:
- 插件沙箱:第三方插件一律运行在隔离进程或容器里,禁止直接访问宿主机文件系统;
- 工具白名单:Harness 只能调用注册表里登记并通过审批的工具,动态新增工具需要二次确认;
- 参数校验:所有工具调用参数都要过 JSON Schema 校验,路径类参数做归一化,封掉
../穿越和绝对路径越权; - 数据脱敏:模型输入和日志输出都要经过脱敏管道,手机号、身份证号、API Key 全部替换成占位符,只保留业务需要的字段。
其中数据脱敏是最容易被忽略的。有一次我在排查问题时直接翻了模型调用日志,发现日志里躺着完整的用户身份证号。从那以后,我把脱敏放到了进入上下文的前置环节,而不是做一个后置清理脚本。安全这东西,必须在前置拦截,事后清理永远不彻底。
4.4 成本、并发与资源预算
成本控制的本质是预算分解。我给每个任务类型制定 Token 预算,比如“资料搜索类任务上限 5 万 Token”“报告生成类任务上限 8 万 Token”,超出后强制终止。全局层面还会按用户或者部门做配额,防止一个人跑批任务把整个团队的账单拉爆。
并发控制同样重要。Loop 本身会持有长连接,如果并发开太多,模型服务的压力会成倍上涨。我在 Harness 里用了信号量控制活跃任务数,超出并发阈值的请求直接排队,而不是全部塞进 Loop 里抢资源。这里有个细节:多窗口、多会话并发启动时,每个 Loop 窗口的上下文和状态必须隔离,我踩过共享同一个内存缓存导致两个任务互相污染的坑,后来给每个会话分配独立的状态键才解决。
4.5 发布管理:插件热更新、回滚与灰度
Agent 服务的发布比普通 Web 服务更敏感,因为一个提示词的改动就可能改变整个行为路径。我的发布流程分三步:
- 灰度发布:先在一个内网测试频道跑 24 小时,只放行内部流量;
- 观察核心指标:工具调用成功率、平均步数、Token 消耗、用户侧拒绝率;
- 逐步放量:10% → 50% → 100%,每个阶段持续至少 30 分钟。
插件更新支持热加载,但热加载并不等于安全加载。我在插件目录里保留上一版本插件包,一旦新插件导致工具注册异常,直接切换回旧目录并重启,回滚过程不到一分钟。
4.6 值得装的几个实用插件
从我实际使用的经验看,有几类插件对生产 Agent 几乎是刚需:
- 提示词优化插件:自动把用户乱糟糟的请求整理成结构化的任务描述,大幅提高后续工具调用的准确率;
- 网页抓取插件:封装了反爬、编码识别、正文抽取的完整流程,直接暴露给模型一个干净的接口;
- 表格处理插件:让模型读取 Excel 或 CSV,输出结构化行数据,避免模型输出各种奇怪的格式;
- JSON 校验与修复插件:把模型输出但格式不完整的 JSON 自动修复,大幅减少下游解析失败;
- Skill 管理插件:从远程仓库拉取新的 Skill 包、校验版本、发布到内网服务器。
插件是好东西,但也不要装太多。每多一个插件,工具列表就会变长,既增加每次请求的 Token 消耗,也增加模型选错工具的概率。我一般把单个 Harness 的工具数量控制在 30 个以内,超过这个数就按场景拆分子图。
5. 常见问题与排查技巧实录
下面这些问题,都是我或者周围同事在实操中真实遇到过的,整理成速查表,方便你直接对照排查。
| 现象 | 常见原因 | 排查与解决办法 |
|---|---|---|
| DeepSeek Harness 安装后启动失败 | 缺少依赖包、配置文件路径错误 | 检查/var/lib/harness依赖目录,用harness doctor做自检 |
| Linux 内网部署时插件加载失败 | 插件依赖未打入离线包 | 重新打包时用ldd和 Pythonpip download把动态库和依赖一起带走 |
harness failed to load plugins web boot: 1 entry did not activate | 插件入口未导出、浏览器端插件缺少 DOM 环境 | 检查插件manifest.yaml的入口声明,web 插件必须在声明文件里指定入口文件 |
self referencing loop detected for property 'mem_memberinfo' | 内存状态序列化时出现循环引用 | 序列化时排除运行时实例引用,只保留可持久化字段 |
| Agent 不断重复调用同一个工具 | 模型陷入死循环,或工具返回值没有真正改变上下文 | 加动作指纹去重,连续三次相同动作直接熔断 |
| Token 费用突然暴涨 | 单任务步数超限、工具返回内容过大 | 设置max_steps和总预算,工具返回结果做截断摘要 |
| 新旧版本行为不一致 | 提示词或插件更新导致工具路径变化 | 做可重复性回归测试,固定随机种子,比对工具调用序列 |
单独展开两个高频问题:
DeepSeek Harness 无法安装,多数情况不是代码问题,而是环境问题。Linux 系统上最常见的是 gcc 版本过低、OpenSSL 依赖缺失、解压后缺少可执行权限。排查时先执行./harness version,如果提示无法执行,用ldd harness查看缺失的动态库,再补齐对应依赖。内网环境建议直接在构建机用静态编译选项打出无动态依赖的二进制,拷到哪里都能跑。
failed to load plugins这个问题,我从一个实际案例说起:部署一套带中文插件的 Harness 时,启动日志报1 entry did not activate,初始化被整体拒绝。查了半天发现,插件目录里确实有文件,但插件的manifest.yaml中声明的入口文件写错了路径。Harness 有保护机制:一个插件入口无法激活时,它不会跳过,而是直接让整个引导流程失败,避免带病启动。解决办法是逐个插件目录执行校验脚本,确认入口文件存在且依赖模块已安装。我把这个校验步骤写进了 CI,发布前先自动验证所有插件清单。
内网部署 Skill 的离线方案也给出来。Skill 包通常包含提示词、工具脚本和依赖清单。在联网机器上把 Skill 下载到本地后,执行harness skill export --output offline-pack.zip,生成一个包含所有链接库的离线包,再拷贝到内网解压到插件目录,最后harness skill install offline-pack.zip注册。注意,离线包要记录版本 hash,以后更新时才好做一致性校验,不至于新旧文件混在一起都不知道。
我个人在实际操作中最深的体会是:任何配置改动都要先备份旧版本。有一次我手滑改了一个工具的默认参数,线上任务立刻开始用新参数抓取数据,结果回滚后才发现旧版本插件包没有保存配置,只能凭 Git 日志手工恢复。从那次开始,我在发布脚本里强制要求“先打 Tag,再改配置”,Tag 就是我的后悔药。
如果你刚开始搭 Agent 工程,不要急着把三层架构全部上齐。先用一个 Harness 把一个最小的 Loop 跑通,确认工具调用、日志、Token 控制都没问题,再根据真实需求引入 Graph。架构是为你服务的,不是为了架构而架构。先让模型能稳定地“转圈”,你才有资格谈怎么“画图”。