Lagent:从零搭建你的 Multi-Agent

环境配置

开发机选择 30% A100,镜像选择为 Cuda12.2-conda。

首先来为 Lagent 配置一个可用的环境

创建环境

bash 复制代码
conda create -n lagent python=3.10 -y

激活环境

bash 复制代码
conda activate lagent

安装 torch

bash 复制代码
conda install pytorch==2.1.2 torchvision==0.16.2 torchaudio==2.1.2 pytorch-cuda=12.1 -c pytorch -c nvidia -y

安装其他依赖包

bash 复制代码
pip install termcolor==2.4.0
pip install streamlit==1.39.0
pip install class_registry==2.1.2
pip install datasets==3.1.0

接下来,我们通过源码安装的方式安装 lagent

创建目录以存放代码

bash 复制代码
mkdir -p /root/agent_camp4
cd /root/agent_camp4
git clone https://github.com/InternLM/lagent.git
cd lagent && git checkout e304e5d && pip install -e . && cd ..
pip install griffe==0.48.0

Lagent框架中Agent的使用

接下来,我们将使用 Lagent 框架,一步步搭建并使用基于 InternLM2.5 的 Web Demo,体验其强大的智能体功能。

首先,需要申请 API 授权令牌 ,请前往 书生·浦语 API 文档 申请并获取 Authorization 令牌,将其填入后续代码的 YOUR_TOKEN_HERE 变量中。

创建一个代码example,创建agent_api_web_demo.py,在里面实现我们的Web Demo:

bash 复制代码
conda activate lagent
cd /root/agent_camp4/lagent/examples
touch agent_api_web_demo.py

Action,也称为工具,Lagent中集成了很多好用的工具,提供了一套LLM驱动的智能体用来与真实世界交互并执行复杂任务的函数,包括谷歌文献检索、Arxiv文献检索、Python编译器等。具体可以查看文档

让我们来体验一下,让LLM调用Arxiv文献检索这个工具:

在agent_api_web_demo.py中写入下面的代码,这里利用 GPTAPI 类,该类继承自 BaseAPILLM,封装了对 API 的调用逻辑,然后利用Streamlit启动Web服务:

bash 复制代码
import copy
import os
from typing import List
import streamlit as st
from lagent.actions import ArxivSearch
from lagent.prompts.parsers import PluginParser
from lagent.agents.stream import INTERPRETER_CN, META_CN, PLUGIN_CN, AgentForInternLM, get_plugin_prompt
from lagent.llms import GPTAPI

class SessionState:
    """管理会话状态的类。"""

    def init_state(self):
        """初始化会话状态变量。"""
        st.session_state['assistant'] = []  # 助手消息历史
        st.session_state['user'] = []  # 用户消息历史
        # 初始化插件列表
        action_list = [
            ArxivSearch(),
        ]
        st.session_state['plugin_map'] = {action.name: action for action in action_list}
        st.session_state['model_map'] = {}  # 存储模型实例
        st.session_state['model_selected'] = None  # 当前选定模型
        st.session_state['plugin_actions'] = set()  # 当前激活插件
        st.session_state['history'] = []  # 聊天历史
        st.session_state['api_base'] = None  # 初始化API base地址

    def clear_state(self):
        """清除当前会话状态。"""
        st.session_state['assistant'] = []
        st.session_state['user'] = []
        st.session_state['model_selected'] = None


