用LangGraph和LangFuse构建工业级多智能体(Multi-Agent)系统实战


在当今快速发展的AI领域,单一的大语言模型(LLM)已经难以满足复杂、多步骤的业务需求。多智能体(Multi-Agent)系统应运而生,它通过将多个专用的子智能体(sub-agent)组合起来,协同完成一项复杂任务,从而显著提升了系统的鲁棒性、可扩展性和问题解决能力。

本文将以一个智能工厂设备运维助手 的实际场景为例,深入探讨如何使用强大的图计算框架 LangGraph 从零开始构建一个功能完备的多Agent系统。我们将覆盖从基础架构设计、状态与记忆管理、人机回环(Human-in-the-Loop),到最终使用开源可观测性平台 LangFuse 进行系统评测的全过程。

这不仅是一篇技术教程,更希望为读者在构建复杂Agent系统时提供一套可复现的开发经验和设计思路。

系统原型展现

为了增加读者的感性认识,先把本系统实现的界面展示一下,方便大家理解后面的拆解。

  • Web界面:
  • 命令行界面:
  • LangFuse跟踪评估界面:

1. 场景设定与架构设计

场景 :假设我们正在为一个现代化智能工厂构建一个AI运维助手。该助手需要处理一线工程师的自然语言请求,例如查询设备状态、分析故障原因,并自动创建维保任务。

为了实现这个目标,我们设计一个基于"主管(Supervisor)"模式的多Agent架构。该架构包含:

* 设备状态查询Agent (Machine Status Agent) :负责连接工厂的物联网(IoT)数据库,查询设备的实时运行参数,如温度、压力、转速等。

* 维保任务调度Agent (Maintenance Scheduler Agent) :负责与工单系统(Ticketing System)交互,根据故障信息创建、查询和更新维保任务。

* 主管Agent (Supervisor) :作为总协调者,负责理解用户意图,并将任务路由给合适的子Agent。它不直接执行工具,而是"指挥"其他Agent工作。

整体工作流程如下:

2. 环境准备与核心概念

在开始编码前,我们需要安装必要的库并设置环境变量。

Bash 复制代码
pip install langchain langchain-openai langgraph langfuse typing_extensions

在您的项目根目录创建一个 .env 文件,用于存放API密钥:

python 复制代码
# .env file  
OPENAI_API_KEY="your-openai-api-key"

# LangFuse credentials  
LANGFUSE_SECRET_KEY="sk-lf-..."  
LANGFUSE_PUBLIC_KEY="pk-lf-..."  
LANGFUSE_HOST="https://cloud.langfuse.com" # 或者你的自托管地址

LangGraph的核心概念

* 状态 (State) :是数据在图中流动的核心载体。它通常是一个字典或TypedDict,每个节点都可以读取和修改它。这构成了Agent的"记忆"基础。

* 节点 (Node) :是图中的基本计算单元。它可以是一个函数或一个Runnable对象,接收当前的状态作为输入,并返回一个更新后的状态字典。

* 边 (Edge) :连接不同的节点,定义了工作流的走向。LangGraph支持常规边(固定流向)和条件边(根据当前状态动态决定下一个节点)。

短期记忆与长期记忆

* 短期记忆 (Short-Term Memory) :用于维持单次对话的上下文。在LangGraph中,这通常由checkpointer(检查点)机制实现,它能保存和恢复图的每一步状态。

* 长期记忆 (Long-Term Memory) :用于跨越多轮对话,持久化存储用户信息或偏好。我们将使用一个外部存储(如数据库)来实现这一点。

首先初始化这些组件:

Python 复制代码
# main.py  
import os  
from dotenv import load_dotenv  
from langgraph.checkpoint.sqlite import SqliteSaver  
from langgraph.store.memory import InMemoryStore

# 加载环境变量  
load_dotenv()

# 短期记忆: 使用SQLite作为检查点后端  
# 这使得对话可以被中断和恢复  
memory = SqliteSaver.from_conn_string(":memory:")

