mcpField
A
agent-orchestrator

agent-orchestrator

github.com/sshelegAgent 工作流4221 次安装3 天前更新
代码仓库

关于 agent-orchestrator

面向构建多 Agent 系统的开发者,汇总生产环境中的编排设计实践。内容涉及工具调用循环、子 Agent、分阶段工作流、模型路由、记忆与预算控制。

主要功能

  1. 01共享上下文与子 Agent 协议
  2. 02工具调用、重试与结果校验
  3. 03多阶段依赖工作流与人工检查点
  4. 04LLM 提供方路由与回退
  5. 05分层记忆与反馈学习
  6. 06上下文预算、可观测性与护栏

适用场景

  1. 01设计可调用工具的 Agent 循环
  2. 02协调需要依赖排序的多阶段任务
  3. 03为模型提供方配置路由和回退
  4. 04控制 Agent 的上下文与调用预算

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/ssheleg/agent-stack --skill agent-orchestrator

安装后在 Claude Code 中输入/agent-orchestrator确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
context-engineering.md
governance.md
graph-engineering.md
kv-cache.md
llm-proxy-billing.md
memory-architecture.md
memory-landscape.md
memory-lifecycle.md
patterns.md
pipeline.md
provider-lifecycle.md
runtime.md

详细介绍

Agent Orchestrator — 生产环境最佳实践

来自生产级多 Agent 系统的模式。正文记录的是决策;具体机制就在一个文件之外,且正文控制在 4750 个 token 以内——正文一旦变长,就会挤占本该拆分出去的那一层,而这个正文一直如此,直到 2026-08-16。

触发词

“工具调用”、“LLM 路由器”、“回退链”、“记忆层”、“token 钱包”、“роутер моделей”、“слой памяти”、“граф задач”。

也会根据范围路由到此处。描述中列出的范围一直保留到 0.30.0:工具调用循环、人工检查点流水线、提供方路由、回退/重试、记忆架构、检索与衰减、上下文预算、子 Agent 协调、错误层级;将工作构建为图(并行层、虚假边、汇合前的检查器);转售相关内容:分级钱包、单一加价边界、跨数据库和提供方 API 的 saga、支出差额轮询、预算与循环防护、每租户密钥。

在 0.31.3 中从描述移至此处:“оркестратор”、“суб-агент”、“human in the loop”、“человек в цикле”、“биллинг LLM”。

架构概览

User Question
    ↓
OrchestratorAgent.run(AgentContext)
    ├─ Shape check → one loop, or a planned path (§5, references/pipeline.md)
    ├─ Context loading (a parallel layer: staleness, sources, KB — §13)
    ├─ History trimming, then context budget allocation
    ├─ System prompt built from the live capabilities (§10)
    └─ Execute:
        ├─ SIMPLE: LLM → tools → sub-agents → results → LLM → … → answer   (§2)
        └─ PLANNED: plan → dependency layers → checker → checkpoints → done (§5)

1. Orchestrator 模式

共享上下文对象

向每个子 Agent 传递同一个近似不可变的上下文对象。它包含请求(project_id、user_question、chat_history)、工具机制(llm_router、tracker、workflow_id)、已解析的提供方和模型,以及一个用于流水线标记的 extra 字典。完整的数据类见 references/patterns.md → Orchestrator 的共享上下文和子 Agent 协议。

关键原则:

  • 子 Agent 从不修改上下文,只返回类型化结果
  • 提供方/模型偏好按用户默认值 → 项目默认值 → 应用默认值的顺序传递
  • extra 字典承载流水线状态、_skip_complexity 等标记以及会话 ID
子 Agent 协议

每个子 Agent 都继承同一个基类,该基类只有一个抽象方法 run(context) -> Result,因此 Orchestrator 无需了解任何子 Agent 的内部工作方式。该类位于同一参考章节中。

每个 Agent 都有对应的类型化结果子类(例如,包含 query、results、attempts 的 SQLAgentResult)。


