动手做一个最小AgentTinyAgent

干货

作者:宋志学,成员

前 言

大家好,我是不要葱姜蒜。 在 ChatGPT 横空出世,夺走 Bert 的桂冠之后,大模型愈发地火热,国内各种模型层出不穷,史称“百模大战”。大模型的能力是毋庸置疑的,但大模型在一些实时的问题上,或是某些专有领域的问题上,可能会显得有些力不从心。因此,我们需要一些工具来为大模型赋能,给大模型一个抓手,让大模型和现实世界发生的事情对齐颗粒度,这样我们就获得了一个更好用的大模型。

这里基于 React 的方式,制作了一个最小的 Agent 结构(其实更多的是调用工具),暑假的时候会尝试将 React 结构修改为 SOP 结构。

一步一步手写 Agent ,可能让我对 Agent 的构成和运作更加地了解。以下是 React 论文中一些小例子。

参考论文: https://arxiv.org/abs/2210.03629

动手做一个最小AgentTinyAgent-2

实现细节

Step 1: 构造大模型

我们需要一个大模型,这里我们使用 InternLM2 作为我们的大模型。 InternLM2 是一个基于 Decoder-Only 的对话大模型,我们可以使用 transformers 库来加载 InternLM2 。

首先,还是先创建一个 BaseModel 类,这个类是一个抽象类,我们可以在这个类中定义一些基本的方法,比如 chat 方法和 load_model 方法。方便以后扩展使用其他模型。

class BaseModel:    def __init__(self, path: str = '') -> None:        self.path = path    def chat(self, prompt: str, history: List[dict]):        pass    def load_model(self):        pass

接着,我们创建一个 InternLM2 类,这个类继承自 BaseModel 类,我们在这个类中实现 chat 方法和 load_model 方法。就和正常加载 InternLM2 模型一样,来做一个简单的加载和返回即可。

class InternLM2Chat(BaseModel):    def __init__(self, path: str = '') -> None:        super().__init__(path)        self.load_model()    def load_model(self):        print('================ Loading model ================')        self.tokenizer = AutoTokenizer.from_pretrained(self.path, trust_remote_code=True)        self.model = AutoModelForCausalLM.from_pretrained(self.path, torch_dtype=torch.float16, trust_remote_code=True).cuda().eval()        print('================ Model loaded ================')    def chat(self, prompt: str, history: List[dict], meta_instruction:str ='') -> str:        response, history = self.model.chat(self.tokenizer, prompt, history, temperature=0.1, meta_instruction=meta_instruction)        return response, history

Step 2: 构造工具

我们在 tools.py 文件中,构造一些工具,比如 Google搜索 。我们在这个文件中,构造一个 Tools 类,这个类中包含了一些工具的描述信息和具体实现。我们可以在这个类中,添加一些工具的描述信息和具体实现。

首先要在 tools 中添加工具的描述信息

然后在 tools 中添加工具的具体实现

使用Google搜索功能的话需要去 serper 官网申请一下 token : https://serper.dev/dashboard

class Tools:    def __init__(self) -> None:        self.toolConfig = self._tools()        def _tools(self):        tools = [            {                'name_for_human': '谷歌搜索',                'name_for_model': 'google_search',                'description_for_model': '谷歌搜索是一个通用搜索引擎,可用于访问互联网、查询百科知识、了解时事新闻等。',                'parameters': [                    {                        'name': 'search_query',                        'description': '搜索关键词或短语',                        'required': True,                        'schema': {'type': 'string'},                    }                ],            }        ]        return tools    def google_search(self, search_query: str):        pass

Step 3: 构造Agent

我们在 Agent 类中,构造一个 Agent ,这个 Agent 是一个 React 的 Agent ,我们在这个 Agent 中,实现了 chat 方法,这个方法是一个对话方法,我们在这个方法中,调用 InternLM2 模型,然后根据 React 的 Agent 的逻辑,来调用 Tools 中的工具。

首先我们要构造 system_prompt , 这个是系统的提示,我们可以在这个提示中,添加一些系统的提示信息,比如 ReAct 形式的 prompt 。

def build_system_input(self):    tool_descs, tool_names = [], []    for tool in self.tool.toolConfig:        tool_descs.append(TOOL_DESC.format(**tool))        tool_names.append(tool['name_for_model'])    tool_descs = 'nn'.join(tool_descs)    tool_names = ','.join(tool_names)    sys_prompt = REACT_PROMPT.format(tool_descs=tool_descs, tool_names=tool_names)    return sys_prompt

