构建一个具备子智能体的个人助手

原文:Build a personal assistant with subagents

概述

监督者模式是一种多智能体架构,其中中央监督者智能体协调专业化的工作智能体。当任务需要不同类型的专业知识时,这种方法尤为有效。与其构建一个需跨领域管理工具选择的单一智能体,不如创建由理解整体工作流程的监督者所协调的专业工作智能体。

在本教程中,您将构建一个个人助手系统,通过真实工作流程展示这些优势。该系统将协调两名职责截然不同的专业工作智能体:

  • 日历智能体:负责处理日程安排、可用性检查与事件管理。
  • 邮件智能体:负责管理通信、起草消息并发送通知。
    我们还将引入人在回路审查机制,允许用户根据需要批准、编辑或拒绝特定操作(例如外发邮件)。

为何使用监督者?

多智能体架构允许您将工具分配至各个工作智能体,每个智能体拥有独立的提示词或指令。试想一个直接访问所有日历与邮件 API 的智能体:它需从众多相似工具中做出选择,理解每项 API 的确切格式,并同时处理多个领域。若性能下降,将相关工具及其关联提示词划分为逻辑组可能更为有利(部分原因在于便于管理迭代改进)。

1. 定义工具

首先定义需要结构化输入的工具。在实际应用中,这些工具会调用真实的 API(例如 Google Calendar、SendGrid 等)。在本教程中,您将使用存根(stubs)来演示该模式。

2. 创建专业化子智能体

接下来,我们将创建处理各个领域的专业化子智能体。

创建日历智能体

日历智能体理解自然语言日程请求,并将其转化为精确的 API 调用。它负责处理日期解析、可用性检查和事件创建。

创建邮件智能体

邮件智能体负责消息撰写和发送。它专注于提取收件人信息、构思合适的主题行和正文内容,以及管理邮件通信。

该智能体从非正式请求中推断收件人,构思专业的主题行和正文,调用 send_email,并返回确认信息。每个子智能体都专注于特定领域,配备领域特定的工具和提示词,使其能够出色地完成特定任务。

3. 将子智能体封装为工具

现在将每个子智能体封装为监督者可调用的工具。这是创建分层系统的关键架构步骤。监督者将看到高级工具(如 schedule_event),而非低级工具(如 create_calendar_event)。

工具描述有助于监督者决定何时使用每个工具,因此请确保它们清晰具体。我们仅返回子智能体的最终响应,因为监督者无需查看中间推理或工具调用。

4. 创建监督者智能体

现在创建协调子智能体的监督者。监督者仅看到高级工具,并在领域级别而非单个 API 级别做出路由决策。

5. 使用监督者

现在使用需要跨多个领域协调的复杂请求来测试您的完整系统:

因为langchain安装的包特别大,所以这里换成非常mini的xsai来展示,需要langchain版本去上面原文看就好了。因为发件需要邮件地址,所以我多加了一个模拟获取公司人员的子智能体

ts 复制代码
/**
 * 个人助理主管示例 (xsai 版本)
 *
 * 本示例演示了多智能体系统中的工具调用模式。
 * 一个主管智能体协调专门的子智能体(日历和邮件),这些子智能体被封装为工具。
 */
import { generateText } from "@xsai/generate-text";
import { tool } from "@xsai/tool";
import { env } from "node:process";
import * as z from "zod";

// ============================================================================
// 步骤 1:定义底层 API 工具(模拟实现)
// ============================================================================

const createCalendarEvent = await tool({
  name: "create_calendar_event",
  description: "创建日历事件。需要使用精确的 ISO 日期时间格式。",
  parameters: z.object({
    title: z.string().describe("事件标题"),
    startTime: z.string().describe("ISO 格式:'2024-01-15T14:00:00'"),
    endTime: z.string().describe("ISO 格式:'2024-01-15T15:00:00'"),
    attendees: z.array(z.string()).describe("参会者邮箱地址列表"),
    location: z.string().optional().default(""),
  }),
  execute: async ({ title, startTime, endTime, attendees, location }) => {
    console.log("[createCalendarEvent] input:", {
      title,
      startTime,
      endTime,
      attendees,
      location,
    });
    // 模拟:实际应用中会调用 Google Calendar API、Outlook API 等
    return `事件已创建:${title},时间从 ${startTime} 到 ${endTime},共 ${attendees.length} 位参会者`;
  },
});

