Skip to content

Agent 进阶

掌握了基本的 Agent 构建后,本章将介绍高级技巧:为 Agent 添加记忆、使用子 Agent 进行任务委托、精细化循环控制,以及按步骤动态配置。

记忆系统

默认情况下,Agent 只有当前对话的上下文(短期记忆)。要实现跨会话的持久记忆,需要引入外部存储。

通过 prepareCall 注入记忆

🔗 Agent 记忆官方文档

ToolLoopAgentprepareCall 钩子在每次调用前执行,适合注入最新的记忆内容:

typescript
import { ToolLoopAgent, tool } from 'ai'
import { z } from 'zod'

// 记忆工具:读写持久存储
const memoryTool = tool({
  description: '保存或读取重要信息到长期记忆',
  inputSchema: z.object({
    action: z.enum(['save', 'read']),
    key: z.string().describe('记忆的键名'),
    value: z.string().optional().describe('要保存的值(save 时必填)'),
  }),
  execute: async ({ action, key, value }) => {
    if (action === 'save' && value) {
      await saveToMemory(key, value) // 自定义持久化函数
      return `已保存: ${key}`
    }
    return await readFromMemory(key) // 自定义读取函数
  },
})

const today = new Date().toISOString().slice(0, 10)

const memoryAgent = new ToolLoopAgent({
  model: 'anthropic/claude-haiku-4.5',
  tools: { memory: memoryTool },
  prepareCall: async (settings) => {
    // 每次调用前,从存储读取核心记忆并注入系统提示
    const coreMemory = await readCoreMemory()
    return {
      ...settings,
      instructions: `今天是 ${today}。

核心记忆:
${coreMemory}

你可以使用 memory 工具保存和读取重要信息。`,
    }
  },
})

记忆策略对比

策略实现方式适用场景
系统提示注入prepareCall 中读取并注入关键事实、用户偏好
工具调用提供 memory 工具按需读写,Agent 自主决定
向量检索嵌入 + 相似度搜索大量历史信息
消息历史直接传入历史消息单会话上下文

子 Agent(Sub-agents)

🔗 子 Agent 官方文档

子 Agent 模式允许主 Agent 将特定任务委托给专业化的子 Agent。子 Agent 有独立的上下文窗口和工具集,完成后将结果返回给主 Agent。

typescript
import { ToolLoopAgent, tool } from 'ai'
import { z } from 'zod'

// 定义研究子 Agent
const researchSubagent = new ToolLoopAgent({
  model: 'openai/gpt-4o',
  instructions: `你是一个研究 Agent。
完成研究后,在最终回复中总结发现。`,
  tools: {
    read: readFileTool,   // 文件读取工具
    search: searchTool,   // 搜索工具
  },
})

// 将子 Agent 封装为工具
const researchTool = tool({
  description: '深入研究一个主题或问题',
  inputSchema: z.object({
    task: z.string().describe('研究任务描述'),
  }),
  execute: async ({ task }, { abortSignal }) => {
    const result = await researchSubagent.generate({
      prompt: task,
      abortSignal,
    })
    return result.text
  },
})

// 主 Agent 使用研究工具
const mainAgent = new ToolLoopAgent({
  model: 'openai/gpt-4o',
  instructions: '你是一个有帮助的助手,可以委托研究任务。',
  tools: {
    research: researchTool,
  },
})

子 Agent 的优势

  • 上下文隔离 — 子 Agent 有独立的上下文窗口,不会污染主 Agent 的上下文
  • 专业化 — 每个子 Agent 可以使用不同的模型、工具和系统提示
  • 并行委托 — 主 Agent 可以同时委托多个子 Agent

注意事项

  • 子 Agent 的结果以文本形式返回,主 Agent 看不到子 Agent 的中间步骤
  • 如果需要在 UI 上显示子 Agent 的进度,建议使用流式 API
  • 注意传递 abortSignal,确保取消操作能正确传播

循环控制

stopWhen 停止条件

使用 stepCountIs() 控制最大步数:

typescript
import { ToolLoopAgent, stepCountIs } from 'ai'