OK, 如果顺利的话,运行出来的示例应该是这样的:

Answer the following questions as best you can. You have access to the following tools:google_search: Call this tool to interact with the 谷歌搜索 API. What is the 谷歌搜索 API useful for? 谷歌搜索是一个通用搜索引擎,可用于访问互联网、查询百科知识、了解时事新闻等。Parameters: [{'name': 'search_query', 'description': '搜索关键词或短语', 'required': True, 'schema': {'type': 'string'}}] Format the arguments as a JSON object.Use the following format:Question: the input question you must answerThought: you should always think about what to doAction: the action to take, should be one of [google_search]Action Input: the input to the actionObservation: the result of the action... (this Thought/Action/Action Input/Observation can be repeated zero or more times)Thought: I now know the final answerFinal Answer: the final answer to the original input questionBegin!

这个 system_prompt 告诉了大模型,它可以调用哪些工具,以什么样的方式输出,以及工具的描述信息和工具应该接受什么样的参数。

目前只是实现了一个简单的 Google搜索 工具,后续会添加更多的关于地理信息系统分析的工具,没错,我是一个地理信息系统的学生。

关于Agent的具体结构可以在Agent.py中查看。这里就简单说一下, Agent 的结构是一个 React 的结构,提供一个 system_prompt ,使得大模型知道自己可以调用那些工具,并以什么样的格式输出。

每次用户的提问,如果需要调用工具的话,都会进行两次的大模型调用,第一次解析用户的提问,选择调用的工具和参数,第二次将工具返回的结果与用户的提问整合。这样就可以实现一个 React 的结构。

下面为 Agent 代码的简易实现,每个函数的具体实现可以在 Agent.py 中查看。

class Agent:    def __init__(self, path: str = '') -> None:        pass    def build_system_input(self):        # 构造上文中所说的系统提示词        pass        def parse_latest_plugin_call(self, text):        # 解析第一次大模型返回选择的工具和工具参数        pass        def call_plugin(self, plugin_name, plugin_args):        # 调用选择的工具        pass    def text_completion(self, text, history=[]):        # 整合两次调用        pass

Step 4: 运行Agent

在这个案例中,使用了 InternLM2-chat-7B 模型, 如果你想要 Agent 运行地更加稳定,可以使用它的 big cup 版本 InternLM2-20b-chat ,这样可以提高 Agent 的稳定性。

from Agent import Agentagent = Agent('/root/share/model_repos/internlm2-chat-20b')response, _ = agent.text_completion(text='你好', history=[])print(response)# Thought: 你好,请问有什么我可以帮助你的吗?# Action: google_search# Action Input: {'search_query': '你好'}# Observation:Many translated example sentences containing "你好" – English-Chinese dictionary and search engine for English translations.# Final Answer: 你好,请问有什么我可以帮助你的吗? response, _ = agent.text_completion(text='周杰伦是哪一年出生的?', history=_)print(response)# Final Answer: 周杰伦的出生年份是1979年。 response, _ = agent.text_completion(text='周杰伦是谁?', history=_)print(response)# Thought: 根据我的搜索结果,周杰伦是一位台湾的创作男歌手、钢琴家和词曲作家。他的首张专辑《杰倫》于2000年推出,他的音乐遍及亚太区和西方国家。# Final Answer: 周杰伦是一位台湾创作男歌手、钢琴家、词曲作家和唱片制作人。他于2000年推出了首张专辑《杰伦》,他的音乐遍布亚太地区和西方国家。他的音乐风格独特,融合了流行、摇滚、嘻哈、电子等多种元素,深受全球粉丝喜爱。他的代表作品包括《稻香》、《青花瓷》、《听妈妈的话》等。 response, _ = agent.text_completion(text='他的第一张专辑是什么?', history=_)print(response)# Final Answer: 周杰伦的第一张专辑是《Jay》。 

https://github.com/KMnO4-zx/TinyAgent

记得给仓库点个小小的 star 哦~

论文参考

ReAct: Synergizing Reasoning and Acting in Language Mod ‍ els

THE END
分享
二维码
< <上一篇
下一篇>>