多Agent设计与工程:用 Harness 与 Memory 搭建可复现的 Skill 协作链路

发布时间:2026/9/30 0:45:37
多Agent设计与工程:用 Harness 与 Memory 搭建可复现的 Skill 协作链路 1. 多 Agent 协作链路为什么总是跑不稳从 Harness 与 Memory 说起多 Agent 系统最容易踩的坑不是模型不够聪明而是链路不可复现。同一个任务今天跑出来是对的明天换个会话就散了采集 Agent 顺手改了文件分析 Agent 又编了数据最后你根本不知道是哪一环出的问题。这类现象背后其实是三个工程约束没处理好模型本身无状态、上下文窗口有限、工具权限没有硬边界。我先把结论摆出来多 Agent 能不能稳定复现取决于 Harness 怎么编排、Memory 怎么持久化、Skill 怎么封装。模型只是推理引擎真正决定协作质量的是外面这层工程结构。这篇会给出可复制的目录结构、Harness 配置片段、Memory 与 Skill 的接口定义并附一轮端到端运行与结果校验步骤你可以直接照着搭一条最小可用的协作链路。适合谁看已经在用 Claude Code、Cline、Codex 这类工具想把单 Agent 升级成多 Agent 流水线的开发者或者被上下文爆炸、角色混乱、权限泄漏折腾过的人。核心检索词就是多 Agent、Harness、Memory、Skill 这四个下面会围绕它们展开。先明确一个类比。把多 Agent 系统想成一家小公司Harness 是总经理办公室负责排班、发任务、收结果Memory 是公司的制度手册和项目档案新员工入职先读它Skill 是各个岗位的 SOP写一次全员复用Sub-Agent 是具体干活的员工各自有独立工位和权限卡。模型是员工的脑子脑子再聪明没有办公室调度和制度约束公司照样乱。这套结构里Harness 比模型更重要。同一个模型换不同 Harness表现差距远大于不同模型换同一个 Harness。所以调教 Harness 的能力才是真正的杠杆点。下面从目录结构开始一步步把这条链路搭起来。2. TaoToken 前置准备把 Base URL、Key、Model ID 三件套配齐在搭多 Agent 链路之前得先有一个稳定的模型调用入口。这里用 TaoToken 作为统一接入层它的作用是让你在 Harness 里配置一次多个 Agent 共享同一个调用通道省得每个 Agent 各配一套。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 。配置任何 Agent 工具核心都是三件套Base URL、API Key、Model ID。这三样缺一不可而且路径要和工具要求的一致否则会出现 401 或者 local proxy failed 这类报错。下面按不同工具分别给出配置位置。如果你用的是 Claude Code配置走的是 settings 文件。在项目根目录或用户目录下创建.claude/settings.json写入{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: 你的_API_KEY, ANTHROPIC_MODEL: claude-sonnet-4-5-20250929 } }注意 Base URL 这里填的是https://taotoken.net/api不要多加路径后缀。Key 从控制台的 API Keys 页面生成地址是 https://taotoken.net/console/api-keys 。Model ID 要和你实际调用的模型对齐写错会报 model not found。如果你用的是 Cline 或者带 MCP 的工具配置通常走mcp_settings.json或者工具自己的 settings 面板。以 Cline 为例在 MCP 配置里加{ mcpServers: { taotoken: { command: npx, args: [-y, taotoken/mcp-server], env: { TAOTOKEN_BASE_URL: https://taotoken.net/api, TAOTOKEN_API_KEY: 你的_API_KEY, TAOTOKEN_MODEL: claude-sonnet-4-5-20250929 } } } }如果你用的是 Codex配置走~/.codex/auth.json{ base_url: https://taotoken.net/api, api_key: 你的_API_KEY, model: claude-sonnet-4-5-20250929 }三件套配好之后先别急着搭多 Agent先用一个最小请求验证通道是通的。这一步很关键因为后面所有 Agent 都依赖这个通道通道不通后面全是白搭。验证命令用 curl 就行curl https://taotoken.net/api/v1/messages \ -H Content-Type: application/json \ -H x-api-key: 你的_API_KEY \ -H anthropic-version: 2023-06-01 \ -d { model: claude-sonnet-4-5-20250929, max_tokens: 64, messages: [{role: user, content: 回复 OK 两个字母}] }返回里能看到content字段带OK说明通道正常。如果返回 401检查 Key 是否复制完整如果返回 local proxy failed检查 Base URL 是否写成了带多余路径的形式。这一步过了再往下搭 Harness。3. 可复制的 Harness 配置与目录结构把编排、Memory、Skill 落到文件这一节是整篇的核心给出可以直接复制的目录结构和配置片段。先看目录结构这是多 Agent 链路能复现的基础所有角色、技能、记忆都落在文件里而不是散在对话里。project-root/ ├── AGENTS.md # 项目级 Memory技术栈、规范、约束 ├── agents/ # 角色定义目录 │ ├── collector.md # 采集 Agent只读 网络 │ ├── analyzer.md # 分析 Agent只读 │ └── organizer.md # 整理 Agent只写 ├── skills/ # 可复用能力目录 │ ├── dedup/ │ │ └── SKILL.md # 去重 SOP │ └── format/ │ └── SKILL.md # 格式化 SOP ├── knowledge/ # 数据流转目录 │ ├── raw/ # 采集原始数据 │ └── articles/ # 整理后产物 └── harness.toml # Harness 编排配置这个结构的关键在于Agent 之间通过文件协作不通过上下文共享。采集 Agent 把结果写到knowledge/raw/分析 Agent 从那里读整理 Agent 写到knowledge/articles/。每个 Agent 的上下文是隔离的主 Agent 只拿到压缩后的摘要。先写项目级 Memory也就是AGENTS.md。它是项目的入职手册控制在 500 行以内最重要的规则放最前面# 项目概述 本项目用于采集技术资讯并整理成结构化文章输出到 knowledge/articles/。 # 技术栈 - 语言Python 3.11 - 数据格式JSON - 测试pytest # 编码规范 - 命名用 snake_case - 禁止在采集环节写入 knowledge/articles/ - 禁止执行 rm -rf 等破坏性命令 # 项目结构 - knowledge/raw/ 存放采集原始数据 - knowledge/articles/ 存放整理后产物 - agents/ 存放角色定义 - skills/ 存放可复用技能 # 工作流程 1. collector 采集 - knowledge/raw/ 2. analyzer 分析 - 输出 JSON 摘要 3. organizer 整理 - knowledge/articles/ # 特殊约束 - 所有 Agent 默认只读写权限需显式声明 - 网络访问仅 collector 允许接着写角色定义。每个 Agent 文件包含身份、权限、职责三部分。以agents/collector.md为例--- name: collector allowed-tools: [Read, WebFetch] denied-tools: [Write, Bash] --- # 角色 你是采集 Agent负责从指定来源抓取技术资讯。 # 职责 1. 读取 knowledge/raw/ 下已有的采集记录避免重复 2. 抓取新内容输出为 JSON 3. 结果写入 knowledge/raw/collector-{timestamp}.json # 约束 - 只读不写 knowledge/articles/ - 不执行任何 shell 命令allowed-tools和denied-tools是硬约束Harness 在执行时会真正切掉权限而不是靠提示词约束。这一点和 mention 有本质区别mention 只是角色提示注入工具权限没被真正切掉。再写 Skill。Skill 不是代码是知识是方法论。以skills/dedup/SKILL.md为例--- name: 去重 trigger: [去重, 重复检查, dedup] context:fork: true --- # 去重 SOP 1. 读取目标 JSON 数组 2. 以 url 字段为唯一键 3. 保留首次出现的记录丢弃后续重复 4. 输出前校验记录数是否减少 5. 返回去重前后的数量对比context:fork: true表示这个 Skill 执行时会自动创建一个临时子代理拥有独立上下文。这样持久知识加隔离执行就合体了。最后写 Harness 编排配置harness.toml[harness] name tech-digest max_rounds 10 context_compress_threshold 0.92 [[harness.pipeline]] agent collector input knowledge/raw/ output knowledge/raw/collector-{timestamp}.json on_error retry max_retries 2 [[harness.pipeline]] agent analyzer input knowledge/raw/collector-{timestamp}.json output knowledge/raw/analyzed-{timestamp}.json skill dedup [[harness.pipeline]] agent organizer input knowledge/raw/analyzed-{timestamp}.json output knowledge/articles/这个配置定义了三个阶段采集、分析、整理。每个阶段指定 Agent、输入、输出分析阶段还挂载了去重 Skill。context_compress_threshold 0.92表示上下文占用到 92% 时自动压缩压缩后AGENTS.md会重新注入这就是 Memory 持久生效的秘密。配置写完后目录结构、Memory、Skill、Harness 四件套就齐了。下面跑一轮端到端验证。4. 端到端运行与结果校验确认多 Agent 链路按预期协作配置齐了之后跑一轮完整链路看每个阶段是否按预期产出。运行方式取决于你用的 Harness 实现这里以命令行触发为例。第一步触发采集阶段harness run --config harness.toml --stage collector预期结果knowledge/raw/下出现collector-20260101-120000.json内容是采集到的原始记录数组。如果这个文件没出现说明 collector 的写权限没生效检查allowed-tools是否包含 Write或者输出路径是否在 denied 范围内。第二步触发分析阶段harness run --config harness.toml --stage analyzer预期结果knowledge/raw/下出现analyzed-20260101-120000.json记录数应该比采集阶段少因为去重 Skill 生效了。你可以用 jq 对比数量jq length knowledge/raw/collector-20260101-120000.json jq length knowledge/raw/analyzed-20260101-120000.json如果两个数字一样说明去重没生效检查 Skill 的 trigger 是否匹配或者context:fork是否配置正确。第三步触发整理阶段harness run --config harness.toml --stage organizer预期结果knowledge/articles/下出现整理后的文章文件。整理 Agent 只有写权限没有网络权限所以它不会去抓新数据只会基于分析结果生成产物。第四步校验整条链路的上下文隔离。查看主 Agent 的上下文占用应该远小于各子 Agent 内部消耗的总和。子 Agent 内部可能消耗 50000 tokens 探索返回主 Agent 的摘要只有 1500 tokens 左右压缩比大约 33:1。这就是上下文隔离的价值主 Agent 的注意力零损耗。第五步校验 Memory 重注入。在分析阶段执行到一半时手动触发一次上下文压缩然后检查AGENTS.md的规则是否还在生效。具体做法是让分析 Agent 尝试写knowledge/articles/如果被拒绝说明 Memory 里的禁止规则在压缩后仍然生效。整条链路跑通后你会看到三个阶段的产物依次出现数量递减权限边界清晰。这时候多 Agent 链路就是可复现的换一个会话、换一天跑结果结构一致。5. 本篇常见报错排查401、local proxy failed、reading choices、OAuth搭链路的过程中报错基本集中在几个地方。这一节按真实报错逐个排查。401 Unauthorized。这个最常见原因是 Key 没配对或者没带上。检查三处一是settings.json或auth.json里的 Key 是否复制完整有没有多余空格二是请求头字段名是否正确Anthropic 协议用x-api-keyOpenAI 协议用Authorization: Bearer三是 Key 是否在控制台被禁用。重新生成一个 Key地址是 https://taotoken.net/console/api-keys 替换后重试。local proxy failed。这个报错通常出现在 Base URL 写错的时候。比如把https://taotoken.net/api写成了https://taotoken.net/api/v1/messages多了路径后缀代理层解析不了。正确做法是 Base URL 只写到/api具体路径由 SDK 自己拼。检查所有配置文件里的 Base URL统一改成https://taotoken.net/api。reading choices 报错。这个一般出现在 OpenAI 兼容协议下返回体里没有choices字段。原因是模型 ID 写错了或者请求发到了 Anthropic 协议端点却用了 OpenAI 格式。检查 Model ID 是否和实际模型对齐检查请求路径是/v1/messages还是/v1/chat/completions两者不能混用。OAuth 相关报错。如果你用的是 Claude Code 并且走了 OAuth 登录流程可能会和 API Key 模式冲突。解决办法是明确用 API Key 模式在settings.json里设置ANTHROPIC_AUTH_TOKEN不要同时保留 OAuth 的凭据文件。如果之前登录过清掉~/.claude/下的凭据缓存再重试。Agent 权限没生效。表现是 collector 写了knowledge/articles/或者 analyzer 执行了 shell 命令。原因是用了 mention 而不是 Task 工具委派。mention 只是角色提示注入工具权限没被真正切掉。解决办法是改用 Harness 的 pipeline 配置让每个阶段走独立的 Agent 定义权限由allowed-tools硬约束。Skill 没触发。表现是去重没生效记录数没减少。检查 Skill 的trigger字段是否包含你实际用的关键词检查context:fork是否配置正确检查 Skill 文件路径是否在skills/目录下且文件名是SKILL.md。排查顺序建议先验证通道curl 最小请求再验证单 Agent 权限最后验证多 Agent 编排。通道不通后面全是白搭权限不对链路会串数据编排不对结果不可复现。6. 把链路跑顺之后Memory 维护与 Skill 复用的几个实用习惯链路跑通只是开始真正决定长期稳定的是 Memory 和 Skill 的维护习惯。分享几个我试过有效的做法。Memory 分层配置。根目录AGENTS.md放通用规则子目录放特定规则。比如knowledge/下可以放一个局部AGENTS.md规定这个目录下的数据格式和命名规范。Harness 在进入子目录时会自动加载局部 Memory这样规则不会全堆在一个文件里。Memory 保持精简。控制在 500 行以内太长反而稀释关键信息。最重要的规则放最前面因为 Agent 的注意力有衰减越靠后的规则越容易被忽略。正面规则和负面规则都要写清楚比如「采集环节只读」和「禁止写入 articles 目录」要同时出现。Memory 纳入 Code Review。把AGENTS.md和agents/*.md当成代码一样评审团队共同维护。过期规则及时清理否则 Agent 会按旧规则执行产生难以排查的问题。Skill 复用优先。遇到重复性任务先想能不能封装成 Skill而不是每次在对话里重新描述。Skill 写一次所有 Agent 都能用而且触发是自动的不需要手动调度。Skill 和 Sub-Agent 的区别在于Sub-Agent 是独立执行上下文需要手动调度知识在对话里是临时的Skill 是持久化知识包AI 自动触发知识在文件里是永久的。两者合体就是context:fork持久知识加自动创建临时子代理。Agent 之间通过文件协作。不要让 Agent 通过上下文共享数据那样会导致上下文爆炸和角色混乱。采集写文件分析读文件写文件整理读文件写文件。每个 Agent 的上下文是隔离的主 Agent 只拿摘要。这样即使某个环节出错也能定位到具体文件而不是在一大堆对话历史里翻。最后一点目标比过程更重要。把需求定义清楚过程完全可以托管给 Agent。多 Agent 链路的价值不在于让 AI 多干活而在于让协作可复现、可审计、可版本控制。链路跑顺之后你只需要定义好输入和期望输出中间的调度交给 Harness。