三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

AgentScope Java Harness:7. 子 Agent 编排 文件驱动的多智能体协作架构

AgentScope Java Harness:7. 子 Agent 编排 文件驱动的多智能体协作架构

当单个 Agent 的能力触及天花板,真正的突破不在于更强的模型,而在于更优雅的协作。AgentScope Harness 用"文件即规格、状态即协调"的设计,让多 Agent 编排从代码硬编码走向声明式配置。

一、引言:为什么单 Agent 不够用?

在实际业务中,我们很快会遇到单 Agent 的三重瓶颈:

瓶颈表现
能力边界一个 Agent 无法同时精通代码审查、数据分析、文档撰写
上下文膨胀所有工具和知识塞进一个 prompt,token 爆炸、注意力分散
职责混乱“全能 Agent” 变成"什么都做但什么都不精"的万金油
传统的多 Agent 框架通常用代码硬编码来解决这个问题:
// 传统方式:编排逻辑写在代码里if(task.equals("code_review")){codeReviewAgent.call(input);}elseif(task.equals("data_analysis")){dataAnalysisAgent.call(input);}

这种方式的问题显而易见:**每新增一个子 Agent,都要改代码、重新编译、重新部署。**编排逻辑和业务逻辑耦合,非技术人员无法参与,版本管理困难。
AgentScope Java 2.0 的 Harness 模块给出了一个截然不同的答案:

子 Agent 的规格声明是文件,不是代码。主 Agent 通过读取工作区中的 Markdown 文件来发现和编排子 Agent。

二、核心设计哲学:文件驱动编排

2.1 三大原则

┌─────────────────────────────────────────────────────────────┐ │ 文件驱动编排的三大原则 │ │ │ │ 1. 规格即文件 子 Agent 的能力描述是 Markdown,不是 Java 类 │ │ 2. 发现即扫描 框架自动扫描 subagents/ 目录,无需手动注册 │ │ 3. 编排即推理 主 Agent 通过 LLM 推理决定委派,不是 if-else │ └─────────────────────────────────────────────────────────────┘

2.2 与传统方式的对比

维度代码硬编码编排Harness 文件驱动编排
新增子 Agent改代码 + 编译 + 部署放一个 .md 文件到 subagents/
非技术人员参与❌ 不可能✅ 编辑 Markdown 即可
版本管理Git commit 散落整个 subagents/ 目录天然 Git 友好
运行时动态调整❌ 需重启✅ 下轮推理自动生效
多环境迁移代码分支 / 配置中心复制目录
人机共编✅ 开发者、PM、领域专家都能编辑

三、子 Agent 规格文件(Subagent Spec)

3.1 文件位置与命名约定

workspace/ └── subagents/ ├── weather-agent.md ← 天气查询子 Agent ├── flight-agent.md ← 航班搜索子 Agent ├── code-reviewer.md ← 代码审查子 Agent └──>3.2 规格文件格式

每个 .md 文件遵循统一的 Front Matter + Body 结构:

--- name: weather-agent description: 查询指定城市的实时天气和未来天气预报 tools: - get_weather - get_forecast model: gpt-4o-mini # 可选:指定子 Agent 使用的模型 max_turns: 5 # 可选:最大推理轮次 timeout_seconds: 30 # 可选:超时时间 --- # Weather Agent ## 职责 你是一个专业的天气查询助手。当用户询问天气相关问题时, 使用 get_weather 和 get_forecast 工具获取准确数据。 ## 输出格式 - 当前天气:温度、湿度、风力、天气状况 - 未来预报:按天列出,包含最高/最低温度和降水概率 ## 约束 - 只回答天气相关问题,其他问题礼貌拒绝 - 数据来源必须是工具返回的结果,不要编造 - 温度单位默认摄氏度,用户指定华氏度时切换

3.3 Front Matter 字段详解

字段必填类型说明
nameString子 Agent 唯一标识,用于委派调用
descriptionString能力描述,注入主 Agent system prompt
toolsList子 Agent 可用的工具白名单
modelString指定模型,不填则继承主 Agent 模型
max_turnsInteger最大 ReAct 推理轮次
timeout_secondsInteger单次执行超时(秒)
sandboxObject沙箱配置(隔离执行)
memoryObject记忆配置(独立记忆空间)

3.4 Body 部分的作用

Body 部分是子 Agent 的完整 system prompt。它定义了:

  • 角色定位和行为约定
  • 输入输出格式规范
  • 约束和禁止行为
  • 领域知识和工作流指引

💡 关键设计:Front Matter 是机器可读的结构化元数据,Body 是人类可读的自然语言指令。两者分离,各司其职。

四、自动发现与装配机制