class StreamlitUI:
    """管理 Streamlit 界面的类。"""

    def __init__(self, session_state: SessionState):
        self.session_state = session_state
        self.plugin_action = []  # 当前选定的插件
        # 初始化提示词
        self.meta_prompt = META_CN
        self.plugin_prompt = PLUGIN_CN
        self.init_streamlit()

    def init_streamlit(self):
        """初始化 Streamlit 的 UI 设置。"""
        st.set_page_config(
            layout='wide',
            page_title='lagent-web',
            page_icon='./docs/imgs/lagent_icon.png'
        )
        st.header(':robot_face: :blue[Lagent] Web Demo ', divider='rainbow')

    def setup_sidebar(self):
        """设置侧边栏,选择模型和插件。"""
        # 模型名称和 API Base 输入框
        model_name = st.sidebar.text_input('模型名称:', value='internlm2.5-latest')
        
        # ================================== 硅基流动的API ==================================
        # 注意,如果采用硅基流动API,模型名称需要更改为:internlm/internlm2_5-7b-chat 或者 internlm/internlm2_5-20b-chat
        # api_base = st.sidebar.text_input(
        #     'API Base 地址:', value='https://api.siliconflow.cn/v1/chat/completions'
        # )
        # ================================== 浦语官方的API ==================================
        api_base = st.sidebar.text_input(
            'API Base 地址:', value='https://internlm-chat.intern-ai.org.cn/puyu/api/v1/chat/completions'
        )
        # ==================================================================================
        # 插件选择
        plugin_name = st.sidebar.multiselect(
            '插件选择',
            options=list(st.session_state['plugin_map'].keys()),
            default=[],
        )

        # 根据选择的插件生成插件操作列表
        self.plugin_action = [st.session_state['plugin_map'][name] for name in plugin_name]

        # 动态生成插件提示
        if self.plugin_action:
            self.plugin_prompt = get_plugin_prompt(self.plugin_action)

        # 清空对话按钮
        if st.sidebar.button('清空对话', key='clear'):
            self.session_state.clear_state()

        return model_name, api_base, self.plugin_action

    def initialize_chatbot(self, model_name, api_base, plugin_action):
        """初始化 GPTAPI 实例作为 chatbot。"""
        token = os.getenv("token")
        if not token:
            st.error("未检测到环境变量 `token`,请设置环境变量,例如 `export token='your_token_here'` 后重新运行 X﹏X")
            st.stop()  # 停止运行应用
            
        # 创建完整的 meta_prompt,保留原始结构并动态插入侧边栏配置
        meta_prompt = [
            {"role": "system", "content": self.meta_prompt, "api_role": "system"},
            {"role": "user", "content": "", "api_role": "user"},
            {"role": "assistant", "content": self.plugin_prompt, "api_role": "assistant"},
            {"role": "environment", "content": "", "api_role": "environment"}
        ]

        api_model = GPTAPI(
            model_type=model_name,
            api_base=api_base,
            key=token,  # 从环境变量中获取授权令牌
            meta_template=meta_prompt,
            max_new_tokens=512,
            temperature=0.8,
            top_p=0.9
        )
        return api_model

    def render_user(self, prompt: str):
        """渲染用户输入内容。"""
        with st.chat_message('user'):
            st.markdown(prompt)

    def render_assistant(self, agent_return):
        """渲染助手响应内容。"""
        with st.chat_message('assistant'):
            content = getattr(agent_return, "content", str(agent_return))
            st.markdown(content if isinstance(content, str) else str(content))


def main():
    """主函数,运行 Streamlit 应用。"""
    if 'ui' not in st.session_state:
        session_state = SessionState()
        session_state.init_state()
        st.session_state['ui'] = StreamlitUI(session_state)
    else:
        st.set_page_config(
            layout='wide',
            page_title='lagent-web',
            page_icon='./docs/imgs/lagent_icon.png'
        )
        st.header(':robot_face: :blue[Lagent] Web Demo ', divider='rainbow')

    # 设置侧边栏并获取模型和插件信息
    model_name, api_base, plugin_action = st.session_state['ui'].setup_sidebar()
    plugins = [dict(type=f"lagent.actions.{plugin.__class__.__name__}") for plugin in plugin_action]

    if (
        'chatbot' not in st.session_state or
        model_name != st.session_state['chatbot'].model_type or
        'last_plugin_action' not in st.session_state or
        plugin_action != st.session_state['last_plugin_action'] or
        api_base != st.session_state['api_base']    
    ):
        # 更新 Chatbot
        st.session_state['chatbot'] = st.session_state['ui'].initialize_chatbot(model_name, api_base, plugin_action)
        st.session_state['last_plugin_action'] = plugin_action  # 更新插件状态
        st.session_state['api_base'] = api_base  # 更新 API Base 地址

        # 初始化 AgentForInternLM
        st.session_state['agent'] = AgentForInternLM(
            llm=st.session_state['chatbot'],
            plugins=plugins,
            output_format=dict(
                type=PluginParser,
                template=PLUGIN_CN,
                prompt=get_plugin_prompt(plugin_action)
            )
        )
        # 清空对话历史
        st.session_state['session_history'] = []

    if 'agent' not in st.session_state:
        st.session_state['agent'] = None

    agent = st.session_state['agent']
    for prompt, agent_return in zip(st.session_state['user'], st.session_state['assistant']):
        st.session_state['ui'].render_user(prompt)
        st.session_state['ui'].render_assistant(agent_return)

    # 处理用户输入
    if user_input := st.chat_input(''):
        st.session_state['ui'].render_user(user_input)

        # 调用模型时确保侧边栏的系统提示词和插件提示词生效
        res = agent(user_input, session_id=0)
        st.session_state['ui'].render_assistant(res)

        # 更新会话状态
        st.session_state['user'].append(user_input)
        st.session_state['assistant'].append(copy.deepcopy(res))

    st.session_state['last_status'] = None


if __name__ == '__main__':
    main()

在终端中记得先将获取的API密钥写入环境变量,然后再输入启动命令:

bash 复制代码
export token='your_token_here'
streamlit run agent_api_web_demo.py
export token='your_token_here'
streamlit run agent_api_web_demo.py

在等待server启动成功后,我们在 本地 的 PowerShell 中输入如下指令来进行端口映射:

bash 复制代码
ssh -CNg -L 8501:127.0.0.1:8501 root@ssh.intern-ai.org.cn -p <你的 SSH 端口号>

接下来,在本地浏览器中打开 http://localhost:8501/:

如果正确输入密钥,可以看到页面如下。

页面的侧边栏有三个内容,分别是模型名称、API Base地址和插件选择,其中如果采用浦语的API,模型名称可以选择internlm2.5-latest,默认指向最新发布的 InternLM2.5 系列模型,当前指向internlm2.5-20b-0719,窗口长度是32K,最大输出4096Tokens。

备注: 如果采用硅基流动API,模型名称需要更改为:internlm/internlm2_5-7b-chat 或者 internlm/internlm2_5-20b-chat。

将ArxivSearch插件选择上,再次输入指令"帮我搜索一下最新版本的MindSearch论文",可以看到,通过调用外部工具,大模型成功理解了我们的任务,得到了我们需要的文献:

3 制作一个属于自己的Agent

在完成了上面的内容后,可能就会同学好奇了,那么我应该如何基于Lagent框架实现一个自己的工具,赋予LLM额外的能力? 本节将会以实时天气查询为例子,通过调用和风天气API,介绍如何自定义一个自己的Agent。

Lagent 框架的工具部分文档可以在此处查看:Lagent 工具文档。

使用 Lagent 自定义工具主要分为以下3步:

(1)继承 BaseAction 类

(2)实现简单工具的 run 方法;或者实现工具包内每个子工具的功能

(3)简单工具的 run 方法可选被 tool_api 装饰;工具包内每个子工具的功能都需要被 tool_api 装饰

首先,为了使用和风天气的 API 服务,你需要获取一个 API Key。请按以下步骤操作:

(1)访问 和风天气 API 文档(需要注册账号)。

(2)点击页面右上角的"控制台"。

(3)在控制台中,点击左侧的"项目管理",然后点击右上角"创建项目"。

(4)输入项目名称(可以使用"Lagent"),选择免费订阅,并在凭据设置中创建新的凭据。

(5)创建后,回到"项目管理"页面,找到你的 API Key 并复制保存。

接着,我们需要在laegnt/actions文件夹下面创建一个天气查询的工具程序。

conda activate lagent

cd /root/agent_camp4/lagent/lagent/actions

touch weather_query.py

将下面的代码复制进去,注意要将刚刚申请的API Key在终端中输入进去:

export weather_token='your_token_here'

import os

import requests

from lagent.actions.base_action import BaseAction, tool_api

from lagent.schema import ActionReturn, ActionStatusCode

class WeatherQuery(BaseAction):

def init (self):

super().init ()

self.api_key = os.getenv("weather_token")

print(self.api_key)

if not self.api_key:

raise EnvironmentError("未找到环境变量 'token'。请设置你的和风天气 API Key 到 'weather_token' 环境变量中,比如export weather_token='xxx' ")

复制代码
@tool_api
def run(self, location: str) -> dict:
    """
    查询实时天气信息。

    Args:
        location (str): 要查询的地点名称、LocationID 或经纬度坐标(如 "101010100" 或 "116.41,39.92")。

    Returns:
        dict: 包含天气信息的字典
            * location: 地点名称
            * weather: 天气状况
            * temperature: 当前温度
            * wind_direction: 风向
            * wind_speed: 风速(公里/小时)
            * humidity: 相对湿度(%)
            * report_time: 数据报告时间
    """
    try:
        # 如果 location 不是坐标格式(例如 "116.41,39.92"),则调用 GeoAPI 获取 LocationID
        if not ("," in location and location.replace(",", "").replace(".", "").isdigit()):
            # 使用 GeoAPI 获取 LocationID
            geo_url = f"https://geoapi.qweather.com/v2/city/lookup?location={location}&key={self.api_key}"
            geo_response = requests.get(geo_url)
            geo_data = geo_response.json()

            if geo_data.get("code") != "200" or not geo_data.get("location"):
                raise Exception(f"GeoAPI 返回错误码:{geo_data.get('code')} 或未找到位置")

            location = geo_data["location"][0]["id"]

        # 构建天气查询的 API 请求 URL
        weather_url = f"https://devapi.qweather.com/v7/weather/now?location={location}&key={self.api_key}"
        response = requests.get(weather_url)
        data = response.json()

        # 检查 API 响应码
        if data.get("code") != "200":
            raise Exception(f"Weather API 返回错误码:{data.get('code')}")

        # 解析和组织天气信息
        weather_info = {
            "location": location,
            "weather": data["now"]["text"],
            "temperature": data["now"]["temp"] + "°C", 
            "wind_direction": data["now"]["windDir"],
            "wind_speed": data["now"]["windSpeed"] + " km/h",  
            "humidity": data["now"]["humidity"] + "%",
            "report_time": data["updateTime"]
        }

        return {"result": weather_info}

    except Exception as exc:
        return ActionReturn(
            errmsg=f"WeatherQuery 异常:{exc}",
            state=ActionStatusCode.HTTP_ERROR
        )

