[](https://lagent.readthedocs.io/en/latest/)
[](https://pypi.org/project/lagent)
[](https://github.com/InternLM/lagent/tree/main/LICENSE)
[](https://github.com/InternLM/lagent/issues)
[](https://github.com/InternLM/lagent/issues)




👋 加入我们的 𝕏 (Twitter)、Discord 和 微信
从源码安装:
git clone https://github.com/InternLM/lagent.git
cd lagent
pip install -e .
Lagent 的设计灵感来源于 PyTorch。我们期望通过类比神经网络层的方式,使工作流更加清晰直观,让用户只需以 Pythonic 的方式关注层的创建和层之间的消息传递。以下是一个简短的教程,帮助您快速上手构建多智能体应用。
智能体之间通过 AgentMessage 进行通信。
from typing import Dict, List
from lagent.agents import Agent
from lagent.schema import AgentMessage
from lagent.llms import VllmModel, INTERNLM2_META
llm = VllmModel(
path='Qwen/Qwen2-7B-Instruct',
meta_template=INTERNLM2_META,
tp=1,
top_k=1,
temperature=1.0,
stop_words=['<|im_end|>'],
max_new_tokens=1024,
)
system_prompt = '你的回答只能从“典”、“孝”、“急”三个字中选一个。'
agent = Agent(llm, system_prompt)
user_msg = AgentMessage(sender='user', content='今天天气情况')
bot_msg = agent(user_msg)
print(bot_msg)
content='急' sender='Agent' formatted=None extra_info=None type=None receiver=None stream_state=<AgentStatusCode.END: 0>
每次前向传播时,输入和输出的消息都会添加到 Agent 的记忆中。此操作在 __call__ 而非 forward 中完成。请参考以下伪代码:
def __call__(self, *message):
message = pre_hooks(message)
add_memory(message)
message = self.forward(*message)
add_memory(message)
message = post_hooks(message)
return message
通过两种方式查看记忆:
memory: List[AgentMessage] = agent.memory.get_memory()
print(memory)
print('-' * 120)
dumped_memory: Dict[str, List[dict]] = agent.state_dict()
print(dumped_memory['memory'])
[AgentMessage(content='今天天气情况', sender='user', formatted=None, extra_info=None, type=None, receiver=None, stream_state=<AgentStatusCode.END: 0>), AgentMessage(content='急', sender='Agent', formatted=None, extra_info=None, type=None, receiver=None, stream_state=<AgentStatusCode.END: 0>)]
------------------------------------------------------------------------------------------------------------------------
[{'content': '今天天气情况', 'sender': 'user', 'formatted': None, 'extra_info': None, 'type': None, 'receiver': None, 'stream_state': <AgentStatusCode.END: 0>}, {'content': '急', 'sender': 'Agent', 'formatted': None, 'extra_info': None, 'type': None, 'receiver': None, 'stream_state': <AgentStatusCode.END: 0>}]
清除当前会话的记忆(默认 session_id=0):
agent.reset()
DefaultAggregator 在底层被调用,用于组装 AgentMessage 并将其转换为 OpenAI 消息格式。
def forward(self, *message: AgentMessage, session_id=0, **kwargs) -> Union[AgentMessage, str]:
formatted_messages = self.aggregator.aggregate(
self.memory.get(session_id),
self.name,
self.output_format,
self.template,
)
llm_response = self.llm.chat(formatted_messages, **kwargs)
...
实现一个支持 few-shot 的简单聚合器:
from typing import List, Union
from lagent.memory import Memory
from lagent.prompts import StrParser
from lagent.agents.aggregator import DefaultAggregator
class FewshotAggregator(DefaultAggregator):
def __init__(self, few_shot: List[dict] = None):
self.few_shot = few_shot or []
def aggregate(self,
messages: Memory,
name: str,
parser: StrParser = None,
system_instruction: Union[str, dict, List[dict]] = None) -> List[dict]:
_message = []
if system_instruction:
_message.extend(
self.aggregate_system_intruction(system_instruction))
_message.extend(self.few_shot)
messages = messages.get_memory()
for message in messages:
if message.sender == name:
_message.append(
dict(role='assistant', content=str(message.content)))
else:
user_message = message.content
if len(_message) > 0 and _message[-1]['role'] == 'user':
_message[-1]['content'] += user_message
else:
_message.append(dict(role='user', content=user_message))
return _message
agent = Agent(
llm,
aggregator=FewshotAggregator(
[
{"role": "user", "content": "今天天气"},
{"role": "assistant", "content": "【晴】"},
]
)
)
user_msg = AgentMessage(sender='user', content='昨天天气')
bot_msg = agent(user_msg)
print(bot_msg)
content='【多云转晴,夜间有轻微降温】' sender='Agent' formatted=None extra_info=None type=None receiver=None stream_state=<AgentStatusCode.END: 0>
在 AgentMessage 中,formatted 字段用于存储由 output_format 从模型输出中解析的信息。
def forward(self, *message: AgentMessage, session_id=0, **kwargs) -> Union[AgentMessage, str]:
...
llm_response = self.llm.chat(formatted_messages, **kwargs)
if self.output_format:
formatted_messages = self.output_format.parse_response(llm_response)
return AgentMessage(
sender=self.name,
content=llm_response,
formatted=formatted_messages,
)
...
使用工具解析器如下:
from lagent.prompts.parsers import ToolParser
system_prompt = "逐步分析并编写Python代码解决以下问题。"
parser = ToolParser(tool_type='code interpreter', begin='```python\n', end='\n```\n')
llm.gen_params['stop_words'].append('\n```\n')
agent = Agent(llm, system_prompt, output_format=parser)
user_msg = AgentMessage(
sender='user',
content='Marie is thinking of a multiple of 63, while Jay is thinking of a '
'factor of 63. They happen to be thinking of the same number. There are '
'two possibilities for the number that each of them is thinking of, one '
'positive and one negative. Find the product of these two numbers.')
bot_msg = agent(user_msg)
print(bot_msg.model_dump_json(indent=4))
{
"content": "首先,我们需要找出63的所有正因数和负因数。63的正因数可以通过分解63的质因数来找出,即\\(63 = 3^2 \\times 7\\)。因此,63的正因数包括1, 3, 7, 9, 21, 和 63。对于负因数,我们只需将上述正因数乘以-1。\n\n接下来,我们需要找出与63的正因数相乘的结果为63的数,以及与63的负因数相乘的结果为63的数。这可以通过将63除以每个正因数和负因数来实现。\n\n最后,我们将找到的两个数相乘得到最终答案。\n\n下面是Python代码实现:\n\n```python\ndef find_numbers():\n # 正因数\n positive_factors = [1, 3, 7, 9, 21, 63]\n # 负因数\n negative_factors = [-1, -3, -7, -9, -21, -63]\n \n # 找到与正因数相乘的结果为63的数\n positive_numbers = [63 / factor for factor in positive_factors]\n # 找到与负因数相乘的结果为63的数\n negative_numbers = [-63 / factor for factor in negative_factors]\n \n # 计算两个数的乘积\n product = positive_numbers[0] * negative_numbers[0]\n \n return product\n\nresult = find_numbers()\nprint(result)",
"sender": "Agent",
"formatted": {
"tool_type": "code interpreter",
"thought": "首先,我们需要找出63的所有正因数和负因数。63的正因数可以通过分解63的质因数来找出,即\\(63 = 3^2 \\times 7\\)。因此,63的正因数包括1, 3, 7, 9, 21, 和 63。对于负因数,我们只需将上述正因数乘以-1。\n\n接下来,我们需要找出与63的正因数相乘的结果为63的数,以及与63的负因数相乘的结果为63的数。这可以通过将63除以每个正因数和负因数来实现。\n\n最后,我们将找到的两个数相乘得到最终答案。\n\n下面是Python代码实现:\n\n",
"action": "def find_numbers():\n # 正因数\n positive_factors = [1, 3, 7, 9, 21, 63]\n # 负因数\n negative_factors = [-1, -3, -7, -9, -21, -63]\n \n # 找到与正因数相乘的结果为63的数\n positive_numbers = [63 / factor for factor in positive_factors]\n # 找到与负因数相乘的结果为63的数\n negative_numbers = [-63 / factor for factor in negative_factors]\n \n # 计算两个数的乘积\n product = positive_numbers[0] * negative_numbers[0]\n \n return product\n\nresult = find_numbers()\nprint(result)",
"status": 1
},
"extra_info": null,
"type": null,
"receiver": null,
"stream_state": 0
}
ActionExecutor 使用与 Agent 相同的数据结构进行通信,但要求输入的 AgentMessage 的 content 为一个包含以下内容的字典:
name: 工具名称,例如 'IPythonInterpreter'、'WebBrowser.search'。parameters: 工具 API 的关键字参数,例如 {'command': 'import math;math.sqrt(2)'}、{'query': ['recent progress in AI']}。您可以注册自定义的钩子进行消息转换。
from lagent.hooks import Hook
from lagent.schema import ActionReturn, ActionStatusCode, AgentMessage
from lagent.actions import ActionExecutor, IPythonInteractive
class CodeProcessor(Hook):
def before_action(self, executor, message, session_id):
message = message.copy(deep=True)
message.content = dict(
name='IPythonInteractive', parameters={'command': message.formatted['action']}
)
return message
def after_action(self, executor, message, session_id):
action_return = message.content
if isinstance(action_return, ActionReturn):
if action_return.state == ActionStatusCode.SUCCESS:
response = action_return.format_result()
else:
response = action_return.errmsg
else:
response = action_return
message.content = response
return message
executor = ActionExecutor(actions=[IPythonInteractive()], hooks=[CodeProcessor()])
bot_msg = AgentMessage(
sender='Agent',
content='首先,我们需要...',
formatted={
'tool_type': 'code interpreter',
'thought': '首先,我们需要...',
'action': 'def find_numbers():\n # 正因数\n positive_factors = [1, 3, 7, 9, 21, 63]\n # 负因数\n negative_factors = [-1, -3, -7, -9, -21, -63]\n \n # 找到与正因数相乘的结果为63的数\n positive_numbers = [63 / factor for factor in positive_factors]\n # 找到与负因数相乘的结果为63的数\n negative_numbers = [-63 / factor for factor in negative_factors]\n \n # 计算两个数的乘积\n product = positive_numbers[0] * negative_numbers[0]\n \n return product\n\nresult = find_numbers()\nprint(result)',
'status': 1
})
executor_msg = executor(bot_msg)
print(executor_msg)
content='3969.0' sender='ActionExecutor' formatted=None extra_info=None type=None receiver=None stream_state=<AgentStatusCode.END: 0>
为了方便起见,Lagent 提供了 InternLMActionProcessor,它与上述 ToolParser 格式化的消息兼容。
Lagent 采用双接口设计,几乎所有组件(LLM、动作、动作执行器等)都通过在其标识符前加上 "Async" 来提供对应的异步变体。建议使用同步智能体进行调试,使用异步智能体进行大规模推理,以充分利用空闲的 CPU 和 GPU 资源。
但是,请确保智能体内部的一致性,即异步智能体应配备异步 LLM 和驱动异步工具的异步动作执行器。
from lagent.llms import VllmModel, AsyncVllmModel, LMDeployPipeline, AsyncLMDeployPipeline
from lagent.actions import ActionExecutor, AsyncActionExecutor, WebBrowser, AsyncWebBrowser
from lagent.agents import Agent, AsyncAgent, AgentForInternLM, AsyncAgentForInternLM
forward 而不是 __call__。session_id 参数,该参数旨在实现并发中记忆、LLM 请求和工具调用(例如,维护多个独立的 IPython 环境)的隔离。通过编程解决数学问题的智能体:
from lagent.agents.aggregator import InternLMToolAggregator
class Coder(Agent):
def __init__(self, model_path, system_prompt, max_turn=3):
super().__init__()
llm = VllmModel(
path=model_path,
meta_template=INTERNLM2_META,
tp=1,
top_k=1,
temperature=1.0,
stop_words=['\n```\n', '<|im_end|>'],
max_new_tokens=1024,
)
self.agent = Agent(
llm,
system_prompt,
output_format=ToolParser(
tool_type='code interpreter', begin='```python\n', end='\n```\n'
),
# `InternLMToolAggregator` 适用于 `ToolParser`,用于聚合包含工具调用和执行结果的消息
aggregator=InternLMToolAggregator(),
)
self.executor = ActionExecutor([IPythonInteractive()], hooks=[CodeProcessor()])
self.max_turn = max_turn
def forward(self, message: AgentMessage, session_id=0) -> AgentMessage:
for _ in range(self.max_turn):
message = self.agent(message, session_id=session_id)
if message.formatted['tool_type'] is None:
return message
message = self.executor(message, session_id=session_id)
return message
coder = Coder('Qwen/Qwen2-7B-Instruct', '借助 Python 代码逐步解决问题')
query = AgentMessage(
sender='user',
content='求向量 $\\mathbf{a}$ 在向量 $\\mathbf{b} = \\begin{pmatrix} 1 \\\\ -3 \\end{pmatrix}$ 上的投影,已知 $\\mathbf{a} \\cdot \\mathbf{b} = 2.$'
)
answer = coder(query)
print(answer.content)
print('-' * 120)
for msg in coder.state_dict()['agent.memory']:
print('*' * 80)
print(f'{msg["sender"]}:\n\n{msg["content"]}')
通过自我改进来提高写作质量的异步博客智能体(原始 AutoGen 示例):
import asyncio
import os
from lagent.llms import AsyncGPTAPI
from lagent.agents import AsyncAgent
os.environ['OPENAI_API_KEY'] = 'YOUR_API_KEY'
class PrefixedMessageHook(Hook):
def __init__(self, prefix: str, senders: list = None):
self.prefix = prefix
self.senders = senders or []
def before_agent(self, agent, messages, session_id):
for message in messages:
if message.sender in self.senders:
message.content = self.prefix + message.content
class AsyncBlogger(AsyncAgent):
def __init__(self, model_path, writer_prompt, critic_prompt, critic_prefix='', max_turn=3):
super().__init__()
llm = AsyncGPTAPI(model_type=model_path, retry=5, max_new_tokens=2048)
self.writer = AsyncAgent(llm, writer_prompt, name='writer')
self.critic = AsyncAgent(
llm, critic_prompt, name='critic', hooks=[PrefixedMessageHook(critic_prefix, ['writer'])]
)
self.max_turn = max_turn
async def forward(self, message: AgentMessage, session_id=0) -> AgentMessage:
for _ in range(self.max_turn):
message = await self.writer(message, session_id=session_id)
message = await self.critic(message, session_id=session_id)
return await self.writer(message, session_id=session_id)
blogger = AsyncBlogger(
'gpt-4o-2024-05-13',
writer_prompt="你是一个写作助手,负责撰写引人入胜的博客文章。你试图为用户的需求生成尽可能好的博客文章。"
"如果用户提供了批评,请回复你之前尝试的修订版本",
critic_prompt="对写作内容提出批评和改进建议。提供详细的建议,包括对篇幅、深度、风格等方面的要求。",
critic_prefix='请对以下写作内容进行反思并提供批评。 \n\n',
)
user_prompt = (
"写一篇引人入胜的博客文章,关于{topic}的最新进展。"
"博客文章应适合普通读者,易于理解。"
"应超过3段,但不超过1000字。")
bot_msgs = asyncio.get_event_loop().run_until_complete(
asyncio.gather(
*[
blogger(AgentMessage(sender='user', content=user_prompt.format(topic=topic)), session_id=i)
for i, topic in enumerate(['人工智能', '生物技术', '新能源', '电子游戏', '流行音乐'])
]
)
)
print(bot_msgs[0].content)
print('-' * 120)
for msg in blogger.state_dict(session_id=0)['writer.memory']:
print('*' * 80)
print(f'{msg["sender"]}:\n\n{msg["content"]}')
print('-' * 120)
for msg in blogger.state_dict(session_id=0)['critic.memory']:
print('*' * 80)
print(f'{msg["sender"]}:\n\n{msg["content"]}')
一个执行信息检索、数据收集和图表绘制的多智能体工作流(原始 LangGraph 示例):
import json
from lagent.actions import IPythonInterpreter, WebBrowser, ActionExecutor
from lagent.agents.stream import get_plugin_prompt
from lagent.llms import GPTAPI
from lagent.hooks import InternLMActionProcessor
TOOL_TEMPLATE = (
"你是一个乐于助人的 AI 助手,与其他助手协作。使用提供的工具来推进问题的解决。"
"如果你无法完全回答,也没关系,其他拥有不同工具的助手会接续你的工作。尽力执行你所能完成的部分。"
"如果你或其他任何助手有了最终答案或可交付成果,请在回复前加上 {finish_pattern},以便团队知道停止。"
"你可以使用以下工具:\n{tool_description}\n请在使用工具时提供你的思考过程,然后按照以下格式给出调用语句:"
"\n{invocation_format}\\\\n**{system_prompt}**"
)
class DataVisualizer(Agent):
def __init__(self, model_path, research_prompt, chart_prompt, finish_pattern="Final Answer", max_turn=10):
super().__init__()
llm = GPTAPI(model_path, key='YOUR_OPENAI_API_KEY', retry=5, max_new_tokens=1024, stop_words=["```\n"])
interpreter, browser = IPythonInterpreter(), WebBrowser("BingSearch", api_key="YOUR_BING_API_KEY")
self.researcher = Agent(
llm,
TOOL_TEMPLATE.format(
finish_pattern=finish_pattern,
tool_description=get_plugin_prompt(browser),
invocation_format='```json\n{"name": {{工具名称}}, "parameters": {{关键字参数}}}\n```\n',
system_prompt=research_prompt,
),
output_format=ToolParser(
"browser",
begin="```json\n",
end="\n```\n",
validate=lambda x: json.loads(x.rstrip('`')),
),
aggregator=InternLMToolAggregator(),
name="researcher",
)
self.charter = Agent(
llm,
TOOL_TEMPLATE.format(
finish_pattern=finish_pattern,
tool_description=interpreter.name,
invocation_format='```python\n{{code}}\n```\n',
system_prompt=chart_prompt,
),
output_format=ToolParser(
"interpreter",
begin="```python\n",
end="\n```\n",
validate=lambda x: x.rstrip('`'),
),
aggregator=InternLMToolAggregator(),
name="charter",
)
self.executor = ActionExecutor([interpreter, browser], hooks=[InternLMActionProcessor()])
self.finish_pattern = finish_pattern
self.max_turn = max_turn
def forward(self, message, session_id=0):
for _ in range(self.max_turn):
message = self.researcher(message, session_id=session_id, stop_words=["```\n", "```python"]) # 覆盖 llm 的 stop words
while message.formatted["tool_type"]:
message = self.executor(message, session_id=session_id)
message = self.researcher(message, session_id=session_id, stop_words=["```\n", "```python"])
if self.finish_pattern in message.content:
return message
message = self.charter(message)
while message.formatted["tool_type"]:
message = self.executor(message, session_id=session_id)
message = self.charter(message, session_id=session_id)
if self.finish_pattern in message.content:
return message
return message
visualizer = DataVisualizer(
"gpt-4o-2024-05-13",
research_prompt="你应该为图表生成器提供准确的数据。",
chart_prompt="你展示的任何图表都将对用户可见。",
)
user_msg = AgentMessage(
sender='user',
content="获取过去5年中国GDP的数据,然后画一个折线图。完成代码后,结束任务。")
bot_msg = visualizer(user_msg)
print(bot_msg.content)
json.dump(visualizer.state_dict(), open('visualizer.json', 'w'), ensure_ascii=False, indent=4)
如果您的研究中使用了本项目,请考虑引用:
@misc{lagent2023,
title={{Lagent: InternLM} 一个轻量级的开源框架,允许用户高效构建基于大型语言模型(LLM)的智能体},
author={Lagent 开发者团队},
howpublished = {\url{https://github.com/InternLM/lagent}},
year={2023}
}
本项目采用 Apache 2.0 许可证。