# 长期记忆: 使用内存存储作为示例  
# 在生产环境中,你可以替换为Redis, Postgres等  
long_term_memory_store = InMemoryStore()

3. 构建第一个子Agent:设备状态查询

我们将从零开始构建一个完整的ReAct(Reasoning and Acting)风格的Agent。

3.1 定义状态 (State)

状态是所有Agent共享的数据结构。

Python 复制代码
from typing import TypedDict, Annotated, List  
from langgraph.graph.message import AnyMessage, add_messages

class AgentState(TypedDict):  
    # 对话历史,add_messages会自动聚合消息  
    messages: Annotated[list[AnyMessage], add_messages]  
    # 从长期记忆中加载的设备历史故障信息  
    device_history: str  
    # 当前操作的设备ID  
    device_id: str  
    # 防止无限循环的计数器  
    remaining_steps: int

3.2 定义工具 (Tools)

工具是Agent与外部世界交互的桥梁。这里我们模拟一个数据库查询函数。

Python 复制代码
from langchain_core.tools import tool  
import random

# 模拟的设备数据库  
DEVICE_DB = {  
    "C-101": {"name": "1号数控车床", "status": "正常", "temp": 65.5, "pressure": 1.2},  
    "P-205": {"name": "5号加压泵", "status": "告警", "temp": 92.1, "pressure": 2.5},  
}

@tool  
def get_device_status(device_id: str) -> dict:  
    """根据设备ID查询其详细运行状态。"""  
    print(f"--- 工具调用: get_device_status, device_id={device_id} ---")  
    return DEVICE_DB.get(device_id, {"error": "未找到设备"})

@tool  
def find_high_temp_devices() -> list[str]:  
    """查询所有温度超过阈值(80度)的设备。"""  
    print(f"--- 工具调用: find_high_temp_devices ---")  
    high_temp_list = []  
    for device_id, data in DEVICE_DB.items():  
        if data["temp"] > 80.0:  
            high_temp_list.append(device_id)  
    return high_temp_list

# 将工具打包  
status_tools = [get_device_status, find_high_temp_devices]

3.3 定义节点 (Nodes)

一个ReAct Agent至少需要两个节点:一个用于思考和调用工具的Agent节点 ,另一个用于执行工具的工具节点

Python 复制代码
from langchain_openai import ChatOpenAI  
from langchain_core.messages import SystemMessage, HumanMessage  
from langgraph.prebuilt import ToolNode

# 绑定工具到LLM,这样LLM才知道有哪些工具可用  
llm = ChatOpenAI(model="gpt-4-turbo")  
llm_with_status_tools = llm.bind_tools(status_tools)

# 1. Agent节点  
def status_agent_node(state: AgentState):  
    """设备状态查询Agent的核心逻辑节点"""  
    prompt = """  
    你是一个专业的设备状态分析员。你的职责是根据用户的请求,使用工具查询设备的实时状态。  
    如果用户没有提供明确的设备ID,但描述了问题(如"哪个设备温度最高"),请选择合适的工具进行查询。  
    请直接、简洁地回答问题。  
    """  
      
    # 将系统提示和当前对话历史传给LLM  
    response = llm_with_status_tools.invoke(  
        [SystemMessage(content=prompt)] + state["messages"]  
    )  
    return {"messages": [response]}

# 2. 工具节点  
# ToolNode是LangGraph预置的,可以方便地执行工具调用  
status_tool_node = ToolNode(status_tools)

3.4 组装图 (Graph Assembly)

现在,我们将节点和边组合成一个可执行的图。

Python 复制代码
from langgraph.graph import StateGraph, START, END

def should_continue(state: AgentState):  
    """条件边:判断是继续调用工具还是结束"""  
    if last_message := state["messages"][-1]:  
        if last_message.tool_calls:  
            return "continue"  
    return "end"

# 创建图  
graph_builder = StateGraph(AgentState)

# 添加节点  
graph_builder.add_node("status_agent", status_agent_node)  
graph_builder.add_node("status_tool_executor", status_tool_node)

# 定义工作流的入口  
graph_builder.add_edge(START, "status_agent")

