大语言模型Agent综述与实践

953 阅读44分钟

Agent是一个由来已久的概念,其可以表示一个能够感知环境、做出决策并采取行动的系统。在大语言模型横空出世之前,Agent的研究与应用已经经历了长期的发展,最近一个发展的高潮是基于强化学习的Agent。基于强化学习的Agent和环境之间具体的交互方式如图11所示。在每一轮交互中,Agent感知到环境目前所处的状态,经过自身的计算给出本轮的动作,将其作用到环境中;环境得到Agent的动作后,产生相应的即时奖励信号并发生相应的状态转移,Agent则在下一轮交互中感知到新的环境状态,依次类推。目前基于强化学习的Agent已广泛应用于机器人、自动驾驶、游戏决策等多个领域。 图1 基于强化学习的Agent和环境之间具体的交互方式 而随着大语言模型的横空出世,基于大语言模型的Agent也在飞速发展。2023年6月OpenAI安全系统团队负责人Lilian Weng发表的博文《LLM Powered Autonomous Agents》对基于大语言模型的Agent进行了综述,另外,大语言模型相关的开发框架(例如LangChain)也对基于大语言模型的Agent进行了支持。目前众多理论研究者和应用开发者在不断提升基于大语言模型的Agent在各自然语言任务中的效果,并探索基于大语言模型的Agent在各领域的应用场景。 本文前半部分是对《LLM Powered Autonomous Agents》这篇综述的阅读笔记,后半部分是基于大语言模型的Agent的实践。如有不足之处,请指正。

综述

总体

基于大语言模型的Agent其核心是大语言模型,大语言模型承担着大脑的角色,用于思考和规划,而围绕着大语言模型,Agent还包含记忆和工具,记忆用于存储短期上下文信息和长期知识信息,工具则承担着感官和四肢的角色,在大语言模型的思考和规划下,Agent一方面可以通过工具获取外部的各种信息用于进一步的思考和规划,另一方可以通过工具执行动作对外部环境施加影响。 图2 基于大语言模型的Agent的整体架构 《LLM Powered Autonomous Agents》中使用图2描述了基于大语言模型的Agent的整体架构,其中包含以下核心组件:

  • 规划(Planning),大语言模型作为Agent的大脑负责思考和规划,而思考和规划的方式又可以分为两部分:
    • 分而治之(Task Decomposition):对于复杂的任务,大语言模型会将其分解为多个相对简单的子任务,每个子任务包含独立的子目标,从而分而治之、逐步求解;
    • 自我反思(Self-Reflection):大语言模型会对过去的规划和执行进行自我反思,分析其中的错误,并对后续的思考和规划进行改进,完善最后输出的结果。
  • 记忆(Memory),记忆是对大语言模型本身由模型结构和参数所蕴含知识的补充,记忆又可以分为短期记忆和长期记忆:
    • 短期记忆,即大语言模型的上下文学习,包括提示、指示、前序步骤的大语言模型推理结果和工具执行结果等;
    • 长期记忆,即外部可快速检索的向量索引,这也就是目前比较流行的一种大语言模型应用的解决方案——RAG(Retrieval-Augmented Generation,检索增强生成)。RAG的流程可以简单概括为,将包含知识的文档切分为块,并对块向量化,构建块向量索引,然后将问题也向量化,然后从块向量索引中检索和问题相关的块,最后将块和问题合并作为大语言模型的输入进行推理。RAG可以有效缓解大语言模型无法扩展知识、由知识局限产生的“幻觉”的问题。
  • 工具(Tool),工具作为Agent的感官和四肢,Agent一方面可以通过工具获取外部的各种信息用于进一步的思考和规划,例如通过搜索引擎搜索某个关键词的最新信息,另一方可以通过工具执行动作对外部环境施加影响,例如调用外部系统的接口执行指令并获取执行结果。

《LLM Powered Autonomous Agents》接下来对以上核心组件进行详细介绍。

规划(Planning)

大语言模型作为Agent的大脑负责思考和规划,而思考和规划的方式又可以分为分而治之和自我反思。

分而治之

对于复杂的任务,大语言模型会将其分解为多个相对简单的子任务,每个子任务包含独立的子目标,从而分而治之、逐步求解。

Chain of Thought

2022年OpenAI科学家Jason Wei发表的论文《Chain-of-Thought Prompting Elicits Reasoning in Large Language Models》提出了一种提示模式——思维链(Chain of Thought,CoT),其针对复杂问题,通过在提示中增加逐步思考的示例或类似“think step by step”的指示,让大语言模型将原始问题拆解为多个简单问题,逐步进行思考,这样可以使得大语言模型在推理阶段,使用更多的计算资源,并且对复杂问题分步求解也符合人类的思维方式,大语言模型输出分步求解过程也使得其思考过程对外可见,最终结果可解释。论文使用思维链在数学问题求解上取得了较高的效果。目前思维链已经成为大语言模型求解复杂问题时的一种标准提示技巧。 图3右侧是论文列出的思维链提示示例,其在提示中增加逐步思考的示例,示例的问题是“Roger has 5 tennis balls. He buys 2 more cans of tennis balls. Each can has 3 tennis balls. How many tennis balls does he have now?”,示例的回答先给出思考过程“Roger started with 5 balls. 2 cans of 3 tennis balls each is 6 tennis balls. 5 + 6 = 11.”,即初始有5个球,又购买了2桶(每桶3个球)共6个球,则球的总数是5和6之和,示例的回答最后再给出答案11,提示最后列出需要大语言模型回答的问题“The cafeteria had 23 apples. If they used 20 to make lunch and bought 6 more, how many apples do they have?”,相比于未使用思维链提示时大语言模型直接输出错误的答案,使用思维链提示后,大语言模型也按照思维链提示中的示例进行逐步思考——“The cafeteria had 23 apples originally, They used 20 to make lunch. So they had 23 - 20 = 3. They bought 6 more apples, so they have 3 + 6 = 9”,即初始有23个苹果,使用了20个,剩余3个,又买了6个,则苹果总数是3和6之和,并在最后输出最终的正确答案。 图3 思维链示例

Tree of Thoughts

