一天一个AI项目|多Agent助手实战

手把手带你拆解一个 GitHub 开源项目,用 LangGraph 从零搭建多 Agent 智能助手。Supervisor 架构 + 5 个专业子 Agent,代码不到 3000 行,适合写进简历的实战项目。

alt

alt

三、架构详解

用户消息 (Telegram / Slack)
        |
        v
  +---------------+
  | Manager Agent |   GPT-4o + SQLite记忆
  +-------+-------+
          |
          | SendMessage 工具(动态注入)
          |
    +-----+-----+-----+-----+
    v     v     v     v     v
  消息   日历   待办  协作   搜索
  Agent  Agent  Agent Agent  Agent
  
  (每个子Agent: GPT-4o-mini + 专属工具集)

三个核心概念

概念1:Agent = LLM + Tools + Prompt

每个 Agent 都是一个 ReAct Agent(Reasoning + Acting),它会:

  • 思考:根据 System Prompt 和用户输入进行推理
  • 行动:调用自己的 Tools 去执行操作
  • 观察:拿到 Tool 的返回结果
  • 循环:直到任务完成

概念2:Supervisor 模式

Manager 不直接做事,它的唯一工具就是 SendMessage——给子 Agent 发消息。它的职责是:

  • 理解用户意图
  • 决定调用哪个子 Agent
  • 如果需要多步骤,按顺序调度多个子 Agent
  • 汇总所有结果,回复用户

概念3:动态工具注入

子 Agent 在初始化时并不知道彼此的存在。是 Orchestrator(编排器)在启动时,自动给 Manager 注入了一个 SendMessage 工具,这个工具的参数是根据所有子 Agent 的 name 和 description 动态生成的。

四、核心代码逐行讲解

4.1 Agent 基类

这是所有 Agent 的父类,定义了 Agent 的基本结构:

from langgraph.prebuilt import create_react_agent
from src.utils import get_llm_by_provider

class Agent:
    def __init__(self, name, description, system_prompt, 
                 tools, sub_agents, model, temperature, memory=None):
        # name: Agent的名字,如 "calendar_agent"
        # description: 一句话描述,Orchestrator用它生成路由信息
        # system_prompt: 指令提示词,定义Agent的行为边界
        # tools: 这个Agent可以调用的工具列表
        # sub_agents: 下级Agent列表(只有Manager才有)
        # model: 格式为 "provider/model",如 "openai/gpt-4o"
        # memory: 可选的记忆存储(只有Manager用)
        self.name = name
        self.description = description
        self.system_prompt = system_prompt
        self.tools = tools
        self.sub_agents = sub_agents
        self.model = model
        self.temperature = temperature
        self.memory = memory
        self.agent = None  # 延迟初始化

    def invoke(self, *args, **kwargs):
        if not self.agent:
            self.initiat_agent()  # 第一次调用时才创建
        print(f"--- Calling {self.name} ---")
        return self.agent.invoke(*args, **kwargs)

    def initiat_agent(self):
        # 根据 provider/model 格式获取对应的LLM实例
        llm = get_llm_by_provider(self.model, self.temperature)
        # 用 LangGraph 创建 ReAct Agent
        self.agent = create_react_agent(
            llm, 
            tools=self.tools,
            state_modifier=self.system_prompt,
            # Manager有memory,子Agent设为False避免冲突
            **({"checkpointer": self.memory} if self.memory 
               else {"checkpointer": False})
        )

学习要点

  • create_react_agent 是 LangGraph 提供的工厂函数,一行代码就能创建一个完整的 ReAct Agent
  • state_modifier 就是 System Prompt,定义了 Agent 的角色和行为
  • checkpointer 是记忆机制,传入 SQLite 存储就能实现多轮对话

4.2 Orchestrator 编排器

这是整个系统最精妙的部分——它负责让 Agent 之间能互相通信:

from pydantic import Field, create_model