4.1 构建期扫描

HarnessAgentmainAgent=HarnessAgent.builder().name("travel-assistant").model(newOpenAIChatModel(apiKey,"gpt-4o")).workspace(Path.of("./workspace"))// 框架自动扫描 workspace/subagents/*.md// 无需手动注册任何子 Agent.build();

扫描流程:

HarnessAgent.build() │ ▼ WorkspaceContextHook │ ├── listDir("subagents/") │ → [weather-agent.md, flight-agent.md, ...] │ ├── 逐个解析 Front Matter │ → SubagentSpec(name, description, tools, ...) │ ├── 生成委派工具描述 │ → "delegate_to_weather_agent: 查询指定城市的实时天气..." │ └── 注入主 Agent system prompt → 主 Agent "知道"自己有哪些子 Agent 可以委派

4.2 注入主 Agent 的内容

框架将每个子 Agent 的 name + description 拼装为一段委派指引,注入主 Agent 的 system prompt:

## Available Sub-Agents You can delegate tasks to the following specialized sub-agents: - **weather-agent**: 查询指定城市的实时天气和未来天气预报 - **flight-agent**: 搜索和比较航班信息,支持多条件筛选 - **code-reviewer**: 审查代码质量、安全性和最佳实践 - **data-analyst**: 分析数据集,生成图表和洞察报告 Use the `delegate_to_<agent_name>` tool to assign tasks. Only delegate when the task clearly matches a sub-agent's capability.

4.3 运行时热更新

由于子 Agent 规格是从文件实时读取的,修改 .md 文件后,下一轮推理立即生效

# 新增一个子 Agentecho"--- name: hotel-agent description: 搜索和推荐酒店,支持价格/星级/位置筛选 tools: - search_hotels - get_hotel_details --- # Hotel Agent ...">workspace/subagents/hotel-agent.md# 无需重启服务,下一次 call() 自动发现 hotel-agent

五、委派执行流程

5.1 完整调用链

用户:"帮我查一下明天北京到上海的航班,然后看看上海天气" │ ▼ 主 Agent 推理 │ 识别出两个子任务:航班查询 + 天气查询 │ ├── ① delegate_to_flight_agent("明天北京到上海的航班") │ │ │ ▼ 框架创建子 Agent 实例 │ │ 加载 flight-agent.md 的完整 spec │ │ 装配 tools 白名单中的工具 │ │ 设置 model / max_turns / timeout │ │ │ ▼ 子 Agent ReAct 推理 │ │ 调用 search_flights 工具 │ │ 生成结构化结果 │ │ │ ▼ 返回结果给主 Agent │ ├── ② delegate_to_weather_agent("上海明天的天气") │ │ │ ▼ 同上流程 │ │ │ ▼ 返回结果给主 Agent │ ▼ 主 Agent 整合两个子任务的结果 │ 生成统一的回复 │ ▼ 返回给用户

5.2 委派工具的内部实现

delegate_to_<agent_name> 是一个由框架自动注册的内部工具,对主 Agent 来说和普通工具没有区别:

{"name":"delegate_to_weather_agent","description":"查询指定城市的实时天气和未来天气预报","parameters":{"type":"object","properties":{"task":{"type":"string","description":"要委派给 weather-agent 的具体任务描述"}},"required":["task"]}}

5.3 子 Agent 的执行隔离

每个子 Agent 在执行时拥有独立的上下文:

维度主 Agent子 Agent
System PromptAGENTS.md + MEMORY.md + 子 Agent 列表子 Agent spec body
工具集全部工具 + 委派工具仅 spec 中声明的 tools
对话历史完整用户对话仅本次委派的 task 描述
记忆共享 MEMORY.md可配置独立记忆空间
沙箱主 Agent 沙箱可配置独立沙箱

💡 设计意图:子 Agent 不需要也不应该看到主 Agent 的完整上下文。这既节省了 token,又避免了信息泄露和注意力分散。

六、高级编排模式

6.1 串行编排(Pipeline)

用户请求 → 主 Agent │ ├── delegate_to_data_collector("收集Q3销售数据") │ ↓ 返回原始数据 ├── delegate_to_data_analyst("分析Q3销售趋势") │ ↓ 返回分析报告 └── delegate_to_report_writer("生成Q3销售报告") ↓ 返回最终报告

主 Agent 通过 LLM 推理自动决定串行顺序,无需代码定义 pipeline。

6.2 并行编排(Fan-out / Fan-in)

用户请求 → 主 Agent │ ├── delegate_to_flight_agent(...) ─┐ ├── delegate_to_hotel_agent(...) ─┼── 并行执行 └── delegate_to_weather_agent(...) ─┘ ↓ 主 Agent 整合三个结果