2023年由Google和普林斯顿大学发表的论文《Tree of Thoughts: Deliberate Problem Solving with Large Language Models》提出了另一种提示模式——思维树(Tree of Thoughts,ToT),其在思维链的基础上扩展,对于思维链中的每一步,会生成多个思考结果,这样就形成如图4右侧的树状结构,而每一步实际采用哪个思考结果,这其实就是在树状结构中求解一个从根节点到某个叶子节点的最优路径,求解的过程可以采用宽度优先遍历(BFS,Breadth-First Search)或深度优先遍历(DFS,Depth-First Search),而在遍历过程中,对于每步的多个思考结果如何选择,论文仍使用大语言模型,通过构建提示由大语言模型对多个思考结果进行评分或投票,选择分数最高或得票最多的思考结果。论文采用思维树在多个复杂任务(24点游戏、创意写作和填字游戏)上取得了较好的效果。 图4 思维树和其他提示模式的对比 图5是在24点游戏中使用思维树的一个示例,对于原始问题“4 9 10 13”,论文构建以下提示“Input: 4 9 10 13\n Possible next steps:”由大语言模型进行第一步思考,给出可能的多个思考结果,包括“4 + 9 = 13(left: 10 13 13)”(4和9相加后剩余10、13、13)和“10 - 4 = 6(left: 6 9 13)”(10和4相减后剩余6、9、13),然后论文对上述多个思考结果中的数字组合分别构建提示,由大语言模型评估是否可以通过四则运算得到24,例如,对于“10 13 13”,构建以下提示“Evaluate if given numbers can reach 24(sure/likely/impossible)\n 10 14: 10 + 14 = 24. sure\n 10 13 13”,由大语言模型推理输出“(13 - 10) * 13 = 3 * 13 = 39\n 10 + 13 + 13 = 36\n there is no way to obtain 24 whith these big numbers. impossible”,即“10 13 13”无法通过四则运算得到24,则对于第一步思考的多个结果,选择“6 9 13”继续进行第二步的思考,如此迭代,直至最后一步中存在一个思考结果仅剩余24,即从该思考结果向上回溯至第一步思考结果所构成的四则运算过程(图5中的绿色路径)能够从原始问题“4 9 10 13”开始计算得到24。 图5 在24点游戏中使用思维树的一个示例

LLM+P

2023年由得克萨斯大学奥斯汀分校和纽约州立大学宾汉姆顿分校发表的论文《LLM+P: Empowering Large Language Models with Optimal Planning Proficiency》则另辟蹊径,提出了如图6所示的LLM+P框架,其在大语言模型的基础上,引入传统的用于解决智能规划问题的规划领域定义语言(Planning Domain Definition Language,PDDL),其第一步先将用自然语言描述的问题通过大语言模型转化为用PDDL描述的问题,这一步大语言模型承担类似机器翻译的职责,第二步再将用PDDL描述的问题通过特定领域的规划引擎推理得到用PDDL描述的规划,第三步将用PDDL描述的规划再通过大语言模型转化为用自然语言描述的规划,这一步大语言模型也承担类似机器翻译的职责。 图6 LLM+P框架 图7是论文给出的一个基于LLM+P求解问题的示例,该问题是使用机械手臂移动多个箱子,并使得各个箱子的上下位置满足一定的约束条件,需要给出机械手臂移动箱子的步骤,论文通过构建提示并增加示例,由GPT-4准确输出了用PDDL描述的移动箱子的问题,并由规划引擎准确输出了用PDDL描述的移动箱子的步骤。 图7 基于LLM+P求解问题的示例 LLM+P框架引入了规划领域定义语言和规划引擎,因此比较适合某些特定领域下的问题求解,例如,上述示例所对应的机器人领域,但可能不一定适用于其他领域。

自我反思

大语言模型会对过去的规划和执行进行自我反思,分析其中的错误,并对后续的思考和规划进行改进,完善最后输出的结果。

ReAct

2023年由Google和普林斯顿大学发表的论文《ReAct: Synergizing Reasoning and Acting in Language Models》提出了另一种大语言模型思考和规划方式——ReAct。前面提到的思维链如图8左侧所示,仅依赖大语言模型进行多步推理,是一种“Reason Only”的思考和规划方式,缺乏执行,不能从外部获取信息,容易造成幻觉,并且错误会在多步推理中传播,另外也不能对外部施加影响,限制了Agent的作用。而ReAct的思考和规划方式如图8右侧所示,整体也是将复杂问题拆分为多个简单问题分步思考和规划,但和思维链不同的是,在每步思考和规划中,ReAct同时包含了推理(Reason)和执行(Action),通过大语言模型的思考和规划,既输出由自然语言描述的思考过程,也输出格式化的工具指令,而论文中设计的工具是可以对维基百科进行关键词检索的API,因此执行大语言模型输出的工具指令可以获取外部信息,将外部信息作为观察结果,和思考过程、工具指令合并,作为大语言模型下一步思考和规划的上下文。论文通过组合推理和执行,能够有效获取外部信息作为大语言模型的知识补充,从而在知识问答(HotpotQA)和事实验证(FEVER)等任务上取得较好的效果,而ReAct的命名即“Reason”(推理)和“Action”(执行)的组合、缩写。 图8 ReAct和其他思考和规划方式的对比 具体如何实现ReAct?图9右侧是论文中给出的一个示例,其通过提示工程让大语言模型对复杂问题分步思考和规划,每步思考和规划按以下格式进行推理和执行:

Thought:[思考过程]

Action:[工具指令]

Observation:[观察结果]

每步思考和规划时,首先由大语言模型在“Thought”和“Action”后分别输出思考过程和工具指令,然后Agent解析“Action”后输出的工具指令,调用相应的工具执行指令,获取执行结果后,再拼接到“Observation”的后面。论文中设计的工具是可以对维基百科进行关键词检索的API,其包含三种格式的指令:

  • Search[entity]:返回和“entity”最相关的5条语句,若不存在,则返回和“entity”最相似的5个实体;
  • Lookup[string]:返回包含“string”的语句;
  • Finish[answer]:结合上下文和大语言模型本身蕴含的知识,已获取当前任务的答案并返回。

图9右侧的示例,原问题是“Aside from the Apple Remote, what other device can control the program Apple Remote was originally designed to interact with?”,即“除了苹果遥控器之外,还有哪些设备可以控制最初设计用于和苹果遥控器交互的程序”,大语言模型第一步思考和规划给出的思考过程和工具指令分别是“I need to search App Remote and find the program it was originally designed to interact with.”和“Search[Apple Remote]”,即检索和“Apple Remote”相关的语句,查找和其交互的程序,因此Agent调用工具的API获取相关信息,拼接到“Observation”的后面,和第一步的思考过程、工具指令合并,作为大语言模型第二步思考和规划的上下文,大语言模型第二步思考和规划给出的思考过程和工具指令分别是“Apple Remote was originally designed to control the Front Row media center program. I need to search Front Row next and find what other device can control it.”和“Search[Front Row]”,即检索和“Front Row”相关的语句,查找可控制“Front Row”的设备,如此经过共4步的思考和规划过程,最终大语言模型输出正确的答案“keyword function keys”,即“keyword function keys”也可以控制最初设计用于和苹果遥控器交互的程序。图9 ReAct和其他思考和规划方式在知识问答任务中的示例 如何控制大语言模型对于原始问题分步思考和规划,且每步按照固定格式分别输出思考过程和工具指令?论文采用的方案是在提示中增加多个示例以引导大语言模型对后续的问题按照ReAct方式进行分步思考和规划。图10是论文在附录中记录的两个示例。 图10 提示中的示例以引导大语言模型对后续问题按照ReAct方式进行思考和规划 目前LangChain等大语言模型的开发框架也支持ReAct方式的分步思考和规划,其中,对于大语言模型,一般是在提示中增加工具功能和指令格式的描述,并增加让模型分步按格式输出思考过程和工具指令的指示,以引导大语言模型按照ReAct方式进行思考和规划,而对于大语言会话模型,一般是基于大语言会话模型的“Function Calling”能力,在和大语言会话模型的交互中,除输入会话消息外,还输入多个可选函数的描述,由大语言会话模型返回需要调用的函数和参数。后面在实践部分,会详细介绍如何在LangChain中实现ReAct方式的分步思考和规划。目前,ReAct已成为一种常用的基于大语言模型和外部工具求解复杂问题的思考和规划方式。