class AgentsOrchestrator:
    def __init__(self, main_agent, agents):
        self.main_agent = main_agent
        self.agents = agents
        self.agent_mapping = {}  # name -> Agent 的映射表

        self._populate_agent_mapping()   # 建立映射
        self._add_send_message_tool()    # 核心:动态注入通信工具

    def invoke(self, message, **kwargs):
        # 统一入口:把用户消息包装成 LangGraph 的消息格式
        messages = {"messages": [("human", message)]}
        response = self.main_agent.invoke(messages, **kwargs)
        # 取最后一条消息作为最终回复
        return response["messages"][-1].content

    def _populate_agent_mapping(self):
        # 建立 name -> Agent 对象的映射,方便后续路由
        for agent in self.agents:
            self.agent_mapping[agent.name] = agent

    def _create_dynamic_send_message_tool(self, agent):
        # 动态生成 SendMessage 工具的输入格式
        # 关键:把所有子Agent的name和description拼成提示
        recipients_description = "\n".join(
            f"{sub.name}: {sub.description}"
            for sub in agent.sub_agents if sub.description
        )
        # 用 Pydantic 动态创建输入模型
        DynamicInput = create_model(
            f"{agent.name}SendMessageInput",
            recipient=(str, Field(..., description=recipients_description)),
            message=(str, Field(..., description="要发送的消息内容")),
        )
        tool = SendMessage(args_schema=DynamicInput)
        tool.agent_mapping = self.agent_mapping
        return tool

    def _add_send_message_tool(self):
        # 遍历所有Agent,给有子Agent的(即Manager)注入SendMessage
        for agent in self.agents:
            if hasattr(agent, "sub_agents") and agent.sub_agents:
                tool = self._create_dynamic_send_message_tool(agent)
                agent.tools.append(tool)
                agent.initiat_agent()  # 重新初始化以绑定新工具

这段代码为什么精妙?

  1. 解耦:子 Agent 完全不知道其他 Agent 的存在,所有路由逻辑由 Orchestrator 统一管理
  2. 动态性:用 Pydantic 的 create_model 动态生成工具的输入格式,新增子 Agent 零代码改动
  3. LLM 原生路由:Manager 看到 SendMessage 工具的 description 里列着所有子 Agent 的名字和职责,自然就知道该"发消息"给谁

4.3 组装完整系统

class PersonalAssistant:
    def __init__(self):
        # === 子Agent区 ===
        msg_agent = Agent(
            name="msg_agent",
            description="处理消息收发相关任务",
            system_prompt=MSG_PROMPT.format(
                current_date=datetime.now().strftime("%Y-%m-%d"),
                current_time=datetime.now().strftime("%H:%M")
            ),
            tools=[ReadMsg(), SendMsg(), FindContacts()],
            model="openai/gpt-4o-mini",
            temperature=0.1,
            sub_agents=[]
        )

        calendar_agent = Agent(
            name="calendar_agent",
            description="管理日程和会议",
            system_prompt=CALENDAR_PROMPT.format(...),
            tools=[GetEvents(), CreateEvent(), FindContacts()],
            model="openai/gpt-4o-mini",
            temperature=0.1,
            sub_agents=[]
        )
        # todo_agent, collab_agent, researcher_agent 同理...

        # === Manager ===
        conn = sqlite3.connect("db/checkpoints.sqlite")
        manager = Agent(
            name="manager",
            description="",
            system_prompt=MANAGER_PROMPT.format(...),
            tools=[],  # 不手动加工具!Orchestrator会自动注入
            sub_agents=[msg_agent, calendar_agent, 
                       todo_agent, collab_agent, researcher_agent],
            model="openai/gpt-4o",
            temperature=0.1,
            memory=SqliteSaver(conn)
        )

        # === 编排器把一切串起来 ===
        self.orchestrator = AgentsOrchestrator(
            main_agent=manager,
            agents=[manager, msg_agent, calendar_agent, 
                   todo_agent, collab_agent, researcher_agent]
        )

五、快速上手

# 1. 克隆
git clone 仓库地址(GitHub搜索personal-ai-assistant)
cd AI-personal-assistant

# 2. 虚拟环境
python -m venv venv
source venv/bin/activate

# 3. 安装依赖
pip install -r requirements.txt

# 4. 配置
cp .env.example .env
# 编辑 .env,按注释填入你的各项服务配置

# 5. 启动
python app.py

六、文件结构速查

src/
├── agents/
│   ├── base/
│   │   ├── agent.py              # Agent基类,所有Agent的模板
│   │   └── agents_orchestrator.py # 编排器,多Agent通信的核心
│   └── personal_assistant.py      # 组装入口,把所有Agent串起来
├── channels/                      # Telegram/Slack 消息渠道适配
├── prompts/                       # 每个Agent的System Prompt
│   ├── manager_agent.py           # Manager的指令:如何路由任务
│   ├── calendar_agent.py          # 日历Agent的指令
│   └── ...
├── tools/                         # 每个Agent的工具实现
│   ├── calendar/                  # 查询日程、创建日程
│   ├── research/                  # 网页搜索、内容抓取
│   └── ...
└── utils.py                       # LLM provider 路由

阅读顺序建议:agent.py -> agents_orchestrator.py -> personal_assistant.py -> 挑一个感兴趣的 tools/ 和 prompts/ 看

alt

alt

#AI项目实战##AI求职实录#
全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务