前端转agent-【python】-10 多 Agent 协作:像微服务一样编排 AI 团队

3 阅读6分钟

前端转agent-【python】-10 多 Agent 协作:像微服务一样编排 AI 团队

一个 Agent 再强,也只有一个脑子。真正复杂的任务需要团队协作——一个负责调研,一个负责写作,一个负责审核。
今天用 LangGraph 搭建一个主管 Agent,指挥两个专业 subAgent 协同工作。
对 Vue 3 开发者来说,这就像把一个大组件拆成多个子组件,再用 provide/inject 或事件总线协调。

为什么需要多 Agent?

单一 Agent 的上下文窗口有限,而且难以同时兼顾多个领域的知识。让多个 Agent 各自专注一个领域,再由“主管” Agent 分配任务、汇总结果,能:

  • 降低单个模型的认知负担
  • 提高输出的专业度
  • 让流程更可控,便于调试

Vue 3 类比:你不可能把所有逻辑都塞进一个 App.vue。你会拆分成 SearchBarProductListShoppingCart,再由父组件管理状态流转。多 Agent 协作就是“组件化”的 AI 流程。

场景设计:智能客服 + 订单查询

假设我们要做一个电商客服系统,用户可能问:

  • “我的订单到哪了?”(需要查数据库)
  • “这件衣服有没有货?”(需要查库存)
  • “我要退货,怎么操作?”(需要政策解答)

我们把任务分给三个角色:

Agent 角色职责可以类比 Vue 组件
Supervisor(主管)分析用户意图,分配给合适的下属,汇总结果父组件 CustomerService
OrderAgent(订单专员)查询物流信息、订单状态子组件 OrderTracker
StockAgent(库存专员)查询商品库存、尺码信息子组件 StockChecker
DefaultAgent(通用回复)解答退货政策、闲聊等默认子组件 FAQ

环境准备

pip install langgraph ollama
ollama pull qwen3:4b

第一步:定义各个 Agent 的“能力”

我们为每个专业 Agent 准备一个模拟函数,假装它在查数据库。也可以让 LLM 来执行,但为了演示清晰,我们用硬编码的函数 + LLM 生成自然语言回复。

# agents.py
def query_order(order_id: str) -> str:
    """模拟订单查询"""
    mock_db = {
        "123": "订单123:已发货,预计明天到达。快递单号:SF1234567890",
        "456": "订单456:正在拣货中,预计后天发货",
    }
    return mock_db.get(order_id, f"未找到订单{order_id}")

def check_stock(product: str) -> str:
    """模拟库存查询"""
    mock_stock = {
        "T恤": "T恤有货,红色L码剩3件",
        "运动鞋": "运动鞋断货,补货时间未定",
    }
    return mock_stock.get(product, f"{product}暂时无货")

def default_reply(query: str) -> str:
    """LLM 自由回复"""
    import ollama
    response = ollama.generate(
        model="qwen3:4b",
        prompt=f"你是客服,用简短的话回答用户问题:{query}"
    )
    return response["response"].strip()

第二步:构建单个专业 Agent 的迷你图

每个专业 Agent 其实就是一个“接收输入 → 处理 → 返回结果”的节点。为了展示 LangGraph 的子图能力,我们把每个 Agent 都封装成自己的小图,然后嵌套进主图。

# specialist_graph.py
from langgraph.graph import StateGraph, END
from typing import TypedDict
from agents import query_order, check_stock, default_reply

class SpecialistState(TypedDict):
    task: str       # 用户具体问题
    result: str     # 处理结果

def create_order_agent():
    builder = StateGraph(SpecialistState)
    def process(state: SpecialistState) -> dict:
        # 简单解析 task,提取订单号(假设格式:"查询订单123")
        # 真实场景由 LLM 提取,这里简化
        order_id = state["task"].replace("查询订单", "").strip()
        result = query_order(order_id) if order_id else "请提供订单号"
        return {"result": result}
    builder.add_node("process", process)
    builder.set_entry_point("process")
    builder.add_edge("process", END)
    return builder.compile()

def create_stock_agent():
    builder = StateGraph(SpecialistState)
    def process(state: SpecialistState) -> dict:
        product = state["task"].replace("查询库存", "").replace("查库存", "").strip()
        result = check_stock(product) if product else "请提供商品名"
        return {"result": result}
    builder.add_node("process", process)
    builder.set_entry_point("process")
    builder.add_edge("process", END)
    return builder.compile()

def create_default_agent():
    builder = StateGraph(SpecialistState)
    def process(state: SpecialistState) -> dict:
        result = default_reply(state["task"])
        return {"result": result}
    builder.add_node("process", process)
    builder.set_entry_point("process")
    builder.add_edge("process", END)
    return builder.compile()

第三步:主管 Agent —— 意图分类与任务委派

主管需要做两件事:

  1. 分析用户意图,决定分配给哪个部门。
  2. 调用子图执行任务,拿回结果后输出给用户。

我们用 LangGraph 的 StateGraph 嵌套子图,通过节点调用 agent.invoke()

# supervisor.py
import ollama
from langgraph.graph import StateGraph, END
from typing import TypedDict, Literal
from specialist_graph import create_order_agent, create_stock_agent, create_default_agent

MODEL = "qwen3:4b"

class SupervisorState(TypedDict):
    user_input: str
    department: str      # "order" / "stock" / "default"
    specialist_result: str
    final_response: str