Reflexion

ReAct虽然在思维链的基础上,融合推理和执行,但其本质仍是一种链式多步思考和规划的过程,并没有对过去的思考和规划进行自我反思,分析其中的错误,对后续的思考和规划进行改进,完善最后输出的结果。2023年由东北大学、麻省理工学院、普林斯顿大学发表的论文《Reflexion: Language Agents with Verbal Reinforcement Learning》提出了Reflexion框架,实现对大语言模型思考和规划的自我反思,该框架的原理借鉴了强化学习,如图11所示,其会对原始问题进行多次尝试,而每次尝试均会使用三个模型——“Actor”、“Evaluator”和“Self-reflection”,这三个模型实际均是大语言模型,首先由“Actor”模型按照思维链或ReAct方式对原始问题进行分步思考,得到答案,生成包含多步思考的轨迹(Trajectory,这里使用了强化学习中的概念),然后由“Evaluator”模型对轨迹进行评估,这里不同类型的任务的评估方式不同,例如,对于决策任务,评估其是否有效(多步思考后仍未成功)或是否产生幻觉(多步思考生成重复的思考结果),对于编码任务,生成单测并评估其是否通过单测,对于推理任务,评估其是否精确匹配正确答案,最后再由“Self-reflection”模型根据轨迹和评估结果进行自我反思,生成由自然语言描述的反思结果,放入长期记忆中,在下次尝试时,作为上下文,引导“Action”模型在思考过程中避免类似错误,完善思考结果。 图11 Reflexion框架 图12是论文列出的在决策、编码和推理任务中应用Reflexion框架的示例,由于引入了自我反思机制,其在各任务上均取得了较好的效果。 图12 在决策、编码和推理任务中应用Reflexion框架的示例 图13是论文在附录列出的在推理任务中应用Reflexion框架的完整示例,问题是“Grown-Ups starred the actor who was best known for which role on "'Allo 'Allo!"?”,即“主演Grown-Ups的演员最知名的角色是"'Allo 'Allo!"中的哪个角色”,第一次尝试的思考过程给出了错误的答案,“Self-reflection”模型反思的结果是“I searched the wrong title for the show, "’Allo ’Allo!", which resulted in no results. I should have searched the show’s main character, Gorden Kaye, to find the role he was best known for in the show.”,即下次尝试应该搜索主演Grown-Ups的演员——“Sam Kelly”的相关信息,而第二次尝试便按照反思的建议进行思考,给出了正确的答案。 图13 在推理任务中应用Reflexion框架的完整示例

Chain of Hindsight

2023年由加州大学伯克利分校发表的论文《Chain of Hindsight Aligns Language Models with Feedback》提出了回顾链(Chain of Hindsight,CoH),引入人类对大语言模型输出的反馈作为训练数据,对大语言模型进行微调,使得大语言模型能够对齐人类的偏好。 图14 CoH的微调方案示意图

CoH的微调方案示意图如图14所示,对于同一个问题(例如“How to explain neural networks to a child?”),大语言模型已生成多个答案,且每个答案已由人类反馈答案的好坏,将人类反馈的好答案和坏答案配对,构建成训练数据集,如下所示:

“How to explain neural networks to a child? {A} is less preferred compared with {B}”

“How to explain neural networks to a child? A good answer is: {B} A bad answer is: {A}”

“How to explain neural networks to a child? Bad: {A} Good: {B}”

然后使用上述数据集对大语言模型进行微调。因为基于事后人类的反馈序列(a sequence of hindsight feedback)对大语言模型进行微调,所以论文将上述方案称为回顾链(Chain of Hindsight,CoH)。

记忆(Memory)

图15 人类记忆的分类 记忆可以被定义为获取、存储和提炼信息的过程,而人类的记忆分为以下几类:

  • 感知记忆(Sensory memory),进一步可分为视觉记忆(Iconic memory),听觉记忆(Echoic memory),触觉记忆(Haptic memory);
  • 短期记忆(Short-term memory);
  • 长期记忆(Long-term memory),进一步可分为显示陈述性记忆(Explicit/declarative memory,即事实或概念)和隐式程序化记忆(即技能)。

而Agent中的各类记忆可以上述人类的各类记忆相对应:

  • Agent中的感知记忆即用户输入的原始问题,包括文本、图片、视频、链接等;
  • Agent中的短期记忆即大语言模型的上下文学习,包括提示、指示、前序步骤的大语言模型推理结果和工具执行结果等;
  • Agent中的长期记忆即外部可快速检索的向量索引,这也就是目前比较流行的一种大语言模型应用的解决方案——RAG(Retrieval-Augmented Generation,检索增强生成)。

关于RAG目前也有大量的研究和应用探索,2023年同济大学和复旦大学联合发表的论文《Retrieval-Augmented Generation for Large Language Models: A Survey》对RAG进行了综述。RAG的流程如图16所示,其主要包含三步:

  • 索引(Indexing),将文档切分为块(Chunk),将块通过Embedding模型编码为向量,然后再将块向量存入向量数据库,构建向量索引;
  • 检索(Retrieval),将问题也通过Embedding模型编码为向量,然后从向量索引中检索和问题向量最相近的若干个块向量;
  • 生成(Generation),将检索召回的块和原始问题合并作为大语言模型的输入,由模型推理给出最终的回答。

图16 RAG的主要流程 目前,众多研究在以上三步的基础上,对RAG进行进一步优化,以提升效果,相关工作读者可以进一步阅读RAG的综述论文,笔者这里不再展开,只简单列出RAG发展过程中的三个阶段:

  • Naive RAG(简单RAG),由索引(Indexing)、检索(Retrieval)、生成(Generation)三部分构成的链式流程;
  • Advanced RAG(高级RAG),流程和Naive RAG类似,仍是链式流程,只是在检索(Retrieval)前后增加前置(Pre-Retrieval)和后置(Post-Retrieval)处理,前置处理包括查询路由(Query Routing)、查询改写(Query Rewriting)、查询扩展(Query Expansion),后置处理包括重排(Rerank)、摘要(Summary)、融合(Fusion);
  • Modular RAG(模块化RAG),将Naive RAG和Advanced RAG中的各步处理抽象为模块,从而可以灵活地组织各个模块生成不同的RAG方案,例如串联Retrieve、Read模块即生成Naive RAG,串联Rewrite、Retrieve、Rerank、Read模块即生成Advanced RAG。