# 添加条件边  
graph_builder.add_conditional_edges(  
    "status_agent",  
    should_continue,  
    {  
        "continue": "status_tool_executor",  
        "end": END,  
    },  
)

# 工具执行后返回Agent节点继续思考  
graph_builder.add_edge("status_tool_executor", "status_agent")

# 编译图  
status_agent_graph = graph_builder.compile(checkpointer=memory)

4. 构建第二个子Agent:维保任务调度

为了展示不同的构建方式,我们将使用LangGraph的预构建函数 create_react_agent 来快速创建维保任务调度Agent。

Python 复制代码
from langgraph.prebuilt import create_react_agent

@tool  
def create_maintenance_ticket(device_id: str, issue_description: str) -> dict:  
    """为指定设备创建维保工单。"""  
    print(f"--- 工具调用: create_maintenance_ticket, device_id={device_id} ---")  
    ticket_id = random.randint(1000, 9999)  
    return {"status": "成功", "ticket_id": ticket_id, "message": f"已为设备 {device_id} 创建工单 {ticket_id}。"}

maintenance_tools = [create_maintenance_ticket]

maintenance_agent_prompt = """  
你是一名维保任务调度员。你的任务是根据分析员的报告,为出现问题的设备创建维保工单。  
在创建工单时,必须提供清晰的设备ID和问题描述。  
"""

# 使用预构建函数快速创建Agent  
maintenance_agent = create_react_agent(  
    llm,  
    tools=maintenance_tools,  
    messages_modifier=maintenance_agent_prompt,  
    checkpointer=memory,  
)

5. 核心大脑:构建Supervisor

Supervisor负责接收用户请求,并将其路由给上述两个子Agent。

Python 复制代码
from langchain_core.tools import tool  
from typing import Literal

# Supervisor需要特殊的"工具"来将任务路由给子Agent  
# 注意:这里的工具函数体是空的,因为Supervisor的LLM输出ToolCall本身就是路由指令  
@tool  
def status_agent_router(query: str):  
    """将有关设备状态、温度、压力等实时数据的查询路由到此。"""  
    pass

@tool  
def maintenance_agent_router(device_id: str, issue: str):  
    """将有关创建维保任务、安排检修的请求路由到此。"""  
    pass

# 定义一个包含所有成员及其路由工具的字典  
members = {  
    "status_agent": {"agent": status_agent_graph, "router": status_agent_router},  
    "maintenance_agent": {"agent": maintenance_agent, "router": maintenance_agent_router},  
}

supervisor_prompt = """  
你是一个智能工厂的运维主管。你的团队有以下成员:  
- status_agent: 负责查询设备实时状态。  
- maintenance_agent: 负责创建维保工单。

根据用户的请求,决定下一步应该由哪个成员来处理。  
如果一个成员完成后还有后续步骤,请继续路由到下一个合适的成员。  
如果所有任务都已完成,或者你不确定下一步做什么,请直接回复用户。  
"""

# Supervisor的核心逻辑  
def supervisor_node(state: AgentState):  
    # ... (省略了完整的Supervisor实现细节,核心是调用LLM决定下一个Agent)  
    # 实际实现会更复杂,需要解析LLM的ToolCall来决定路由  
    # 为了简化,我们假设它能返回下一个agent的名字或"END"  
    # 在实际的LangGraph中,这通常通过一个专门的`MessageRouter`或`ToolRouter`实现  
    # 这里我们用一个简化的概念表示  
    print("--- 主管决策中 ---")  
    # ...  
    # 返回下一个节点的名称  
    pass 

注意:一个完整的Supervisor实现较为复杂,LangGraph官方文档提供了 create_supervisor 预构建函数,它能极大地简化这个过程。为了篇幅,这里我们仅展示其概念,具体实现细节可以看实现源代码。

6. 引入"人"的智慧:Human-in-the-Loop

在工业场景中,某些关键操作(如停机检修)需要人工确认。我们可以通过LangGraph的interrupt功能实现这一点。

