Skip to content

课 2 · Multi-Agent 实现

本课目标

用 Vercel AI SDK 实现 Boss-Worker 模式,完成任务拆解 → 执行 → 汇总的完整流程。课后你会拿到一个可运行的 Multi-Agent 系统。

这一课先掌握“用显式函数把多角色流程编排起来”就够了,不要求一上来把 Multi-Agent 做成完全自主规划、自主讨论的复杂系统。

上一课分析了三种设计模式。这一课选编排型(Boss-Worker)来实现——它是最常用、最实用的模式。

为什么这里不是单 Agent 多工具就够了

如果只是“查一个资料再回答”,单 Agent 多工具通常已经够用。但这节课要演示的是另一类任务:不同步骤的职责差异很大,而且你希望研究、写作、审校彼此隔离。

把研究、写作、审校都塞进一个 Agent 里,往往会遇到三个问题:

  • 角色要求互相干扰,一个 Prompt 里既要“只整理事实”又要“负责润色表达”
  • 调试时不容易看出问题出在哪一段
  • 后续想替换某个环节时,边界不清楚

所以这里用 Multi-Agent,不是为了显得更复杂,而是为了把角色分工讲清楚。

架构设计

我们要实现一个"研究助手":用户给一个主题,系统自动完成 研究 → 撰写 → 审校 三步。

用户: "帮我研究一下 RAG 的最佳实践"

Boss Agent(规划任务)
  ├── Researcher(搜索资料、整理要点)
  ├── Writer(基于资料撰写内容)
  └── Reviewer(检查质量、提修改建议)

Boss Agent(汇总输出)

最终报告

三个角色:

  • Researcher:负责信息收集,返回结构化要点
  • Writer:基于研究结果撰写内容
  • Reviewer:审查质量,决定是否通过

这三个角色是一个可替换的分工模板,不是唯一标准答案。课程想让你学会的是“把职责拆开”,不是要求所有 Multi-Agent 系统都必须叫这三个名字。

定义 Agent 角色

每个 Agent 是一个独立的函数,有自己的 System Prompt 和输出 Schema。这一版先用代码显式编排流程,不给每个角色单独挂工具。

这也是课程推荐的起步路线:先把角色边界和数据流写死,确保流程稳定可调试;等你确认这套协作关系真的有效,再考虑把更多调度权交给模型。

Researcher Agent

typescript
import { generateText, Output, tool } from 'ai'
import { createOpenAI } from '@ai-sdk/openai'
import { z } from 'zod'

const model = createOpenAI({
  apiKey: process.env.CHAT_API_KEY,
  baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
}).chat('qwen3.6-plus')

const ResearchResult = z.object({
  keyPoints: z.array(z.string()).describe('核心要点,每条一句话'),
  sources: z.array(z.string()).describe('信息来源'),
  summary: z.string().describe('总结,100字以内'),
})

async function runResearcher(topic: string) {
  const { output } = await generateText({
    model,
    output: Output.object({ schema: ResearchResult }),
    system: `你是研究员。职责:
- 围绕给定主题整理关键信息
- 只返回事实和要点,不做创作
- 标注信息来源`,
    prompt: `请研究以下主题,整理核心要点:${topic}`,
  })
  return output
}

注意 System Prompt 的写法:明确告诉 Agent 做什么、不做什么。

Writer Agent

typescript
const DraftResult = z.object({
  title: z.string(),
  content: z.string().describe('正文内容,500-1000字'),
  wordCount: z.number(),
})

async function runWriter(
  topic: string,
  research: z.infer<typeof ResearchResult>
) {
  const { output } = await generateText({
    model,
    output: Output.object({ schema: DraftResult }),
    system: `你是技术写手。职责:
- 基于研究员提供的资料撰写内容
- 面向前端工程师,技术准确、表达清晰
- 不自行补充未经验证的信息`,
    prompt: `主题:${topic}

研究资料:
${research.keyPoints.map((p, i) => `${i + 1}. ${p}`).join('\n')}

总结:${research.summary}

请基于以上资料撰写一篇技术文章。`,
  })
  return output
}

Writer 的输入是 Researcher 的输出。Agent 之间传递结构化数据(Zod Schema),不是自由文本。