2. 工具调用循环(简单路径)

循环分为六个步骤,每一步都至关重要:

  1. 优先管理上下文压力——先裁剪上下文;如果窗口即将填满,还要注入收尾指令,然后再发起调用。
  2. 携带工具调用模型,并启用重试。
  3. 没有工具调用就表示得到了答案——这是循环唯一的正常退出方式。
  4. 追加助手回合及其工具调用,不作修改。
  5. 分发调用——独立工具使用 asyncio.gather,有状态工具则按顺序执行。
  6. 按 tool_call_id 关联并追加每个结果,然后继续迭代。

for 循环上的 else 是防护措施:循环耗尽时,根据已收集的内容尽力组织答案,而不是返回空结果。完整代码见 references/patterns.md → 完整的工具调用循环。

关键细节:

  • 并行分发工具:独立工具使用 asyncio.gather,有状态工具按顺序执行(例如,依赖先前查询结果的数据处理)
  • 注入收尾指令:上下文容量达到约 70% 时,注入系统消息,告诉大模型停止调用工具并给出最终答案
  • 循环内裁剪:上下文容量达到约 80% 时,将较早的助手与工具交互对折叠成单行摘要
  • token 限制恢复:遇到 LLMTokenLimitError 时,将上下文压缩至 60% 并重试一次。如果仍然失败,则返回部分答案
  • 最大迭代次数防护:始终设置硬性上限。次数耗尽时,根据目前收集的数据尽力组织答案
  • 迭代次数返还:可恢复的提供方错误不计入该防护的次数——费用仍会产生,只在成功路径上核算会低估最糟糕的运行成本
  • 预算感知:告知模型剩余预算,否则 300 步的表现会和 30 步一样

3. 元工具(编排器级)

编排器的工具会委派给子 Agent,而非直接执行: query_database 接受自然语言问题,由其背后的 SQL Agent 负责生成、验证和执行。一个参数,一项职责,调用方也无需知道子 Agent 的存在。

def get_tools(*, has_db=False, has_kb=False, has_mcp=False) -> list[Tool]:
    """Assembled per request from the same capability flags that build the prompt (§10)."""
    tools = []
    if has_db:  tools.extend([QUERY_DB, PROCESS_DATA, MANAGE_RULES, ASK_USER])
    if has_kb:  tools.append(SEARCH_CODEBASE)
    if has_mcp: tools.append(QUERY_MCP)
    return tools

有两条规则属于这一层,而非提示词:请求不具备的能力不会对应任何工具;工具接受的每个枚举值都必须在 schema 中封闭定义(ask_user 的 question_type 只能是 yes_no | multiple_choice | free_text,而不是自由文本)。如何描述工具,让模型选对工具——包括说明何时使用它,以及它容易与哪个相邻工具混淆——见 agent-harness/references/tools.md。


4. 子 Agent 重试与验证

每次调用子 Agent 都要同时进行重试和验证,并将二者分开处理:调用抛出异常,与调用返回了不可用内容,需要不同的应对方式。前者重试,后者重新提示或失败。

后续设计应遵循以下决策:

  • 在类型层面将错误划分为可重试和致命错误,而不是在调用点划分。无效凭据和服务提供方过载都会抛出异常,但只有一种值得再次尝试。
  • 在结果交给用户前验证结果,并依据调用方预期的结构进行检查——是否有行、列名是否正确、是否附有引用。只检查是否抛出异常的验证,无法发现自信却错误的答案。
  • 限制尝试次数并返回最佳部分结果,因为相比部分答案,另一种选择并不是更好的答案,而是没有答案且预算已耗尽。
  • 达到限制而停止,不等于等待。 继续处理独立工作,并安排在有界且经过授权的情况下恢复已停止的工作线程;并非每次沉默都代表停滞。 保留服务提供方的时序信息、尝试次数与截止时间预算,以及不确定效果的恢复机制。 支出和计费限制仍由人员把关。机制与准入规则见: references/runtime.md → 达到限制而停止,不等于等待。