const sendEmail = await tool({
  name: "send_email",
  description: "通过邮件 API 发送邮件。需要使用格式正确的邮箱地址。",
  parameters: z.object({
    to: z.array(z.string()).describe("收件人邮箱地址列表"),
    subject: z.string().describe("邮件主题"),
    body: z.string().describe("邮件正文"),
    cc: z.array(z.string()).optional().default([]).describe("抄送邮箱地址列表"),
  }),
  execute: async ({ to, subject, body, cc }) => {
    console.log("[sendEmail] input:", { to, subject, body, cc });
    // 模拟:实际应用中会调用 SendGrid、Gmail API 等
    return `邮件已发送至 ${to.join(", ")} - 主题:${subject}`;
  },
});

const getAvailableTimeSlots = await tool({
  name: "get_available_time_slots",
  description: "检查指定日期内给定参会者的日历可用时间段。",
  parameters: z.object({
    attendees: z.array(z.string()).describe("参会者邮箱地址列表"),
    date: z.string().describe("ISO 格式:'2024-01-15'"),
    durationMinutes: z.number().describe("会议时长(分钟)"),
  }),
  execute: async ({ attendees, date, durationMinutes }) => {
    console.log("[getAvailableTimeSlots] input:", {
      attendees,
      date,
      durationMinutes,
    });
    // 模拟:实际应用中会查询日历 API
    // xsai 工具返回值会被自动序列化,数组建议显式转为字符串
    return JSON.stringify(["09:00", "14:00", "16:00"]);
  },
});

const getTeamMembers = await tool({
  name: "get_team_members",
  description: "获取指定公司团队的人员联系信息,包括姓名、手机号和邮箱。",
  parameters: z.object({
    teamName: z
      .enum(["设计团队", "研发团队", "产品团队", "运营团队"])
      .describe("团队名称"),
  }),
  execute: async ({ teamName }) => {
    console.log("[getTeamMembers] input:", { teamName });

    // 模拟团队人员数据库
    const teamDatabase: Record<
      string,
      Array<{ name: string; phone: string; email: string }>
    > = {
      设计团队: [
        { name: "张三", phone: "138-0013-8001", email: "zhangsan@company.com" },
        {
          name: "王美设计",
          phone: "138-0013-8002",
          email: "wangmei@company.com",
        },
        {
          name: "陈创意",
          phone: "138-0013-8003",
          email: "chencreative@company.com",
        },
      ],
      研发团队: [
        { name: "李四", phone: "139-0014-9001", email: "lisi@company.com" },
        {
          name: "赵代码",
          phone: "139-0014-9002",
          email: "zhaocode@company.com",
        },
        {
          name: "孙算法",
          phone: "139-0014-9003",
          email: "sunalgo@company.com",
        },
      ],
      产品团队: [
        { name: "周产品", phone: "137-0012-7001", email: "zhoupm@company.com" },
        { name: "吴需求", phone: "137-0012-7002", email: "wureq@company.com" },
      ],
      运营团队: [
        {
          name: "郑运营",
          phone: "136-0011-6001",
          email: "zhengops@company.com",
        },
        {
          name: "冯增长",
          phone: "136-0011-6002",
          email: "fenggrowth@company.com",
        },
      ],
    };

    const members = teamDatabase[teamName] || [];

    if (members.length === 0) {
      return `未找到团队「${teamName}」的人员信息`;
    }

    // 格式化返回结果,便于 LLM 理解
    const formatted = members
      .map((m) => `${m.name} | 手机: ${m.phone} | 邮箱: ${m.email}`)
      .join("\n");
    return `【${teamName}】成员列表:\n${formatted}`;
  },
});