图17 RAG发展过程中的三个阶段

Maximum Inner Product Search (MIPS)

长期记忆依赖外部可快速检索的向量索引,因此《LLM Powered Autonomous Agents》在记忆这部分继续介绍了目前向量索引实现快速检索的常用算法。向量索引保存了块向量,需要从中检索和问题向量最相近的若干个块向量,该检索过程即最大内积检索(Maximum Inner-Product Search,MIPS)。实现最大内积检索的一种简单算法即对向量索引中的所有块向量,计算和问题向量的内积,然后再按内积倒序排序,选取靠前的若干块向量返回。上述算法在向量索引中的块向量数量较少时可行,但当数量较大时,则会因计算量过大导致无法实时返回,因此,目前一般采用一些近似近邻(Approximate Nearest Neighbors,ANN)算法以牺牲一定计算准确度的代价、保证大数据量下的若干邻近向量的实时返回。以下列出一些常用的近似近邻算法。

LSH

《A Survey on Locality Sensitive Hashing Algorithms and their Applications》这篇论文对局部敏感哈希(Locality-Sensitive Hashing,LSH)算法及其应用进行综述,以下简单介绍一下局部敏感哈希算法的原理。对于dd维向量空间的数据集DRdD\in\mathbb{R}^d中的两个点xxyy,若哈希函数HH满足以下条件,则可以认为其满足局部敏感特性:

if xyR, then Pr[H(x)=H(y)]p1, andif xy>cR, then Pr[H(x)=H(y)]p2\begin{align} &\text{if }|x-y|\le R\text{, then }Pr[H(x)=H(y)]\ge p_1\text{, and} \\ &\text{if }|x-y|\gt cR\text{, then }Pr[H(x)=H(y)]\le p_2 \end{align}

即当xxyy的距离小于或等于RR时,两者哈希值相等的概率大于或等于p1p_1,当xxyy的距离大于或等于cRcR时,两者哈希值相等的概率小于或等于p2p_2图18 常用哈希算法和局部敏感哈希算法的不同 图18形象地描述了常用哈希算法和局部敏感哈希算法的不同,常用哈希算法尽可能将各值打散到不同的分桶中,避免哈希冲突,而局部敏感哈希算法则尽可能将相近的值路由到同一个分桶中。如果需要在高维向量空间中查找某个点邻近的其他点,则可以先路由到该点所在的分桶中,然后再对该分桶中的所有点求解和原点的距离,最后选取距离最小的点返回。由于只对分桶中的所有点求解和原点的距离,这样相比于原先对向量空间中所有点求解和原点的距离,可以大大减少计算量,从而以牺牲一定计算准确度的代价(相近的点也有较小的概率路由到不同的分桶),提升查找的响应速度。

ANNOY

ANNOY(Approximate Nearest Neighbors Oh Yeah)是一个开源的C++库。ANNOY在索引构建时会构建多棵二叉树,而每棵二叉树的构建过程均从向量空间中的所有点开始,首先随机选择两个点,然后用两点的中垂线将所有点切分为两部分,构造一个二叉树,树的根节点表示切分的中垂线,树的左右子节点分别表示切分后的两部分点的某一部分,然后再分别对左右子节点,从该节点所表示的某一部分点中随机选择两个点,用两点的中垂线将该部分的点再切分为两部分,如此递归,直至最后的叶子节点中只包含最多K个点,图19表示对所有点进行某次切分得到的切分结果平面图。ANNOY在索引查询时会查询多棵二叉树,而每棵二叉树的查询过程从根节点开始逐层往下遍历,在每层中查询与原点距离最近的子节点,直至最后的叶子节点,返回叶子节点中包含的点,再将各棵树返回的点合并,对其再次排序返回最终与原点最近的若干个点,因此通过ANNOY查询的时间复杂度即二叉树的深度——O(logN)O(\log N)图19 对所有点进行某次切分得到的切分结果平面图

HNSW

HNSW算法是基于图的最近邻搜索算法之一。《Approximate nearest neighbor algorithm based on navigable small world graphs》《Efficient and robust approximate nearest neighbor search using hierarchical navigable small world graphs》这两篇论文先后于2014年和2018年提出了NSW(Navigable Small World)算法和HNSW(Hierarchical Navigable Small World)算法进行图的构建和搜索,其中,HNSW在NSW的基础上,引入分层思想,效果更好。HNSW在索引构建时会构建多层图,如图20所示,最下面一层包含所有点,逐层向上,每层包含的点逐渐减少,每层的点均被构建为一个NSW图,如图21所示,在一个NSW图中,若要查找离原点(图中绿点)最近的点,则随机从一个点(图中的entry point)开始沿红边遍历各点,记录离原点最近的点。HNSW在索引查询时从最上面一层开始逐层向下遍历,在最上面一层,从当前层的NSW图中随机选择一个点开始,然后按照前述遍历NSW图的方法,查找到离原点最近的点,然后移至下一层,从上一层离原点最近的点开始,继续按照前述遍历NSW图的方法,查找到离原点最近的点,然后继续移至下一层,如此递归,直至在最下面一层中查找到离原点最近的点并返回。 图20 HNSW算法示意图 图21 NSW图示意图

FAISS

FAISS(Facebook AI Similarity Search)是FaceBook的AI团队针对大规模相似度检索问题开发的一个工具,其使用C++编写,能够做到10亿量级的索引可以毫秒级返回检索结果。FASIS在索引构建时对所有点进行聚类,得到各聚类的中心。FASIS在索引查询时,先查询和原点最近的聚类中心,再在该聚类中查询和原点最近的点。点的聚类、量化一般可以采用乘积量化(Product Quantization)算法,其由2011年的论文《Product Quantization for Nearest Neighbor Search》提出,原理如图22所示,对于点的原始向量,该算法将其分为多个子向量,对每个子向量进行聚类,每个子向量可以由其聚类中心的ID表示,这样点的原始高维向量可以压缩至由其子向量聚类中心ID组合而成的低维向量。 图22 乘积量化(Product Quantization)算法原理

ScaNN