层级结构、循环流程和各领域的验证器见: references/patterns.md。

5. 多阶段流水线(复杂路径)

如果问题不适合通过单个循环处理——例如多个相互依赖的数据步骤、需要人员在中途审批,或运行必须跨越两条消息之间的间隔继续——编排器应先制定计划,再执行各阶段,而不是调用工具。

这里涉及三个决策;具体机制见 references/pipeline.md。

  • 分两层检测复杂度,先采用成本较低的方式:先用关键词启发式判断,仅在启发式无法确定时才调用一次小型模型。为每个问题都调用模型进行分类,会给常见情况增加成本。
  • 按依赖层执行,绝不按列表顺序执行(§13)。计划声明了 depends_on,执行时却按列表顺序遍历,就会把任务串行化;而包含多个阶段的层,在任何阶段使用其结果前都应先经过检查器。
  • 检查点是一种能释放工作线程的暂停方式。 如果等待人工审批会占用进程,长时间审批就会被悄悄从设计中剔除——这正是一个人机协作系统逐渐不再有人参与的原因。

6. LLM 提供商路由

在提供商前设置路由器,而不是在应用前设置提供商客户端: 按顺序尝试,失败时转到下一个,并向上层返回统一的错误层级, 让调用方无法判断是哪个供应商响应的。

阅读 references/llm-proxy-billing.md → 模型路由与回退,了解 回退链和各提供商的指数退避重试;阅读其中的 防护措施部分,了解预算、循环检测和自动暂停。

三个会让你真金白银付出代价的陷阱:

  • 重试循环和回退链会相乘。 三个提供商各重试三次,就会为一个 提示词调用九次;应限制总尝试次数,而不是限制每个提供商的尝试次数。
  • 只在故障时运行的健康检查无法恢复。 被标记为不健康的提供商需要定时 探测,否则调用链会永久少用一个提供商,而且无人察觉——请求仍然会成功。
  • 模型选择有三个层级——请求、租户、系统默认值——如果租户覆盖值会被请求 参数悄无声息地压过,那么廉价模型就可能按高价模型的费率计费。
  • 这三点都假设请求是可移植的;轨迹却不是。 推理内容携带着供应商凭据——有时 还在工具调用中——因此在一轮对话中途切换可能导致 400,而剥除所有推理内容 正是导致这种情况的原因。应在轮次之间切换。
  • 能力消耗并不均匀:瓶颈在规划器,因此最强的模型应分配给管理器,而不是分配给 工作量最大的 Agent。

7. 多层记忆系统

四个层级,各自有不同的生命周期,也有各自存在的理由:

层级范围生命周期保存内容
1 聊天历史每个会话分钟各轮对话,裁剪至令牌预算
2 工作记忆每个资源天该任务目前已确认的信息
3 长期经验每个资源月有效的方法及其置信度评分
4 洞察每个项目永久超越其资源生命周期的结论

哪些内容会进入第 3 层和第 4 层,由 references/patterns.md 决定——置信度管理、 经验提取启发式、模糊去重、冲突解决。

这四层描述的是生命周期,而生命周期并不是分类法。 第 3 层和第 4 层都属于 经验性内容;这里没有事实存储,关于用户的过时事实会让 Agent 显得无礼, 关于环境的过时事实则会让它出错。这里没有提及检索,而检索涉及四项决策;第一项 ——是否进行检索——如果失败,就会生成一个凭空而来的自信回答,且错误日志中 不会留下任何记录。

请依据 references/memory-architecture.md 设计记忆层,而不要依据这张表——它还涵盖上下文预算陷阱、第 0 层信息延续和工作区 规模。写入路径和自行构建或采用现有方案的问题,在下方索引中各有对应条目。

8. 自学习反馈循环