const agent = new ToolLoopAgent({
  model: 'openai/gpt-4o',
  tools: { /* ... */ },
  stopWhen: stepCountIs(20), // 最多 20 步
})

默认情况下,ToolLoopAgent 的最大步数为 20。每一步会产生文本生成或工具调用。

prepareStep 逐步配置

prepareStep 回调在每一步执行前运行,可以用来:

  • 动态修改消息历史(上下文压缩)
  • 调整模型参数
  • 注入上下文信息

上下文窗口管理

长循环中,消息历史可能超出上下文长度限制。使用 prepareStep 裁剪消息:

typescript
import { ToolLoopAgent } from 'ai'

const agent = new ToolLoopAgent({
  model: 'openai/gpt-4o',
  tools: { /* ... */ },
  prepareStep: async ({ messages }) => {
    // 消息过多时,只保留系统指令和最近 10 条消息
    if (messages.length > 20) {
      return {
        messages: [
          messages[0],          // 保留系统指令
          ...messages.slice(-10), // 保留最近 10 条
        ],
      }
    }
    return {}
  },
})

Token 使用量追踪

通过 experimental_context 在步骤之间传递状态:

typescript
import { ToolLoopAgent, tool } from 'ai'
import { z } from 'zod'

type TContext = {
  lastInputTokens: number
}

const agent = new ToolLoopAgent({
  model: 'anthropic/claude-haiku-4.5',
  callOptionsSchema: z.object({
    lastInputTokens: z.number(),
  }),
  tools: {
    greet: tool({
      description: '问候某人',
      inputSchema: z.object({ name: z.string() }),
      execute: async ({ name }) => `已问候 ${name}`,
    }),
  },
  prepareCall: ({ options, ...settings }) => {
    return {
      ...settings,
      experimental_context: { lastInputTokens: options.lastInputTokens },
    }
  },
  prepareStep: ({ steps, experimental_context }) => {
    const lastStep = steps.at(-1)
    const lastStepUsage =
      lastStep?.usage?.inputTokens ??
      (experimental_context as TContext)?.lastInputTokens ??
      0

    console.log('上一步输入 token:', lastStepUsage)

    // 可以根据 token 使用量实施上下文压缩策略
    return {
      experimental_context: {
        ...(experimental_context as TContext),
        lastStepUsage,
      },
    }
  },
})

动态提示缓存

对于长上下文的 Agent 循环,使用 prepareStep 配合 Prompt Caching 优化成本:

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

const result = await generateText({
  model: anthropic('claude-sonnet-4-5'),
  prompt: '帮我分析这个代码库并提出改进建议。',
  maxSteps: 10,
  tools: {
    analyzeFile: tool({
      description: '分析代码库中的一个文件',
      inputSchema: z.object({
        path: z.string().describe('文件路径'),
      }),
      execute: async ({ path }) => {
        return { analysis: `${path} 的分析结果` }
      },
    }),
  },
  prepareStep: ({ messages, model }) => ({
    // 为消息添加缓存控制标记
    messages: addCacheControlToMessages({ messages, model }),
  }),
})

自定义 Agent 实现

如果 ToolLoopAgent 不满足需求,可以实现自定义 Agent:

typescript
import type { Agent, ToolSet, AgentCallParameters, GenerateTextResult } from 'ai'

class MyCustomAgent implements Agent<never, ToolSet, never> {
  readonly version = 'agent-v1' as const
  readonly id = 'my-custom-agent'
  readonly tools: ToolSet

  constructor(private config: { model: string; tools: ToolSet }) {
    this.tools = config.tools
  }

  async generate(options: AgentCallParameters<never, ToolSet>) {
    // 自定义生成逻辑
    // ...
  }

  async stream(options: AgentCallParameters<never, ToolSet>) {
    // 自定义流式逻辑
    // ...
  }
}

自定义 Agent 只需实现 Agent 接口的契约(versionidtoolsgeneratestream),即可与 AI SDK 的所有工具链无缝集成。

下一步

学习文档整合站点