译者 | 朱先忠
审校 | 重楼
简介
当今,LLM应用非常出色——它们可以记住大量信息、回答一般知识问题、编写代码、生成故事,甚至还可以纠正语法错误,等等。然而,它们并非没有局限性。它们会产生幻觉,知识断点可能从几个月到几年不等,并且仅限于生成文本,无法与现实世界互动。显然,这限制了它们在需要实时数据、来源引用或文本生成以外功能的任务中的实用性。这正是代理和工具试图解决的主要问题:它们通过为LLM增加附加功能来弥补这一差距。这些改进使LLM能够访问最新的信息、与API交互、搜索,甚至能够影响例如调节智能家居的温度参数等的物理世界。
在本文中,我们将构建一个简单的LLM代理,该代理配备了四个工具,可用于回答用户的问题。具体地讲,此代理将实现以下功能:
- 可以使用最新的可验证信息回答一般知识问题。
- 可以使用四种类型的工具:DuckDuckGo搜索、获取网页内容、维基百科搜索、获取维基百科页面内容。
- 允许LLM推理用户的输入、消息历史记录、上一个工具调用及其结果,以决定是否接下来使用某个工具——如果使用的话,要使用哪个参数。
- 代理允许在每个时间步骤中同时使用多个工具。
基于Gemini 2.0和LangGraph实现自主多工具AI代理-AI.x社区
示例程序代理组件整体架构
实现过程中,我们将使用LangGraph作为代理实现,并使用Gemini 2.0作为LLM。但是,你将能够以最少的代码修改把它切换到大多数你的其他类型的LLM应用程序。本示例项目的完整代码位于地址:https://github.com/CVxTz/document_ai_agents/blob/master/document_ai_agents/document_multi_tool_agent.py
工具
首先,让我们构建其中一个工具,例如Web搜索工具:
在这里,我们的“工具”是一个简单的Python函数,它使用duckduckgo_search库来获取与搜索查询相关的搜索结果。LLM将根据用户的消息决定选择什么查询。此函数的输出如下所示:
接下来,让我们继续看一下get_wikipedia_page函数:
此函数获取页面的完整html,并将其删除的html标签返回给调用者。输出如下所示:
总的来说,我们定义了四个这样的函数:
- search_wikipedia(search_query: str)
- get_wikipedia_page(page_title: str, max_text_size: int = 16_000):
- search_duck_duck_go(search_query: str)
- get_page_content(page_title: str, page_url: str)
所有这些函数都传递给Gemini客户端,如下所示:
客户端将从函数定义中推断出调用参数及其类型。它还将传递此生成的模型中的函数的文档字符串,因此我们需要向LLM解释该工具如何在此文档字符串中工作以获得最佳结果。
LLM
我们将使用Gemini客户端与LLM(特别是Gemini 2.0)进行交互。首先,你需要设置一个API密钥——可以从Google AI Studio获取。使用此客户端,我们将提示LLM生成响应或创建函数调用(或两者兼而有之)。然后,这些函数调用将应用于我们上面定义的工具。
当使用提示调用时,客户端可能会使用常规文本或包含函数调用的内容进行响应,例如:
这种类型的响应包含工具的名称及其参数。这允许LLM选择要访问哪些外部资源以及如何访问。
代理
以下是我们代理的完整实现(仅70行代码):
首先,在上述代码中,我们定义LLM客户端,其中包含一组工具和一个简单的提示。
其次,我们定义第一个节点“call_llm”,使用消息历史记录调用LLM API。
第三,我们定义一个工具调用节点,执行LLM请求的所有函数调用。
最后,我们定义代理的状态转换图:
基于Gemini 2.0和LangGraph实现自主多工具AI代理-AI.x社区
示例项目所定义代理的状态图
代理将继续运行,直到LLM的新响应不包含任何工具调用。这会在状态图中创建一个循环,并允许代理的执行优化搜索查询和工具使用,直到找到用户询问的内容。
示例运行
现在,让我们对这个代理进行测试运行。我们将从一个简单的查询开始。
英文提问:
译文:
河豚有毒吗?如果是,请解释原因并列出一些其他有毒(无毒)的鱼。不要只引用维基百科。
以下是与代理交流的过程(我为了简洁而删减了大部分文本):
User(用户):
英文提问:
译文:
河豚有毒吗?如果是,请解释原因并列出一些其他有毒(无毒)的鱼。不要只引用维基百科。
模型(Model):
工具(Tool):
部分译文:
page_title:有毒的河豚如何成为美味佳肴(以及致命的事故……
page_summary:河豚,或称河豚,是一种流行且昂贵的……
page_url:https://www.tastingtable.com/1616127/poisonous-pufferfish-explained/
page_title:触摸河豚有毒吗?以下是你需要了解的内容
page_summary:河豚含有一种致命的毒素,称为河豚毒素,如果摄入或接触会导致瘫痪和死亡。……
page_url: https://safeharborfishing.com/are-puffer-fish-poisonous-to-touch-heres-what-you-need-to-know/
….
模型(Model)响应:
译文:
是的,河豚有毒。它们含有一种致命的神经毒素,称为河豚毒素,如果摄入或接触会导致瘫痪和死亡。这种毒素不会被烹饪或冷冻破坏。河豚的食物包括无脊椎动物和藻类,如贻贝和贝类,这导致了河豚毒素的存在。
以下是其他一些有毒(无毒)的鱼:
北方观星鱼:这些鱼的眼睛后面有毒刺。
蟾鱼:某些种类的蟾鱼有有毒的刺。
兔鱼:这些鱼的刺含有毒素。
…
这次交流涉及一轮工具调用,包括使用DuckDuckGo进行网络搜索,这足以让LLM获得所需的所有信息。
现在,让我们尝试一个更复杂的场景,其中代理需要进行多次工具调用:
英文提问:
译文:
南方公园第几集和第几季有时间旅行移民?那一集的导演是谁?他出生在哪里和什么时候?给我有关它的维基百科页面链接。
如果没有工具支持,模型会回答:
英文提问:
译文:
你提到的南方公园剧集是**第18季,第7集,标题为“Grounded Vindaloop”**。它没有时间旅行移民,而是关注……
显然,这个答案是错误的。
下面,借助于工具支持,有如下结果:
用户(User):
译文:
南方公园第几季出现了穿越时空的移民?那一集的导演是谁?他何时何地出生?给我它的维基百科页面链接。
模型(Model)响应:
工具(Tool):
模型(Model)响应:
译文:
你提到的《南方公园》剧集名为“Goobacks”。它是第八季的第七集。
模型(Model)响应:
工具(Tool):
部分译文:
内容:《南方公园》第八季第七集。《Goobacks》是动画电视连续剧《南方公园》第八季第七集,也是该系列的第118集。按制作顺序,它是该季的第六集。它最初于2004年4月28日在美国喜剧中心播出。该集由Trey Parker执导。
模型(Model)响应:
译文:
“Goobacks”一集的导演是特雷·帕克 (Trey Parker)。
模型(Model)响应:
工具(Tool):
模型(Model)响应:
译文:
崔·帕克出生于1969年10月19日。
模型(Model)响应:
工具(Tool):
部分译文:
内容:兰道夫·塞文·“特雷”·帕克三世(1969年10月19日出生)是美国演员、动画师、作家、制片人、导演和音乐家。他出生于美国科罗拉多州康尼弗。
模型(Model)响应:
译文:
特雷·帕克于1969年10月19日出生于美国科罗拉多州康尼弗。
这是他的维基百科页面链接:https://en.wikipedia.org/wiki/Trey_Parker
该代理能够迭代地进行工具调用(总共四次调用),以回答我们这个复杂的问题。但是,其中明确的答案不在第一个结果中,而是需要多个推理步骤和搜索查询。
结论
我们在本文中开发了一个自主型的LLM代理,它能够搜索网络、浏览维基百科,并使用Gemini2.0作为其核心LLM推理检索到的信息。该代理不仅能够获取相关数据,还实现了根据初始结果优化其搜索查询,以找到用户请求的确切信息。
上述实现为创建你自己的自主代理奠定了良好的基础。通过将自定义工具定义为Python函数并将其集成到代理中,你可以轻松地根据你的特定需求进行调整。另外,开发本示例代理过程中得益于LangGraph框架,其实现的简单性使得整个定制过程变得更为简单。
现在,你可以开始构建适用于你自己的使用场景和应用程序的强大、合适的代理应用了。
本文完整源代码下载地址:https://github.com/CVxTz/document_ai_agents/blob/master/document_ai_agents/document_multi_tool_agent.py
译者介绍
朱先忠,51CTO社区编辑,51CTO专家博客、讲师,潍坊一所高校计算机教师,自由编程界老兵一枚。
原文标题:Building Autonomous Multi-Tool Agents with Gemini 2.0 and LangGraph,作者:Youness Mansar