ScalNN(Scalable Nearest Neighbors)是由Google研发的ANN工具,其也采用量化算法,将点向量由高维向量空间压缩至低维向量空间,具体量化算法采用论文《Accelerating Large-Scale Inference with Anisotropic Vector Quantization》中提出的Anisotropic Vector Quantization算法,其能够保证量化压缩后低维空间内原点和其他点的距离和量化压缩前高维空间内原点和其他点的距离近似,这样,就不需要分两阶段先查询和原点最近的聚类中心,再查询该聚类中和原点最近的点,而是直接在低维空间中查询和原点最近的点。 图23 最大内积检索各工具和算法的评测结果 图23中列出了最大内积检索各工具和算法的评测结果,横坐标是准确率(采用TOP10的召回率),纵坐标是查询性能(采用QPS),各工具和算法在准确率和查询性能上进行权衡,随着准备率的提升,查询性能下降,因此各工具和算法的评测结果曲线均是从左上至右下,从中可以看出,ScalNN的整体性能相对最优。更加详细的评测结果可以浏览网站ANN-Benchmarks

工具(Tool)

工具则承担着Agent的感官和四肢的角色,在大语言模型的思考和规划下,Agent一方面可以通过工具获取外部的各种信息用于进一步的思考和规划,另一方可以通过工具执行动作对外部环境施加影响。《LLM Powered Autonomous Agents》在这部分介绍了大语言模型和工具交互的多项工作。

MRKL

2022年由AI21 Labs发表的论文《MRKL Systems: A modular, neuro-symbolic architecture that combines large language models, external knowledge sources and discrete reasoning》设计了MRKL(Modular Reasoning, Knowledge and Language)系统,实现大语言模型和工具的交互,以解决大语言模型存在的一些缺陷问题。 图24 大语言模型存在的一些缺陷问题 论文首先介绍了如图24所示的大语言模型存在的一些缺陷问题,包括缺少最新的或专有的知识、缺乏数学计算的能力等。针对上述问题,论文设计了一个如图25所示的基于神经符号架构的系统——MRKL,其将基于深度神经网络的大语言模型作为路由器,对原始问题进行思考分析和参数抽取,并路由至某个外部的专家模块,而专家模块也可以分为两类,一类仍是深度神经网络,另一类则是各类符号系统,例如负责数学计算的计算器、负责日期查询的日历、负责天气查询的天气API、负责信息检索的维基百科API等,由各专家模块协助大语言模型完成特定功能后,再由大语言模型结合专家模块输出的结果进一步推理,输出由自然语言描述的最终结果。论文指出,相比于外部专家模块本身的运行,如何稳定地让大语言模型从原始问题中识别出需要调用哪些专家模块以及如何调用专家模块(传递哪些参数)是一个更为关键的问题。论文对大语言模型进行微调,以期望能改善从自然语言描述中识别出数学计算问题、并生成相应表达式的准确度和稳定性,生成表达式后,则将表达式传入计算器,由其完成数值计算。 图25 MRKL系统架构

TALM

2022年由Google发表的论文《TALM: Tool Augmented Language Models》提出了TALM,即工具增强的大语言模型,其整体方案如图26所示,即对于原始问题,先由大语言模型生成工具输入,再调用工具生成工具结果,最后将工具结果拼接到工具输入后,由大语言模型生成最终结果。 图26 TALM整体方案 TAML中大语言模型和工具的交互方式和示例如图27所示,对于原始问题“task input text”,由大语言模型按以下格式续写出工具输入:

|tool-call {工具输入}

然后由大语言模型接着续写出“|result”,当系统识别到大语言模型输出“|result”,则抽取前面的工具输入调用工具生成工具结果,并拼接到“|result”后:

|result {工具结果}

最后由大语言模型生成最终结果:

|output {最终结果}

图27 TAML中大语言模型和工具的交互方式和示例 为了让大语言模型能够针对原始问题准确按格式生成工具输入、并根据工具结果生成最终结果,论文对大语言模型进行微调,而为了扩充微调所使用的样本集,论文使用了“Self-Play”的方式分多次迭代进行模型微调,如图28所示,每次迭代中,先使用已有样本集微调模型,再使用微调后的模型对原始问题进行模型推理、工具调用和结果输出,生成新的样本,最后过滤出结果正确的样本,扩充到样本集中,用于下次迭代。 图28 “Self-Play”的方式分多次迭代进行模型微调

Toolformer

2023年的论文《Toolformer: Language Models Can Teach Themselves to Use Tools》提出了Toolformer,其和TAML类似,也通过微调使得大语言模型能够使用工具。 大语言模型如何和工具交互?论文通过微调引导大语言模型在续写时按以下格式输出需要调用的工具名称和工具输入:

[{工具名称}({工具输入})]

[{工具名称}({工具输入})->{工具结果}]

系统识别到大语言模型按上述格式输出文本后,则调用相应的工具,待工具执行完成后,则继续大语言模型的续写,若工具返回结果,则会在大语言模型续写前,将工具结果追加到工具输入后。图29是Toolformer中大语言模型续写时调用工具的示例,其中“QA”、“Calculator”、“MT”、“WikiSearch”分别表示表示问答、计算器、翻译、检索工具。 图29 Toolformer中大语言模型续写时调用工具的示例 微调大语言模型的样本集如何生成?论文对未使用工具的样本集按照图30中的方式生成使用工具的样本集,其中,对于未使用工具的样本,先通过提示工程由大语言模型在样本中按格式添加工具名称和工具输入,然后执行工具获取工具结果,再拼接到样本中,这样就得到未经过滤的使用工具的样本,然后将使用工具的样本由大语言模型续写,若续写的交叉熵损失函数值小于未使用工具的样本或使用工具但无结果的样本,说明使用工具后相比使用工具前,样本具备更多有价值的信息,大语言模型更能够续写出准确的内容,则保留这些使用工具的样本,最后将所有保留的使用工具的样本作为微调样本集对大语言模型进行微调。 图30 生成使用工具的样本集的方式

ChatGPT的插件机制

目前工业界中的大语言模型产品一般提供了比较便捷的工具使用方式,使得用户无需感知底层模型和工具交互的实现细节。例如,ChatGPT提供了插件(plugin)机制,用于协助ChatGPT获取最新信息、执行数学计算或完成第三方提供的其他服务。图31是在ChatGPT中使用官方提供的两个插件的示例,图31左侧的问题是“查询奥斯卡最佳主角、配角等信息,并通过一首诗将他们联系在一起”,这里ChatGPT使用了“Browsing”插件查询奥斯卡获奖信息,图31右侧的问题是一道数学应用题,这里ChatGPT使用了“Code interpreter”插件执行数学计算。另外,第三方开发者也可以根据ChatGPT的协议开发和发布插件,供他人使用。 图31 在ChatGPT中使用官方提供的两个插件的示例

OpenAI API的Function Calling机制

OpenAI还提供了API方式供用户使用大语言会话模型,而其API协议支持Function Calling机制,在调用API时,除输入会话消息外,还可输入多个可选函数的描述,由API返回需要调用的函数和参数。目前,除了OpenAI外,其他不少公司的大语言会话模型的API协议也支持Function Calling机制。

HuggingGPT

