张高兴的大模型开发实战:(四)使用 LangGraph 实现多智能体应用

随着大语言模型(LLM)技术的快速发展,人们期望利用 LLM 解决各种复杂问题,在此背景下,构建智能体(Agent)应用受到了广泛关注。用户与 LLM 的交互可以被视为一种 单智能体(Single-Agent) 行为:用户通过提示词(prompt)与通用 LLM 进行对话,LLM 理解问题并提供反馈。然而,单一智能体在处理复杂任务时存在明显局限性,例如需要用户多次引导、缺乏对外部环境的感知能力、对话历史记忆有限等。

试想以下场景:在不同处理阶段调用不同的模型;当 LLM 无法完成任务时,自动查询外部知识库;或者由 LLM 自主纠正生成内容中的幻觉和错误。这些需求如何实现? 多智能体(Multi-Agent) 系统正是解决这类问题的有效工具。通过提示词模板为每个智能体分配角色并规范其行为,多个智能体相互协作,从而完成复杂的任务。

然而,构建多智能体应用并非易事,开发者需要面对智能体设计、通信协议、协调策略等多方面的问题。LangGraph 提供了一种以图(graph)为核心的解决方案,清晰定义了智能体之间的关系与交互规则,并通过内置的通信接口和协调策略,帮助开发者快速构建高效且可扩展的分布式智能系统。

接下来,我们将通过一个实例展示如何使用 LangGraph 构建一个多智能体应用,并结合 Streamlit 实现用户友好的前端界面。 该应用具备以下功能:

  1. 根据对话类型将请求路由到适当的处理节点。
  2. 支持联网搜索,获取实时信息。
  3. 根据问题和对话历史生成优化的搜索提示词。
  4. 支持文件上传与处理。
  5. 利用编程专用的 LLM 解决代码相关问题。
  6. 基于提供的文档内容,总结生成答案。

环境搭建与配置

项目结构如下:

.
├── .streamlit  # Streamlit 配置
│   └── config.toml
├── chains  # 智能体
│   ├── generate.py
│   ├── models.py
│   └── summary.py
├── graph   # 图结构
│   ├── graph.py
│   └── graph_state.py
├── upload_files    # 上传的文件
│   └── .keep
├── .env   # 环境变量配置
├── app.py  # Streamlit 应用
├── main.py # 命令行程序
└── requirements.txt    # 依赖

requirements.txt 中列出了程序必要的包,使用命令 pip install -r requirements.txt 安装依赖。

# LangChain 相关包
langchain
langchain-ollama
langchain-chroma
langchain-community
langgraph
chromadb
tavily-python
python-dotenv

# 文档处理相关包
marker-pdf
weasyprint
mammoth
openpyxl
unstructured[all-docs]
libmagic

# Streamlit 相关包
streamlit
streamlit-chat
streamlit-extras

# 文档使用GPU处理时,安装GPU版PyTorch
# use 'pip install -r requirements.txt --proxy=127.0.0.1:23474' to accelerate download speed
# --extra-index-url https://download.pytorch.org/whl/cu124
# torch==2.6.0+cu124
# torchvision==0.21.0+cu124

相关的环境变量配置在 .env 文件中,在程序中通过 dotenv 包读取。

TAVILY_API_KEY=tvly-dev-xxxxxx  # Tavily API 密钥
OMP_NUM_THREADS=8   # 设置线程数

其中 TAVILY_API_KEY 是 Tavily 的 API 密钥,用于网络搜索服务,需要在 Tavily AI 注册并获取,每月有 1000 次的免费额度。

定义智能体

在 LangChain 中,使用 链(chain) 来定义用户与 LLM 交互的行为,即智能体。链是一个可调用的对象,接收输入并返回输出。在 chains 目录下,定义了两个链:summary.py 和 generate.py,分别用于提取关键词和生成回答。

.
├── chains  # 智能体
│   ├── generate.py
│   ├── models.py
│   └── summary.py

加载模型

在定义智能体之前,需要先定义好加载模型的方法。models.py 文件负责根据提供的模型名称加载相应的模型。

from langchain_ollama import ChatOllama, OllamaEmbeddings
from langchain_core.vectorstores import InMemoryVectorStore

def load_model(model_name: str) -> ChatOllama:
    """
    加载语言模型

    参数:
        model_name (str): 模型名称

    返回:
        ChatOllama实例,用于生成文本和回答问题
    """
    return ChatOllama(model=model_name)
    
def load_embeddings(model_name: str) -> OllamaEmbeddings:
    """
    加载嵌入模型

    参数:
        model_name (str): 模型名称

    返回:
        OllamaEmbeddings实例,用于将文本转换为向量表示
    """
    return OllamaEmbeddings(model=model_name)