其中,WeatherQuery 类继承自 BaseAction,这是 Lagent 的基础工具类,提供了工具的框架逻辑。tool_api 是一个装饰器,用于标记工具中具体执行逻辑的函数,使得 Lagent 智能体能够调用该方法执行任务。run 方法是工具的主要逻辑入口,通常会根据输入参数完成一项任务并返回结果。

在具体函数实现上,利用GeoAPI 获取 LocationID,当用户输入的 location 不是经纬度坐标格式(如 116.41,39.92),则使用和风天气的 GeoAPI 将位置名转换为 LocationID,并通过 Weather API 获取目标位置的实时天气数据。最后,解析返回的 JSON 数据,并格式化为结构化字典:

在/root/agent_camp4/lagent/lagent/actions/init .py中加入下面的代码,用以初始化WeatherQuery方法:

from .weather_query import WeatherQuery
all = [

'BaseAction', 'ActionExecutor', 'AsyncActionExecutor', 'InvalidAction',

'FinishAction', 'NoAction', 'BINGMap', 'AsyncBINGMap', 'ArxivSearch',

'AsyncArxivSearch', 'GoogleSearch', 'AsyncGoogleSearch', 'GoogleScholar',

'AsyncGoogleScholar', 'IPythonInterpreter', 'AsyncIPythonInterpreter',

'IPythonInteractive', 'AsyncIPythonInteractive',

'IPythonInteractiveManager', 'PythonInterpreter', 'AsyncPythonInterpreter',

'PPT', 'AsyncPPT', 'WebBrowser', 'AsyncWebBrowser', 'BaseParser',

'JsonParser', 'TupleParser', 'tool_api', 'WeatherQuery' # 这里

]

接下来,我们将修改 Web Demo 脚本来集成自定义的 WeatherQuery 插件。

打开agent_api_web_demo.py, 修改内容如下,目的是将该工具注册进大模型的插件列表中,使得其可以知道。

  • from lagent.actions import ArxivSearch
  • from lagent.actions import ArxivSearch, WeatherQuery

初始化插件列表

复制代码
     action_list = [
复制代码
         ArxivSearch(),
复制代码
    ]
复制代码
     action_list = [
复制代码
         ArxivSearch(),
复制代码
         WeatherQuery(),
复制代码
    ]

再次启动Web程序,streamlit run agent_api_web_demo.py。

可以看到左侧的插件栏多了天气查询插件,我们首先输入命令"帮我查询一下上海现在的天气",可以看到模型无法知道现在的实时天气情况。

将2个插件同时勾选上,用以说明模型具备识别调用不同工具的能力,什么任务对应什么工具来解决。

如果我们再次询问,让其搜索文献,可以看到,模型具备了根据任务情况调用不同工具的能力。

相关推荐
GIOTTO情15 分钟前
媒介宣发的技术革命:Infoseek如何用AI重构企业传播全链路
大数据·人工智能·重构
CoderJia程序员甲17 分钟前
GitHub 热榜项目 - 日榜(2025-10-14)
ai·开源·大模型·github·ai教程
阿里云大数据AI技术24 分钟前
云栖实录 | 从多模态数据到 Physical AI,PAI 助力客户快速启动 Physical AI 实践
人工智能
小关会打代码31 分钟前
计算机视觉进阶教学之颜色识别
人工智能·计算机视觉
IT小哥哥呀37 分钟前
基于深度学习的数字图像分类实验与分析
人工智能·深度学习·分类
机器之心1 小时前
VAE时代终结?谢赛宁团队「RAE」登场,表征自编码器或成DiT训练新基石
人工智能·openai
机器之心1 小时前
Sutton判定「LLM是死胡同」后,新访谈揭示AI困境
人工智能·openai
大模型真好玩1 小时前
低代码Agent开发框架使用指南(四)—Coze大模型和插件参数配置最佳实践
人工智能·agent·coze
jerryinwuhan1 小时前
基于大语言模型(LLM)的城市时间、空间与情感交织分析:面向智能城市的情感动态预测与空间优化
人工智能·语言模型·自然语言处理
落雪财神意1 小时前
股指10月想法
大数据·人工智能·金融·区块链·期股