Python 复制代码
from langgraph.errors import GraphInterrupt  
from langgraph.graph import StateGraph

def confirmation_node(state: AgentState):  
    """检查是否需要人工确认"""  
    # 假设state中包含了任务的关键性信息  
    if state.get("is_critical_maintenance"):  
        # 抛出中断,图会在这里暂停  
        raise GraphInterrupt("关键维保任务需要停机,请输入'yes'确认:")  
    return

def human_input_node(state: AgentState):  
    """一个虚拟节点,用于接收中断后的输入"""  
    pass

# ... 在你的图中加入中断逻辑 ...  
# workflow.add_node("confirmation", confirmation_node)  
# workflow.add_conditional_edges(  
#     "previous_node",  
#     lambda state: "confirmation" if state.get("is_critical_maintenance") else "next_node"  
# )  
# workflow.add_edge("confirmation", "human_input_node")

当图执行到confirmation_node并抛出GraphInterrupt时,程序会暂停。你可以捕获这个中断,获取提示信息展示给用户,然后将用户的输入重新invoke回图中,从而继续执行。

7. 赋予Agent"记忆":长期记忆集成

为了让Agent"记住"一台设备的历史故障,我们需要集成长期记忆。

Python 复制代码
# 节点1: 加载记忆  
def load_memory_node(state: AgentState):  
    device_id = state.get("device_id")  
    if device_id:  
        # 从长期记忆存储中获取数据  
        history = long_term_memory_store.get(f"device:{device_id}:history")  
        return {"device_history": str(history or "无历史故障记录")}  
    return {}

# 节点2: 创建/更新记忆  
def create_memory_node(state: AgentState):  
    # 分析整个对话,判断是否产生了新的故障记录  
    # ... LLM分析逻辑 ...  
    new_fault_detected = True # 假设分析后发现新故障  
    if new_fault_detected:  
        device_id = state.get("device_id")  
        issue = "温度超过100度"  
        # 更新长期记忆  
        long_term_memory_store.put(f"device:{device_id}:history", issue)  
    return {}

# 将这两个节点整合进你的主工作流图的开始和结束位置

8. 效果如何?使用LangFuse进行测试评估

构建完复杂的系统后,如何科学地评估其表现至关重要。我们将使用开源的 LangFuse 平台。

8.1 LangFuse设置与代码集成

首先,初始化LangFuse客户端。

Python 复制代码
from langfuse import Langfuse  
from langfuse.decorators import traceable

# 初始化Langfuse  
langfuse = Langfuse()

使用 @traceable 装饰器可以非常方便地追踪整个Agent的执行过程。

Python 复制代码
@traceable(name="smart_factory_agent_run")  
def run_agent(question: str):  
    # 这里是调用你的最终版Agent图的代码  
    # config = {"configurable": {"thread_id": "some_unique_id"}}  
    # response = final_graph.invoke({"messages": [HumanMessage(content=question)]}, config)  
    # return response  
    # 为了演示,我们返回一个模拟结果  
    print(f"Agent is processing: {question}")  
    return {"output": "工单 #8848 已为设备 C-101 创建,问题:温度异常。"}

当你运行 run_agent("...") 时,完整的调用链、输入输出、耗时、Token消耗等信息都会被自动发送到你的LangFuse项目中。

8.2 创建评估数据集

在LangFuse中,我们可以创建一个数据集来存放测试用例。

Python 复制代码
# 在Langfuse UI中创建一个名为 "factory-agent-test" 的数据集,  
# 或者使用SDK创建:  
langfuse.create_dataset(name="factory-agent-test")

# 为数据集添加测试用例  
langfuse.create_example(  
    dataset_name="factory-agent-test",  
    input={"question": "1号车床温度有点高,是什么情况?"},  
    expected_output="1号数控车床当前温度为65.5度,状态正常。"  
)  
langfuse.create_example(  
    dataset_name="factory-agent-test",  
    input={"P-205加压泵告警了,帮我安排检修。"},  
    expected_output="已为设备 P-205 创建维保工单" # 预期输出可以模糊一些,用于后续LLM评估  
)