# 实例化子 Agent(全局复用)
order_agent = create_order_agent()
stock_agent = create_stock_agent()
default_agent = create_default_agent()

def classify_department(state: SupervisorState) -> dict:
    """用 LLM 判断该分给哪个部门"""
    prompt = f"""你是客服主管。用户问题:"{state['user_input']}"
请只输出以下三个单词之一:order, stock, default
order: 查询订单、物流
stock: 查询库存、商品信息
default: 其他问题"""
    res = ollama.generate(model=MODEL, prompt=prompt)
    dept = res["response"].strip().lower()
    if dept not in ["order", "stock", "default"]:
        dept = "default"
    return {"department": dept}

def delegate_to_order(state: SupervisorState) -> dict:
    result_state = order_agent.invoke({"task": state["user_input"]})
    return {"specialist_result": result_state["result"]}

def delegate_to_stock(state: SupervisorState) -> dict:
    result_state = stock_agent.invoke({"task": state["user_input"]})
    return {"specialist_result": result_state["result"]}

def delegate_to_default(state: SupervisorState) -> dict:
    result_state = default_agent.invoke({"task": state["user_input"]})
    return {"specialist_result": result_state["result"]}

def format_response(state: SupervisorState) -> dict:
    return {"final_response": f"【{state['department']}部门回复】:{state['specialist_result']}"}

# 条件路由
def route_by_department(state: SupervisorState) -> Literal["order", "stock", "default"]:
    return state["department"]

# 构建主管图
builder = StateGraph(SupervisorState)
builder.add_node("classify", classify_department)
builder.add_node("order_delegate", delegate_to_order)
builder.add_node("stock_delegate", delegate_to_stock)
builder.add_node("default_delegate", delegate_to_default)
builder.add_node("format", format_response)

builder.set_entry_point("classify")
builder.add_conditional_edges("classify", route_by_department, {
    "order": "order_delegate",
    "stock": "stock_delegate",
    "default": "default_delegate"
})
builder.add_edge("order_delegate", "format")
builder.add_edge("stock_delegate", "format")
builder.add_edge("default_delegate", "format")
builder.add_edge("format", END)

supervisor = builder.compile()

第四步:运行多 Agent 系统

# main.py
from supervisor import supervisor
if __name__ == "__main__":
    print("🤖 多 Agent 客服系统 (主管 + 订单/库存/通用)")
    while True:
        user = input("\n🧑 你: ")
        if user == "/bye":
            break
        state = {"user_input": user}
        result = supervisor.invoke(state)
        print(result["final_response"])

运行效果:

🧑 你: 查询订单123
🤖 【order部门回复】:订单123:已发货,预计明天到达。快递单号:SF1234567890

🧑 你: T恤有货吗
🤖 【stock部门回复】:T恤有货,红色L码剩3件

🧑 你: 我要退货怎么操作
🤖 【default部门回复】:您可以进入“我的订单”点击申请退货,7天内可无理由退换哦。

Vue 3 横向对比

这个多 Agent 架构,在 Vue 3 中就像 组件拆分 + 动态组件 + 事件通信

<!-- CustomerService.vue 父组件 -->
<template>
  <div>
    <input v-model="userInput" @keyup.enter="handleQuery" />
    <component :is="currentComponent" :task="userInput" @result="onResult" />
    <p>{{ finalResponse }}</p>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue';
import OrderAgent from './OrderAgent.vue';
import StockAgent from './StockAgent.vue';
import DefaultAgent from './DefaultAgent.vue';

const userInput = ref('');
const department = ref('default');
const specialistResult = ref('');
const finalResponse = ref('');

const currentComponent = computed(() => {
  const map = { order: OrderAgent, stock: StockAgent, default: DefaultAgent };
  return map[department.value];
});

async function handleQuery() {
  // 1. 意图分类(调用 LLM 或规则)
  department.value = await classifyIntent(userInput.value);
  // 子组件会自动渲染并执行其内部逻辑(如 API 调用)
}

function onResult(result) {
  specialistResult.value = result;
  finalResponse.value = `【${department.value}部门回复】:${result}`;
}
</script>

OrderAgent.vue 内部可能就是一个独立的表单或查询模块,有自己的状态和逻辑。
LangGraph 的多 Agent 协作就是把这种“父组件调度子组件”的模式搬到了后端,让 AI 流程也具有同样的模块化能力。

进一步升级:让 Agent 之间直接对话

上面的例子是主管单向委派。更复杂的场景下,Agent 之间可以直接对话,比如:

  • 订单 Agent 查完后发现用户还问了库存,主动把请求转给库存 Agent。
  • 或者多个 Agent 通过共享的“黑板”(全局状态)交换信息。

LangGraph 支持多图嵌套、Send API 实现动态并行分发,这些在后续文章中我们可以深入。

总结

  • 多 Agent 协作 = 父图 + 子图,每个子 Agent 是一个独立的状态机。
  • 主管 Agent 负责意图分类和任务委派,就像 Vue 3 的父组件管理多个子组件。
  • 用 Ollama + Qwen3:4b 本地跑,全部免费,适合开发测试。
  • 这种架构让 AI 应用可以像微服务一样拆分、独立维护、组合复用。

现在你可以搭建自己的“AI 团队”了,比如让一个 Agent 写代码,另一个审代码,再一个写测试,爽不爽?

python main.py