// ============================================================================
// 步骤 2:创建专门的子智能体(手动实现 agent 循环)
// ============================================================================

const llmConfig = {
  apiKey: env.OPENAI_API_KEY!,
  baseURL: env.OPENAI_BASE_URL!,
  model: "DeepSeek-V3.2", // xsai 兼容 OpenAI 格式的模型
};

/**
 * 日历子智能体 - 处理调度相关请求
 */
async function calendarAgent(request: string): Promise<string> {
  const systemPrompt = `
你是一个日历调度助手。
将自然语言的调度请求(例如:"下周二下午 2 点")解析为正确的 ISO 日期时间格式。
必要时使用 get_available_time_slots 检查可用性。
如果没有合适的时间段,请停止并在回复中确认可用性不足。
使用 create_calendar_event 创建事件。
始终在最终回复中确认已安排的内容。
  `.trim();

  console.log("[calendarAgent] input:", request);
  const { text } = await generateText({
    ...llmConfig,
    messages: [
      { role: "system", content: systemPrompt },
      { role: "user", content: request },
    ],
    tools: [createCalendarEvent, getAvailableTimeSlots],
    maxSteps: 3,
  });
  console.log("[calendarAgent] output:", text);

  return text!;
}

/**
 * 邮件子智能体 - 处理邮件相关请求
 */
async function emailAgent(request: string): Promise<string> {
  const systemPrompt = `
你是一个邮件助手。
根据自然语言请求撰写专业邮件。
提取收件人信息并撰写合适的主题行和正文内容。
使用 send_email 发送邮件。
始终在最终回复中确认已发送的内容。
  `.trim();

  console.log("[emailAgent] input:", request);
  const { text } = await generateText({
    ...llmConfig,
    messages: [
      { role: "system", content: systemPrompt },
      { role: "user", content: request },
    ],
    tools: [sendEmail],
    maxSteps: 3,
  });
  console.log("[emailAgent] output:", text);

  return text!;
}

/**
 * 团队信息子智能体 - 处理人员查询相关请求
 */
async function teamInfoAgent(request: string): Promise<string> {
  const systemPrompt = `
你是一个公司通讯录助手。
帮助用户查询各个团队的人员联系信息。
- 识别用户请求中的团队名称(如"设计团队"、"研发团队"等)
- 使用 get_team_members 工具获取人员列表
- 将结果以清晰友好的格式返回给用户
- 如果团队不存在,礼貌告知并建议可用团队选项
  `.trim();

  console.log("[teamInfoAgent] input:", request);
  const { text } = await generateText({
    ...llmConfig,
    messages: [
      { role: "system", content: systemPrompt },
      { role: "user", content: request },
    ],
    tools: [getTeamMembers],
    maxSteps: 3,
  });
  console.log("[teamInfoAgent] output:", text);

  return text!;
}

// ============================================================================
// 步骤 3:将子智能体封装为主管智能体可调用的工具
// ============================================================================

const scheduleEvent = await tool({
  name: "schedule_event",
  description: `
使用自然语言调度日历事件。

当用户想要创建、修改或查看日历预约时使用此工具。
处理日期/时间解析、可用性检查和事件创建。

输入:自然语言调度请求(例如:"下周二下午 2 点与设计团队开会")
    `.trim(),
  parameters: z.object({
    request: z.string().describe("自然语言调度请求"),
  }),
  execute: async ({ request }) => {
    // 调用日历子智能体处理请求
    return await calendarAgent(request);
  },
});

