🚀 Vercel AI SDK 使用指南: 子代理 (Subagents)

在构建复杂的 AI Agent 系统时,我们经常会遇到一个棘手的问题:上下文爆炸

如果让主 Agent 直接去阅读长篇文档、检索引擎或者分析代码库,大量的中间信息不仅会迅速消耗 Context Window,还会导致模型"精神涣散",降低最终回答的质量。相比于在 LangGraph 等框架中手动编排复杂的节点图和状态流转,Vercel AI SDK 提供了一种更直观、原生的高级抽象:子代理 (Subagents)

今天我们就来详细拆解如何在 Vercel AI SDK 中使用子代理,并以阿里千问最新模型 (qwen-max) 为例进行实战演示。

什么是子代理?

一句话总结:子代理是一个可以被"父代理"作为工具 (Tool) 调用的独立 Agent。

子代理会在自己隔离的上下文中自主运行,执行具体的脏活累活。等任务完成后,它只将提炼后的结果返回给主代理。

适用场景分析

✅ 推荐使用子代理的场景 ❌ 避免使用子代理的场景
任务需要吞吐大量的 Token(如文件阅读、信息搜索) 任务非常简单且聚焦
需要并行处理相互独立的研究任务 简单的线性顺序处理即可完成
希望按特定能力隔离工具箱,避免主节点工具泛滥 当前工具集完全可以安全共存,且不越界

基础实战:不带流式输出的 Subagent

最简单的子代理模式不需要任何黑魔法。主代理只需要拥有一个在 execute 函数中调用另一个 Agent 的 Tool。

由于阿里千问 (Qwen) 已经完美兼容 OpenAI 的接口规范,我们可以直接使用 @ai-sdk/openai 配合 DashScope 的 Endpoint 来驱动我们的 Agent。

TypeScript

php 复制代码
import { ToolLoopAgent, tool } from 'ai';
import { z } from 'zod';
import { createOpenAI } from '@ai-sdk/openai';

// 配置阿里千问大模型
const dashscope = createOpenAI({
  baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
  apiKey: process.env.DASHSCOPE_API_KEY,
});
const qwenModel = dashscope('qwen-max');

// 1. 定义一个专门用于"深度调研"的子代理
const researchSubagent = new ToolLoopAgent({
  model: qwenModel,
  instructions: `你是一个专业的学术和数据分析代理。
  请自主完成调研任务。
  IMPORTANT: 当你完成所有步骤后,请在最终回复中写下条理清晰的研究总结。`,
  tools: {
    // 假设这些是你写好的基础工具
    read: readFileTool,  
    search: searchTool,  
  },
});

// 2. 创建一个工具,将工作委托给子代理
const researchTool = tool({
  description: '深入研究一个主题或问题。',
  inputSchema: z.object({
    task: z.string().describe('需要完成的调研任务描述'),
  }),
  // 传入 abortSignal 以便在用户取消请求时中断子代理
  execute: async ({ task }, { abortSignal }) => {
    const result = await researchSubagent.generate({
      prompt: task,
      abortSignal, 
    });
    return result.text; // 将总结文本返回给主代理
  },
});

// 3. 主代理:专注于核心调度
export const mainAgent = new ToolLoopAgent({
  model: qwenModel,
  instructions: '你是一个全能的私人助理。遇到需要深度查阅或调研的问题时,请委派给 researchTool。',
  tools: {
    research: researchTool,
  },
});

在这个模式下,主节点发起调用后会一直等待,直到子代理吐出最终文本。

(💡 Tip: 如果主流程被取消,abortSignal 会抛出 AbortError。为了防止后续对话出现不完整的工具调用记录,可以在解析历史记录时使用 convertToModelMessages(messages, { ignoreIncompleteToolCalls: true }))


进阶:流式进度展示 (Streaming Progress)

在真实的生产环境中,子代理干活可能需要好几十秒。为了用户体验,我们必须把子代理"正在搜索XXX"、"正在阅读XXX"的中间状态实时推送到前端 UI。

Vercel AI SDK 通过 async function* (异步生成器) 和 readUIMessageStream 优雅地解决了这个问题:

TypeScript

php 复制代码
import { readUIMessageStream, tool } from 'ai';

const streamingResearchTool = tool({
  description: '深入研究一个主题并返回流式进度。',
  inputSchema: z.object({
    task: z.string(),
  }),
  // 1. 将 execute 改为 async function*
  execute: async function* ({ task }, { abortSignal }) {
    // 2. 调用子代理的 stream 方法
    const result = await researchSubagent.stream({
      prompt: task,
      abortSignal,
    });

    // 3. 拦截并 yield 出完整的、不断累加的 UI 消息状态
    for await (const message of readUIMessageStream({
      stream: result.toUIMessageStream(),
    })) {
      yield message; 
    }
  },
});

核心秘籍:精准控制主模型"所见内容"