⚠️ 注意:并行执行取决于主 Agent 的推理能力和模型的 function calling 支持。部分模型支持在一次响应中调用多个工具。

6.3 条件编排(Conditional)

用户请求 → 主 Agent │ ├── 判断任务类型 │ ├── 代码相关 → delegate_to_code_reviewer │ ├── 数据相关 → delegate_to_data_analyst │ └── 通用问题 → 自己回答 │ ▼ 根据 LLM 推理结果动态选择

**关键点:**条件判断由 LLM 推理完成,不是代码中的 if-else。新增分支只需添加新的子 Agent 文件。

6.4 嵌套编排(Hierarchical)

主 Agent ├── delegate_to_research_agent("调研竞品") │ ├── delegate_to_web_searcher("搜索竞品信息") │ └── delegate_to_doc_reader("阅读竞品文档") └── delegate_to_report_writer("生成竞品分析报告")

子 Agent 本身也可以是 HarnessAgent,拥有自己的 subagents/ 目录,形成多级编排树。

七、子 Agent 与工作区的深度集成

7.1 共享工作区 vs 独立工作区

// 方式一:共享主 Agent 工作区(默认)// 子 Agent 可以读取主 Agent 的 knowledge/、skills/ 等// 方式二:独立工作区HarnessAgentsubAgent=HarnessAgent.builder().name("isolated-analyst").workspace(Path.of("./workspaces/analyst"))// 独立目录.build();
模式适用场景优势风险
共享工作区子 Agent 需要访问主 Agent 的知识/技能资源共享,避免重复子 Agent 可能误改主 Agent 文件
独立工作区子 Agent 完全自治强隔离,互不干扰需要单独维护知识和配置

7.2 任务记录持久化

子 Agent 的执行记录自动写入工作区:

workspace/agents/<mainAgentId>/tasks/ ├── sess-001.json ← 会话级任务汇总 └── sess-001/ ├── task-001-flight.json ← 航班查询任务详情 └── task-002-weather.json ← 天气查询任务详情

每个任务记录包含:

  • 委派的 task 描述
  • 子 Agent 的完整推理过程
  • 工具调用日志
  • 最终返回结果
  • 耗时和 token 消耗

7.3 记忆联动

编辑# subagents/data-analyst.md Front Mattermemory:enabled:truescope:independent# independent | sharedflush_trigger:always
scope行为
shared子 Agent 读写主 Agent 的 MEMORY.md
independent子 Agent 拥有独立的 MEMORY.md 和 memory/ 目录

八、完整配置示例

8.1 差旅助手主 Agent

HarnessAgenttravelAssistant=HarnessAgent.builder().name("travel-assistant").model(newOpenAIChatModel(apiKey,"gpt-4o")).workspace(Path.of("./workspace"))// 记忆.memory(MemoryConfig.defaults())// 压缩.compaction(CompactionConfig.builder().triggerMessages(30).keepMessages(10).build())// 沙箱.filesystem(newSandboxFilesystemSpec().backend(newDockerSandboxBackend().image("python:3.11-slim").build()).isolationScope(IsolationScope.USER).build())// 状态持久化.stateStore(newRedisAgentStateStore(redisClient))// 子 Agent 自动从 workspace/subagents/ 发现// 无需额外配置!.build();

8.2 对应的工作区结构

workspace/ ├── AGENTS.md │ # Travel Assistant │ ## 角色 │ 你是一个企业差旅助手,负责协调航班、酒店、天气等信息查询。 │ ## 编排策略 │ - 航班查询委派给 flight-agent │ - 酒店推荐委派给 hotel-agent │ - 天气查询委派给 weather-agent │ - 简单问题直接回答,不要过度委派 │ ├── MEMORY.md ├── tools.json ├── knowledge/ │ └── reimbursement-policy.md │ ├── subagents/ │ ├── flight-agent.md │ ├── hotel-agent.md │ ├── weather-agent.md │ └── expense-calculator.md │ └── agents/travel-assistant/ ├── sessions/ └── tasks/

九、与其他子系统的协作