HuggingFace平台作为目前比较热门的模型仓库,拥有大量的模型,因此,2023年的论文《HuggingGPT: Solving AI Tasks with ChatGPT and its Friends in Hugging Face》提出了HuggingGPT,其将HuggingFace上的模型作为工具,将ChatGPT作为控制器,对于某个任务,按图32所示的流程分以下4步完成任务:

  • Task Planing,通过ChatGPT将原始任务拆分为多个子任务,例如,图32的示例中将原始任务拆分为图像分类、目标检测、图生文三个子任务;
  • Model Selection,对于每个子任务,通过ChatGPT从HuggingFace选择合适的模型来求解子任务,例如,图32的示例中选择了“google/vit”、“facebook/detr-resnet-101”、“nlpconnet/vit-gpt2-image-captioning”分别求解图像分类、目标检测、图生文这三个子任务;
  • Task Execution,对于每个子任务,调用选中的HuggingFace中的模型求解,获取结果;
  • Response Generation,根据各模型返回的结果,由ChatGPT总结输出最终结果。

图32 HuggingGPT的流程

API-Bank

2023年发表的论文《API-Bank: A Comprehensive Benchmark for Tool-Augmented LLMs》提出了API-Bank,其包含多个常用工具,设计了多个使用工具的对话,对目前各类工具增强的大语言模型进行评测。

实践

LangChain

介绍

图33 LangChain整体框架 LangChain是一个面向大语言模型的应用开发框架,它可以将外部数据源、工具和大语言模型结合在一起。目前LangChain整体框架如图33所示,由以下部分组成:

  • LangChain库:
    • langchain-core:核心组件的抽象定义和LangChain表达式语言;
    • langchain-community:各种第三方实现的集成;
    • langchain:各类Chain和Agent的实现;
  • LangGraph:用于以图的方式将多个基于大语言模型的智能应用组合在一起;
  • LangServe:用于将LangChain应用部署为REST API的服务;
  • LangSmith:开发者平台,用于调试、评估和监控基于大语言模型构建的应用。

LangChain包含以下核心组件:

  • Model,表示大语言模型;
  • Prompt,表示提示;
  • Tool,表示工具;
  • Chain,表示将Model、Tool等组件串联在一起,甚至可以递归地将其他Chain串联在一起;
  • Agent,相对于Chain已固定执行链路,Agent能够基于大语言模型进行思考和规划,根据不同的问题实现动态的执行链路;

关于Chain的实践可以阅读笔者之前撰写的《Mac本地部署大模型体验AIGC能力》,以下是对Tool和Agent的介绍和使用。本文所用的LangChain的版本是“0.1.20”,而目前LangChain的最新版本是“0.2.36”。

Tool

LangChain中Tool的定义为“langchain_core.tools.BaseTool”,开发者可以通过继承该定义或在已有方法上添加注解等方式,实现自定义工具,而自定义工具需要包括工具名称——“name”、工具描述——“description”,以及工具功能的具体实现(例如调用搜索引擎的API查询相关信息)等。工具名称和描述会通过提示或Function Calling的方式传递给大语言模型,由大语言模型进行思考和规划,判断是否使用工具并选择工具,提取工具参数,生成工具指令,因此,工具名称和描述是否详细、准确对大语言模型能否稳定、准确地思考和规划至关重要。工具参数的格式应尽量简单,例如只有一个字符串类型的字段。

另外,LangChain以及第三方已提供多个工具,下表是LangChain中已有的部分工具,这些工具一般依赖了第三方提供的服务,因此,需要在第三方网站上注册账户并申请服务秘钥,才能使用他们提供的工具。