def load_vector_store(model_name: str) -> InMemoryVectorStore:
    """
    创建内存向量存储

    参数:
        model_name (str): 用于生成嵌入的模型名称

    返回:
        InMemoryVectorStore实例,用于存储和检索向量化的文本
    """
    embeddings = load_embeddings(model_name)
    return InMemoryVectorStore(embeddings)

提取关键词

在 summary.py 文件中,定义了 SummaryChain 类,用于从用户问题和聊天记录中提取关键词,并生成高效的搜索查询。

from langchain.prompts import ChatPromptTe
<think>我们正在使用虾哥开源的小智项目(xiaozhi-esp32)通过MCP协议控制LED灯。MCP(Micro-Controller Protocol)是该项目中用于设备间通信的协议。以下是具体步骤:###一、硬件连接-LED正极 →ESP32的GPIO引脚(例如GPIO2,串联限流电阻) -LED负极 →GND###二、代码修改####1.注册LED设备在`device_manager.cpp`中注册LED设备: ```cpp#include "devices/led_device.h"//包含LED设备头文件void DeviceManager::InitializeDevices(){// ...其他设备初始化代码AddDevice(std::make_shared<LedDevice>());//注册LED设备}``` ####2.创建LED设备类- **头文件**`led_device.h`:```cpp#pragmaonce#include "thing.h"class LedDevice :public Thing{ public:LedDevice();void TurnOn();voidTurnOff();//通过MCP协议控制需要实现HandleMethod方法boolHandleMethod(const std::string& method, constJsonObject& params, JsonObject&result)override;private:boolstatus_;constgpio_num_tgpio_ =GPIO_NUM_2;//使用GPIO2}; ```- **实现文件**`led_device.cpp`:```cpp#include"led_device.h" #include<driver/gpio.h> LedDevice::LedDevice() :Thing("LED","MCP控制的LED灯") {gpio_reset_pin(gpio_);gpio_set_direction(gpio_,GPIO_MODE_OUTPUT);gpio_set_level(gpio_,0);//初始关闭status_ =false;//注册属性(用于MCP协议状态报告)properties_.AddBooleanProperty("status","当前状态",[this](){return status_; });//注册方法(MCP调用)methods_.AddMethod("TurnOn","开灯",ParameterList(), [this](constParameterList&){ TurnOn();});methods_.AddMethod("TurnOff","关灯",ParameterList(), [this](const ParameterList&) {TurnOff(); });} voidLedDevice::TurnOn(){status_= true;gpio_set_level(gpio_,1);//高电平点亮} voidLedDevice::TurnOff(){status_= false;gpio_set_level(gpio_,0);//低电平熄灭}//处理MCP调用boolLedDevice::HandleMethod(conststd::string&method,const JsonObject&params,JsonObject& result) {if(method== "TurnOn") {TurnOn();result["status"] ="success";return true;} elseif (method =="TurnOff"){TurnOff();result["status"]= "success";returntrue;}returnfalse;//方法未处理}``` ###三、通过MCP协议控制LED小智项目内置了MCP服务器,其他设备(如手机APP、网页)可通过MCP协议发送控制命令。协议格式示例: ```json{"device": "LED",//设备名称"method": "TurnOn",//方法名"params":{}//参数(本例无参数) }```###、测试MCP控制1. **启动MCP服务器**:小智项目启动时会自动监听MCP端口(默认`6789`)。 2. **发送控制命令**(使用`netcat`测试):```bashecho '{"device":"LED","method":"TurnOn","params":{}}' |nc192.168.1.1006789```响应示例:```json{"status":"success"}``` 3. **观察LED状态**:发送命令后LED应点亮。###五、结合BotSharp智能体框架(可选) BotSharp可作为上层应用,通过MCP协议控制小智设备[^2]。在BotSharp中创建智能体时,调用MCP接口:```csharp// BotSharp智能体示例代码publicasyncTask<string> ControlLed(string deviceIp,stringaction) {using(varclient =new HttpClient()){var mcpCommand= new{device= "LED",method= action== "on"? "TurnOn" :"TurnOff",params= new{ }};varresponse= awaitclient.PostAsJsonAsync($"http://{deviceIp}:6789", mcpCommand);return awaitresponse.Content.ReadAsStringAsync();} }```>提示:实际开发中需处理网络异常和超时。§§相关问题§§1.MCP协议如何实现设备发现? 2.如何在小智项目中加密MCP通信? 3.如何通过MQTT转发MCP命令实现远程控制? 4. BotSharp智能体如何解析自然语言生成MCP命令? [^1]:创客社区地址BotSharp文档桌面桌面机器人仓库地址.NETIoT库源码地址.NET IoT官方地址.NET IoT社区人员高兴推荐什么是lottie动画?参考的语音对话项目conversational-speaker小智C#客户端C#MCPSDK树莓派学习之旅(2)—使用USB声卡进行录音BotSharp官方GitHubEdgeVoiceAI聊天机器人[^2]:结合BotSharp智能体框架开发语音交互。BotSharp是一个开源的多智能体应用开发框架,从简单的聊天机器人,再到多智能体协作,以及复杂的任务如【TextTo Sql】框架都提供了开箱即用的使用方法,可以快速的将大模型的能力接入到现有的业务系统中,并且内置知识库和会话管理功能等。大语言模型的函数调用(这个是理解BotSharp框架的核心知识点)。</think>### 如何使用虾哥开源小智项目通过MCP协议控制LED灯 以下是基于小智项目通过MCP协议实现LED控制的完整步骤,结合硬件连接、代码修改和协议配置: #### 一、硬件准备 1. **ESP32开发板**:使用支持MCP协议的ESP32开发板 2. **LED连接**: - LED正极 → GPIO2(串联220Ω限流电阻) - LED负极 → GND 3. **网络连接**:确保ESP32接入局域网 #### 二、代码修改(小智项目端) ##### 1. 创建LED设备类 在`devices`目录下创建`led_device.h`: ```cpp #pragma once #include "thing.h" #include <driver/gpio.h> class LedDevice : public Thing { public: LedDevice(); void TurnOn(); void TurnOff(); bool HandleMCPCommand(const std::string& method, const JsonObject& params); // MCP协议处理 private: bool status_ = false; const gpio_num_t gpio_ = GPIO_NUM_2; // 使用GPIO2 }; ``` ##### 2. 实现LED控制逻辑(`led_device.cpp`) ```cpp #include "led_device.h" LedDevice::LedDevice() : Thing("LED", "MCP控制的LED灯") { gpio_reset_pin(gpio_); gpio_set_direction(gpio_, GPIO_MODE_OUTPUT); gpio_set_level(gpio_, 0); // 初始关闭 // 注册MCP可调用方法 methods_.AddMethod("TurnOn", "开灯", ParameterList()); methods_.AddMethod("TurnOff", "关灯", ParameterList()); } void LedDevice::TurnOn() { status_ = true; gpio_set_level(gpio_, 1); // 高电平点亮 } void LedDevice::TurnOff() { status_ = false; gpio_set_level(gpio_, 0); // 低电平熄灭 } // 处理MCP协议命令 bool LedDevice::HandleMCPCommand(const std::string& method, const JsonObject& params) { if (method == "TurnOn") { TurnOn(); return true; } else if (method == "TurnOff") { TurnOff(); return true; } return false; } ``` ##### 3. 注册设备并启用MCP服务 在`main.cpp`中: ```cpp #include "devices/led_device.h" #include "network/mcp_server.h" // MCP协议服务 void setup() { // ...其他初始化 DeviceManager::AddDevice(std::make_shared<LedDevice>()); // 启动MCP服务器(默认端口6789) MCPServer::Start(6789); } ``` #### 三、MCP协议控制端实现(C#示例) 使用C# MCP SDK发送控制命令[^1]: ```csharp using MCP.Client; class Program { static void Main() { // 连接到小智设备 var device = new McpDevice("192.168.1.100", 6789); // ESP32的IP // 发送开灯命令 var response = device.InvokeMethod("LED", "TurnOn"); if (response.Status == "success") { Console.WriteLine("开灯成功"); } else { Console.WriteLine($"控制失败: {response.Error}"); } } } ``` #### 、MCP协议命令格式 通过TCP发送的原始MCP协议格式: ```json { "device": "LED", // 设备名称 "method": "TurnOn", // 调用的方法 "params": {}, // 参数(本例不需要) "request_id": 123 // 请求ID(可选) } ``` 成功响应示例: ```json { "status": "success", "device": "LED", "response": {} } ``` #### 五、测试流程 1. 烧录修改后的小智固件到ESP32 2. 启动C#控制程序(或使用网络调试工具发送MCP命令) 3. 观察LED状态变化 4. 调试命令: ```bash echo '{"device":"LED","method":"TurnOn"}' | nc 192.168.1.100 6789 ``` > 提示:BotSharp框架可作为上层应用生成MCP命令,实现自然语言控制LED[^2]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值