子代理之所以能解决"上下文爆炸",关键在于前端 UI 和主模型看到的内容是不一样的

用户在 UI 上需要看到子代理调用的每一个工具、每一次思考,但主代理只需要看最后的那句"总结"。我们可以利用 toModelOutput 完美实现这种信息差:

TypeScript

javascript 复制代码
const advancedResearchTool = tool({
  // ...前面的 inputSchema 和 execute 逻辑保持不变...
  
  toModelOutput: ({ output: message }) => {
    // 从子代理的一大堆流式输出中,只提取最后一段文本作为总结
    const lastTextPart = message?.parts.findLast(p => p.type === 'text');
    return {
      type: 'text',
      value: lastTextPart?.text ?? '任务已完成。',
    };
  },
});

这样一来,子代理可能在背后消耗了 10 万 Token 来翻阅资料,但主代理的 Context Window 里仅仅增加了几百 Token 的总结。极大地保持了主节点逻辑的清晰!


在 UI 层优雅渲染

对于熟悉 React 生态的开发者来说(尤其是习惯了组件化思维或者刚从 Vue 3 迁移到 React 的朋友),结合 @ai-sdk/reactuseChat Hook 来捕获这套复杂状态简直如丝般顺滑。

通过检查 part.statepart.preliminary,我们可以精准渲染出主代理的文本与子代理的动态进度:

TypeScript

ini 复制代码
'use client';

import { useChat } from '@ai-sdk/react';
import type { InferAgentUIMessage } from 'ai';
// 假设你导出了 mainAgent
import type { MainAgentMessage } from '@/lib/agents'; 

export function Chat() {
  const { messages } = useChat<MainAgentMessage>();

  return (
    <div className="flex flex-col gap-4">
      {messages.map(message =>
        message.parts.map((part, i) => {
          // 渲染主代理对话
          if (part.type === 'text') {
            return <p key={i} className="text-gray-800">{part.text}</p>;
          }
          
          // 渲染子代理 (Research Tool) 状态
          if (part.type === 'tool-research') {
            return (
              <div key={i} className="bg-gray-50 p-4 rounded-lg">
                {part.state !== 'input-streaming' && (
                  <div className="font-bold">🔎 正在调研: {part.input.task}</div>
                )}
                
                {/* 捕获并渲染子代理返回的流式 / 最终结果 */}
                {part.state === 'output-available' && (
                  <div className="mt-2 border-l-2 border-blue-400 pl-2">
                    {part.output.parts.map((nestedPart, j) => {
                      if (nestedPart.type === 'text') {
                        return <p key={j}>{nestedPart.text}</p>;
                      }
                      return null;
                    })}
                  </div>
                )}
              </div>
            );
          }
          return null;
        })
      )}
    </div>
  );
}

⚠️ 避坑指南

在使用 Subagents 时,有几个细节需要特别留意:

  1. 不支持人工审批 :子代理内部的工具不能配置 needsApproval,所有的工具必须能够自动化静默执行。
  2. 默认上下文隔离 :子代理的每次调用都是一个白纸般的全新上下文。如果你确实需要让它知道主聊天的历史,需要手动在 execute 中拼接 messages(但这往往会削弱子代理"卸载上下文"的核心优势,建议谨慎使用)。

结语

子代理 (Subagents) 是将 AI 应用从"玩具"推向"企业级系统"的关键一环。它以极低的认知成本,赋予了单一 Agent 分治、并行和长文本处理的拓展能力。

如果你觉得有帮助,欢迎在评论区交流你在开发复杂 Agent 系统时遇到的痛点!👏

相关推荐
山间小僧5 小时前
「AI学习笔记」Agent Memory(二)跨会话记忆:从聊天记录到可演化的长期记忆
aigc·agent·vibecoding
dong_junshuai12 小时前
每天一个开源项目#99 OpenResearch:2.2K星的本地研究Agent工作台
开源·github·agent
花椒技术12 小时前
把 AI 视频接进直播:提前生成、按次生成、持续生成怎么选?
agent·音视频开发
用户80825986668712 小时前
用 LangGraph 重写自己手写的 Agent:框架替你解决了什么,什么它不管
agent
能不能静下心来看12 小时前
从 RAG 到 Agent:跑通两个 Demo 后,我终于分清了这两个词
agent
ZGi.ai12 小时前
知识库权限变了,怎样避免答错?
agent·权限管理·知识库·工作流·zgi·客服运营
大模型真好玩12 小时前
DeepSeek Harness 入门很简单(四)——DeepSeek Harness接入插件
人工智能·agent·deepseek
掰头战士13 小时前
MCP、Skill、Plugin,都是给agent拓展能力,到底有何区别?
typescript·llm·agent
嘟嘟嘟952714 小时前
AI Agent 的边缘困境
人工智能·架构·agent
HYDtomako14 小时前
Agent checkpoint设计
ai·agent·checkpoint·claude code