Reviewer Agent

typescript
const ReviewResult = z.object({
  approved: z.boolean().describe('是否通过审核'),
  score: z.number().min(1).max(10).describe('质量评分'),
  issues: z.array(z.string()).describe('发现的问题'),
  suggestions: z.array(z.string()).describe('改进建议'),
})

async function runReviewer(draft: z.infer<typeof DraftResult>) {
  const { output } = await generateText({
    model,
    output: Output.object({ schema: ReviewResult }),
    system: `你是技术审校。职责:
- 检查内容的准确性、完整性、可读性
- 评分 1-10,7 分以上通过
- 指出具体问题和改进建议`,
    prompt: `请审查以下文章:

标题:${draft.title}

${draft.content}`,
  })
  return output
}

Reviewer 决定是否通过。不通过则打回修改,这是质量保障的关键。

这里的审校回路是一个“质量兜底手段”:它让输出更稳,但不是 Multi-Agent 的最低门槛。即使你先只实现 Researcher → Writer 两段,只要角色分工清楚,依然是在做有效的 Multi-Agent 编排。

Boss Agent:编排整个流程

Boss Agent 负责调度三个 Worker,根据 Reviewer 的反馈决定是否需要修改。

typescript
async function runBossAgent(userRequest: string) {
  console.log(`\n📋 Boss Agent 收到任务: ${userRequest}\n`)

  // 阶段 1:研究
  console.log('🔍 阶段 1/3:调用 Researcher...')
  const research = await runResearcher(userRequest)
  console.log(`  找到 ${research.keyPoints.length} 个要点`)

  // 阶段 2:撰写
  console.log('✍️  阶段 2/3:调用 Writer...')
  let draft = await runWriter(userRequest, research)
  console.log(`  生成初稿:${draft.title}(${draft.wordCount} 字)`)

  // 阶段 3:审校(最多修改 3 次)
  const MAX_REVISIONS = 3
  for (let i = 0; i < MAX_REVISIONS; i++) {
    console.log(`🔎 阶段 3/3:调用 Reviewer(第 ${i + 1} 轮)...`)
    const review = await runReviewer(draft)
    console.log(`  评分: ${review.score}/10`)

    if (review.approved) {
      console.log('  ✅ 审核通过!')
      return { draft, review, research, revisions: i }
    }

    console.log(`  ❌ 未通过,问题:${review.issues.join('、')}`)
    console.log('  📝 打回修改...')

    // 基于反馈修改
    draft = await runWriter(
      `${userRequest}(请根据以下建议修改:${review.suggestions.join(';')})`,
      research
    )
  }

  console.log('⚠️ 达到最大修改轮次')
  return { draft, research, revisions: MAX_REVISIONS }
}

流程中的设计要点:

  1. 结构化传递:每个 Agent 输出都是 Zod Schema,下游 Agent 可以直接使用
  2. 反馈循环:Reviewer 不通过,Writer 基于建议修改,最多 3 轮
  3. 日志可观测:每步打印状态,方便调试
  4. 终止保障:限制最大修改轮次,防止无限循环

如果你是第一次做 Multi-Agent,建议先把第 1、3、4 点做好,再决定要不要加完整的审校反馈循环。

显示执行过程

Multi-Agent 系统的调试比单 Agent 复杂,清晰的日志至关重要。

typescript
function logAgentStep(agent: string, action: string, detail?: string) {
  const timestamp = new Date().toLocaleTimeString()
  const icons: Record<string, string> = {
    Boss: '👔',
    Researcher: '🔍',
    Writer: '✍️',
    Reviewer: '🔎',
  }
  const icon = icons[agent] || '🤖'
  console.log(`[${timestamp}] ${icon} ${agent}: ${action}`)
  if (detail) console.log(`   ${detail}`)
}

生产环境中通常用结构化日志(JSON),配合 tracing 系统追踪每个 Agent 的执行耗时和 token 消耗。

用 generateText 实现简化版

上面的实现把每个 Agent 写成独立函数。也可以用 Vercel AI SDK 的 tool 机制,让 Boss Agent 通过工具调用来触发子 Agent:

typescript
const result = await generateText({
  model,
  system: `你是任务协调者。根据用户需求,按顺序调用以下工具完成任务:
1. research — 研究主题
2. write — 撰写内容
3. review — 审查质量
如果审查未通过,基于建议重新调用 write。`,
  tools: {
    research: tool({
      description: '调用研究员搜索和整理资料',
      inputSchema: z.object({ topic: z.string() }),
      execute: async ({ topic }) => runResearcher(topic),
    }),
    write: tool({
      description: '调用写手撰写内容',
      inputSchema: z.object({
        topic: z.string(),
        keyPoints: z.array(z.string()),
      }),
      execute: async ({ topic, keyPoints }) =>
        runWriter(topic, { keyPoints, sources: [], summary: '' }),
    }),
    review: tool({
      description: '调用审校检查质量',
      inputSchema: z.object({
        title: z.string(),
        content: z.string(),
      }),
      execute: async ({ title, content }) =>
        runReviewer({ title, content, wordCount: content.length }),
    }),
  },
  stopWhen: stepCountIs(10),
  prompt: userRequest,
})

两种方式的对比:

独立函数generateText + tools
控制粒度精确,每步可控依赖模型决策
灵活度代码控制流程模型自动选择
可预测性
适用场景流程固定流程需要灵活调整

推荐起步方式:先用独立函数实现(可预测、好调试),等流程稳定后再考虑是否要让模型自主编排。 这也是这节课的默认路线。

并入基础项目

本模块能力会在基础项目 basic/project/ 中收束,当前课内 Demo 见下方运行方式。

为什么这一课属于 basic/project: 前面的版本已经把聊天、Prompt、RAG、检索优化、Agent、记忆与规划逐步接进主线;到了 basic/project,主线才真正从“单 Agent 多工具”升级为“多角色协作”。

这一课给主线新增了什么能力:

模块能力
模块 1-6聊天、Prompt、RAG、检索优化、Agent、记忆与规划
模块 7Boss 负责任务编排,Researcher / Writer / Reviewer 分工执行,多轮审校后汇总最终结果

当前 basic/project 的落地口径:

  • 默认入口切换为 Boss-Worker 多角色流程
  • 已支持研究、撰写、审校三角色编排
  • 已支持审校反馈驱动的修改循环
  • 还没有把更多协作模式写成主线默认能力

本课产物

  • ✅ 实现 Boss-Worker 编排模式
  • ✅ 三个角色:Researcher → Writer → Reviewer
  • ✅ Agent 之间传递结构化数据(Zod Schema)
  • ✅ 反馈循环:审校不通过则打回修改
  • ✅ 终止条件:最大修改轮次

完整代码在 basic/examples/07-multi-agent/02-implementation/index.ts

试试看

bash
cd daqi-ai-agent
pnpm exec tsx basic/examples/07-multi-agent/02-implementation/index.ts
  1. 输入一个技术主题,观察 Researcher → Writer → Reviewer 三个角色如何分工协作
  2. 故意输入一个范围很大或要求很模糊的主题,观察 Reviewer 是否会提出修改意见,以及修改循环是否生效
  3. 在现有基础上新增一个 Sub-Agent 或调整某个角色的 system prompt,接入协作流程并观察结果变化

面试追问

Q:Boss-Worker 模式怎么实现?

Boss Agent 负责任务拆解和调度,Worker Agent 负责执行。实现上,每个 Worker 是一个独立函数,有自己的 System Prompt 和输出 Schema。Boss 按顺序调用 Worker,根据结果决定下一步。关键设计:用 Zod Schema 约束 Agent 之间传递的数据格式,设置最大轮次防止循环。

Q:Agent 之间怎么传递信息?

用结构化数据,不用自由文本。每个 Agent 的输出用 generateText + Output.object() + Zod Schema 约束格式,下游 Agent 直接使用上游的输出字段。这样做的好处:格式可预测、不依赖模型的表述方式、出错容易定位。

Q:怎么防止 Multi-Agent 系统无限循环?

三个手段:1. 设最大轮次(如审校最多 3 轮)。2. 设超时时间。3. 自然终止条件(如审校评分 >= 7 通过)。至少要有前两个硬限制,第三个是设计上的终止条件。

面向前端工程师和独立开发者的 AI 应用工程课程