三个循环为记忆层提供输入,它们的信号来源各不相同:先失败后修正的尝试、用户的 评价,以及时间。

本节存在的核心规则: 经验必须基于对比来写入——把失败的尝试与成功的尝试 并列比较——绝不能只基于一次成功运行。只从成功中学习的系统,学到的只是自己的习惯。

提取器、置信度计算和晋升查询: references/patterns.md。

9. 可观测性

一个总线,每个步骤对应一个事件,并在同一总线上以分块形式流式传输答案。两个 特性决定了它是信息流还是装饰:每个事件都带有单调递增 ID, 以便重连的客户端能够续接,而不会错过本次运行;信息流是持久化轨迹的视图, 而不是记录本身——没有存储下来的流,是 agent-evals 无法评估的一次运行。追踪器的 结构: references/runtime.md。

10. 动态系统提示词

在组装工具(§3)的同一轮中,根据实际存在的能力组装提示词:为每项当前可用的能力命名一个部分;如果有资源映射,也加入其中;然后加入当前的经验,最后加入指南。描述了 Agent 实际并未获得的工具的提示词,会让模型浪费一轮去调用一个不存在的工具。

该文本应该包含什么、详略到什么程度,以及如何列出词汇表以避免 Agent 臆造状态值,都由 agent-harness 技能的 agent-harness/references/system-prompt.md 规定——所有内容集中于一处,而不在这里。这个技能负责的是接线:每次请求都会根据与工具列表相同的能力标志重新构建提示词,因此两者绝不会不一致。只有逐字节完全相同时,重新构建才没有代价;如果会话中途能力发生变化,应追加变更,而不是重写前缀——编辑边界之前的内容会产生什么代价,请参阅 references/kv-cache.md。


11. 澄清请求(ask_user)

暂停并提问,与检查点一样,都是挂起后恢复,只是调用方不同——它们遵循同一套契约,而非两套(references/pipeline.md)。

12. 上下文工程

压缩是一套阶梯式流程,而不是一次调用。 清除旧的工具结果,折叠过大的内容块,精简消息,然后才调用摘要器——每迈一步都重新测量。大多数压力在第一次调用之前就能缓解。

绝不要让 tool_use 孤立。 每个截断点都必须落在 assistant+tool 对之间,否则下一次请求会在任务中途返回 400 错误。

跨越边界时,以有类型的内容块而非散文传递状态——目标、产物、已验证的工作、限制模式。摘要器会保留讨论内容,却丢掉状态,包括那个表示不要写入任何内容的标志。

将大型工具结果移至文件,并保留路径。 裁剪历史记录无法挽救已被一次工具输出填满的上下文窗口。

子 Agent 的价值在于它自己的上下文窗口:它返回的是有类型的摘要,而非对话记录。


13. 工作即图

在循环、流水线或子 Agent 之前:先确定结构。 节点是一个工作单元;边是依赖关系,且边承载数据。完整模型、模型的来源,以及此主机实际执行的内容,详见 references/graph-engineering.md。

四条规则,真正会改变代码的就是这些:

  • 为每条边标注类型:数据、控制、授权或资源。 对继承的任何链路运行虚假边测试——只有在箭头不承载四者中的任何一种时才删除它:没有负载、因果约束、权限或共享资源。backup→migration、approval→charge 和 lease→edit 不传输字节,却都是真实依赖;通常每个工作流中,测试找出的只有两三条仅表示某人输入顺序的箭头。分支前,先比较读写集合——只读分支可以并行;两个写入者操作同一事物时,原本就该有一条资源边。
  • depends_on 是一种声明,因此要按层执行。 §5 的执行器按列表顺序遍历 plan.stages,而模型却声明了依赖关系——这会把一个特意声明无需串行的计划串行化。对图运行 Kahn 算法;出现环路时应判定计划失败,而不是让运行陷入死锁。
  • 并行层在汇合前需要检查器。 三个分支运行,其中一个返回幻觉,综合节点却无法分辨:它会整合三个结果,并自信地作答。检查器只判定可用/不可用,不做其他事;汇合节点依赖的是检查器,绝不能直接依赖某个分支。
  • 除非能明确说出动态结构的必要原因,否则使用静态结构——这是为了可预测性,而非可审计性。当运行保存了执行记录(已运行的节点/边/事件、策略版本、确定性的预算/深度/节点上限、来源信息)时,就可审计;保存了该记录的动态图同样可审计。设计图本身绝不是证据:图中画出的结构,并不一定是实际运行的结构。