const manageEmail = await tool({
  name: "manage_email",
  description: `
使用自然语言发送邮件。

当用户想要发送通知、提醒或任何邮件通信时使用此工具。
处理收件人提取、主题生成和邮件撰写。

输入:自然语言邮件请求(例如:"给他们发一封关于会议的提醒")
    `.trim(),
  parameters: z.object({
    request: z.string().describe("自然语言邮件请求"),
  }),
  execute: async ({ request }) => {
    // 调用邮件子智能体处理请求
    return await emailAgent(request);
  },
});

const queryTeamMembers = await tool({
  name: "query_team_members",
  description: `
查询公司团队的人员联系信息。

当用户想要获取某个团队的成员姓名、电话或邮箱时使用此工具。
支持团队:设计团队、研发团队、产品团队、运营团队

输入:自然语言查询请求(例如:"设计团队有哪些人?" 或 "研发李四的邮箱是多少")
    `.trim(),
  parameters: z.object({
    request: z.string().describe("自然语言人员查询请求"),
  }),
  execute: async ({ request }) => {
    // 调用团队信息子智能体处理请求
    return await teamInfoAgent(request);
  },
});

// ============================================================================
// 步骤 4:创建主管智能体
// ============================================================================

/**
 * 主管智能体 - 协调日历和邮件子智能体
 */
async function supervisorAgent(userRequest: string): Promise<string> {
  const systemPrompt = `
  你是一个贴心的个人助理。
  你可以:
  1. 调度日历事件(创建会议、检查可用时间)
  2. 发送邮件通知和提醒
  3. 查询公司团队人员联系信息
  
  将用户请求分解为适当的工具调用并协调结果。
  当请求涉及多个操作时,按顺序使用多个工具。
  如果用户询问某团队人员并要联系他们,可组合使用 query_team_members + manage_email/schedule_event。
  `.trim();

  const { text } = await generateText({
    ...llmConfig,
    messages: [
      { role: "system", content: systemPrompt },
      { role: "user", content: userRequest },
    ],
    maxSteps: 20,
    tools: [scheduleEvent, manageEmail, queryTeamMembers],
  });

  return text!;
}

// ============================================================================
// 步骤 5:使用主管智能体
// ============================================================================

async function main() {
  // 示例:需要同时协调日历和邮件的用户请求
  const userRequest =
    "安排下周二下午 2 点与设计团队进行 1 小时的会议," +
    "并给他们发送一封邮件提醒,关于审阅新的设计稿。";

  console.log("用户请求:", userRequest);
  console.log(`\n${"=".repeat(80)}\n`);

  try {
    const result = await supervisorAgent(userRequest);
    console.log("主管智能体回复:\n", result);
  } catch (error) {
    console.error("执行出错:", error);
  }
}

// 执行入口
main();

一个简单的MultiAgent就完成辣!!!

相关推荐
火山引擎开发者社区2 小时前
OpenViking x OpenClaw:开箱即用 解决 Agent 的长期记忆困局
人工智能
一瓢西湖水2 小时前
Windows安装OpenClaw实践指南
人工智能·windows·ai
翱翔的苍鹰3 小时前
实际项目中使用LangChain DeepAgent的完整流程(落地版)
大数据·人工智能·深度学习·语言模型·自然语言处理·langchain
冬奇Lab3 小时前
一天一个开源项目(第52篇):OPB-Skills - 一人公司的 AI 团队,91 个专业 Skill 覆盖完整业务
人工智能·开源·资讯
刀法如飞3 小时前
Agentic AI时代,程序员必备的算法思想指南
人工智能·算法·agent
罗西的思考3 小时前
【GUI-Agent】阶跃星辰 GUI-MCP 解读---(1)---论文
人工智能·机器学习
yongui478344 小时前
基于小波分析与神经网络结合的风速预测方法
人工智能·深度学习·神经网络
萤丰信息4 小时前
智慧园区系统:赋能园区数字化升级,开启智慧运营新时代
大数据·人工智能·科技·架构·智慧城市·智慧园区
九硕智慧建筑一体化厂家4 小时前
楼控系统内 DDC 控制箱连接前端传感器、执行器、设备控制箱线缆类型说明
人工智能