做关于什么样的网站好,做蛋糕网站,江苏省建设厅官网网站首页,培训机构网站建设编者按#xff1a; 本文通过作者的实践对比发现#xff0c;框架的选择应基于项目具体需求和团队特点#xff0c;而不是简单追求某个特定框架。不同框架各有优势#xff1a; 无框架方案实施最为简单直接#xff0c;代码结构清晰#xff0c;适合理解智能体原理#xff0c;… 编者按 本文通过作者的实践对比发现框架的选择应基于项目具体需求和团队特点而不是简单追求某个特定框架。不同框架各有优势 无框架方案实施最为简单直接代码结构清晰适合理解智能体原理但随着项目复杂度增加可能变得难以维护。LangGraph提供完整的智能体结构规范特别适合团队协作和智能体结构新手但框架限制较多如不认同其理念可能面临较大调试挑战。LlamaIndex Workflows采用事件驱动架构在框架约束和开发自由度之间取得平衡对框架依赖较少但其固有的异步特性可能增加某些场景的复杂度。 框架选择需要考虑三个关键因素项目是否已深度集成了特定框架、团队对智能体架构的熟悉程度、是否有可供参考的相似项目案例。 作者 | Aparna Dhinakaran
编译 | 岳扬 Image by author
智能体Agents正迎来辉煌时刻。伴随着众多新框架的涌现和对该领域的持续投资[1]现代 AI 智能体正在跨越起初的不稳定阶段[2]迅速取代 RAG 成为开发首选。那么2024 年是否会成为 autonomous AI 系统全面接管撰写邮件、预订航班、数据分析等任务的一年呢
也许吧但要实现这一点还有很多工作要做。开发人员在构建智能体时不仅要决定使用何种模型、应用场景和技术架构还要挑选合适的开发框架。是坚持较为早期的 LangGraph还是转向新兴的 LlamaIndex Workflows或者走传统路线自己编写全部代码呢
这篇文章的目的就是让您更轻松地做出选择。在过去几周里我使用多个主流框架构建了相同的智能体并从技术角度分析了它们各自的优缺点。每个智能体的所有代码都可以在此代码仓库[3]中找到。
本文测试用智能体的基本概述
本次测试所采用的智能体整合了多项功能包括执行函数调用function calling、使用多种工具或技能、与外部资源建立连接以及实现状态或记忆的共享。
该智能体具备以下几项核心能力
基于知识库进行问题解答数据交互针对 LLM 应用程序的数据进行问题解答数据洞察对获取的数据进行更高层次的趋势和模式分析
为了达成上述目标智能体需要掌握三项基本技能结合产品文档的 RAG、在相关数据库上生成 SQL 语句的能力以及数据分析技巧。智能体的用户界面使用 gradio 搭建而智能体本身则以聊天机器人chatbot的形式构建。
01 Code-Based Agent不使用智能体框架
在着手开发智能体时您可以选择不依赖任何框架而是完全自主构建。在启动这个项目之初我首先采用了这种方法。 Image by author
1.1 纯代码架构
下面是基于纯代码构建的智能体其核心是一个由 OpenAI 提供支持的技能路由器它通过函数调用来确定使用哪项技能。技能执行完毕后控制权将返回给技能路由器以便调用其他技能或直接向用户作出回应。
智能体会持续记录用户消息和智能体响应并在每次调用时将这一完整列表传递给技能路由器确保在整个交互过程中保留上下文。 各项技能均在独立的类中进行定义例如“GenerateSQLQuery”类这些类都保存在 SkillMap 中。技能路由器仅与 SkillMap 进行交互通过它来加载技能的名称、描述以及可调用的函数。这种设计理念使得向智能体中添加新技能变得非常简单只需将该技能编写为一个独立的类并将其加入到 SkillMap 的技能列表即可。这样做的目的是为了在不影响技能路由器代码的前提下轻松实现新技能的添加。 总的来说这种实现方式虽然简单易行但仍然存在一些需要克服的难题。
1.2 使用纯代码智能体面临的挑战
第一个困难在于如何设计技能路由器的系统提示词system prompt。 在上面的例子中技能路由器往往倾向于自行生成 SQL 语句而不是交给相应的技能模块去处理。如果你有过试图让大语言模型停止执行某项任务的经历那你可能深知这其中的挫败感为了找到合适的提示词我不得不进行了多次调试。此外处理每个步骤产生的不同输出格式也是一项复杂的工作。 由于我选择不使用结构化输出因此必须为技能路由器和各项技能中大语言模型的调用准备多种格式的应对策略。
1.3 纯代码智能体的优点
基于代码的方法提供了一个扎实的基础和出发点是一种绝佳的学习途径让我们可以在不依赖现成框架提供的智能体教程的情况下了解智能体的运作原理。虽然引导大语言模型按既定行为模式运作确实存在难度但代码结构本身简洁明了易于操作对于某些使用场景而言这种做法是完全合理的具体分析将在下文展开。
02 LangGraph
LangGraph 是众多智能体框架中历史最为悠久的之一它于 2024 年 1 月首次发布。该框架的设计初衷是为了解决现有流程和链条的非循环性问题它通过采用 Pregel 图结构来解决这一问题。 LangGraph 通过引入节点nodes、边edges以及条件边conditional edges的概念简化了在智能体中创建循环流程的过程使得图的遍历变得更加直观。LangGraph 是基于 LangChain 构建的它继承了后者的对象objects和类型types。 Image by author
2.1 LangGraph 架构
从表面上看LangGraph 智能体与基于代码的智能体有相似之处但它们的底层代码却有大不相同。虽然 LangGraph 在技术上也使用了“路由器router”这一概念即通过代码函数调用 OpenAI 并利用其响应来推进到下一个步骤但程序在不同技能之间的切换控制机制却完全不同。 在此定义的图graph中包含了一个用于初始化 OpenAI 调用的节点即上文中提到的“agent”以及一个用于工具处理步骤节点即“tools”。LangGraph 内置了一个名为 ToolNode 的对象它能够接收一系列可调用的工具并根据 ChatMessage 的响应来触发这些工具完成操作后再次回到“agent”节点。 每当“agent”节点也可以理解为基于代码的智能体中的技能路由器router被调用之后should_continue 这条边将判断是将响应直接返回给用户还是转给 ToolNode 来处理工具调用。
在每个节点中“state” 负责保存与 OpenAI 的交互消息和响应列表这一点与基于代码的智能体保持上下文的方式相似。
2.2 使用 LangGraph 面临的挑战
在处理 LangGraph 构建的智能体示例时遇到的主要难题在于必须借助 Langchain 对象才能确保流程的顺畅。
挑战 1函数调用的 validation 错误
为了能够使用 ToolNode 对象我不得不对 Skill 代码进行大规模的重构。ToolNode 需要一组可调用的函数列表我本以为可以直接使用现成的函数但是函数参数配置出了问题导致流程受阻。
这些技能skills是以类形式定义的每个类都有一个可调用的成员函数其中“self”是首个参数。GPT-4o 足够智能能够在生成函数调用function call时自动排除“self”参数但 LangGraph 却因此认为缺少了必要参数从而抛出了 validation 错误。
这个问题让我摸索了好几小时才搞清楚因为错误信息把函数里的第三个参数数据分析技能中的“args”错误地标记为缺失参数missing parameter 需要指出的是这个误导性的错误信息其实来自 Pydantic而非 LangGraph。
最后我下定决心改用 Langchain 的 tool 装饰器将我的技能skills重新编写为基本方法这样程序就能正常运行了。 挑战 2Debugging
正如前文所述在框架中调试非常困难。主要是因为错误信息混乱不清以及框架中的抽象概念它们使得追踪和查看变量变得非常复杂。
抽象概念主要体现在尝试跟踪智能体间传递的消息时。LangGraph 会将消息保存在 state[“messages”] 里。Graph 中的一些节点会自动从这些消息messages中提取信息这样的自动化过程可能会让节点在访问消息messages时我们难以把握消息messages的具体内容。 智能体行动的顺序视图图片由作者提供
2.3 LangGraph 的优点
LangGraph 的最大优势在于其易用性。它的图结构代码简洁且易于理解。对于那些拥有复杂节点逻辑的场景LangGraph 能够提供一个清晰的图视图让我们更轻松地把握智能体的连接方式。此外LangGraph 还可以直接转换以 LangChain 构建的现有应用程序。
2.4 经验之谈
当我们只使用 LangGraph 框架的相关功能时一切都会运行得非常流畅但一旦我们尝试跳出框架就要准备好进行一些令人头疼的调试了。
03 LlamaIndex Workflows
Workflows 是智能体框架领域的新晋成员它于今年夏初首次亮相。与 LangGraph 类似它的设计宗旨是简化可循环智能体的构建过程。此外Workflows 特别强调其异步执行的能力。
在 Workflows 中某些设计元素似乎是为了直接对标 LangGraph尤其是它采用事件events而非边edges或条件边conditional edges作为连接逻辑的方式。在 Workflows 中智能体逻辑被封装在“步骤steps”中与 LangGraph 中的“节点nodes”相对应而事件events的发出和接收则负责在不同的步骤steps间传递信息。 Image by author
上述框架与 LangGraph 的结构颇为相似但有一点不同我给 Workflow 增加了一个初始化步骤用于准备智能体的环境上下文稍后我会详细介绍这一点。尽管两者的结构相似但它们所依赖的代码实现却截然不同。
3.1 Workflows 架构
以下代码段描绘了 Workflow 的架构。与 LangGraph 相仿在这一部分我配置了状态信息state并将各项技能skills绑定到了 LLM 对象上。 在这里我还定义了一个额外的步骤——“prepare_agent”。该步骤负责将用户输入转换成 ChatMessage并将其存储到工作流的记忆存储中。将这一过程作为一个独立的步骤分离出来意味着智能体在遍历工作步骤steps时可以重复回到这一步从而避免反复将用户信息加入到记忆存储中。
在 LangGraph 的实现案例中我通过一个位于图graph之外的 run_agent 方法实现了相同的功能。这一改变主要是出于风格上的考虑但我认为将这一逻辑整合到 Workflow 和图graph中会更加整洁和高效。
在 Workflow 配置完成后我继续编写了路由代码 以及工具调用处理代码 它们的实现方式似乎更接近于纯代码的智能体而非 LangGraph 智能体。这主要是因为 Workflows 选择在各步骤steps中维护条件路由conditional routing逻辑而不是像 LangGraph 那样使用条件边conditional edge第 18-24 行在 LangGraph 中是条件边而现在它们只是路由步骤的一部分。另外LangGraph 中的 ToolNode 对象能够在 tool_call_handler 方法中自动处理大部分任务。
在路由步骤之后我们能够将 SkillMap 以及基于纯代码的智能体中已有的技能skills直接应用于 Workflows。这些技能skills无需任何修改即可与 Workflows 配合使用这大大简化了我的工作。
3.2 使用 Workflows 面临的挑战
挑战 1Sync vs Async
尽管对于在线运行的智能体来说异步执行是更优的选择但调试同步执行的智能体通常更为简便。Workflows 本身是为了异步操作而设计的因此尝试将其改为同步执行非常困难。
起初我以为只需去掉“async”方法标识并将函数名“achat_with_tools”改为“chat_with_tools”即可。但是由于 Workflow 类内部的方法同样采用了异步标记为了实现同步运行我不得不重新定义这些方法。尽管如此我最终还是选择了异步处理方式幸运的是这并没有增加调试的难度。 智能体行动的顺序视图图片由作者提供
挑战 2Pydantic Validation Errors
与 LangGraph 的问题类似在智能体的技能skills处也出现了令人困惑的 Pydantic Validation Errors。幸运的是由于 Workflows 能够很好地处理成员函数这些问题这次比较容易解决。最终我不得不更加规范地为智能体技能skills创建 LlamaIndex FunctionTool 对象 从构建 FunctionTools 的 AgentFlow.init 文件中摘录
3.3 Workflows 的优点
与 LangGraph 相比我在使用 Workflows 构建智能体时要轻松得多主要原因是 Workflows 并未提供内置功能而是需要我自己编写路由逻辑和工具操作代码。 这也使得我的 Workflow 智能体与基于纯代码的智能体看起来极为相似。
最大的区别在于事件events的使用上。我使用两个自定义事件在智能体中的各个步骤之间移动 这种基于事件的发射器-接收器架构emitter-receiver取代了直接调用智能体中某些方法的做法例如工具调用处理tool call handler。
对于那些步骤steps更为复杂、异步触发且可能产生多个事件events的系统来说这种架构就非常有助于干净利落地管理这些步骤。
Workflows 的其他优点还包括其轻量级特性不会施加过多的结构限制除了必须使用特定的 LlamaIndex 对象外并且其基于事件event-based的架构为直接函数调用提供了一种有效的替代方案这对于处理复杂、异步的应用场景尤为有益。
04 对这些方法进行比较
对比这三种方法各有其独到之处。
无框架方法实施起来最简单。由于所有抽象层都是由开发者自行定义如前例中的 SkillMap 对象因此管理不同类型types和对象objects相对简单。但是代码的可读性和易用性完全取决于开发者个人可以预见如果没有一定的智能体结构约束智能体的复杂性增加后可能会变得难以驾驭。
LangGraph 提供了丰富的智能体结构支持使得智能体的定义非常清晰。对于多人协作开发的智能体来说这种智能体结构设定有助于统一架构规范。LangGraph 也为那些对智能体结构不太熟悉的开发者提供了帮助。不过这样做也有代价 —— 由于 LangGraph 为你做了许多工作如果你不完全认同这个框架它可能会让你头疼不已代码可能会非常简洁但你可能要为此进行更多的调试工作。
Workflows 则处于两者之间。基于事件event-based的架构在某些项目中可能极具价值而且因为它对 LlamaIndex 类型的使用要求不高对于那些没有在应用程序中完全使用该框架的开发者来说提供了更大的自由度。 Image created by author
归根结底关键问题可能在于“你是否已经在使用 LlamaIndex 或 LangChain 来组织应用程序” LangGraph 和 Workflows 都与它们所依赖的框架紧密集成因此每个特定智能体框架的额外优势可能不足以成为转换使用的理由。
纯代码方法可能永远是一个有吸引力的选择。如果你能够严格地记录并执行所创建的任何抽象概念那么确保外部框架不会成为你的阻碍就很容易了。
05 在选择智能体框架时需要考虑的关键问题
当然单纯一句“具体情况具体分析”这样的回答总是让人不太满意。以下三个问题或许能帮你选择下一个智能体项目应该采用哪个框架。
你的项目是否已经深度集成了 LlamaIndex 或 LangChain
如果是的话不妨优先考虑这两个选项。
你对智能体的常见架构是否熟悉还是更希望有人告诉你应该如何构建智能体结构
如果你倾向于后者那么 Workflows 可能是个不错的选择。如果你非常倾向于后者那么 LangGraph 或许更适合你。
你要构建的智能体是否有参考样例
框架的一个优势在于每个框架都有大量的教程和实例供你参考。而纯代码构建智能体的参考实例相对较少。 Image created by author
06 Conclusion
选择一个智能体框架只是影响生成式人工智能系统在生产环境中表现众多决策中的一项建立强大的安全保障和对大语言模型LLM的监控[4]是必要的 —— 同时面对新智能体框架、研究成果和模型对传统技术的颠覆我们还需保持灵活应对的态度。
Thanks for reading!
Hope you have enjoyed and learned new things from this blog!
About the authors
Aparna Dhinakaran
Co-Founder and CPO of Arize AI. Formerly Computer Vision PhD at Cornell, Uber Machine Learning, UC Berkeley AI Research.
END
本期互动内容
❓请分享一下你最常使用的智能体开发方式为什么
文中链接
[1]https://foundationcapital.com/goodbye-aiops-welcome-agentsres-the-next-100b-opportunity/
[2]https://arxiv.org/html/2405.13966v1
[3]https://github.com/Arize-ai/phoenix/tree/main/examples/agent_framework_comparison
[4]https://docs.arize.com/phoenix/tracing/llm-traces
原文链接
https://towardsdatascience.com/choosing-between-llm-agent-frameworks-69019493b259