名称实现类说明
Apifylangchain_community.utilities.apify.ApifyWrapper从各类网络应用中抓取网页和抽取数据的云平台工具。
ArXiv API Toollangchain_community.tools.arxiv.tool.ArxivQueryRun从arxiv.org搜索相关论文的工具。
AWS Lambda APIlangchain_community.utilities.awslambda.LambdaWrapper调用AWS云Serverless服务的工具。
Shell Toollangchain_community.tools.shell.tool.ShellTool执行Shell命令的工具。
Bing Searchlangchain_community.tools.bing_search.tool.BingSearchRun从Bing搜索相关信息的工具。
Brave Searchlangchain_community.tools.brave_search.tool.BraveSearch从Brave Search搜索相关信息的工具。
ChatGPT Pluginslangchain_community.tools.plugin.AIPluginTool调用ChatGPT插件的工具。
Connery Action Toollangchain_community.tools.connery.service.ConneryService调用Connery插件的工具(Connery是一个开源的AI插件基础设施,用于快速部署AI插件)。
Dall-E Image Generatorlangchain_community.utilities.dalle_image_generator.DallEAPIWrapper调用OpenAI的DALL-E模型生成图片的工具。
DataForSEOlangchain_community.utilities.dataforseo_api_search.DataForSeoAPIWrapper从Google、Bing、Yahoo等搜索引擎搜索相关信息的工具。
Dataheraldlangchain_community.utilities.dataherald.DataheraldAPIWrapper将自然语言描述转化为SQL的工具。
DuckDuckGo Searchlangchain_community.tools.ddg_search.tool.DuckDuckGoSearchRun从DuckDuckGo Search搜索相关信息的工具。
E2B Data Analysislangchain_community.tools.e2b_data_analysis.tool.E2BDataAnalysisTool在云沙箱中执行代码进行数据分析的工具。
Eden AIlangchain_community.tools.edenai.image_explicitcontent.EdenAiExplicitImageTool, langchain_community.tools.edenai.image_objectdetection.EdenAiObjectDetectionTool, langchain_community.tools.edenai.ocr_identityparser.EdenAiParsingIDTool, langchain_community.tools.edenai.ocr_invoiceparser.EdenAiParsingInvoiceTool, langchain_community.tools.edenai.audio_speech_to_text.EdenAiSpeechToTextTool, langchain_community.tools.edenai.text_moderation.EdenAiTextModerationTool, langchain_community.tools.edenai.audio_text_to_speech.EdenAiTextToSpeechTool集成多个AI工具(包括目标检测、文本转语音、语音转文字等)的工具。
Eleven Labs Text2Speechlangchain_community.tools.eleven_labs.text2speech.ElevenLabsText2SpeechTool调用ElevenLabs的API实现文本转语音的工具。
Exa Search需实现Tool接口封装对Exa Search API的调用从Exa Search按语义搜索相关信息的工具。
File Systemlangchain_community.tools.file_management.copy.CopyFileTool, langchain_community.tools.file_management.delete.DeleteFileTool, langchain_community.tools.file_management.file_search.FileSearchTool, langchain_community.tools.file_management.move.MoveFileTool, langchain_community.tools.file_management.read.ReadFileTool, langchain_community.tools.file_management.write.WriteFileTool, langchain_community.tools.file_management.list_dir.ListDirectoryTool读写文件的工具。
Golden Querylangchain_community.utilities.golden_query.GoldenQueryAPIWrapper从Golden Query的知识图谱搜索相关信息的工具。
Google Cloud Text-to-Speechlangchain_community.tools.google_cloud.texttospeech.GoogleCloudTextToSpeechTool由Google云提供的文本转语音工具。
Google Drivelangchain_googledrive.tools.google_drive.tool.GoogleDriveSearchToolGoogle云盘搜索工具。
Google Financelangchain_community.tools.google_finance.tool.GoogleFinanceQueryRunGoogle财经搜索工具。
Google Jobslangchain_community.tools.google_jobs.tool.GoogleJobsQueryRunGoogle工作搜索工具。
Google Lenslangchain_community.tools.google_lens.tool.GoogleLensQueryRunGoogle图像识别和搜索工具。
Google Placeslangchain_community.tools.google_places.tool.GooglePlacesToolGoogle地图搜索工具。
Google Scholarlangchain_community.tools.google_scholar.tool.GoogleScholarQueryRunGoogle学术搜索工具。
Google Searchlangchain_community.utilities.google_search.GoogleSearchAPIWrapperGoogle搜索工具。
Google Serperlangchain_community.utilities.google_serper.GoogleSerperAPIWrapperGoogle搜索工具。
Google Trendslangchain_community.tools.google_trends.tool.GoogleTrendsQueryRunGoogle趋势搜索工具。
Gradiogradio_tools.tools.image_captioning.ImageCaptioningTool.langchain, gradio_tools.tools.prompt_generator.StableDiffusionPromptGeneratorTool.langchain, gradio_tools.tools.stable_diffusion.StableDiffusionTool.langchain, gradio_tools.tools.text_to_video.TextToVideoTool.langchain调用Gradio应用实现文生图、文生视频等功能的工具。
GraphQLlangchain_community.tools.graphql.tool.BaseGraphQLTool查询GraphQL API的工具。
HuggingFace Hub Tools和HuggingFace Hub交互的多个工具,包括获取某个任务下载最多的模型名称。
Human as a toollangchain_community.tools.human.tool.HumanInputRun由人输出结果的工具。
IFTTT WebHookslangchain_community.tools.ifttt.IFTTTWebhook使用IFTTT服务自动执行网络操作的工具。
Infobiplangchain_community.utilities.infobip.InfobipAPIWrapper调用Infobip的API发送短信或邮件的工具。
Ionic Shopping Toolionic_langchain.tool.IonicTool获取电商商品信息的工具。
Memorizelangchain_community.tools.memorize.tool.Memorize对支持微调的大语言模型进行微调使其记住特定信息的工具。
Mojeek Searchlangchain_community.tools.mojeek_search.tool.MojeekSearch从Mojeek Search搜索相关信息的工具。
Nuclia Understandinglangchain_community.tools.nuclia.tool.NucliaUnderstandingAPI使用Nuclia对非结构化数据(文本、网页、文档、音频、视频等)构建索引、查询信息的工具。
NVIDIA Riva: ASR and TTSlangchain_community.utilities.nvidia_riva.RivaASR, langchain_community.utilities.nvidia_riva.RivaTTS基于NVIDIA Riva(GPU加速的语音AI SDK)实现文本转语音、语音转文本的工具。
OpenWeatherMaplangchain_community.tools.openweathermap.tool.OpenWeatherMapQueryRun从OpenWeatherMap获取天气信息的工具。
Polygon Stock Market API Toolslangchain_community.utilities.polygon.PolygonAPIWrapper从Polygon.io获取美国证劵交易信息的工具。
PubMedlangchain_community.tools.pubmed.tool.PubmedQueryRun从MEDLINE查询相关生物医学论文的工具。
Python REPLlangchain_community.utilities.python.PythonREPL执行Python命令的工具。
Reddit Searchlangchain_community.tools.reddit_search.tool.RedditSearchRun从Reddit搜索相关信息的工具。
Requestslangchain_community.tools.requests.tool.RequestsGetTool, langchain_community.tools.requests.tool.RequestsPostTool, langchain_community.tools.requests.tool.RequestsPatchTool, langchain_community.tools.requests.tool.RequestsPutTool, langchain_community.tools.requests.tool.RequestsDeleteTool执行HTTP请求的工具。
SceneXplainlangchain_community.tools.scenexplain.tool.SceneXplainTool使用SceneXplain识别图像信息的工具。
SQL Databaselangchain_community.utilities.sql_database.SQLDatabase执行SQL查询的工具。
Tavily Searchlangchain_community.tools.tavily_search.tool.TavilySearchResults从Tavily搜索相关信息的工具。
Twiliolangchain_community.utilities.twilio.TwilioAPIWrapper使用Twilio的API发送短信或WhatsApp消息的工具。

Agent

LangChain中部分Agent的实现如下表所示。

实现类AgentType枚举值说明
langchain.agents.mrkl.base.ZeroShotAgentAgentType.ZERO_SHOT_REACT_DESCRIPTION参考MRKL系统设计的Agent,其在提示中增加工具功能和指令格式的描述,并增加让模型分步按格式输出思考过程和工具指令的指示,以引导大语言模型按照ReAct方式进行思考和规划。
langchain.agents.react.base.ReActDocstoreAgentAgentType.REACT_DOCSTORE参考ReAct论文设计的Agent,和论文一致,其在提示中增加多个示例以引导大语言模型对后续的问题按照ReAct方式进行分步思考和规划,也要求工具和论文一致,必须是“Search”和“LookUp”,且实现功能论文中一致的功能,即分别返回相关的语句或包含的语句。
langchain.agents.self_ask_with_search.base.SelfAskWithSearchAgentAgentType.SELF_ASK_WITH_SEARCH参考Self-Ask论文设计的Agent,和论文一致,其在提示中增加多个示例以引导大语言模型对后续的问题按照Self-Ask方式进行分步提问,也要求工具和论文一致,必须是“Intermediate Answer”,且实现功能论文中一致的功能,即返回提问的回答。
langchain.agents.conversational.base.ConversationalAgentAgentType.CONVERSATIONAL_REACT_DESCRIPTION和ZeroShotAgent类似,只是在提示中还增加了人类和AI的历史会话消息以模拟会话场景。
langchain.agents.chat.base.ChatAgentAgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION和ZeroShotAgent类似,只是在提示中要求按JSON格式返回“Action”后的工具指令,包括工具名称和工具输入。
langchain.agents.conversational_chat.base.ConversationalChatAgentAgentType.CHAT_CONVERSATIONAL_REACT_DESCRIPTION和ZeroShotAgent类似,但是在提示中还增加了人类和AI的历史会话消息以模拟会话场景,同时在提示中要求按JSON格式返回“Action”后的工具指令,包括工具名称和工具输入。
langchain.agents.structured_chat.base import StructuredChatAgentAgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION和ChatAgent类似。
langchain.agents.openai_functions_agent.base.OpenAIFunctionsAgentAgentType.OPENAI_FUNCTIONS基于大语言会话模型的“Function Calling”能力,在和大语言会话模型的交互中,除输入会话消息外,还输入多个可选函数的描述,由大语言会话模型返回需要调用的函数和参数
langchain.agents.openai_functions_multi_agent.base.OpenAIMultiFunctionsAgentAgentType.OPENAI_MULTI_FUNCTIONS和OpenAIFunctionsAgent类似,但模型在规划和思考时可以选择多个动作执行。

