照片由 Jaredd Craig 拍摄,来自 Unsplash。
大型语言模型已经展现了令人印象深刻的能力,并且每发布一代新模型,它们都在稳步改进。诸如聊天机器人和摘要生成等应用可以直接利用大型语言模型的语言能力,因为生成文本输出正是它们的自然应用场景。大型语言模型还展示了理解并解决复杂任务的出色能力,然而,只要它们的解决方案仍停留在“纸上”,即纯文本形式,就需要外部用户来执行该行动并反馈结果。代理系统则通过让模型利用一系列可以执行特定任务的工具,解决了这一问题。这样,大型语言模型可以在与环境互动中通过试错找到迭代解决方案。
一个有趣的情况是当大型语言模型(LLM)代理可以访问的工具实际上是其他代理:这是多代理系统的核心概念。多代理系统通过将任务分配给专门的模型,并像拼图一样将它们的输出组合起来以解决问题。实现这样系统的一种常见方式是使用一个管理代理来协调和管理其他代理的工作流程。
智能代理系统,尤其是多智能代理系统,需要一个强大的大型语言模型作为核心,才能正常工作,因为底层模型需要能够理解和判断各种工具的目的和适用范围,并将原始问题拆解成各个工具能解决的小问题。因此,专有模型如ChatGPT或Anthropic的Claude成为代理系统的首选。幸运的是,开源大型语言模型在性能上也有了显著提升,有些开源模型在某些方面已能与专有模型媲美。更有趣的是,即使是规模较小的开源模型,现在也能处理以前看似不可能完成的复杂任务。
在这篇博客文章中,我将展示一个可以在消费级硬件上运行的「小」LLM如何足以支持一个多代理系统并取得不错的效果。特别是,我将提供一个使用这个模型_Qwen2.5–7B-Instruct_来创建RAG系统的教程。您可以在下面的GitHub仓库和Colab笔记本中查看代码。
在深入了解系统架构之前,我将回顾一些有关LLM代理(LLM Agent)的基本概念。这些概念将有助于更好地理解该框架的内容。
ReAct(互动)ReAct,在《ReAct: 提升语言模型推理和行动协同》(arxiv.org/abs/2210.03629)中提出,是一个流行的构建LLM代理的框架。此方法的主要思想是将链式思考(Chain of Thought)的提示效果整合进代理框架中。ReACT由交替的推理和行动步骤构成:大型语言模型被提示提供一个思考序列,然后执行一个动作。这样,模型可以创建动态的推理轨迹来引导行动,并随时更新高层次的计划,同时在与环境互动的过程中整合新信息。这允许采用迭代和渐进的方式解决给定任务。在实际操作中,ReAct代理的工作流程包括思考、行动和观察三个步骤:在思考步骤中,模型会生成一般规划和特定工具使用的推理,然后在行动步骤中调用相应的工具,最后从环境中获取反馈。
下面是一个ReACT框架的例子。
ReACT、Chain-of-Thought 和 Act-Only 框架在问答任务中的比较和对照。图片来源:ReAct:在语言模型中促进推理与行动的协同。
代码代理人代码代理是一种特殊的LLM代理,它们使用可执行的Python代码来与环境交互。它们基于论文《可执行代码动作产生更好的LLM代理》(Executable Code Actions Elicit Better LLM Agents)中提出的CodeAct框架。CodeAct框架与ReAct框架非常相似,区别在于每个动作是由任意可执行的Python代码组成的,这些代码可以执行多种操作。代理被提供现成的Python函数作为工具,这些函数可以被调用执行任务。
相比使用JSON或其它文本格式的传统代理,基于代码的代理拥有独特的优势。
- 他们可以利用现有的软件包结合手工制作的任务特定工具。
- 他们可以利用错误发生的返回消息来自行调试生成的代码。
- 由于在预训练数据中广泛存在代码,LLMs 对编写代码很熟悉,这使得编写其操作更加自然的格式。
- 代码自然支持存储中间结果,并将多个操作组合成单个动作,而 JSON 或其他文本格式可能需要多个步骤来完成同样的任务。
因此,代码代理程序可以比使用JSON或其他文本格式的代理更快更高效。
代码代理与使用JSON或文本作为动作的代理进行比较。图片源自《使用可执行代码的动作的LLM代理更优质》(https://arxiv.org/abs/2402.01030)。
以下是一个来自原论文的具体例子,展示了代码助手如何用更少的步骤完成某些任务。
代码代理与使用JSON/文本格式的代理相比。代码代理能够在一次操作中执行多个任务。这张图来自可执行代码动作带来更好的LLM代理。[RIVEDERE]
Hugging Face 的 transformers 库提供了构建代理(尤其是代码代理)的有用模块。Hugging Face 的 transformer 代理框架以清晰和模块化为核心设计原则。这些原则在构建代理系统时尤为重要:由于工作流的复杂性,控制架构中所有相互关联的部分变得至关重要。这些设计选择使 Hugging Face 代理成为构建自定义和灵活代理系统的绝佳选择。当使用开源模型来驱动代理引擎时,Hugging Face 代理框架则进一步提供了轻松访问 Hugging Face 生态系统中丰富模型和工具的优势。
Hugging Face 代码代理也解决了不安全的代码执行问题。实际上,让一个 LLM 无限制生成代码可能会带来严重风险,因为它可能会执行一些不需要的操作。例如,错误的预测可能会导致代理删除重要文件。为了降低这一风险,Hugging Face 代码代理实现采用了一种从底部开始确保代码执行安全的方式:代码解释器只能执行明确定义的授权操作。这与通常从一个完全功能的 Python 解释器开始,然后禁止可能危险的行为的自上而下方法相反。Hugging Face 的实现包括一个可以执行的安全函数列表,以及一个安全的模块列表,可以导入。除非用户事先授权,否则其他任何内容都不能执行。更多信息请参阅他们在博客文章中的介绍:
她我代理的RAG (代理的RAG)检索增强生成技术已经成为涉及大型语言模型的信息检索任务的事实上的标准。它可以帮助保持大型语言模型(LLM)的信息更新,提供特定信息的访问,并降低幻觉概率。通过返回模型生成答案所使用的来源,它还可以增强人类的可解释性和监督。传统的RAG流程包括基于语义相似性对用户查询的检索过程以及通过检索的信息增强模型上下文,对于某些特定任务来说,这种流程还不够充分。不适合传统RAG的任务包括需要与信息来源进行交互的任务,例如查询数据库,需要多个信息片段才能回答的查询,以及需要复杂处理才能与实际来源信息关联的复杂查询。
传统RAG系统的一个具体且具有挑战性的例子是多跳问答(mhqa)。它涉及提取并结合多个信息片段,可能需要多次迭代推理过程,这些过程涉及提取的信息和仍然缺少的信息。例如,如果模型被问到“桦木胶合板是否能浮在乙醇中?”,即使用于RAG的来源包含了这两种材料密度的信息,标准的RAG框架也可能会失败,因为它无法直接将这两条信息联系起来。
一种流行的增强RAG(检索增强生成)的方法是使用代理系统来避免上述缺点。大语言模型代理可以将原始查询拆分成一系列子查询,通过语义搜索工具检索这些子查询的相关片段,并根据收集到的更多信息调整其计划。它可以自主判断是否已收集足够的信息来回答每个查询,或者是否需要继续搜索。通过扩展到一个多代理系统,代理RAG框架可以进一步增强,其中每个代理都有自己的任务和职责。这使得高级任务规划和文档源交互可以分离。在下一节中,我将描述这种系统的具体实现。
代码助手增强的多代理检索生成模型在这部分,我将讨论我为实现一个多代理RAG系统所做的一般架构选择,该系统基于代码的代理并遵循ReAct框架。您可以在GitHub仓库中找到完整的代码实现的其他细节。
这个多代理系统的目标是通过在维基百科上搜索相关资料来回答问题。它由三个代理构成。
- 一个任务经理代理,其职责是将任务分解为子任务,并利用各个子任务的结果来生成最终答案。
- 一个维基搜索代理,用于查找相关维基百科页面并整合从这些页面获取的信息。
- 一个网页搜索代理,用于从给定的维基百科页面中提取并总结与查询相关的信息。
这三个代理按层级组织:每个代理都可以将其下一级的代理当作工具来使用。特别是,管理者代理可以调用维基百科搜索代理来查找与查询相关的信息,后者还可以利用页面搜索代理从维基百科页面中提取所需信息。
以下是架构图示,指明了每个代理可以调用的手工工具(包括封装其他代理的工具)。请注意,由于代码代理是通过代码执行来工作的,实际上他们还可以使用任何授权的原生 Python 操作和函数,而不仅限于这些手工工具。
架构示意图展示了代理和手写工具。图由作者绘制。
让我们深入探讨一下架构中涉及的代理的细节。
经理代理人这是最顶层的代理,它接收用户的问题并返回答案。它可以通过向维基百科搜索代理发出查询请求并接收搜索的最终结果来使用该工具。它的目的是通过把用户的问题拆分成一系列小查询,从维基百科收集所需信息,并将搜索结果整合起来。
以下为该代理使用的系统提示文本。它是在Hugging Face的默认提示模板基础上构建的。请注意,提示中的示例遵循了模型的聊天模板,在这种情况下是Qwen2.5–7B-Instruct。
你是一位专家助手,能够使用代码片段和工具在网上查找答案。为此,你被授予了一组工具的访问权限:这些工具基本上是Python函数,你可以通过代码调用它们。
你的任务是回答用户的问题,你应该通过从维基百科检索必要信息来回答问题。仅信任并使用你检索到的信息,不要编造虚假事实。
为了帮助你,你被授予了搜索代理的访问权限,可以将其作为工具使用。你可以使用搜索代理来查找维基百科上的信息,并且只使用你检索到的信息,不要编造虚假事实。如需查询城市的人口,可以使用`wikipedia_search_agent`工具。
你需要一步步规划并执行任务,使用‘Thought:’、‘Code:’和‘Observation:’序列的格式。
在每个步骤中,在‘Thought:’序列中,你应该首先解释你解决任务的思路和你想要使用的工具。
然后在‘Code:’序列中,你应该用简单的Python编写代码。代码序列必须以‘<end_action>’序列结束。
在每个中间步骤中,你可以使用‘print()’来保存你在后续步骤中需要的重要信息。这些打印输出将由用户在‘Observation:’字段中提供,作为下一步的输入。始终打印工具的输出,不要在检查之前尝试处理或抽取信息。
如果在执行代码时出现错误,它将在‘Observation:’字段中显示。在这种情况下,修复代码并重新尝试。
最后,需要使用`final_answer`工具返回最终答案。
这里是一些示例:
---
## 维基百科搜索工具
这个代理向经理代理汇报工作,它接收来自经理代理的查询请求,并被要求返回从维基百科检索到的信息。它的任务是返回从维基百科检索到的信息,它能使用两个工具。
* 一个维基百科搜索工具,它使用了[wikipedia包](https://pypi.org/project/wikipedia/)的内置搜索功能。它接收一个查询输入,并返回维基百科页面及其摘要的列表。
* 一个页面搜索代理,它从特定的维基百科页面检索关于查询的信息。
该代理收集信息来回答查询,将其细分为更具体的子查询,必要时从多个页面提取信息。这是通过使用wikipedia工具包中的搜索工具来识别可能包含回答查询所需信息的潜在页面来完成的:代理可以使用页面摘要或调用页面搜索代理从特定页面提取更多信息。一旦收集了足够的数据,它将答案返回给管理程序。
系统提示是基于Hugging Face默认提示做了一些小改动,后面跟了一些特定示例,遵循聊天模板。
你是一个从维基百科检索信息的专家助手,使用代码片段和工具。为此,你被赋予了访问一组工具的权限:这些工具基本上是Python函数,你可以像调用代码一样调用它们。
你会收到一个一般性的查询,你的任务是从给定的维基百科页面中检索和总结与查询相关的片段信息。使用并信任从维基百科检索到的信息,不要编造虚假事实。尽量用几句话来总结信息。
为了解决任务,你必须分步骤地进行,按照“Thought:”、“Code:”和“Observation:”的循环步骤前进。
在每个步骤的“Thought:”序列中,你首先要解释你的解决任务的推理和你想要使用的工具。
然后在“Code:”序列中,你需要写简单的Python代码。代码序列必须以‘<end_action>’结束。
在每个中间步骤中,你可以使用“print()”保存你将在下一步中需要的重要信息。这些打印输出将在“Observation:”字段中由用户提供,可以在下一步中作为输入使用。始终打印工具的输出,不要在检查之前处理或尝试提取信息,而应在观察之后进行处理。
如果在执行代码时出现错误,它将出现在“Observation:”字段中。在这种情况下,修复代码并重新尝试。
最终你需要使用`final_answer`工具返回最终答案。
以下是一些假设的例子:
---
现在开始并解决任务。
## 页面搜索工具
这个代理向维基百科搜索代理报告,后者为其提供查询和维基百科页面的标题,它的任务是从该页面中检索相关信息以回答查询。说白了,这是一个单代理的RAG系统。为了完成任务,它生成自定义查询,并使用语义搜索工具检索与这些查询更相关的段落。语义搜索工具采用了一种简单的实现方式,将页面内容分割成块状,并使用它提供的FAISS向量数据库进行嵌入。
下面是系统提示,依然是基于Hugging Face默认提供的版本构建的。
你是一位专家助理,能够通过查阅维基百科、使用代码和工具来回答问题。为此,你被授予了访问一组工具的权限:这些工具主要是Python函数,你可以调用它们。
你将被给出一个一般性的查询,你的任务是使用从维基百科检索到的信息来找到该查询的答案。只使用并信任从维基百科检索到的信息,不要编造虚假信息。引用你找到信息的页面。
可以使用search_wikipedia
工具从维基百科搜索页面及其摘要,使用search_info
工具查找特定页面中的特定段落。你应该决定如何使用这些工具来找寻合适的答案:某些查询可以通过查看一个页面的摘要来回答,而其他查询可能需要查看多个页面上的特定段落。
为了完成任务,你必须在一个由“Thought:”,“Code:”和“Observation:”组成的步骤循环中向前规划。
在每个步骤中,在“Thought:”序列中,你应该首先解释你为解决任务所做的推理和你想要使用的工具。
然后在“Code:”序列中,你应该写简单的Python代码。代码序列必须以“<end_action>”结尾。
在每个中间步骤中,你可以使用print()
来保存你需要在后续步骤中使用的任何重要信息。这些打印输出将由用户在“Observation:”字段中提供,可用于下一个步骤的输入。始终打印工具的输出,不要在检查之前处理或提取信息。
如果在执行代码时发生错误,它将显示在“Observation:”字段中。在这种情况下,修正代码并重新尝试。
最终,你需要使用final_answer
工具返回最终答案。
现在开始并解决任务!
## 实现的选择
在本子节中,我将概述与直接使用Hugging Face代理实现的架构的主要不同之处。这些不同点是在有限的试验和错误之后获得的结果,并非最优的选择。我还没有进行广泛的测试和消融实验。
* **提示:** 如前所述,每个代理都有自己的专门系统提示,不同于Hugging Face Code Agents提供的默认提示。我注意到,也许是因为所用模型的大小有限,一般的系统提示无法取得良好的效果。模型似乎在任务提示与任务紧密相关的情况下表现最佳,包括重要的用例示例,以便更贴近模型运行时的格式。由于我使用的是聊天模型(chat model),旨在改善指令遵循行为,提供的示例遵循模型的聊天模板,以尽可能接近运行时的格式。
* **总结历史:** 长的执行历史对执行速度和任务表现两者都有负面影响。后者可能是因为模型从长上下文中检索必要信息的能力有限。此外,极长的执行历史可能超过引擎模型的最大上下文长度。为缓解这些问题并加快执行速度,我选择不显示之前的思考-行动-观察步骤的所有细节,而是仅收集之前的观察。更具体地说,每个步骤模型只接收以下聊天历史:系统消息、包含任务的首个消息、其最后的动作,以及所有之前的观察历史。此外,执行错误仅在最后一步出现在观察历史中,之前的错误如果已经解决则会被丢弃。
* **工具与管理代理:** Hugging Face代理实现原生支持管理代理,但将它们封装为工具允许更好地控制提示并简化实现。特别是,Hugging Face实现为管理代理及其管理程序添加了特定的提示。虽然我没有看到解决给定任务的能力上有显著差异,但我更喜欢第二种方法,因为它在提出的架构中更灵活且更简洁,并且有助于更轻松地控制代理的行为。这也有助于减少提示长度,从而有助于加快计算速度。
* **限制页面搜索代理(page search agent)的最大尝试次数:** 有时页面搜索代理(page search agent)会不断在不包含所需信息的页面上寻找信息。减少最大尝试次数减轻了这一问题:达到该次数后,代理执行停止,工具返回代码执行的最后观察。
* **更改工具响应以作为用户消息:** 这更多是一个技术性、实现特定的要点。由于_Qwen2.5–7B-Instruct_ 聊天模板仅支持系统、用户和助手角色,观察会作为用户消息返回。
# 一个例子
在本节中,我将收集一些使用所述架构创建的示例。我还将提供所有涉及代理的详细执行过程,以展示它们在某些实际情境中的互动情况。您可以通过说明性的[Colab 笔记本](https://colab.research.google.com/drive/1ZxF-Fkv4QV31uo79FagZWFm8tvXgCtBN)探索更多示例。提取的所有段落均来自指定的维基百科页面中的内容。
让我们从一个简单的例子开始,这个例子需要我们结合多个来源的信息并运用一些基本的算术知识。
问题:埃菲尔铁塔建成到帝国大厦建成相隔了多少年?
以下是管理程序执行的日志。它能正确拆分任务,分别查找两个建筑物的建造时期,并计算它们之间的年份差异。
Agent: 思考:首先,我需要找出埃菲尔铁塔和帝国大厦的建造时间。我将使用 `wikipedia_search_agent` 工具来查找这些资料。
代码:
```py
wikipedia_search_agent(query="Eiffel Tower construction year")
```<end_action>
观察:打印结果:
代码片段的最后输出:
根据从维基百科页面“埃菲尔铁塔”提取的信息,埃菲尔铁塔建造于1887年至1889年。
Agent: 思考:现在我有了埃菲尔铁塔的建造时间。接下来,我需要找到帝国大厦的建造时间。我将再次使用 `wikipedia_search_agent` 工具。
代码:
```py
wikipedia_search_agent(query="Empire State Building construction year")
```<end_action>
观察:打印结果:
代码片段的最后输出:
根据从维基百科页面“帝国大厦”提取的信息,帝国大厦的建造时间是1930年至1931年。
Agent: 思考:现在我有了埃菲尔铁塔和帝国大厦的建造时间。现在我可以计算它们建造之间的时间差了。
代码:
```py
construction_eiffel_tower_start = 1887
construction_eiffel_tower_end = 1889
construction_empire_state_building_start = 1930
construction_empire_state_building_end = 1931
years_passed = construction_empire_state_building_start - construction_eiffel_tower_end
answer = f"埃菲尔铁塔和帝国大厦建造之间的时间差是 {years_passed} 年。"
final_answer(answer)
```<end_action>
观察:打印结果:
代码片段的最后输出:
埃菲尔铁塔和帝国大厦的建造相隔了41年。
我们现在来看看维基百科搜索代理的执行情况。它搜索并报告了页面摘要中找到的建设时期,而无需逐个检查页面。有趣的是,尽管它被问到了“建设年份”这个问题,但它报告了完整的建设时期,因为不清楚“年份”是指建设工作的开始还是结束。
任务:埃菲尔铁塔建造年份
代理:思考:我将使用 `search_wikipedia` 工具查找有关埃菲尔铁塔建造年份的信息。
代码:
```py
search_wikipedia('埃菲尔铁塔建造年份')
```<结束操作>
观察:
打印输出:
代码片段的最后输出:
搜索“埃菲尔铁塔建造年份”的页面:
页面:埃菲尔铁塔(法语:Tour Eiffel [tuʁ ɛfɛl])
摘要:埃菲尔铁塔是法国巴黎战神广场的一座铁制镂空塔。塔以工程师居斯塔夫·埃菲尔的名字命名,他的公司从 1887 年到 1889 年设计并建造了该塔。
当地昵称为“铁娘子”,作为 1889 年世界博览会的中心项目,以及法国大革命百年庆典的纪念建筑。尽管最初受到了一些法国艺术家和知识分子对其设计的批评,它现在已成为法国乃至全球的文化象征,并且是世界上最知名的人造建筑之一。2022 年,埃菲尔铁塔接待了 5889000 名游客。它是世界上收费门票最多的景点,2015 年有 691 万人参观。1964 年,埃菲尔铁塔被指定为历史遗迹,并在 1991 年被列入联合国教科文组织世界遗产名录(巴黎塞纳河岸)。
塔高 330 米(约 1083 英尺),大约是
页面:埃菲尔铁塔(德克萨斯州巴黎市)
摘要:德克萨斯州的埃菲尔铁塔是德克萨斯州巴黎市的一个地标。塔是在 1993 年建造的。它是一个 1:20 的复制品,位于法国巴黎。
页面:居斯塔夫·埃菲尔
摘要:居斯塔夫·埃菲尔 (1832 年 12 月 15 日—1923 年 12 月 27 日) 是一名法国土木工程师。毕业于中央理工学院,他因法国铁路网络的各种桥梁而闻名,最著名的是加拉比铁路桥。他最著名的是设计并建造了 1889 年巴黎世界博览会的世界著名埃菲尔铁塔,以及他为纽约自由女神像的建造所做出的贡献。退休后,埃菲尔专注于气象学和空气动力学的研究,并在两个领域都做出了重要贡献。
页面:沃金塔
摘要:沃金塔是伦敦惠灵顿公园的一座部分完成的铁制镂空塔。该项目雄心勃勃,旨在在惠灵顿公园北部创建一座 358 米(1175 英尺)高的游客景点,该公园由铁路企业家爱德华·沃金领导。它被宣传为“伦敦大塔”,设计比巴黎的埃菲尔铁塔更高,是惠灵顿公园作为休闲场所的一部分。该塔从未完工,1907 年被拆除。该塔址现在被温布利体育场占用。
页面:埃菲尔铁塔(田纳西州巴黎市)
摘要:埃菲尔铁塔是田纳西州巴黎市的一个地标。它是一个 1:20 的复制品,位于法国巴黎。
代理:思考:从“埃菲尔铁塔”页面的摘要中,可以看出埃菲尔铁塔的建造时间为 1887 年至 1889 年。我将使用 `final_answer` 工具返回答案。
代码:
```py
final_answer("根据从维基百科页面 '埃菲尔铁塔' 提取的信息,埃菲尔铁塔的建造时间为 1887 年至 1889 年。")
```<结束操作>
观察:
打印输出:
代码片段的最后输出:
根据从维基百科页面 '埃菲尔铁塔' 提取的信息,埃菲尔铁塔的建造时间为 1887 年至 1889 年。
------------------------------
任务:帝国大厦建造年份
代理:思考:我将使用 `search_wikipedia` 工具查找帝国大厦的建设年份。
代码:
```py
search_wikipedia('帝国大厦建造年份')
```<结束操作>
观察:
打印输出:
代码片段的最后输出:
搜索“帝国大厦建造年份”的页面:
页面:帝国大厦
摘要:帝国大厦是位于美国纽约市中心曼哈顿区的一座 102 层的装饰艺术风格摩天大楼。该楼由 Shreve, Lamb & Harmon 设计,建于 1930 年至 1931 年。其名称来源于“帝国州”,即纽约州的别名。帝国大厦的屋顶高度为 1250 英尺(380 米),总高度(含天线)为 1454 英尺(443.2 米)。帝国大厦在 1970 年世界第一贸易中心的北塔封顶之前,是世界上最高的建筑;2001 年 9 月 11 日恐怖袭击后,帝国大厦成为纽约市最高的建筑,直到 2012 年被世界贸易中心一号楼超越。截至 2024 年,该建筑是纽约市第七高的建筑,美国第九高的摩天大楼,以及世界上第五十七高的摩天大楼。
帝国大厦的所在地位于第五大道西侧,介于西 33 街和 34 街之间。
页面:英国帝国大厦
摘要:英国帝国大厦,又称作 620 第五大道,是位于纽约市中城洛克菲勒中心的一栋商业建筑。该建筑于 1933 年完工,由雷蒙德·胡德设计,采用了装饰艺术风格,是洛克菲勒中心的主要建筑师。英国帝国大厦与南面几乎一样的法国之家和北面的高层国际大厦一起,形成了一个名为国际大厦群的零售与办公综合体。英国帝国大厦与法国之家之间设有运河花园,是一个设有步行道的步行区,通向综合体的下层广场。
外墙由石灰岩制成,主入口在第五大道,次入口在 50 号街和运河花园。英国帝国大厦的顶部设有退台、屋顶花园和部分七层的顶层公寓。建筑入口处装饰精美。
页面:2012 年帝国大厦枪击案
摘要:2012 年 8 月 24 日,一名前同事在纽约市帝国大厦外枪杀一名雇员。警方在嫌疑人杰弗里·T·约翰逊首次开枪后,将他击毙。九名旁观者因警察射出的子弹和弹片而受伤,但无人生命垂危。
页面:帝国大厦在流行文化中的地位
摘要:帝国大厦是美国纽约市曼哈顿中城第五大道 33 街和 34 街之间的一座 102 层的摩天大楼。它高 1250 英尺(381 米),加上天线总高度为 1454 英尺(443.2 米)。帝国大厦自 1931 年初建成以来,作为世界最高建筑统治了近 40 年,直到 1970 年底世界贸易中心一号塔封顶。它在许多电影、电视节目、歌曲、电子游戏、书籍、照片和艺术作品中都有出现。
页面:尼尔森·A·罗克韦尔州长的帝国州立广场
摘要:尼尔森·A·罗克韦尔州长的帝国州立广场是纽约州首府奥尔巴尼的一个建筑群。
该建筑群于 1965 年至 1976 年间建成,总花费约为 20 亿美元。它容纳了纽约州行政部门的多个部门,并与 1899 年完工的纽约州议会大厦相连,后者容纳了州议会。广场内设有卫生部门和大实验室。帝国州艺术收藏品,一系列 60 年代和 70 年代的巨幅抽象艺术品,永久展出。纽约州公共服务局负责维护广场。纽约州公共服务局帝国州立广场表演艺术中心成立于 1979 年,负责管理广场内的表演艺术设施。
代理:思考:从“帝国大厦”页面的摘要中,可以看出该建筑设计和建造时间为 1930 年至 1931 年。我将使用 `final_answer` 工具返回答案。
代码:
```py
final_answer("根据从维基百科页面 '帝国大厦' 提取的信息,帝国大厦的建造始于 1930 年并结束于 1931 年。")
```<结束操作>
观察:
打印输出:
代码片段的最后输出:
根据从维基百科页面 '帝国大厦' 提取的信息,帝国大厦的建造始于 1930 年并结束于 1931 年。
通道检索代理没有被使用,因为所有需要的信息都已经在页面摘要中找到。
我们现在来尝试一个更复杂的例子,看看白桦胶合板是否能在酒精中浮起来。这个问题需要基本的物理知识和算术能力,以及一些复杂的查找任务。
_问题:白桦木板会浮在酒精里吗?_
让我们先看看各个代理的日志可视化。
代理程序日志.
AGENT: 思考:要确定桦木胶合板是否能在乙醇中浮起来,我们需要知道桦木胶合板的密度和乙醇的密度。如果桦木胶合板的密度小于乙醇的密度,它就会浮起来。我将使用 `wikipedia_search_agent` 工具来查找桦木胶合板的密度和乙醇的密度。
代码:
```py
birch_plywood_density_info = wikipedia_search_agent("密度 of 桦木胶合板")
ethanol_density_info = wikipedia_search_agent("密度 of 乙醇")
print(birch_plywood_density_info)
print(ethanol_density_info)
```<end_action>
观察:打印结果:
桦木胶合板的密度约为 680 kg/m³。
20°C 时乙醇的密度约为 0.7894 g/cm³。
AGENT: 思考:现在我已经得到了密度,我可以进行比较。桦木胶合板的密度为 680 kg/m³,乙醇的密度为 0.7894 g/cm³。由于 1 g/cm³ 等于 1000 kg/m³,所以乙醇的密度为 789.4 kg/m³。由于桦木胶合板的密度(680 kg/m³)小于乙醇的密度(789.4 kg/m³),桦木胶合板可以在乙醇中浮起来。我现在将使用 `final_answer` 函数提供这个结论。
代码:
```py
final_answer("桦木胶合板可以在乙醇中浮起来,因为它的密度(680 kg/m³)小于乙醇的密度(789.4 kg/m³)。")
```<end_action>
观察:最终输出:
桦木胶合板可以在乙醇中浮起来,因为它的密度(680 kg/m³)小于乙醇的密度(789.4 kg/m³)。
维基百科搜索日志
任务:桦木胶合板的密度
代理:思考:我将使用`search_wikipedia`工具来查找关于桦木胶合板密度的信息。
代码:
```py
search_wikipedia('birch plywood')
```
观察:
打印输出:
查询“birch plywood”的搜索结果页面:
页面:胶合板
摘要:胶合板是一种由薄木层(称为“木皮”)层压胶合而成的复合材料。它是一种工程木材,属于包括胶合板、中密度纤维板(MDF)、定向刨花板(OSB)和刨花板(或称刨花板)在内的层压板家族。
所有的胶合板都由树脂和木纤维片(木质细胞是长、强、薄)结合而成,形成复合材料。木层被堆叠成每层的纹理通常与相邻层垂直(见下文)。这种纹理的交替称为交错纹理,其优点包括:减少钉子固定时木板破裂的倾向;减少厚度膨胀和收缩,提供更好的尺寸稳定性;使面板的强度在各个方向上一致。通常木层是奇数,所以面板是平衡的,即表面层的纹理方向是平衡的。
页面:桦木
摘要:桦木是薄叶落叶硬木树,属于白桦属(Betula),在桦木科中。它与橡树-山毛榉科(Fagaceae)密切相关。白桦属包含30至60个已知分类群,其中11个被列入2011年《濒危物种红色名录》。它们通常是短命的先锋物种,在北半球广泛分布,特别是在温带气候和寒带气候的北部地区。桦木木材被广泛用于各种用途。
页面:桦木木材
摘要:桦木木材是一种桦木的木材。桦木木材是淡黄色的木材,具有紧密、直纹和均匀的纹理,可以打磨成光滑的表面。有时会被染色以模仿桃花心木。这种木材被用于火柴、旋工制品、家具、橱柜、工具把手、箍、胶合板、地板和鞋跟。
页面:Falco F8L
摘要:Falco F8L 是一种意大利设计的轻型双座特技飞机。
页面:Crusader(摩托艇)
摘要:Crusader 是约翰·科布驾驶的喷气式摩托艇。
该艇的船体形式和涡轮喷气发动机推进结合体由里德·雷尔顿提出,科布的顾问。一个火箭推进的缩尺模型在哈斯勒进行了测试。全尺寸设计由彼得·杜·凯恩设计,由朴茨茅斯的沃普斯建造。技术援助来自萨沃·罗伊和维克尔斯-超级马林。1949年价格为15000英镑。
它是银色和红色的,长10米。
发动机是来自航空部的德·哈维兰鬼影 Mk 48 离心涡轮喷气发动机提供的,由主要福兰克·霍福德请求。发动机的推力为5000磅,由两个前驾驶舱的吸入管供气。
船体是三体船形式,一个带有滑水台阶的主船体和两个较小的后置外侧船体。结构由桦木胶合板框架和龙骨组成。船体用桦木胶合板覆盖并用涂有涂层的织物和金属的表面加固。
代理:思考:搜索返回的页面中没有直接提到桦木胶合板的密度。但是,桦木木材的页面提到桦木木材被用于胶合板。我将使用`search_info`工具从“胶合板”页面查找有关桦木胶合板密度的更具体的信息。
代码:
```py
search_info('密度为桦木胶合板', '胶合板')
```
观察:
打印输出:
从页面“胶合板”获取的查询“桦木胶合板的密度”的信息:
桦木胶合板的密度大约为680 kg/m³。
代理:思考:我已经从维基百科页面“胶合板”找到了桦木胶合板的密度。密度大约为680 kg/m³。
代码:
```py
final_answer("桦木胶合板的密度大约为680 kg/m³。")
```
观察:
打印输出:
桦木胶合板的密度大约为680 kg/m³。
------------------------------
任务:乙醇的密度
代理:思考:我将使用`search_wikipedia`工具从维基百科查找乙醇的密度。
代码:
```py
search_wikipedia('密度为乙醇')
```
观察:
打印输出:
查询“乙醇的密度”的搜索结果页面:
页面:乙醇(数据页)
摘要:此页面提供了乙醇的补充化学数据。
页面:乙醇
摘要:乙醇(也称为乙基酒精、谷物酒精、饮用酒精或简称为酒精),是一种有机化合物,其化学式为CH3CH2OH。它是一种醇类,其化学式也可写为C2H5OH、C2H6O或EtOH,其中Et代表乙基。乙醇是一种易挥发、易燃、无色的液体,具有葡萄酒的典型气味和浓郁的口感。在自然界中,葡萄糖通过发酵作用分解为酒精或碳酸,无需添加任何物质。作为一种精神抑制剂,它是酒精饮料中的活性成分,也是全球第二大消费药物,仅次于咖啡因。
乙醇可以通过发酵过程由酵母作用于糖类自然产生,也可以通过石化过程如乙烯水合产生。历史上它曾作为一种全身麻醉剂使用,现在在医疗上作为抗微生物剂、消毒剂、溶剂、解甲醇中毒和乙二醇中毒的解毒剂。它被用作化学品。
页面:酒精含量
摘要:酒精体积含量(缩写为 alc/vol 或 ABV)是一种标准的量度,表示给定体积的含酒精饮料中所含纯酒精的体积百分比。它被定义为在20°C(68°F)下,100 mL(3.5 imp fl oz;3.4 US fl oz)溶液中纯酒精的毫升数。纯酒精的毫升数是纯酒精的质量除以20°C(68°F)下的密度,即0.78945 g/mL(0.82353 oz/US fl oz;0.79122 oz/imp fl oz;0.45633 oz/cu in)。alc/vol标准在全世界范围内使用。国际计量局有关于不同浓度和温度下水-乙醇混合物的密度表。
在一些国家,例如法国,酒精体积含量通常被称为盖·吕萨克度(以法国化学家约瑟夫·路易·盖·吕萨克命名),尽管由于温度的国际标准大气值是15°C(59°F),所以略有不同。
页面:酒精燃料
摘要:各种醇类被用作内燃机的燃料。前四种直链醇类(甲醇、乙醇、丙醇和丁醇)
因为它们可以通过化学或生物过程合成,并且具有可供使用在内燃机的特性,因此引起了人们的兴趣。醇类燃料的一般化学式为 CnH2n+1OH。
大多数甲醇是通过天然气生产的,尽管也可以通过非常相似的化学过程从生物质中生产。乙醇通常通过发酵过程从生物材料中产生。生物丁醇在燃烧发动机中的优势在于其能量密度更接近汽油,但比简单的醇类高25%以上;然而,生物丁醇的生产目前比乙醇或甲醇更困难。当从生物材料或生物过程中获得时,它们被称为生物醇(例如“生物乙醇”)。在燃烧时,植物吸收的二氧化碳被部分抵消,因此生物醇燃料可能比化石燃料具有更低的碳足迹。
兴趣在于生物丁醇的潜在用途在于它可以替代由玉米或甘蔗生产的乙醇。由于这些植物也被用于食品产品,这会引发食品价格上涨;生物来源的乙醇通常不与食品竞争,因为植物纤维对人类来说大多不可食用。另一个潜在优势是其高多样性和丰富的来源;草、树和藻类几乎存在于每一个环境。
代理:思考:页面“乙醇”的摘要提到乙醇是一种具有葡萄酒气味和强烈口感的酒精。但是,它没有提供乙醇的密度。我将使用`search_info`工具从“乙醇(数据页)”获取有关乙醇密度的信息。
代码:
```py
search_info('乙醇的密度', '乙醇(数据页)')
```
观察:
打印输出:
从页面“乙醇(数据页)”获取的查询“乙醇的密度”的信息:
乙醇在20°C的密度约为0.7894 g/cm³。
代理:思考:我已经从维基百科页面“乙醇(数据页)”找到了乙醇的密度。我将使用`final_answer`工具返回结果为。
代码:
```py
final_answer("乙醇在20°C的密度约为0.7894 g/cm³。")
```
观察:
打印输出:
乙醇在20°C的密度约为0.7894 g/cm³。
页面搜索代理程序的日志文件
任务:从 Wikipedia 的 "Plywood" 页面中检索有关查询 "山毛榉胶合板的密度" 的信息。
代理:思考:我需要找到山毛榉胶合板的密度。我将使用工具 `retrieve_passages` 查找维基百科页面上的数据。
代码:
```py
result = retrieve_passages("山毛榉胶合板的密度")
print(result)
```<end_action>
观察:打印输出:
检索到的查询 "山毛榉胶合板的密度" 的段落:
段落 0: ... . 剪切应力相反,在面板中心和外层纤维中较高。在欧洲,基本胶合板可分为三大类:山毛榉胶合板(密度约 680 kg/m3),混合胶合板(密度约 620 kg/m3)和松木胶(密度 460–520 kg/m3)。 ...
段落 1: ... . 航空级胶合板由山毛榉制成,总厚度为 0.40 毫米(1/64 英寸),非常坚固且轻便。 ...
段落 2: ... 巴尔蒂克山毛榉胶合板是波罗的海地区的产品。最初为欧洲橱柜制造商制造,但现在在美国也很受欢迎。它非常稳定,由无空隙的山毛榉芯材制成,外层使用耐候胶粘剂。面层薄板比传统橱柜级胶合板更厚。 ...
段落 3: ... . 结果,更厚的面板可以在相同负载下跨越更大的距离。在弯曲过程中,最大应力发生在面层,一种在拉伸,另一种在压缩。弯曲应力从面层的最大值递减到中央层接近零。剪切应力相反,在面板中心和外层纤维中较高。在欧洲,基本胶合板可分为三大类:山毛榉胶合板(密度约 ...
段落 4: ... 风力涡轮机叶片
液化天然气(LNG)运输船的绝缘箱
光滑的表面和精确的厚度以及材料的耐用性使得山毛榉胶合板适用于多种特殊用途,例如:
高端扬声器
印模切割板
木地板的支撑结构
游乐场设备
家具
户外广告的标志和围栏
乐器
运动器材...
代理:思考:从段落中,我可以看到山毛榉胶合板的密度约为 680 kg/m³。我将使用 `final_answer` 提供答案。
代码:
```py
final_answer("山毛榉胶合板的密度约为 680 kg/m³。")
```<end_action>
观察:打印输出:
代码片段的最后输出:
山毛榉胶合板的密度约为 680 kg/m³。
------------------------------
任务:从 Wikipedia 的 "Ethanol (data page)" 页面中检索有关查询 "乙醇在不同温度下的密度" 的信息。
代理:思考:我需要找到乙醇的密度。我将使用工具 `retrieve_passages` 从维基百科页面获取信息。
代码:
```py
result = retrieve_passages("乙醇在不同温度下的密度")
print(result)
```<end_action>
观察:打印输出:
检索到的查询 "乙醇在不同温度下的密度" 的段落:
段落 0: ... == 乙醇在不同温度下的密度 ==
数据从 Lange 1967 获得
这些数据与 ρ [g/cm3] = -8.461834×10−4 T [°C] + 0.8063372 相关,R2 = 0.99999。
== 含水的乙醇溶液的性质 ==
数据从 Lange 1967 获得
== 含水溶液的沸点 ==
数据从 CRC 化学手册(第 2117 页)获得: 2391
‡ 均质混合物
== 图表 ==
== 参考资料 == ...
段落 1: ... 本页提供了乙醇的补充化学数据。
== 材料安全数据表 ==
外部 MSDS
== 结构和性质 ==
== 热力学性质 ==
== 光谱数据 ==
== 液体乙醇的气压 ==
== 乙醇在不同温度下的密度 ==
数据从 Lange 1967 获得
这些数据与 ρ [g/cm3] = -8.461834×10−4 T [°C] + 0.8063372 相关,R2 = 0.99999。
== 含水的乙醇溶液的性质 ==
数据从 Lange 1967 获得...
代理:思考:从段落中,我可以看到乙醇的密度随温度变化。我可以使用提供的线性方程来计算特定温度下的乙醇密度。我将使用 `math` 模块进行计算。
代码:
```py
# 提取方程中的系数
a = -8.461834e-4
b = 0.8063372
# 计算特定温度下的乙醇密度,以 20°C 为例
temperature = 20
density = a * temperature + b
print(density)
```<end_action>
观察:打印输出:
0.789413532
代理:思考:段落中提供了用于计算不同温度下乙醇密度的线性方程。我现在可以使用这个方程来提供特定温度下的乙醇密度。我将使用 `final_answer` 返回相关信息。
代码:
```py
final_answer(f"乙醇在 20°C 的密度约为 {round(0.789413532, 4)} g/cm³。")
```<end_action>
观察:打印输出:
代码片段的最后输出:
乙醇在 20°C 的密度约为 0.7894 g/cm³。
模型正确地识别了密度差异是导致漂浮或沉没的原因,并将任务分解为找出两种物质的密度值,即使这些数据的单位不一样,模型也能够根据检索到的数据得出正确的结论。
# 限制:
所讨论的系统拥有令人印象深刻的能力,特别是考虑到使用的引擎模型较小的规模,但当然,它也有自身的局限性。一个主要的局限与引擎模型本身的能力有关:虽然它似乎能够理解和遵循复杂且长的指令,并且能够正确使用提供的工具,但它有时无法遵循简单的指导方针,并倾向于重复无效的动作,而不作任何改变。这种情况尤为明显,当代理寻找的信息不在提供的页面中(或难以通过语义搜索工具提取)时。
另一个缺点是这种内在相关性,这种相关性与代理系统内在相关,而在多代理系统中则更为突出,即各个代理执行所需的计算时间。在某些简单情况下,如果标准的RAG工作流足以回答问题,这可能会导致不必要的计算负担。
最后,多个代理互相交互会增加幻觉的风险,因为一个代理产生的幻觉可能会在整个执行链条中传播,这也会使得追踪非常难。
让我们看一个例子,在这种情况中,系统虽然提供了正确的答案,但过度调用了工具,并重复了一些动作,无谓地延长了执行时间。
问题:当时,君士坦丁堡陷落之际,统治中国的又是哪个朝代?
这项任务需要一定的抽象思维,因为这两个事件之间没有直接联系,而且该王朝的页面上可能不会提到君士坦丁堡陷落的具体年份。
代理执行仍然简洁明了,通过先检索1453年君士坦丁堡沦陷的日期,再查找那一年的中国朝代来正确解决问题。然而,我们已经可以看出,虽然包含所需信息,但看起来有点奇怪。
Agent: 想:要回答这个问题,我需要找出君士坦丁堡沦陷的时间,以及当时中国的朝代。我将先查一下君士坦丁堡沦陷的年份。
代码:
```py
wikipedia_search_agent(query="Fall of Constantinople")
```<end_action>
观察:打印输出:
代码的最后输出结果:
1453年,君士坦丁堡沦陷时,奥斯曼帝国的领导人穆罕默德二世,也被称为征服者穆罕默德。
Agent: 想:从之前的搜索中我们得知,君士坦丁堡是1453年沦陷的,被穆罕默德二世所征服。现在我需要查一下1453年时中国是哪个朝代。我将查一下1453年左右中国的朝代。
代码:
```py
wikipedia_search_agent(query="Chinese dynasties 1453")
```<end_action>
观察:打印输出:
代码的最后输出结果:
1453年,明朝统治中国,并且提供的信息中没有特别提到1453年的具体事件。
Agent: 想:根据查找到的信息,明朝在1453年完全统治了中国。因此,当君士坦丁堡沦陷时,统治中国的朝代是明朝。
代码:
```py
final_answer("1453年,当君士坦丁堡沦陷时,统治中国的朝代是明朝。")
```<end_action>
观察:打印输出:
代码的最后输出结果:
1453年,当君士坦丁堡沦陷时,统治中国的朝代是明朝。
让我们来检查一下对应的维基百科搜索代理任务及其执行情况。我们可以看到,可能是由于任务“君士坦丁堡的陷落”较为广泛,代理在找到正确日期后,还查看了其他相关信息。在检索关于中国朝代的信息时,同样的情况也会出现,还有一个问题是,有些工具调用被重复了。
任务:君士坦丁堡的陷落
代理:我将使用 `查询维基百科` 工具查找关于君士坦丁堡陷落的信息。这很可能提供该事件的摘要,并可能包含相关页面的链接。
代码:
```py
查询维基百科("君士坦丁堡的陷落")
```<end_action>
观察:打印输出:
代码片段最后输出:
查询 '君士坦丁堡的陷落' 找到的页面:
页面:君士坦丁堡的陷落
摘要:君士坦丁堡的陷落,又称君士坦丁堡的征服,是奥斯曼帝国占领拜占庭帝国首都的事件。该城于1453年5月29日被占领,这是从4月6日开始的长达55天的围攻的最终结果。
进攻的奥斯曼军队在人数上远超君士坦丁堡的守军,由21岁的苏丹穆罕默德二世(后来被称为“征服者”)指挥,而拜占庭军队则由皇帝君士坦丁十一世·帕列奥洛格领导。占领该城后,穆罕默德二世把君士坦丁堡定为新的奥斯曼帝国首都,取代了阿德里安堡。
君士坦丁堡的陷落和拜占庭帝国的灭亡是中世纪晚期的一个分水岭,标志着罗马帝国实际上的终结,该帝国始于公元27年左右,持续了近1500年。对于许多现代历史学家而言,君士坦丁堡的陷落标志着中世纪的结束和早期现代时期的开始。该城的陷落也标志着一个转折点。
页面:君士坦丁堡的劫掠
摘要:君士坦丁堡的劫掠发生在1204年4月,标志着第四次十字军东侵的高潮。十字军劫掠并摧毁了拜占庭帝国首都君士坦丁堡的大部分。占领该城后,拉丁帝国(拜占庭人称之为法兰克占领)建立,弗兰德伯爵巴尔杜因在那里被加冕为君士坦丁堡的第一任皇帝。
城市被劫掠后,拜占庭帝国的大部分领土被分配给了十字军。拜占庭贵族们也建立了一些独立的小公国,其中之一是尼西亚帝国,该帝国在1261年重新夺回君士坦丁堡,并宣布恢复帝国。然而,恢复后的帝国从未能夺回所有旧领土或恢复以前的经济实力,并在接下来的两个世纪里逐渐败给了奥斯曼帝国。
拜占庭帝国变得贫穷,规模缩小。
页面:君士坦丁堡
摘要:君士坦丁堡(其他名称见下文)在君士坦丁大帝统治期间(330年)成为罗马帝国的首都。随着西部罗马帝国在5世纪末崩溃,君士坦丁堡成为东罗马帝国(亦即拜占庭帝国,330年—1204年和1261年—1453年)、拉丁帝国(1204年—1261年)和奥斯曼帝国(1453年—1922年)的首都。在土耳其独立战争之后,土耳其首都迁至安卡拉。1930年,该城市正式更名为伊斯坦布尔,现已成为欧洲最大的城市,横跨博斯普鲁斯海峡,部分位于欧洲,部分位于亚洲,是土耳其的金融中心。
324年,随着东部和西部罗马帝国的重新统一,古老的拜占庭城被选为罗马帝国的新首都,君士坦丁大帝将其命名为“新罗马”,或“Nova Roma”。330年5月11日,它被命名为君士坦丁堡,并献给君士坦丁。君士坦丁。
页面:莫斯科,第三罗马
摘要:莫斯科,第三罗马(俄语:Москва — третий Рим;Moskva, tretii Rim)是一个神学和政治概念,认为莫斯科是古罗马的继承者,俄罗斯世界在传承罗马帝国的遗产。术语“第三罗马”指的是欧洲文化中一个历史话题:继“第一罗马”(罗马,位于西部罗马帝国)和“第二罗马”(君士坦丁堡,位于东部罗马帝国)之后继任的城市。
页面:拜占庭帝国
摘要:拜占庭帝国,也被称为东部罗马帝国,是罗马帝国在君士坦丁堡的延续,其统治范围在晚期古代和中世纪时期。该帝国的东部部分在5世纪末的条件导致西部帝国崩溃之后继续存在,直到1453年被奥斯曼帝国攻陷。在它的大部分存在时间内,该帝国一直是地中海世界最强大、经济、文化和军事力量。该术语“拜占庭帝国”是在帝国灭亡之后才被提出;其公民将政体称为“罗马帝国”,并自称“罗马人”。由于帝国的首都从罗马迁至拜占庭,采用国家基督教,以及希腊语取代拉丁语,现代历史学家继续将早期罗马帝国与后期拜占庭帝国区分开来。
在早期和平罗马时期,帝国的西部部分
代理:从“君士坦丁堡的陷落”页面的摘要中,我可以看到该事件发生在1453年。我将使用 `查询信息` 工具来查找更多关于该事件的详细信息,特别是当时奥斯曼帝国的领导人是谁。
代码:
```py
查询信息("1453年奥斯曼帝国的领导人", "君士坦丁堡的陷落")
```<end_action>
观察:打印输出:
代码片段最后输出:
从页面 '君士坦丁堡的陷落' 中获取的查询 '1453年奥斯曼帝国的领导人' 的信息:
1453年奥斯曼帝国的领导人是穆罕默德二世,也被称为征服者。
代理:从摘要中可以发现,1453年奥斯曼帝国的领导人是穆罕默德二世,也被称为征服者。我将使用 `最终答案` 工具来提供答案。
代码:
```py
最终答案("1453年,奥斯曼帝国的领导人是穆罕默德二世,也被称为征服者。")
```<end_action>
观察:打印输出:
代码片段最后输出:
1453年,奥斯曼帝国的领导人是穆罕默德二世,也被称为征服者。
------------------------------
任务:1453年的中国王朝
代理:我将使用 `查询维基百科` 工具查找1453年左右的中国王朝的信息。我将提供一个与该时期中国王朝相关的通用查询。
代码:
```py
查询维基百科("1453年的中国王朝")
```<end_action>
观察:打印输出:
代码片段最后输出:
查询 '1453年的中国王朝' 找到的页面:
页面:中文帝国
摘要:中文帝国(或称中国帝国)是指由中国的皇帝统治的领域,在中国帝国时代被称为华夏帝国。该术语由西方学者使用,用来描述明清两代(或一般意义上的华夏帝国)。另一个术语是“天朝帝国”,指的是皇帝作为天子的地位。公元前221年,中国第一次被统一在一位皇帝的统治之下,从那时起,由世袭君主统治的各个朝代或帝国统治中国长达两千年,包括秦、汉、晋、隋、唐、宋、元、明和清。
页面:明朝
摘要:明朝,正式名称为大明,是中国的王朝,从1368年到1644年,继蒙古统治的元朝崩溃后统治中国。明朝是最后一个由汉族统治的中国皇帝王朝,汉族是中国的主要民族。虽然主要首都北京在1644年被李自成领导的起义军攻陷(他建立了短暂的顺朝),但是由明朝皇室残余统治的各个南明朝政权在1662年之前继续存在。
明朝的创始人洪武帝(1368年至1398年在位)试图建立一个由自给自足的农村社区组成的严格、固定的社会体系,这个体系将保证并支持他王朝永久的士兵阶层:帝国的常备军超过一百万士兵,南京的海军船坞是当时世界上最大的。
页面:时间周期列表
摘要:将过去划分成命名的、有时间界定的时期叫做时代划分。这是在各个研究领域定义的各种时间周期名称的列表。
这些可以被大致分为前历史时期和有历史记录的时期
(当开始书写记录时)。
在考古学和人类学中,前历史时期被分为三阶段系统,这个列表包括使用三阶段系统和参考三阶段系统中的各个子阶段的各种用法。
每个阶段的日期可以因地区不同而不同。在地质时间尺度上,全新世时期从最后一个冰河时期的结束开始(约公元前10000年)并持续到今天。通常认为,新石器时代的开始对应于全新世时期的开始。
页面:天朝帝国
摘要:天朝帝国(中文:天朝;拼音:Tiāncháo;意为“天之王朝”)是古代中国或中国帝国的一个旧名称,这是对中国术语的文学性翻译,是中国文化圈中的许多中国名称之一。该名称是基于皇帝在中国文化圈中的地位作为天子而使用的。
因此,在19世纪,该名称“天朝”用来指中国人。这两个术语在当时的英文大众媒体中被广泛使用,但后来逐渐被废弃。到了2015年,该术语的使用又变得流行起来,尤其是在中文互联网用户中。该术语被用来指21世纪作为超级大国出现的中国共产党政权,根据上下文的不同,可以表示对该政权的政治镇压和傲慢的不满,或者对中国作为一个国家在21世纪崛起的民族自豪感。
现代时期,该名称大多已经过时,但在各种媒体中仍然可以看到“天朝帝国”的名称,如
页面:朝代列表
摘要:这是按地理区域组织的王朝和宗族政权的列表。现存的王朝用粗体表示并突出显示。
代理:从摘要中,我可以看出明朝从1368年统治到1644年,涵盖1453年。我将使用 `查询信息` 工具来查找1453年明朝更详细的信息。
代码:
```py
查询信息('1453年', '明朝')
```<end_action>
观察:打印输出:
代码片段最后输出:
从页面 '明朝' 中获取的查询 '1453年' 的信息:
1453年属于明朝统治的时期,明朝在中国的统治从1368年到1644年。在这一时期,明朝控制了中国,并经历了诸如建造紫禁城和扩建长城等重大发展。
代理:现在我知道明朝在1453年处于统治地位,我可以提供关于该年在中国发生的事情的更具体答案。但是提供的信息没有给出1453年具体的事件。我将使用 `查询信息` 工具来查找1453年明朝更详细的信息。
代码:
```py
查询信息('1453年', '明朝')
```<end_action>
观察:打印输出:
代码片段最后输出:
从页面 '明朝' 中获取的查询 '1453年' 的信息:
1453年属于明朝统治的时期,明朝在中国的统治从1368年到1644年。在这一时期,明朝控制了中国,并经历了诸如建造紫禁城和扩建长城等重大发展。
代理:从提供的信息可以看出,明朝在1453年统治着中国并经历了重大发展。但没有具体提到1453年的事件。我将使用 `查询信息` 工具来查找1453年明朝更详细的信息。
代码:
```py
查询信息('1453年', '明朝')
```<end_action>
观察:打印输出:
代码片段最后输出:
从页面 '明朝' 中获取的查询 '1453年' 的信息:
1453年属于明朝统治的时期,明朝在中国的统治从1368年到1644年。在这一时期,明朝控制了中国,并经历了诸如建造紫禁城和扩建长城等重大发展。
代理:提供的信息表明,明朝在1453年统治着中国。但没有提到具体在1453年发生了什么事件。我将使用 `查询信息` 工具来查找1453年明朝更详细的信息。
代码:
```py
查询信息('1453年', '明朝')
```<end_action>
观察:打印输出:
代码片段最后输出:
从页面 '明朝' 中获取的查询 '1453年' 的信息:
1453年属于明朝统治的时期,明朝在中国的统治从1368年到1644年。在这一时期,明朝控制了中国,并经历了诸如建造紫禁城和扩建长城等重大发展。
代理:提供的信息没有具体提到1453年的事件。但我们可以推断,在明朝时期发生了重大发展。我将使用 `查询信息` 工具来查找1453年明朝更详细的信息。
代码:
```py
查询信息('1453年', '明朝')
```<end_action>
观察:打印输出:
代码片段最后输出:
从页面 '明朝' 中获取的查询 '1453年' 的信息:
1453年属于明朝统治的时期,明朝在中国的统治从1368年到1644年。在这一时期,明朝控制了中国,并经历了诸如建造紫禁城和扩建长城等重大发展。
代理:提供的信息表明,明朝在1453年统治着中国。但是没有具体提到1453年的事件。我将使用 `最终答案` 工具来提供基于我们所知信息的答案。
代码:
```py
最终答案("1453年,明朝统治着中国。提供的信息中没有提到1453年的具体事件。")
```<end_action>
观察:打印输出:
代码片段最后输出:
1453年,明朝统治着中国。提供的信息中没有提到1453年的具体事件。
进一步深入页面搜索代理的执行,我们看到这还多次重复了带有相同参数的工具调用。
任务:查询“1453年奥斯曼帝国的领导人”,从维基百科页面“君士坦丁堡的陷落”获取相关信息。
代理:思考:我需要找出1453年的奥斯曼帝国领导人是哪位。我将使用`retrieve_passages`工具在维基百科页面“君士坦丁堡的陷落”中搜索此信息。
代码:
```py
result = retrieve_passages("1453年的奥斯曼帝国领导人")
print(result)
<end_action>
观察:
检索到的查询“1453年的奥斯曼帝国领导人”的段落:
段落 0:当穆罕默德二世于1451年继承其父亲时,他19岁。许多欧洲宫廷认为这位年轻的奥斯曼统治者不会认真挑战巴尔干和爱琴海的基督教霸权。实际上,欧洲庆祝穆罕默德登基,并希望他的缺乏经验会将奥斯曼帝国引入歧途。这种计算因为穆罕默德对欧洲使节的友好姿态而得到加强。然而,穆罕默德的温和言辞没有与他的行动相匹配……
段落 1:……进攻君士坦丁堡的奥斯曼军队远远多于君士坦丁堡的守军,由21岁的苏丹穆罕默德二世(后来被称为“征服者”)指挥,而拜占庭军队则由皇帝康斯坦丁十一世·帕列奥列格斯领导。征服该城后,穆罕默德二世将君士坦丁堡设为新奥斯曼首都,取代了阿德里安堡……
段落 2:……长子改名为穆拉德,成为穆罕默德的宠臣,并担任鲁梅利亚(巴尔干)的贝伊尔贝伊(总督)。小儿子改名为梅希帕夏,成为奥斯曼舰队的海军元帅和加利波利省的桑卡克贝(总督)。他最终在拜扎德二世的统治下两次担任大维齐尔……
段落 3:……==外部链接==
……
代理:思考:从这些段落可以推断,1453年的奥斯曼帝国领导人是穆罕默德二世,也被称为穆罕默德征服者。我将使用final_answer
工具提供答案。
代码:
final_answer("1453年的奥斯曼帝国领导人是穆罕默德二世,也被称为穆罕默德征服者。")
<end_action>观察:
代码片段的最后输出:
1453年的奥斯曼帝国领导人是穆罕默德二世,也被称为穆罕默德征服者。
任务:查询“1453年”,从维基百科页面“明朝”获取相关信息。
代理:思考:我需要找到有关年份1453的信息。我将使用retrieve_passages
工具获取相关段落。
代码:
result = retrieve_passages("1453年 明朝")
print(result)
<end_action>
观察:
检索到的查询“1453年 明朝”的段落:
段落 0:……明朝,正式名称为大明,是中国的一个朝代,1368年蒙古统治的元朝崩溃后,明朝统治中国,直到1644年。明朝是中国最后一个由汉族统治的朝代……
段落 1:……===参考书籍和主要来源===
……
段落 2:……===洪武皇帝的统治 ===
……
段落 3:……明朝是中国最后一个由汉族统治的朝代,直到1644年北京被李自成领导的叛乱所占领(建立了短期的顺朝),一些明朝的残余王朝(被称为南方明朝)一直持续到1662年……
段落 4:……朱棣登基为永乐皇帝(1402-1424年在位),他的统治被学者们普遍视为明朝的“第二次创立”,因为他逆转了许多他父亲的政策……
代理:思考:这些段落并没有直接提到1453年。然而,根据这些信息可以推断,1453年属于明朝的统治时期,其统治时间是从1368年到1644年。在这段时间里,明朝统治着整个中国,并经历了许多重大发展,例如故宫的建造和长城的扩建。
代码:
final_answer("1453年属于明朝的统治时期,其统治时间是从1368年到1644年。在这段时间里,明朝统治着整个中国,并经历了许多重大发展,例如故宫的建造和长城的扩建。")
结论段
在这篇文章里,我解释了如何使用代码代理和一个较小的开源LLM(例如Qwen2.5–7B-Instruct)来创建一个多代理RAG架构。我讨论了主要的架构特点,并分享了相对于Hugging Face代码代理实现的一些特定选择,以提高效果。完整的代码细节可以在该GitHub仓库中找到。
描述的多代理体系,尽管这个小型模型是在消费级硬件上运行的,能够解决与复杂查询相关的多步问答任务。尤其:
- 它可以将查询拆分为可管理的小任务;
- 它可以识别包含所需信息的维基百科页面;
- 它可以整合来自多个页面的信息;
- 它可以在维基百科页面上查找详细信息;
- 它可以判断是否需要更多资料并尝试查找;
- 它能够修正生成的代码中的小错误,并处理工具中的错误(例如维基百科的歧义问题)。
我也概述了系统的一些限制,例如增加的计算时间、重复的操作以及幻觉可能的传播。后者可以通过在系统中添加一个“校对”代理来减轻,该代理会检查报告的信息是否与检索到的来源相符。
值得注意的是,由于代理系统架构的核心采用了RAG方法,所有常规的提高效率和准确性的技术都可以在这个框架下应用。
另一种可能的改进是使用技术增加测试计算时间,让模型有更多时间“思考”,类似于OpenAI的o1和o3模型。不过需要注意的是,这种改动还会进一步增加运行时间。
最后,由于多代理系统由专注于单一任务的代理组成,为每个代理使用不同的模型引擎可能会提升性能。特别是,可以为系统中的每个任务微调不同的模型,进一步提升性能。这对小型模型特别有好处。值得一提的是,我们可以通过在一组预定义的任务上运行系统,并在系统产生正确答案时保存代理的输出来收集微调数据,这样就避免了昂贵的人工标注数据的需要。