用纯Python打造ReAct AI代理:增强大型语言模型的交互能力
本文详细介绍了一种在“纯”Python中实现简单ReAct代理的方法,不依赖任何现有的框架如LangChain、AutoGen或CrewAI。作者通过这一过程深入了解了ReAct代理的工作原理,并且增强了自己构建更复杂系统的信心。 什么是ReAct代理? ReAct(Reason and Act)框架由一篇名为“ReAct: Synergizing Reasoning and Acting in Language Models”的论文提出。该框架的核心思想是将语言模型(LM)的推理和行动结合起来。传统的聊天机器人仅能响应用户输入,而ReAct代理则可以在响应之前采取多种行动,例如查询Wikipedia、搜索ArXiv上的研究论文或执行简单的计算。这种结合显著提高了语言模型的智能水平和响应质量。 问题陈述 作者希望能够构建一个不仅仅能够响应用户输入的LLM,还能根据不同查询浏览Wikipedia、搜索ArXiv上的免费研究论文或执行简单计算。为此,他选择OpenAI的GPT-4作为基础语言模型,并实现了三种主要的行动功能:Wikipedia查询、ArXiv搜索和计算。 实现过程 环境设置 首先,创建一个新的Python环境并激活: bash conda create -n react-agent python==3.12 conda activate react-agent 然后,导入必要的包: python from openai import OpenAI import re import httpx import requests import xml.etree.ElementTree as ET import json 加载API密钥: python from dotenv import load_dotenv load_dotenv() # 加载OPENAI_API_KEY 检查语言模型是否正常工作: python chat_completion = client.chat.completions.create( model="gpt-4", messages=[{"role": "user", "content": "Hello there!"}] ) print(chat_completion.choices[0].message.content) 输出应该是“Hello! How can I assist you today?”,表明设置成功。 聊天机器人 构建一个基本的聊天机器人类(ChatBot),它接收用户的输入消息,将这些消息与系统提示一起发送给LLM,并返回LLM的响应: ```python class ChatBot: def init(self, system=""): self.system = system self.messages = [] if self.system: self.messages.append({"role": "system", "content": system}) def __call__(self, message): self.messages.append({"role": "user", "content": message}) result = self.run_llm() self.messages.append({"role": "assistant", "content": result}) return result def run_llm(self): completion = client.chat.completions.create( model="gpt-4", temperature=0, messages=self.messages ) return completion.choices[0].message.content ``` ReAct代理 将聊天机器人封装在ReAct代理类(Agent)中。ReAct代理在一个循环中运行,根据用户的查询进行推理和行动,并获取观察结果: ```python class Agent: def init(self, system_prompt="", max_turns=1, known_actions=None): self.max_turns = max_turns self.bot = ChatBot(system_prompt) self.known_actions = known_actions def run(self, question): i = 0 next_prompt = question while i < self.max_turns: i += 1 result = self.bot(next_prompt) print(result) actions = [action_re.match(a) for a in result.split('\n') if action_re.match(a)] if actions: action, action_input = actions[0].groups() if action not in self.known_actions: raise Exception("Unknown action: {}: {}".format(action, action_input)) print(" -- running {} {}".format(action, action_input)) observation = self.known_actions[action](action_input) print("Observation:", observation) next_prompt = "Observation: {}".format(observation) else: return ``` 行动函数 定义三种行动函数: Wikipedia查询:使用httpx库查询Wikipedia,返回相关条目的简短摘要。 ArXiv搜索:调用ArXiv的API,返回最相关的研究论文的标题和摘要。 计算:使用Python的eval函数执行简单的数学计算。 ```python def wikipedia(q): response = httpx.get("https://en.wikipedia.org/w/api.php", params={ "action": "query", "list": "search", "srsearch": q, "format": "json" }) return response.json()["query"]["search"][0]["snippet"] def arxiv_search(q): url = f'http://export.arxiv.org/api/query?search_query=all:{q}&start=0&max_results=1' response = requests.get(url) root = ET.fromstring(response.content) for entry in root.findall(f"{ARXIV_NAMESPACE}entry"): title = entry.find(f"{ARXIV_NAMESPACE}title").text.strip() summary = entry.find(f"{ARXIV_NAMESPACE}summary").text.strip() return json.dumps({"title": title, "summary": summary}) def calculate(what): return eval(what) ``` 提示 为ReAct代理编写详细的提示,说明如何在“思考、行动、暂停、观察”的循环中运行,并提供具体的行动示例: python prompt = """ You run in a loop of Thought, Action, PAUSE, Observation. At the end of the loop you output an Answer. Use Thought to describe your thoughts about the question you have been asked. Use Action to run one of the actions available to you - then return PAUSE. Observation will be the result of running those actions. Your available actions are: calculate: e.g. calculate: 4 * 7 / 3 Runs a calculation and returns the number - uses Python so be sure to use floating point syntax if necessary wikipedia: e.g. wikipedia: France Returns a summary from searching Wikipedia arxiv_search: e.g. arxiv_search: lightrag Returns a summary of research papers Example session: Question: What is the capital of France? Thought: I should look up France on Wikipedia Action: wikipedia: France PAUSE You will be called again with this: Observation: France is a country. The capital is Paris. You then output: Answer: The capital of France is Paris """ 测试代理 将所有行动函数收集到一个字典中,创建一个代理实例,并运行测试查询: python known_actions = { "wikipedia": wikipedia, "calculate": calculate, "arxiv_search": arxiv_search } agent = Agent(prompt, max_turns=3, known_actions=known_actions) agent.run("What is the capital of Indonesia?") agent.run("Explain the LightRAG paper") 第一个查询返回“印尼的首都是雅加达”。第二个查询返回LightRAG论文的详细摘要,展示了代理如何自动选择合适的行动来获取相关信息。 评价与背景 这一实现方法充分展示了ReAct框架的强大之处。通过结合推理和行动,代理能够更智能地应对复杂的用户查询,不仅提高了响应的质量,还增强了系统的灵活性。业内人士认为,ReAct框架是一个重要的技术进步,为未来更智能的对话系统和代理开发奠定了基础。作者在X和YouTube上分享AI新闻和研究更新,读者可以关注以了解更多相关信息。