Agent的初始化原先可以采用“initialize_agent”方法,指定Agent类型,并传入工具、模型,代码如下所示(部分):

from langchain.agents import initialize_agent

agent= initialize_agent(tools, model, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, handle_parsing_errors=True, verbose = True)

目前则建议采用各类型Agent的“create”方法进行Agent的初始化,例如参考MRKL系统、按ReAct方式进行思考和规划的Agent的初始化的代码如下所示(部分):

from langchain import hub
from langchain.agents import create_react_agent

prompt = hub.pull("hwchase17/react")
agent = create_react_agent(model, tools, prompt)

“initialize_agent”方法初始化时,使用各类型Agent中内置的提示模版,而“create”方法初始化时,需要指定提示模版,而LangChain通过LangChainHub(LangChainHub目前已集成至LangSmith中)集中管理由开发者共享的提示、链或代理,因此可以从LangChainHub上拉取所需的提示模版。上例中地址为“hwchase17/react”的提示模版的具体内容如下所示:

Answer the following questions as best you can. You have access to the following tools:

{tools}

Use the following format:

Question: the input question you must answer

Thought: you should always think about what to do

Action: the action to take, should be one of [{tool_names}]

Action Input: the input to the action

Observation: the result of the action

... (this Thought/Action/Action Input/Observation can repeat N times)

Thought: I now know the final answer

Final Answer: the final answer to the original input question

Begin!

Question: {input}

Thought:{agent_scratchpad}

其和“langchain.agents.mrkl.base.ZeroShotAgent”中内置的提示模版一致,即参考MRKL系统设计的Agent,在提示中增加工具功能和指令格式的描述,并增加让模型分步按格式输出思考过程和工具指令的指示,以引导大语言模型按照ReAct方式进行思考和规划。

示例1

大语言模型使用通义千问,工具使用“Tavily Search”,实现MRKL系统设计的Agent,按照ReAct方式进行思考和规划,针对问题——“what's the weather of San Francisco?”,先使用“Tavily Search”查询天气,再根据查询结果回答问题。代码如下所示:

import os
from langchain import hub
from langchain.agents import AgentExecutor, create_react_agent
from langchain_community.llms import Tongyi
from langchain_community.tools.tavily_search import TavilySearchResults

LANGCHAIN_API_KEY = 'xxx'
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = LANGCHAIN_API_KEY

DASHSCOPE_API_KEY = 'xxx'
os.environ["DASHSCOPE_API_KEY"] = DASHSCOPE_API_KEY

TAVILY_API_KEY = 'xxx'
os.environ["TAVILY_API_KEY"] = TAVILY_API_KEY

prompt = hub.pull("hwchase17/react")
model = Tongyi()
search = TavilySearchResults(max_results=2)
tools = [search]
agent = create_react_agent(model, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools)

agent_executor.invoke(
    {
        "input": "what's the weather of San Francisco?",
    }
)

执行代码后从LangSmith上可以看到,模型第一步思考和规划认为需要使用工具,且按格式给出了工具名称和工具参数,如图34所示。 图34 模型第一步思考和规划 Agent随后调用工具查询天气,并将查询结果作为观察结果追加到提示中。模型第二步思考和规划,认为从提示中已可得到最终的答案并返回答案,如图35所示。 图35 模型第二步思考和规划

示例2

在示例1的基础上,引入“ConversationBufferMemory”记忆人类和AI的历史会话消息,在提示中增加了历史会话消息以模拟会话场景,代码如下所示:

import os
from langchain.agents import AgentType
from langchain.agents import initialize_agent
from langchain.memory import ConversationBufferMemory
from langchain_community.llms import Tongyi
from langchain_community.tools.tavily_search import TavilySearchResults

LANGCHAIN_API_KEY = 'xxx'
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = LANGCHAIN_API_KEY

DASHSCOPE_API_KEY = 'xxx'
os.environ["DASHSCOPE_API_KEY"] = DASHSCOPE_API_KEY

TAVILY_API_KEY = 'xxx'
os.environ["TAVILY_API_KEY"] = TAVILY_API_KEY

model = Tongyi()
search = TavilySearchResults(max_results=2)
tools = [search]
memory = ConversationBufferMemory(memory_key="chat_history")
agent = initialize_agent(tools, model, agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION, verbose=True, memory=memory)

agent.run(input = "hi im bob! and i live in sf")
agent.run(input = "whats the weather where I live?")

对于第一句人类会话,模型直接进行了回答,如图36所示。 图36 对于第一句人类会话,模型直接进行了回答 对于第二句人类会话,模型从历史会话消息中识别出当前人的地域信息,后续和示例1相同,分两步思考和规划,第一步使用工具获取天气信息,第二步根据获取的天气信息给出答案,如图37和图38所示。 图37 对于第二句人类会话,第一步使用工具获取天气信息 图38 对于第二句人类会话,第二步根据获取的天气信息给出答案 示例1和2使用的是通义千问的大语言模型,系统和大语言模型的交互简单来说,就是提供一段文字,让大语言模型续写后面的文字。而大语言会话模型,则是在大语言模型的基础上,针对会话场景进行优化,其输入包括会话消息序列和函数描述等,会话信息进一步可分为系统消息、人类消息和AI消息三类,系统消息类似提示。大语言会话模型根据输入返回AI消息以及需要执行的函数等。

后记

目前大语言模型和大语言模型应用框架还在不断发展,由于大模型Agent不管应用于哪个业务场景,其核心抽象组件仍是模型和工具,其核心抽象流程仍是由模型进行思考和规划,由工具获取信息或执行动作,因此目前有较多平台和框架基于上述大模型Agent组件和流程的抽象,提供可视化的大模型Agent的创建和部署能力,极大降低了大模型Agent的开发门槛,而随着应用场景越来越复杂,大模型Agent也在向多Agent的协同进行演进,由各个Agent各司其职、分工协作来共同完成任务。笔者所在的团队也在从投放广告的广告主视角出发,在智能客服、智能数据洞察、智能投放助手上进行探索,采用大模型Agent帮助广告主进行投放分析和操作,以提升投放效率和效果,降低人力成本,后续我们会对自己的工作另外通过文章进行分享。

参考文献