清单 — 构建新的编排器

上述章节是地图。读者无法从标题中推断出的要点都列在这里——这些都是从犯错中学到的:

  • 在上下文窗口用量约达 80% 时于循环中裁剪内容,在约 70% 时注入收尾提示,并设置最大迭代次数保护,以整理出部分答案,而不是空手返回
  • 可恢复的提供商错误会退还其占用的迭代次数;配置错误不得消耗本用于阻止运行失控的预算
  • 重试和回退次数的总量设有上限——三个提供商各重试三次,就会针对一个提示词调用九次
  • 按计划探测被标记为不健康的提供商;只在失败时运行的健康检查永远无法恢复,调用链会少运行一次,却无人察觉
  • 聊天历史设有下限——为保留旧有认知而裁剪历史的会话,是在选择泛泛而谈,而非保留用户一分钟前说的话
  • 每项已声明的依赖都注明其承载的数据;计划按层执行,多于一个节点的层在任何内容使用其结果前都要经过检查器(§13)
  • 观察过检查器拒绝人为植入的错误输入,且其判定以评分形式存储——从未拒绝过任何内容的检查器,本身就是一个发现
  • 子 Agent 返回的是提炼后的摘要,而非文字记录;返回值与输入成正比的函数调用,只不过是穿了件伪装外衣
  • 在一个边界处根据配置或提供商解析模型、上下文窗口和价格——绝不从源码中的供应商 ID 表解析
  • 实现前先添加可观测性,调整提示词前先进行评估,否则调整就只是凭经验;只有语料可以等到生产环境
  • 负责回答用户问题的 Agent,应按 agent-evals/references/support-quality.md 衡量:记录、指标、评分标准、评审器和每日事件

参考资料

每个文件开头都有自己的 在以下情况下加载 行——权威触发条件以该行为准;此表仅作索引。

文件在以下情况下阅读
references/graph-engineering.md你正在决定工作的形态——虚假边、菱形、检查器节点、静态还是动态
references/pipeline.md单个循环不符合形态——规划的路径、检查点、恢复,以及请求人工介入的中断
references/patterns.md你需要了解底层的数据模型和算法
references/memory-architecture.md正在为 Agent 提供记忆,或者记忆运行异常
references/memory-lifecycle.md记忆的写入路径——哪些内容进入,哪些内容离开
references/memory-landscape.md你要构建或采用记忆层,并想了解哪些做法已成为成熟实践
references/context-engineering.md循环即将耗尽上下文窗口
references/kv-cache.md你正在决定请求中的内容如何安排——静态前缀与轨迹的划分、缓存未命中的代价、运行时边界条件会产生的 2^N 种缓存分布、已命名的失效因素,以及四种常见的优化(它们付出了缓存代价,却一无所获)
references/runtime.mdAgent 必须在崩溃、暂停、收到第二条消息、定时任务或使用限额导致的停止后继续运行
references/governance.md问题关乎权限,而非成本——它可以做什么,以及如何证明;事件检查清单(DNS 出站、机密拆分、摘要注入、外部影响、发现的凭证、暴露的 Docker API)
references/llm-proxy-billing.md产品转售大模型访问权限
references/provider-lifecycle.md问题关乎人员队伍,而非循环——服务提供商生命周期、金丝雀信任、集群预算

评论

支持 Markdown