8.3 定义评估标准 (Evaluator)

我们可以使用一个强大的LLM作为"考官",来评估Agent的回答质量。

Python 复制代码
from langfuse.model import CreateScore  
from langfuse.evaluation import Evaluation

# 使用LLM作为评估者  
@traceable(name="correctness_evaluator")  
def correctness_evaluator(output, expected_output):  
    system_prompt = """  
    你是一个严格的评估员。你的任务是判断"实际输出"是否在事实上与"预期输出"一致。  
    你需要给出'1'(完全一致或更好)或'0'(不一致或错误)的分数,并提供简短的评估理由。  
    """  
      
    human_prompt = f"""  
    [预期输出]: {expected_output}  
    [实际输出]: {output}  
      
    请严格按照预期输出进行评估。如果实际输出包含了预期输出的核心信息,即可判为正确。  
    """  
      
    response = llm.invoke([  
        SystemMessage(content=system_prompt),  
        HumanMessage(content=human_prompt)  
    ]).content  
      
    # 简单的解析逻辑  
    score = 1 if '1' in response else 0  
    reason = response  
      
    return CreateScore(name="correctness", value=score, comment=reason)

8.4 运行评估

LangFuse提供了便捷的工具来对数据集中的每一项运行你的Agent,并用评估函数打分。

Python 复制代码
evaluation = Evaluation(  
    name="agent-v1-eval",  
    dataset_name="factory-agent-test",  
    model=run_agent, # 你的Agent执行函数  
    scorers=[correctness_evaluator] # 评估函数列表  
)

# 运行评估任务  
# 这会遍历数据集,执行agent,然后用scorer打分  
evaluation_job = evaluation.run()

print("评估完成!请前往LangFuse仪表板查看详细结果。")

评估完成后,你可以在LangFuse的UI上看到每个测试用例的详细执行Trace、最终得分、评估理由、以及整体的平均分、延迟、成本等聚合指标。这为你迭代和优化Agent提供了强有力的数据支持。

总结

本文通过一个智能工厂运维助手的实例,完整地展示了使用LangGraph构建一个包含 主管-下属架构、短期/长期记忆、人机回环 的复杂多Agent系统的全过程。 此外,我们引入了开源可观测性平台LangFuse ,演示了如何通过追踪、数据集和评估器对我们构建的复杂系统进行科学、量化的评估。这形成了一个从"开发"到"运维"的闭环,是构建生产级AI应用不可或缺的一环。

相关推荐
yumgpkpm8 小时前
数据可视化AI、BI工具,开源适配 Cloudera CMP 7.3(或类 CDP 的 CMP 7.13 平台,如华为鲲鹏 ARM 版)值得推荐?
人工智能·hive·hadoop·信息可视化·kafka·开源·hbase
亚马逊云开发者8 小时前
通过Amazon Q CLI 集成DynamoDB MCP 实现游戏场景智能数据建模
人工智能
nix.gnehc8 小时前
PyTorch
人工智能·pytorch·python
J_Xiong01178 小时前
【VLNs篇】17:NaVid:基于视频的VLM规划视觉语言导航的下一步
人工智能·机器人
小殊小殊8 小时前
【论文笔记】视频RAG-Vgent:基于图结构的视频检索推理框架
论文阅读·人工智能·深度学习
IT_陈寒8 小时前
Vite 5.0实战:10个你可能不知道的性能优化技巧与插件生态深度解析
前端·人工智能·后端
大模型真好玩9 小时前
LangChain1.0实战之多模态RAG系统(二)——多模态RAG系统图片分析与语音转写功能实现
人工智能·langchain·mcp
机器之心9 小时前
智能体&编程新王Claude Opus 4.5震撼登场,定价大降2/3
人工智能·openai
小殊小殊9 小时前
【论文笔记】知识蒸馏的全面综述
人工智能·算法·机器学习
hans汉斯9 小时前
【数据挖掘】基于深度学习的生产车间智能管控研究
人工智能·深度学习·数据挖掘