┌─────────────────────────────────────────────────────────────┐ │ 子 Agent 编排生态 │ │ │ │ ┌──────────────┐ │ │ │ subagents/*.md│ ← 规格文件(人类编辑 / Agent 自生成) │ │ └──────┬───────┘ │ │ │ 构建期扫描 │ │ ▼ │ │ ┌──────────────┐ 注入 system prompt │ │ │ 主 Agent │ ◀────────────────────────────────────┐ │ │ │ (HarnessAgent)│ │ │ │ └──────┬───────┘ │ │ │ │ delegate_to_xxx │ │ │ ▼ │ │ │ ┌──────────────┐ │ │ │ │ 子 Agent │ ── 任务记录 ──▶ tasks/*.json │ │ │ │ (HarnessAgent)│ │ │ │ └──────┬───────┘ │ │ │ │ │ │ │ ┌────┴────┬──────────┬──────────┐ │ │ │ ▼ ▼ ▼ ▼ │ │ │ ┌──────┐ ┌──────┐ ┌────────┐ ┌────────┐ │ │ │ │Tools │ │Memory│ │Sandbox │ │Session │ │ │ │ │白名单│ │独立/ │ │独立/ │ │持久化 │ │ │ │ │ │ │共享 │ │共享 │ │ │ │ │ │ └──────┘ └──────┘ └────────┘ └────────┘ │ │ │ │ │ │ 主 Agent system reminder ◀── 任务状态反馈 ─────────────┘ │ └─────────────────────────────────────────────────────────────┘
子系统与子 Agent 的关系
Workspace规格文件存储在工作区,任务记录写入工作区
记忆可配置独立或共享记忆空间
沙箱可配置独立沙箱或共享主 Agent 沙箱
压缩子 Agent 有独立的上下文窗口和压缩策略
权限工具白名单在 spec 中声明,框架强制执行
Session子 Agent 的推理过程持久化为任务记录

十、最佳实践

10.1 规格文件编写指南

原则说明
description 要精确这是主 Agent 决定委派的唯一依据,模糊描述导致误委派
Body 要自包含子 Agent 看不到主 Agent 的上下文,spec body 必须包含所有必要信息
tools 要最小化只授予完成任务必需的工具,遵循最小权限原则
约束要明确明确写出"不做什么"比"做什么"更重要
输出格式要标准化便于主 Agent 解析和整合

10.2 编排策略建议

场景推荐策略
任务明确、边界清晰文件驱动委派(本文方案)
需要复杂条件分支主 Agent LLM 推理 + 子 Agent 文件
需要严格顺序保证在主 Agent AGENTS.md 中写明编排流程
子 Agent 间需要通信通过主 Agent 中转,不要让子 Agent 直接对话
动态生成子 AgentAgent 自己写 .md 文件到 subagents/,下轮生效

10.3 常见反模式

# ❌ description 太模糊 description: 处理各种任务 # ❌ Body 依赖主 Agent 上下文 ## 注意事项 请参考上面用户提到的报销标准... # 子 Agent 看不到"上面" # ❌ tools 过多 tools: [tool_a, tool_b, tool_c, ..., tool_z] # 违反最小权限 # ❌ 没有约束 # 缺少"禁止行为"段落,子 Agent 可能越界

十一、设计哲学总结

1. 规格即文件,编排即推理

这是整个子 Agent 系统的基石。规格不是代码,编排不是 if-else。这使得非技术人员可以参与 Agent 能力建设,也使得系统可以在运行时自我进化。

2. 发现优于注册

框架自动扫描 subagents/ 目录,无需手动注册。新增子 Agent 的唯一操作就是放一个文件。这种"约定优于配置"的设计大幅降低了使用门槛。

3. 隔离优于共享

子 Agent 默认拥有独立的上下文、工具集和对话历史。共享是显式配置的例外,不是默认行为。这确保了子 Agent 的专注性和安全性。

4. 记录优于遗忘

每次委派的完整过程都持久化为任务记录。这不仅支持事后审计,也为主 Agent 提供了"反思"的素材——它可以回顾过去的委派效果,优化未来的编排决策。

5. 组合优于继承

子 Agent 不是主 Agent 的子类,而是独立的、可组合的能力单元。同一个子 Agent 可以被多个主 Agent 复用,也可以在嵌套编排中被其他子 Agent 调用。

十二、结语

AgentScope Harness 的子 Agent 编排设计,回答了一个根本问题:

如何让多 Agent 协作像搭积木一样灵活,而不是像写代码一样僵硬?

答案是:把编排的"知识"从代码中解放出来,变成人类可读、机器可解析、Agent 可自生成的文件。
当你把子 Agent 的规格看作"文档"而非"配置"时,很多设计决策就变得自然而然了:

  • 文档可以 Git 管理 → 版本控制免费获得
  • 文档可以由任何人编辑 → 团队协作门槛降低
  • 文档可以在运行时更新 → 热更新零成本
  • 文档可以由 Agent 自己撰写 → 自我进化成为可能

如果你正在构建需要多能力协作的 Agent 系统,这套"文件驱动编排"的设计思路值得深入研究和借鉴。它不仅仅是一种技术方案,更是一种让 AI 系统回归人类可理解、可参与、可治理的工程哲学。

← 返回列表