{ "id": "conv_20251104_122143_063", "title": "做带有多轮对话的agent时,查询到返回的内容是以tool还是system?", "created_at": "2025-11-04T12:21:43.063631", "updated_at": "2025-11-06T21:45:51.599937", "messages": [ { "role": "user", "content": "做带有多轮对话的agent时,查询到返回的内容是以tool还是system?", "timestamp": "2025-11-04T12:22:02.694019" }, { "role": "assistant", "content": "我来帮你理解多轮对话agent中查询返回内容的类型问题。", "timestamp": "2025-11-04T12:22:04.282555" }, { "role": "assistant", "content": "", "timestamp": "2025-11-04T12:22:04.284608", "tool_calls": [ { "id": "web_search:0", "index": 0, "type": "function", "function": { "name": "web_search", "arguments": "{\"query\": \"多轮对话agent tool system 返回内容类型 对话系统\"}" } } ] }, { "role": "tool", "content": "{\"success\": true, \"summary\": \"🔍 搜索查询: 多轮对话agent tool system 返回内容类型 对话系统\\n📅 搜索时间: 2025-11-04T12:22:08.203250\\n🎯 过滤条件: Topic: general\\n\\n📝 AI摘要:\\nMulti-turn dialogue agents use tools and context management to handle complex tasks over several interactions. They maintain context across turns and utilize tools for specific functions. Effective design requires robust context tracking and state management.\\n\\n---\\n\\n📊 搜索结果:\\n\\n1. 构建多轮对话问答系统:基于大模型的Agent与Tools探索 - CSDN博客\\n 🔗 https://blog.csdn.net/Fanstay985/article/details/142335892\\n 📄 公安备案号11010502030143\\n 京ICP备19004658号\\n 京网文〔2020〕1039-165号\\n 经营性网站备案信息\\n 北京互联网违法和不良信息举报中心\\n 家长监护\\n 网络110报警服务\\n 中国互联网举报中心\\n Chrome商店下载\\n 账号管理规范\\n 版权与免责声明\\n 版权申诉\\n 出版物许可证\\n 营业执照\\n ©1999-2025北京创新乐知网络技术有限公司\\n\\n评论\\n\\n被折叠的 条...\\n\\n2. LangChain Tutorials:多轮对话Agent(LangGraph + ReAct) - 知乎\\n 🔗 https://zhuanlan.zhihu.com/p/717573383\\n 📄 print(f\\\"ContentString: {response.content}\\\") ## 没有返回回答\\nprint(f\\\"ToolCalls: {response.tool_calls}\\\") ## 返回了工具调用\\n \\nContentString: \\nToolCalls: [{'name': 'tavily_search_results_json', 'args': {'query': 'weat...\\n\\n3. 如何借助LLM 设计和实现任务型对话Agent - Thoughtworks\\n 🔗 https://www.thoughtworks.com/zh-cn/insights/blog/machine-learning-and-ai/how-to-design-task-based-dialogue-Agent-with-LLM\\n 📄 为了确保 Agent 在多轮对话场景中表现良好,我们还需要验证 Tracker 模块的上下文追踪和状态更新策略,确保核心模块的准确性。此外,策略测试阶段还需聚焦于对话管理策略的可靠性,确保 Agent 能够正确判断任务完成和转交对话。针对多轮对话场景,我们需要模拟用户的不同回复,并对应检验策略模块的决策是否合理。这通常需要 mock NLU 模块的输出结果 (包括用户意图和提取的槽位值),并将其输...\\n\\n4. AI Agent 上下文管理:基于搭叩的七大原则与实践 - InfoQ\\n 🔗 https://www.infoq.cn/article/ufsvugyl6fvvmqx67ycc\\n 📄 例如,一个任务经过 20 轮对话后,包含了最初的任务描述、2 次计划更新、5 条 Agent 的重要发现、3 段检索的知识。这些信息散落在历史消息中,压缩时 LLM 难以快速定位和评估这些关键信息的重要性。\\n\\n目标漂移问题场景:\\n\\nImage 20\\n\\n##### 解决思路\\n\\n我们引入了上下文锚定(ChatDashboard)机制,它是一个关键信息收集器:\\n\\n1. 多类型消息收集:系统维护一个全...\\n\\n5. 如何通过Function Calling 实现工具调用 - 阿里云文档\\n 🔗 https://help.aliyun.com/zh/model-studio/qwen-function-calling\\n 📄 用户可能在第一轮提问“北京天气”,第二轮提问“上海的呢?”若模型上下文中没有第一轮的信息,模型无法判断需要调用哪个工具。建议在多轮对话场景中,每轮结束后维持 messages 数组,在此基础上添加 User Message并发起 Function Calling以及后续步骤。messages 结构如下所示:\\n\\n```\\n[ System Message -- 指引模型调用工具的策略 User Mes...\\n\\n6. [译] Anthropic 是如何构建Multi-Agent Research 系统的(2025)\\n 🔗 https://arthurchiao.github.io/blog/built-multi-agent-research-system-zh/\\n 📄 不要求 sub-agent 必须通过 Lead Agent 传递所有信息,允许专门的 Agent 创建独立持久的输出。\\n sub-agent 调用 Tool,将工作存储在外部系统中,然后将轻量级引用传递回协调器。\\n\\n这可以防止多阶段处理过程中的信息丢失,并减少通过对话历史复制大输出而产生的 token 开销。 该模式特别适用于代码、报告或数据可视化等结构化输出,其中 sub-agent 的专门提示...\\n\\n7. Agentic AI基础设施实践经验系列(三):Agent记忆模块的最佳实践\\n 🔗 https://aws.amazon.com/cn/blogs/china/agentic-ai-infrastructure-deep-practice-experience-thinking-series-three-best-practices-for-agent-memory-module/\\n 📄 上下文窗口的限制导致遗忘问题。LLM通过一个有限的“上下文窗口”(Context Window)来处理信息。所有输入(包括Prompt和之前的对话片段)都必须塞入这个窗口。一旦信息超出这个窗口,LLM就“忘记”了它,无法再访问。这导致了所谓的“遗忘”问题;\\n 难以处理多轮/复杂任务。对于需要跨越多轮对话、追踪状态或执行一系列子任务的复杂任务,LLM很难保持连贯性和进展,因为它会不断“忘记”之前的步...\\n\\n8. 构建多轮对话智能体场景:从技术选择到落地实践的深度思考\\n 🔗 https://www.betteryeah.com/blog/building-multi-turn-dialogue-agent-scenarios-deep-thinking\\n 📄 构建多轮对话智能体场景是一个充满挑战但极具价值的工程。它不仅要求我们掌握先进的技术,更要求我们深刻理解人类交流的本质和需求。\\n\\n在这个过程中,我们不仅在构建更智能的系统,也在重新思考人与机器的关系。多轮对话智能体不应该是人类的替代品,而应该是人类能力的延伸和增强。它们帮助我们更好地处理信息、解决问题、创造价值。\\n\\n未来的多轮对话智能体将不仅仅是工具,更可能成为我们的伙伴、顾问、助手。它们将深度参与...\\n\\n9. Agentic AI基础设施实践经验系列(六):Agent质量评估\\n 🔗 https://aws.amazon.com/cn/blogs/china/agent-quality-evaluation/\\n 📄 | | | | |\\n --- --- |\\n| 框架名称 | 主要聚焦 | 特点 | 商用/开源 |\\n| AgentBoard | 轨迹与事件回放 | 细粒度多轮交互评测、可视化回放 | 开源 |\\n| AgentBench | LLM-as-Agent 综合基准 | 8 大模拟环境覆盖对话、游戏、文件操作等场景 | 开源 |\\n| τ-bench (Tau-bench) | 用户-Agent...\\n\\n10. 快时尚电商行业智能体设计思路与应用实践(二)借助LangChain ...\\n 🔗 https://aws.amazon.com/cn/blogs/china/fast-fashion-e-commerce-agent-design-ideas-and-application-practice-part-two/\\n 📄 显式的状态管理:每个节点只关心自己处理的那部分状态,极大降低了耦合度,也方便后续维护和调试。\\n 动态、灵活的代理路由:通过条件边和循环结构,系统可以根据实时上下文动态选择执行路径,实现高度个性化和智能的对话或决策流程。\\n 易于扩展和维护:新增节点或调整路由只需局部修改,不会影响整体架构,极大提升了系统的可维护性。\\n 支持复杂的状态转换逻辑:无论是多轮对话、条件推理还是长流程任务,LangGraph...\", \"filters\": {\"topic\": \"general\"}, \"query\": \"多轮对话agent tool system 返回内容类型 对话系统\", \"results\": [{\"index\": 1, \"title\": \"构建多轮对话问答系统:基于大模型的Agent与Tools探索 - CSDN博客\", \"url\": \"https://blog.csdn.net/Fanstay985/article/details/142335892\", \"content\": \"公安备案号11010502030143\\n 京ICP备19004658号\\n 京网文〔2020〕1039-165号\\n 经营性网站备案信息\\n 北京互联网违法和不良信息举报中心\\n 家长监护\\n 网络110报警服务\\n 中国互联网举报中心\\n Chrome商店下载\\n 账号管理规范\\n 版权与免责声明\\n 版权申诉\\n 出版物许可证\\n 营业执照\\n ©1999-2025北京创新乐知网络技术有限公司\\n\\n评论\\n\\n被折叠的 条评论\\n为什么被折叠?\\n到【灌水乐园】发言\\n\\n查看更多评论\\n\\n添加红包\\n\\n发出的红包\\n\\n实付元\\n\\n钱包余额\\n0\\n\\n1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。 \\n 2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。\\n\\n余额充值\\n\\n确定取消\\n\\n举报\\n\\n 包含不实信息\\n 涉及个人隐私\\n\\n请选择具体原因(必选)\\n\\n 侮辱谩骂\\n 诽谤\\n\\n请选择具体原因(必选)\\n\\n 搬家样式\\n 博文样式\\n\\nAI 搜索 智能体 AI 编程 AI 作业助手\\n\\n专业的中文 IT 技术社区,与千万技术人共成长\\n客服\\n返回顶部 [...] 原创 郑德权、衣景龙等 悦智网 2021-10-29 15:30━━━━近年来,随着人工智能的快速发展,人机交互能力不断增强,其中问答技术能够在保证一定准确度的情况下极大地简化用户的搜索操作,在节约时间的同时,还能够加深用户对搜索事物的了解程度,百度公司的小度、苹果公司的Siri等正是基于问答技术形成的产品。作为问答技术的关键构造,对话系统根据对话目的或应用场景的不同,可以分为任务型对话以及非任务型对话。前者一般面向特定任务,目的是尽可能准确且使用较少对话轮次完成用户提问、查询等语义任务;后者则以聊天机器人\\n\\n谈面向任务的多轮对话系统(TOD)\\n\\n姚家湾博客\\n\\n07-09\\n\\n1942\\n\\n面向任务对话系统(Task-Oriented Dialogue (TOD) Systems)主要是为解决特定任务的,比如订票任务(订机票,电影票等),预定饭店等。这种对话往往需要多轮对话才能够完成。\\n\\n 关于我们\\n 招贤纳士\\n 商务合作\\n 寻求报道\\n 400-660-0108\\n kefu@csdn.net\\n 在线客服\\n 工作时间 8:30-22:00 [...] 在探索大语言模型的教育应用过程中,我们开发了一个基于多 Agent 的智能教学系统。本文将介绍如何通过教师和学生两个专业化的 Agent 协同工作,实现自然的教学对话互动。\\n\\n大模型应用:多轮对话(prompt工程)\\n\\nfydw\\\\_715的博客\\n\\n02-27\\n\\n6561\\n\\n大型语言模型在新聊天中通过创建新会话、引入系统提示来管理上下文。系统提示设定模型的角色、语气和行为准则,对用户不可见,但影响模型回复。随着对话进行,系统记录消息队列,模型参考该队列生成连贯的回复。由于上下文窗口限制,系统截断早期对话,优先保留系统提示和近期内容,确保模型行为一致。模型只负责生成文本,应用程序负责会话管理、上下文维护等,实现多轮对话并保护用户隐私。示例代码展示了多轮对话的实现方法。\\n\\n多轮问答归纳\\n\\nqq\\\\_43993879的博客\\n\\n06-05\\n\\n2972\\n\\n多模态多轮问答\\n\\n对话式AI系列:任务型多轮对话的实践与探索\\n\\nzhoguancunkj的博客\\n\\n12-15\\n\\n3922\", \"score\": 0.55362654, \"published_date\": \"\"}, {\"index\": 2, \"title\": \"LangChain Tutorials:多轮对话Agent(LangGraph + ReAct) - 知乎\", \"url\": \"https://zhuanlan.zhihu.com/p/717573383\", \"content\": \"print(f\\\"ContentString: {response.content}\\\") ## 没有返回回答\\nprint(f\\\"ToolCalls: {response.tool_calls}\\\") ## 返回了工具调用\\n \\nContentString: \\nToolCalls: [{'name': 'tavily_search_results_json', 'args': {'query': 'weather san francisco'}, 'id': 'toolu_01VTP7DUvSfgtYxsq9x4EwMp'}]\\n \\n```\\n\\n我们可以看到,现在没有文本内容,但有一个工具调用!它想要我们调用Tavily搜索工具。\\n\\n这还没有真正调用那个工具——它只是告诉我们要调用它。为了实际调用它,我们需要创建我们的代理。\\n\\n## 创建 Agent [...] Tool output was: [{'url': ' 'content': \\\"{'location': {'name': 'San Francisco', 'region': 'California', 'country': 'United States of America', 'lat': 37.78, 'lon': -122.42, 'tz_id': 'America/Los_Angeles', 'localtime_epoch': 1714427052, 'localtime': '2024-04-29 14:44'}, 'current': {'last_updated_epoch': 1714426200, 'last_updated': '2024-04-29 14:30', 'temp_c': 17.8, 'temp_f': 64.0, 'is_day': 1, 'condition': {'text': 'Sunny', 'icon': '//cdn.weatherapi.com/weather/64x64/day/113.png', 'code': 1000}, [...] {'agent': {'messages': [AIMessage(content=[{'text': 'To get current weather information for your location in San Francisco, let me invoke the search tool:', 'type': 'text'}, {'id': 'toolu_01BGEyQaSz3pTq8RwUUHSRoo', 'input': {'query': 'san francisco weather'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}], response_metadata={'id': 'msg_013AVSVsRLKYZjduLpJBY4us', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'input_tokens': 347,\", \"score\": 0.5440511, \"published_date\": \"\"}, {\"index\": 3, \"title\": \"如何借助LLM 设计和实现任务型对话Agent - Thoughtworks\", \"url\": \"https://www.thoughtworks.com/zh-cn/insights/blog/machine-learning-and-ai/how-to-design-task-based-dialogue-Agent-with-LLM\", \"content\": \"为了确保 Agent 在多轮对话场景中表现良好,我们还需要验证 Tracker 模块的上下文追踪和状态更新策略,确保核心模块的准确性。此外,策略测试阶段还需聚焦于对话管理策略的可靠性,确保 Agent 能够正确判断任务完成和转交对话。针对多轮对话场景,我们需要模拟用户的不同回复,并对应检验策略模块的决策是否合理。这通常需要 mock NLU 模块的输出结果 (包括用户意图和提取的槽位值),并将其输入到策略模块,检查策略生成的对话行为是否符合预期。\\n\\n1. Mock NLU 识别为 \\\"查询航班\\\" 意图,但没有提取到任何槽位值 (如出发地、目的地等),策略应决定询问缺失的必要信息。\\n2. Mock NLU 识别为 \\\"预订机票\\\" 意图,已提取部分槽位值 (如出发地),策略应决定询问其余缺失的信息。\\n3. 经过多轮交互,Mock NLU 模块模块提取了全部必要信息 (如出发地、目的地、日期等),策略应决定进入机票搜索和预订流程,或进行确认。 [...] 权衡检索召回率与准确率,如果模型分析和推理能力很强,可以适当的降低相似度阈值,将可能与用户问题相关的内容全部检索到并放入 Prompt 中交给大模型来分析具体什么内容可以参考,什么内容与用户的问题相关;但相对而言,如果基础大模型的分析能力不够强,这时候就需要尽量提高相似度阈值,过滤掉容易干扰模型分析思考的内容,进而降低模型出现幻觉的概率。\\n\\n优化用于检索的 Query,在多轮对话的场景中,用户当前轮的消息可能会存在缺少主语导致检索内容不完整的情况。而如果单纯将多轮消息直接拼接用于检索内容,又会降低检索内容的准确率,两种情况都会影响 LLM 最终回复内容的准确性。这里可以引入一个多轮转写模型,将用户的对话历史输入到模型,模型能够自动补齐当前用户消息中的主语,忽略掉与当前轮用户消息无关的信息,如下例所示:\\n\\n<对话历史>\\n\\nUser: 网银如何在线下开通\\n\\nAgent: 需要。。。。\\n\\nUser: 真麻烦\\n\\nAgent: 抱歉。。。\\n\\nUser: 线上呢\\n\\n<转写结果>\\n\\n网银如何在线上开通 [...] 对话过程是连续的,Tracker 需要正确的追踪上下文信息,记录用户历史意图、槽位值的变化情况以及完整的对话历史记录,确保对话 Agent 具备深层次的理解和回应能力。对话过程也是动态变化的,Tracker 需要实现高效的状态更新策略。在每一轮对话后,正确地更新对话状态且保持信息的一致性。可采用合理的算法和数据结构来优化更新过程,确保实时性和高性能。在小规模应用中,可以将状态直接存储在内存中;而在大规模分布式应用中,则需要将状态持久化存储,如使用 Redis 等内存数据库,根据 session ID 快速读写对话状态。用户的意图不仅需要考虑当前用户消息给到模型后的结果,还需要考虑上下文信息以及槽位等信息。\\n\\n基于上下文的意图更新: 根据历史意图和最新对话轮的最新意图综合判断用户的当前意图。例如在下面的对话历史中,用户第一轮意图是订飞机票,第二轮的意图是填充槽位,由于填充槽位只是辅助意图,Agent 可以认为订飞机票这个意图将继续保持。\\n\\n例:\\n\\nUser: 订飞机票 -> 订机票\\n\\nAgent: 请问您要预订的线路是什么\\n\\nUser: 成都到东京的 -> 订机票\", \"score\": 0.37655488, \"published_date\": \"\"}, {\"index\": 4, \"title\": \"AI Agent 上下文管理:基于搭叩的七大原则与实践 - InfoQ\", \"url\": \"https://www.infoq.cn/article/ufsvugyl6fvvmqx67ycc\", \"content\": \"例如,一个任务经过 20 轮对话后,包含了最初的任务描述、2 次计划更新、5 条 Agent 的重要发现、3 段检索的知识。这些信息散落在历史消息中,压缩时 LLM 难以快速定位和评估这些关键信息的重要性。\\n\\n目标漂移问题场景:\\n\\nImage 20\\n\\n##### 解决思路\\n\\n我们引入了上下文锚定(ChatDashboard)机制,它是一个关键信息收集器:\\n\\n1. 多类型消息收集:系统维护一个全局的 GlobalChatBoard,收集整个任务过程中的关键信息:\\n\\n Task 类型:初始任务描述(通过 SetContent 设置)\\n\\n Plan 类型:任务计划和计划更新(通过 AppendContent 追加)\\n\\n Agent 类型:Agent 的重要消息和发现(带 AgentID 和时间戳)\\n\\n Knowledge 类型:检索到的相关知识\\n\\n TaskDone 类型:任务完成的总结(带任务名称)\\n\\n 结构化存储:将收集的消息按类型格式化为 XML 结构,例如:\\n\\n `任务描述` [...] 搭叩这种异步的长链路任务的 Agent 存在 Tools 多,任务的复杂度更高场景更丰富,执行步骤多等特点;我们发现长时间、复杂任务的执行面临着一个核心挑战:上下文窗口限制。随着对话轮次的增加,历史消息、工具调用结果、环境状态信息等内容会迅速累积,导致 token 消耗呈指数级增长。当上下文超出模型的处理能力时,系统要么截断历史信息导致\\\"失忆\\\",要么直接崩溃无法继续工作。\\n\\n这不仅仅是一个技术限制问题,更关乎 AI Agent 能否真正承担起复杂的、需要长期规划和执行的任务。一个真正实用的 AI Agent 系统,必须能够在有限的上下文窗口内,既保留关键历史信息,又能持续获取最新的环境状态,还要支持多 Agent 协作和能力动态扩展。 [...] 工具结果重复:某些工具(如查看文件、列出目录)可能在不同时刻被重复调用,产生大量相似的输出\\n\\n 实时性问题:Agent 需要看到的是\\\"此时此刻\\\"的环境状态,而不是历史快照\\n\\n举个例子,如果 Agent 在第 5 轮对话时看到\\\"当前时间是 10:00,编辑了 3 个文件\\\",到了第 20 轮时,这些信息早已过时,但仍然在上下文中占据位置。\\n\\nImage 18\\n\\n##### 解决思路\\n\\n我们设计了一套动态替换机制:\\n\\n1. 定位最新用户消息:系统会自动找到最新的用户消息位置\\n\\n2. 清理历史 dashboard:遍历之前的所有用户消息,使用正则表达式移除其中的`...`内容\\n\\n3. 注入最新 dashboard:在最新用户消息末尾添加实时生成的 dashboard\\n\\n4. ContentReplace 机制:为了保留审计和调试能力,我们在消息对象中保存了原始内容(Content)和替换内容(ContentReplace),在压缩时使用替换内容,但日志中保留原始内容\\n\\nImage 19\\n\\n##### 竞品调研\", \"score\": 0.3708637, \"published_date\": \"\"}, {\"index\": 5, \"title\": \"如何通过Function Calling 实现工具调用 - 阿里云文档\", \"url\": \"https://help.aliyun.com/zh/model-studio/qwen-function-calling\", \"content\": \"用户可能在第一轮提问“北京天气”,第二轮提问“上海的呢?”若模型上下文中没有第一轮的信息,模型无法判断需要调用哪个工具。建议在多轮对话场景中,每轮结束后维持 messages 数组,在此基础上添加 User Message并发起 Function Calling以及后续步骤。messages 结构如下所示:\\n\\n```\\n[ System Message -- 指引模型调用工具的策略 User Message -- 用户的问题 Assistant Message -- 模型返回的工具调用信息 Tool Message -- 工具的输出信息 Assistant Message -- 模型总结的工具调用信息 User Message -- 用户第二轮的问题 ]\\n```\\n\\n### 流式输出\\n\\n为了提升用户体验和减少等待时间,可使用流式输出实时获取工具函数名称与入参信息。其中:\\n\\n 工具函数名称:仅在第一个流式返回的对象(delta)中出现。\\n 入参信息:以流式方式输出。 [...] ```\\n# 步骤7:大模型总结工具输出 # 请将以下代码粘贴到步骤6 代码后 print(\\\"正在总结工具输出...\\\") completion = function_calling()\\n```\\n\\n```\\n// 步骤7:大模型总结工具输出 // 请将以下代码粘贴到步骤6 代码后 console.log(\\\"正在总结工具输出...\\\"); const completion_1 = await functionCalling();\\n```\\n\\n可从`content`得到回复内容:“上海今天的天气是多云。如果您有其他问题,欢迎继续提问。”\\n\\n```\\n{ \\\"content\\\": \\\"上海今天的天气是多云。如果您有其他问题,欢迎继续提问。\\\", \\\"refusal\\\": null, \\\"role\\\": \\\"assistant\\\", \\\"audio\\\": null, \\\"function_call\\\": null, \\\"tool_calls\\\": null }\\n```\\n\\n到此,您已完成了一次完整的 Function Calling 流程。\\n\\n## 进阶用法\\n\\n### 多轮对话 [...] 如果您希望无论输入什么问题,Function Calling 都不会进行工具调用(返回对象中包含回复内容`content`而`tool_calls`参数为空),可设定`tool_choice`参数为`\\\"none\\\"`,或不传入`tools`参数,Function Calling 返回的`tool_calls`参数将始终为空。\\n\\n 假设当前场景中的问题均无需调用工具,可修改 function\\\\_calling 代码为:\\n\\n ```\\n def function_calling(): completion = client.chat.completions.create( model=\\\"qwen-plus\\\", messages=messages, tools=tools, tool_choice=\\\"none\\\" ) print(completion.model_dump_json()) function_calling()\\n ```\", \"score\": 0.35904998, \"published_date\": \"\"}, {\"index\": 6, \"title\": \"[译] Anthropic 是如何构建Multi-Agent Research 系统的(2025)\", \"url\": \"https://arthurchiao.github.io/blog/built-multi-agent-research-system-zh/\", \"content\": \"不要求 sub-agent 必须通过 Lead Agent 传递所有信息,允许专门的 Agent 创建独立持久的输出。\\n sub-agent 调用 Tool,将工作存储在外部系统中,然后将轻量级引用传递回协调器。\\n\\n这可以防止多阶段处理过程中的信息丢失,并减少通过对话历史复制大输出而产生的 token 开销。 该模式特别适用于代码、报告或数据可视化等结构化输出,其中 sub-agent 的专门提示词产生的结果优于通过通用 lead agent 过滤的结果。\\n\\n# 7 总结\\n\\n构建 AI Agent 时,最后一公里往往需要投入巨大精力。\\n\\n尽管存在很多挑战,但已经证明,Multi-Agent 系统是解决开放式任务的最有效方式之一。\\n\\n# 致谢 [...] 这种方法承认 Agent 可能会找到实现同一目标的不同路径,同时确保它们提供预期的结果。\\n 对于复杂的工作流,将评估分解为应发生特定状态变化的离散 checkpoint,而不是试图验证每一个中间步骤。\\n\\n## 6.2 长跨度(超过上下文窗口限制)对话管理\\n\\n生产 Agent 通常进行跨越数百轮的对话,需要仔细的上下文管理策略。\\n\\n随着对话的延长,标准上下文窗口变得不足,需要智能的压缩和记忆机制。\\n\\n我们实现了这样的模式:\\n\\n1. Agent 在完成工作阶段后进行总结,并将基本信息存储在外部存储中,然后再继续执行新任务。 当接近上下文限制时,Agent 可以生成新 sub-agent,交接保持连续性。\\n2. 此外,它们可以从外部存储中检索上下文,而不是在达到上下文限制时丢失先前的工作。 这种分布式方法防止了上下文溢出,同时在扩展交互中保持对话连贯性。\\n\\n## 6.3 sub-agent 输出到文件系统,最小化“传话开销”\\n\\n某些类型的结果,sub-agent 输出可以直接绕过 lead agent,从而提高保真度和性能。 [...] ## 5.4 同步执行造成瓶颈\\n\\n目前,我们的 Lead Agent 同步执行 sub-agent,等待每组 sub-agent 完成后再继续。 这简化了协调,但在 Agent 之间造成了瓶颈,整个系统可能会在等待单个 sub-agent 完成搜索。\\n\\n改进方式:Agent 并发工作,并在需要时创建新的 sub-agent。 但这种异步性在结果协调、状态一致性和 sub-agent 之间的错误传播方面增加了挑战。\\n\\n随着模型能够处理更长、更复杂的研究任务,我们期望性能提升能够证明复杂性是值得的。\\n\\n# 6 其他技巧\\n\\n## 6.1 状态随时间变化的 Agent:进行最终状态评估\\n\\n评估在多轮对话中修改持久状态的 Agent 带来了独特的挑战。 与只读研究任务不同,每个动作都会改变后续步骤的环境,产生传统评估方法难以处理的依赖关系。\\n\\n我们发现,关注最终状态评估而不是逐轮分析是成功的。 不判断 Agent 是否遵循了特定流程,而是评估其是否达到了正确的最终状态。\", \"score\": 0.3193235, \"published_date\": \"\"}, {\"index\": 7, \"title\": \"Agentic AI基础设施实践经验系列(三):Agent记忆模块的最佳实践\", \"url\": \"https://aws.amazon.com/cn/blogs/china/agentic-ai-infrastructure-deep-practice-experience-thinking-series-three-best-practices-for-agent-memory-module/\", \"content\": \"上下文窗口的限制导致遗忘问题。LLM通过一个有限的“上下文窗口”(Context Window)来处理信息。所有输入(包括Prompt和之前的对话片段)都必须塞入这个窗口。一旦信息超出这个窗口,LLM就“忘记”了它,无法再访问。这导致了所谓的“遗忘”问题;\\n 难以处理多轮/复杂任务。对于需要跨越多轮对话、追踪状态或执行一系列子任务的复杂任务,LLM很难保持连贯性和进展,因为它会不断“忘记”之前的步骤和决策。特别是在Agent的场景,工具的定义和工具的返回值都会存在于上下文中,同时由于Agent具有自主工作的能力,和LLM的平均交互的轮数也大大增加;\\n 无法个性化。由于不记住特定用户的历史偏好、习惯或之前的互动,LLM难以提供真正个性化的体验。每次互动都像是第一次见面; [...] 智能体的记忆更新可通过轮数或事件触发。轮数触发是每隔3-5轮对话自动生成摘要存入记忆;事件触发则在完成任务、场景转换等关键节点记录信息。例如,客服完成问题处理时保存解决方案,个人助理更新日程后写入日历。开发者可实现监控逻辑,在对话累积或话题转换时,让大模型对近期对话生成摘要,提取关键信息并添加标签便于检索。\\n\\n系统也可支持用户主动标记需要记住的信息,如通过口头指令或界面操作。这不仅让用户指定重要内容,也支持删除特定记忆的需求,确保用户对数据的控制权。\\n\\n### 记忆存储:记忆组织结构设计\\n\\n记忆数据通常采用用户→会话→记忆片段的三层结构管理。用户层区分不同账号空间,会话层隔离各对话上下文,记忆片段层存储具体内容及元数据(如时间、关键词、来源等)。复杂系统可能需要维护多个记忆库,包括短期工作记忆、长期情节记忆、语义知识库等。合理的结构设计有助于快速检索和有效管理记忆内容。\\n\\n### 记忆检索:记忆查询与召回逻辑 [...] 长期记忆(Long-term Memory, LTM)是智能体用于跨会话、跨任务长期保存知识的记忆形式。它对应于人类的大脑中持久保存的记忆,例如事实知识、过去经历等。长期记忆的实现通常依赖于外部存储或知识库,包括但不限于:\\n\\n 摘要记忆:将长对话内容提炼为关键摘要存储;\\n 结构化知识库:使用数据库或知识图谱存储结构化信息;\\n 向量化存储:通过向量数据库实现基于语义的记忆检索。\\n\\n长期记忆使智能体能够随着时间累积经验和知识,它特别适用于知识密集型应用和需要长期个性化的场景。\\n\\n## 记忆管理与使用相关技术\\n\\n设计开发Agent的记忆系统时要考虑不同场景下如何选择记忆内容、设计写入策略、组织记忆结构、实现检索召回四个方面。\\n\\n### 记忆产生:判断哪些信息需要被记忆\\n\\n在构建智能体记忆系统时,首先要根据具体的场景确定哪些信息值得记忆。这些记忆往往是多维度和动态的信息结构,包括时间维度(理解时间依赖的关系和序列)、空间维度(解释基于位置的和几何关系)、参与者状态(跟踪多个实体及其不断变化的状况)、意图上下文(理解目标、动机和隐含的目的),以及文化上下文(在特定社会和文化框架内解释交流)。\", \"score\": 0.30516952, \"published_date\": \"\"}, {\"index\": 8, \"title\": \"构建多轮对话智能体场景:从技术选择到落地实践的深度思考\", \"url\": \"https://www.betteryeah.com/blog/building-multi-turn-dialogue-agent-scenarios-deep-thinking\", \"content\": \"构建多轮对话智能体场景是一个充满挑战但极具价值的工程。它不仅要求我们掌握先进的技术,更要求我们深刻理解人类交流的本质和需求。\\n\\n在这个过程中,我们不仅在构建更智能的系统,也在重新思考人与机器的关系。多轮对话智能体不应该是人类的替代品,而应该是人类能力的延伸和增强。它们帮助我们更好地处理信息、解决问题、创造价值。\\n\\n未来的多轮对话智能体将不仅仅是工具,更可能成为我们的伙伴、顾问、助手。它们将深度参与我们的工作和生活,成为数字化时代人类智慧的重要组成部分。\\n\\n这个未来并不遥远,它正在我们每一次的技术突破、每一个项目实践、每一次用户交互中逐步成为现实。构建多轮对话智能体场景,我们不仅在创造技术,更在塑造未来。\\n\\n快速搭建AI智能客服:2025年企业级部署完整指南\\n\\n智能体与普通AI工具的核心区别:从被动响应到主动决策的技术跃迁\\n\\n返回列表\\n\\n获取案例\\n\\n### 最新发布\\n\\n### 热门推荐\\n\\nAI+制造AI+电商大模型应用开发AI智能体AI+零售AI AgentRAG知识管理企业知识库智能营销智能问答大模型技术AI工作流 [...] 我们在系统中加入了情感智能模块,不仅识别用户的情感,还会相应地调整回复的语气和内容。当用户表现出沮丧时,系统会使用更温暖的语言;当用户表现出焦虑时,系统会提供更多的安慰和支持。\\n\\n这种情感智能的实现需要大量的心理学知识和人文素养。我们与心理学专家合作,建立了情感回应的知识库,确保系统的回复既专业又充满人文关怀。\\n\\n### 5.3 运维监控:系统健康的守护者\\n\\n多轮对话智能体系统的复杂性决定了它需要完善的监控和运维体系。与传统的Web应用不同,对话系统的问题往往具有隐蔽性,不容易被及时发现。\\n\\n我们建立了多维度的监控体系:技术指标监控系统性能,业务指标监控对话质量,用户指标监控满意度变化。更重要的是,我们实现了异常对话的自动识别和告警机制。\\n\\n在实际运维中,我们发现对话质量的下降往往是渐进的。系统可能因为数据漂移、模型老化等原因逐渐失效,但这种变化很难被及时察觉。我们开发了对话质量的趋势分析工具,能够及早发现质量下降的趋势,提前进行干预。\\n\\n### 5.4 持续学习:系统进化的动力源泉 [...] 更重要的是,我们引入了知识的\\\"对话相关性评分\\\"机制。不同的知识在对话的不同阶段具有不同的重要性。在对话初期,概括性的知识更重要;在对话深入阶段,具体的细节知识更有价值。这种动态的知识权重调整显著提升了回复的准确性和相关性。\\n\\n### 3.4 Function Calling:让对话系统具备\\\"行动力\\\"\\n\\nFunction Calling技术让多轮对话智能体从单纯的\\\"聊天工具\\\"升级为\\\"行动助手\\\"。但在实际应用中,我们发现这项技术的挑战主要在于时机把握和参数提取。\\n\\n在一个智能客服项目中,系统需要能够帮助用户查询订单、修改地址、申请退款等。表面上看,这些操作都很直接,但实际情况复杂得多。\\n\\n用户说\\\"我想改个地址\\\"时,系统需要判断是要修改哪个订单的地址,是收货地址还是发票地址。这需要结合对话历史和业务逻辑来判断。如果信息不足,系统需要智能地询问补充信息,而不是简单地报错。\\n\\n我们设计了一个\\\"渐进式参数收集\\\"机制。系统首先基于对话历史尝试填充函数参数,对于缺失的参数,会生成自然的询问来收集。整个过程对用户来说是流畅的对话体验,而不是生硬的表单填写。\", \"score\": 0.2725656, \"published_date\": \"\"}, {\"index\": 9, \"title\": \"Agentic AI基础设施实践经验系列(六):Agent质量评估\", \"url\": \"https://aws.amazon.com/cn/blogs/china/agent-quality-evaluation/\", \"content\": \"| | | | |\\n --- --- |\\n| 框架名称 | 主要聚焦 | 特点 | 商用/开源 |\\n| AgentBoard | 轨迹与事件回放 | 细粒度多轮交互评测、可视化回放 | 开源 |\\n| AgentBench | LLM-as-Agent 综合基准 | 8 大模拟环境覆盖对话、游戏、文件操作等场景 | 开源 |\\n| τ-bench (Tau-bench) | 用户-Agent 真实对话评测 | 三层评估(数据库、策略文档、用户模拟),聚焦零售客服、航旅场景 | 开源 |\\n| GAIA | 测评AI助手在解决现实复杂、多模态、多步骤问题上的通用能力,强调多轮推理和综合应用 | – 多模态(文本、图像等)、多阶段真实问题任务 – 任务多样,通用性强,考察系统性AI能力 | 开源 |\\n| WebArena | AI智能体在仿真Web上的自动任务执行与复杂交互,通过虚拟Web页面评测Agent能力 | – 高仿真、可控、可复现的Web交互环境 – 覆盖电商、论坛、协作开发等多类网站 – 包含实用工具、知识资源,支持复杂任务链 | 开源 | [...] | | | |\\n --- \\n| 组件 | 作用 | 关键技术/实现细节 |\\n| 环境模拟器 | 构建部分可观测环境(如网页、游戏、仿真) | 使用虚拟环境、API封装,限制信息访问 |\\n| Agent接口 | 连接待评测Agent,支持多轮交互 | API封装,支持多模型、多策略 |\\n| 轨迹记录器 | 记录每轮交互的状态、动作、工具调用 | 日志存储、事件追踪(JSON/数据库) |\\n| 能力拆解指标计算器 | 计算“进度率”、“探索效率”、“计划一致性”等指标 | 规则定义、自动统计 |\\n| 可视化面板 | 轨迹回放、指标分析、热力图 | 前端交互、动态图表(D3.js、Mermaid) |\\n\\n(2)评测指标\\n\\nAgentBoard 提供了多维度、细粒度的评测指标,主要包括以下几类: [...] Task Success Rate (pass¹):Agent 在单次对话中,将数据库状态从初始状态变更到目标状态的比例(即一次性成功率)。举例,若在 100 次零售场景对话中,Agent 有 60 次能正确完成退货流程,则 pass¹= 60%。\\n Stability over Repeats (passᵏ):Agent 连续 k 次重复执行同一任务,全部成功的概率,衡量一致性和可靠性。举例,若 pass³ = 0.22,表示在 100 次任务中,仅有 22 次能连续三次都成功,反映 Agent 在多轮反复使用时性能会显著下降\\n Rule Compliance Rate:在任务过程中,Agent 是否严格遵循领域策略文档(Domain Policies,如“基础经济舱不可改签”)的比例。在 58 次成功的航旅改签对话中,若 58 次均按“退票再订票”规则执行,则规则合规率 = 100%\", \"score\": 0.2692868, \"published_date\": \"\"}, {\"index\": 10, \"title\": \"快时尚电商行业智能体设计思路与应用实践(二)借助LangChain ...\", \"url\": \"https://aws.amazon.com/cn/blogs/china/fast-fashion-e-commerce-agent-design-ideas-and-application-practice-part-two/\", \"content\": \"显式的状态管理:每个节点只关心自己处理的那部分状态,极大降低了耦合度,也方便后续维护和调试。\\n 动态、灵活的代理路由:通过条件边和循环结构,系统可以根据实时上下文动态选择执行路径,实现高度个性化和智能的对话或决策流程。\\n 易于扩展和维护:新增节点或调整路由只需局部修改,不会影响整体架构,极大提升了系统的可维护性。\\n 支持复杂的状态转换逻辑:无论是多轮对话、条件推理还是长流程任务,LangGraph 都能胜任。\\n 人机协同决策支持:通过人机协同(Human-in-the-Loop)机制,LangGraph 能够在工作流的关键节点暂停执行,等待人工干预、审核或决策输入,然后基于人类反馈继续执行后续流程。\\n\\n总体而言,LangGraph 以其图结构和显式状态管理,为构建复杂、动态、多智能体协作的智能系统提供了强大工具。随着业务复杂度提升,LangGraph 让 Agent 系统更灵活、可控、具有扩展能力。针对基于 LangGraph 的 Multi-Agent 与复杂路由的场景,我们将在后续博客中进行进一步演示。\\n\\n### 工具生态(MCP) [...] Additional arguments specific to each agent Returns: tuple[str, str]: (response message, conversation_id) \\\"\\\"\\\" pass [...] 多轮对话状态管理:在多轮对话系统中,用户需求往往跨越多个阶段,涉及意图识别、信息收集、异常处理等环节。LangGraph 通过“状态驱动的图结构”,可以将每个对话阶段拆解为独立节点,每个节点专注于特定任务,并通过条件边灵活流转。例如,针对客户服务流程,可以用 State 结构体显式管理意图、订单详情、补偿等级、升级需求等关键状态信息。节点函数根据当前状态动态决定下一个节点,实现流程的自动分支和升级。在多代理协作场景下,LangGraph 支持将不同领域专家(如订单专家、物流专家、高级专家)作为独立节点,根据实时上下文和复杂度自动路由请求至最合适的专家节点,极大提升了多智能体系统的协作效率和灵活性。\", \"score\": 0.26764545, \"published_date\": \"\"}], \"total_results\": 10}", "timestamp": "2025-11-04T12:22:08.205128", "tool_call_id": "web_search:0", "name": "web_search" }, { "role": "assistant", "content": "", "timestamp": "2025-11-04T12:22:10.779989", "tool_calls": [ { "id": "extract_webpage:1", "index": 0, "type": "function", "function": { "name": "extract_webpage", "arguments": "{\"url\": \"https://zhuanlan.zhihu.com/p/717573383\"}" } } ] }, { "role": "tool", "content": "{\"success\": true, \"url\": \"https://zhuanlan.zhihu.com/p/717573383\", \"content\": \"🌐 网页内容 (30532 字符):\\n📍 URL: https://zhuanlan.zhihu.com/p/717573383\\n==================================================\\n* [首页](//www.zhihu.com/)\\n* [知乎直答](//www.zhihu.com/zhida)\\n* [知乎知学堂](//www.zhihu.com/education/learning)\\n* [等你来答](//www.zhihu.com/question/waiting)\\n\\nLangChain Tutorials:多轮对话 Agent(LangGraph + ReAct)\\n\\n首发于\\n[Agent:通过AGI之路](//www.zhihu.com/column/c_1310246938189443072)\\n\\n切换模式\\n\\n# LangChain Tutorials:多轮对话 Agent(LangGraph + ReAct)\\n\\n[​](https://www.zhihu.com/question/48510028)\\n[​](https://www.zhihu.com/kvip/purchase)\\n\\n北京大学 计算机技术硕士\\n\\n[收录于 · Agent:通过AGI之路](https://www.zhihu.com/column/c_1310246938189443072)\\n\\n13 人赞同了该文章\\n\\n​\\n\\n目录\\n\\n收起\\n\\n写在前面\\n\\n什么是 Agent?\\n\\n完整的端到端 agent(案例核心代码)\\n\\nTavily\\n\\n定义工具\\n\\n使用语言模型\\n\\n创建 Agent\\n\\n运行 Agent\\n\\n输出结果整理如下\\n\\n流式消息 Streaming\\n\\nToken Steaming\\n\\n添加记忆:memory + checkpointer + thread\\\\_id\\n\\n示例LangSmith跟踪\\n\\n结论\\n\\n## 写在前面\\n\\n本文翻译自 LangChain 的官方文档 “Build an Agent”, 基于:\\n\\n* **[LangGraph](https://zhida.zhihu.com/search?content_id=247590653&content_type=Article&match_order=1&q=LangGraph&zd_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ6aGlkYV9zZXJ2ZXIiLCJleHAiOjE3NTYwMDMzOTcsInEiOiJMYW5nR3JhcGgiLCJ6aGlkYV9zb3VyY2UiOiJlbnRpdHkiLCJjb250ZW50X2lkIjoyNDc1OTA2NTMsImNvbnRlbnRfdHlwZSI6IkFydGljbGUiLCJtYXRjaF9vcmRlciI6MSwiemRfdG9rZW4iOm51bGx9.ZUuQho83zx0BF1G-eiiysekgJcp88alJGKffJuLOKmQ&zhida_source=entity)**\\n* 封装好的 ReAct agent:**from langgraph.prebuilt import create\\\\_react\\\\_agent**\\n* 封装好的 **Memory Savor**\\n* 本人加入一些补充说明\\n\\n## 什么是 Agent?\\n\\n语言模型本身无法执行操作——**它们只能输出文本(作用其实就相当于人类。所以为什么说 Agent 是目前实现 AGI 最可能的途径)**。LangChain 的一个重要用例是创建代理 Agent。Agent 是使用 LLM 作为推理引擎的系统,**用于确定要采取的操作(决策)和传递给它们的输入(提供工具/函数运行需要的参数)**。在执行操作后,结果可以反馈回 LLM,以确**定是否需要执行更多操作,或者是否可以完成**。\\n\\n在本教程中,我们将构建一个可以与搜索引擎交互的 Agent。您可以向这个代理提问,观察它调用搜索工具,并与它进行对话。\\n\\n### 完整的端到端 agent(案例核心代码)\\n\\n下面的代码片段表示一个完全可用的代理,**它使用 LLM 来决定使用哪些工具**。它配备了一个通用的搜索工具,并具有**会话记忆功能**——这意味着它可以用作**多轮对话**的聊天机器人。\\n\\n在本指南的其余部分,我们将逐步介绍各个组件及其作用——但如果您想立即获取代码并开始使用,可以随时使用它!\\n\\n```\\n\\n ## 导入相关功能\\nfrom langchain_anthropic import ChatAnthropic\\nfrom langchain_community.tools.tavily_search import TavilySearchResults ## 搜索引擎\\nfrom langchain_core.messages import HumanMessage\\nfrom langgraph.checkpoint.memory import MemorySaver ## 保存记忆\\nfrom langgraph.prebuilt import create_react_agent\\n\\n## 创建代理\\nmemory = MemorySaver()\\nmodel = ChatAnthropic(model_name=\\\"claude-3-sonnet-20240229\\\") ## 这里用了 claude 的模型,也可以换成 GPT-4o\\nsearch = TavilySearchResults(max_results=2) ## 实例化搜索引擎\\ntools = [search] ## 定义工具,这里咱只有一个\\nagent_executor = create_react_agent(model, tools, checkpointer=memory) ## 使用 checkpointer 建立记忆\\n\\n## 使用代理\\nconfig = {\\\"configurable\\\": {\\\"thread_id\\\": \\\"abc123\\\"}} #指定 thread_id 的名称,针对性地管理 memory\\n\\n## 打个招呼\\nfor chunk in agent_executor.stream(\\n {\\\"messages\\\": [HumanMessage(content=\\\"hi im bob! and i live in sf\\\")]}, config\\n):\\n print(chunk)\\n print(\\\"----\\\")\\n\\n## 询问天气\\nfor chunk in agent_executor.stream(\\n {\\\"messages\\\": [HumanMessage(content=\\\"whats the weather where I live?\\\")]}, config\\n):\\n print(chunk)\\n print(\\\"----\\\")\\n \\n```\\n\\n输出结果:\\n\\n```\\n\\n ## 打招呼的结果\\n{'agent': {'messages': [AIMessage(content=\\\"Hello Bob! Since you didn't ask a specific question, I don't need to use any tools to respond. It's nice to meet you. San Francisco is a wonderful city with lots to see and do. I hope you're enjoying living there. Please let me know if you have any other questions!\\\", response_metadata={'id': 'msg_01Mmfzfs9m4XMgVzsCZYMWqH', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 271, 'output_tokens': 65}}, id='run-44c57f9c-a637-4888-b7d9-6d985031ae48-0', usage_metadata={'input_tokens': 271, 'output_tokens': 65, 'total_tokens': 336})]}}\\n----\\n\\n## 询问天气的结果\\n## 第一步,决定调用工具\\n{'agent': {'messages': [AIMessage(content=[{'text': 'To get current weather information for your location in San Francisco, let me invoke the search tool:', 'type': 'text'}, {'id': 'toolu_01BGEyQaSz3pTq8RwUUHSRoo', 'input': {'query': 'san francisco weather'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}], response_metadata={'id': 'msg_013AVSVsRLKYZjduLpJBY4us', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'input_tokens': 347, 'output_tokens': 80}}, id='run-de7923b6-5ee2-4ebe-bd95-5aed4933d0e3-0', tool_calls=[{'name': 'tavily_search_results_json', 'args': {'query': 'san francisco weather'}, 'id': 'toolu_01BGEyQaSz3pTq8RwUUHSRoo'}], usage_metadata={'input_tokens': 347, 'output_tokens': 80, 'total_tokens': 427})]}}\\n----\\n\\n## 第二步,给工具提供参数,并获取工具(函数)执行的得到的结果\\n{'tools': {'messages': [ToolMessage(content='[{\\\"url\\\": \\\"https://www.weatherapi.com/\\\", \\\"content\\\": \\\"{\\\\'location\\\\': {\\\\'name\\\\': \\\\'San Francisco\\\\', \\\\'region\\\\': \\\\'California\\\\', \\\\'country\\\\': \\\\'United States of America\\\\', \\\\'lat\\\\': 37.78, \\\\'lon\\\\': -122.42, \\\\'tz_id\\\\': \\\\'America/Los_Angeles\\\\', \\\\'localtime_epoch\\\\': 1717238643, \\\\'localtime\\\\': \\\\'2024-06-01 3:44\\\\'}, \\\\'current\\\\': {\\\\'last_updated_epoch\\\\': 1717237800, \\\\'last_updated\\\\': \\\\'2024-06-01 03:30\\\\', \\\\'temp_c\\\\': 12.0, \\\\'temp_f\\\\': 53.6, \\\\'is_day\\\\': 0, \\\\'condition\\\\': {\\\\'text\\\\': \\\\'Mist\\\\', \\\\'icon\\\\': \\\\'//cdn.weatherapi.com/weather/64x64/night/143.png\\\\', \\\\'code\\\\': 1030}, \\\\'wind_mph\\\\': 5.6, \\\\'wind_kph\\\\': 9.0, \\\\'wind_degree\\\\': 310, \\\\'wind_dir\\\\': \\\\'NW\\\\', \\\\'pressure_mb\\\\': 1013.0, \\\\'pressure_in\\\\': 29.92, \\\\'precip_mm\\\\': 0.0, \\\\'precip_in\\\\': 0.0, \\\\'humidity\\\\': 88, \\\\'cloud\\\\': 100, \\\\'feelslike_c\\\\': 10.5, \\\\'feelslike_f\\\\': 50.8, \\\\'windchill_c\\\\': 9.3, \\\\'windchill_f\\\\': 48.7, \\\\'heatindex_c\\\\': 11.1, \\\\'heatindex_f\\\\': 51.9, \\\\'dewpoint_c\\\\': 8.8, \\\\'dewpoint_f\\\\': 47.8, \\\\'vis_km\\\\': 6.4, \\\\'vis_miles\\\\': 3.0, \\\\'uv\\\\': 1.0, \\\\'gust_mph\\\\': 12.5, \\\\'gust_kph\\\\': 20.1}}\\\"}, {\\\"url\\\": \\\"https://www.timeanddate.com/weather/usa/san-francisco/historic\\\", \\\"content\\\": \\\"Past Weather in San Francisco, California, USA \\\\\\\\u2014 Yesterday and Last 2 Weeks. Time/General. Weather. Time Zone. DST Changes. Sun & Moon. Weather Today Weather Hourly 14 Day Forecast Yesterday/Past Weather Climate (Averages) Currently: 68 \\\\\\\\u00b0F. Passing clouds.\\\"}]', name='tavily_search_results_json', tool_call_id='toolu_01BGEyQaSz3pTq8RwUUHSRoo')]}}\\n----\\n\\n## 第三步,基于工具返回的结果,生成最终的回答\\n{'agent': {'messages': [AIMessage(content='Based on the search results, the current weather in San Francisco is:\\\\n\\\\nTemperature: 53.6°F (12°C)\\\\nConditions: Misty\\\\nWind: 5.6 mph (9 kph) from the Northwest\\\\nHumidity: 88%\\\\nCloud Cover: 100% \\\\n\\\\nThe results provide detailed information like wind chill, heat index, visibility and more. It looks like a typical cool, foggy morning in San Francisco. Let me know if you need any other details about the weather where you live!', response_metadata={'id': 'msg_019WGLbaojuNdbCnqac7zaGW', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 1035, 'output_tokens': 120}}, id='run-1bb68bf3-b212-4ef4-8a31-10c830421c78-0', usage_metadata={'input_tokens': 1035, 'output_tokens': 120, 'total_tokens': 1155})]}}\\n----\\n \\n```\\n\\n下面是具体的步骤分解。\\n\\n**安装 LangChain 运行:**\\n\\n```\\n\\n %pip install -U langchain-community langgraph langchain-anthropic tavily-python langgraph-checkpoint-sqlite \\n \\n```\\n\\n**LangSmith(不是必须的):**\\n\\n您使用LangChain构建的许多应用程序将包含多个步骤,并多次调用LLM。随着这些应用程序变得越来越复杂,能够检查链或代理内部发生的确切内容变得至关重要。最好的方法是使用LangSmith。\\n\\n在上述链接中注册后,请确保设置您的环境变量以开始记录跟踪:\\n\\n```\\n\\n export LANGCHAIN_TRACING_V2=\\\"true\\\"\\nexport LANGCHAIN_API_KEY=\\\"...\\\"\\n \\n```\\n\\n或者,如果在 notebook 中,您可以使用以下代码设置:\\n\\n```\\n\\n import getpass\\nimport os\\n\\nos.environ[\\\"LANGCHAIN_TRACING_V2\\\"] = \\\"true\\\"\\nos.environ[\\\"LANGCHAIN_API_KEY\\\"] = getpass.getpass()\\n \\n```\\n\\n### Tavily\\n\\n我们将使用Tavily(一个搜索引擎)作为工具。为了使用它,您需要获取并设置API密钥:\\n\\n```\\n\\n export TAVILY_API_KEY=\\\"...\\\"\\n \\n```\\n\\n或者:\\n\\n```\\n\\n import getpass\\nimport os\\n\\nos.environ[\\\"TAVILY_API_KEY\\\"] = getpass.getpass() \\n \\n```\\n\\n## 定义工具\\n\\n我们首先需要创建要使用的工具。我们的主要选择工具是Tavily——一个搜索引擎。我们在LangChain中有一个内置工具,可以轻松地使用Tavily搜索引擎作为工具。\\n\\n```\\n\\n from langchain_community.tools.tavily_search import TavilySearchResults\\n\\nsearch = TavilySearchResults(max_results=2) ## 保留前2的搜索结果\\nsearch_results = search.invoke(\\\"what is the weather in SF\\\")\\nprint(search_results)\\n\\n\\n# 如果需要,我们可以创建其他工具。\\n# 一旦我们拥有所有想要的工具,我们可以将它们放入一个列表中,稍后将引用该列表。\\ntools = [search] \\n \\n```\\n\\n前 2 的网页结果返回:\\n\\n```\\n\\n [{'url': 'https://www.weatherapi.com/',\\n 'content': \\\"{'location': {'name': 'San Francisco', 'region': 'California', 'country': 'United States of America', 'lat': 37.78, 'lon': -122.42, 'tz_id': 'America/Los_Angeles', 'localtime_epoch': 1717238703, 'localtime': '2024-06-01 3:45'}, 'current': {'last_updated_epoch': 1717237800, 'last_updated': '2024-06-01 03:30', 'temp_c': 12.0, 'temp_f': 53.6, 'is_day': 0, 'condition': {'text': 'Mist', 'icon': '//cdn.weatherapi.com/weather/64x64/night/143.png', 'code': 1030}, 'wind_mph': 5.6, 'wind_kph': 9.0, 'wind_degree': 310, 'wind_dir': 'NW', 'pressure_mb': 1013.0, 'pressure_in': 29.92, 'precip_mm': 0.0, 'precip_in': 0.0, 'humidity': 88, 'cloud': 100, 'feelslike_c': 10.5, 'feelslike_f': 50.8, 'windchill_c': 9.3, 'windchill_f': 48.7, 'heatindex_c': 11.1, 'heatindex_f': 51.9, 'dewpoint_c': 8.8, 'dewpoint_f': 47.8, 'vis_km': 6.4, 'vis_miles': 3.0, 'uv': 1.0, 'gust_mph': 12.5, 'gust_kph': 20.1}}\\\"},\\n {'url': 'https://www.wunderground.com/hourly/us/ca/san-francisco/date/2024-01-06',\\n 'content': 'Current Weather for Popular Cities . San Francisco, CA 58 ° F Partly Cloudy; Manhattan, NY warning 51 ° F Cloudy; Schiller Park, IL (60176) warning 51 ° F Fair; Boston, MA warning 41 ° F ...'}] \\n \\n```\\n\\n### 使用语言模型\\n\\n接下来,让我们学习如何使用语言模型调用工具。LangChain支持许多不同的语言模型,您可以互换使用它们——选择您想要使用的模型!\\n\\n* OpenAI\\n* Anthropic\\n* Azure\\n* Google\\n* Cohere\\n* NVIDIA\\n* FireworksAI\\n* Groq\\n* MistralAI\\n* TogetherAI\\n\\n```\\n\\n pip install -qU langchain-openai\\n\\nimport getpass\\nimport os\\n\\nos.environ[\\\"OPENAI_API_KEY\\\"] = getpass.getpass()\\n\\nfrom langchain_openai import ChatOpenAI\\n\\nmodel = ChatOpenAI(model=\\\"gpt-4\\\")\\n \\n```\\n\\n您可以通过传递消息列表来调用语言模型。默认情况下,响应是一个内容字符串。\\n\\n```\\n\\n from langchain_core.messages import HumanMessage\\n\\nresponse = model.invoke([HumanMessage(content=\\\"hi!\\\")])\\nresponse.content\\n \\n```\\n\\n结果返回:\\n\\n我们现在可以查看启用此模型进行工具调用的效果。为了启用它,我们使用 `.bind_tools` 来为语言模型赋予这些工具的知识。\\n\\n```\\n\\n model_with_tools = model.bind_tools(tools) \\n \\n```\\n\\n我们现在可以调用模型。首先让我们调用一个普通消息,看看它如何响应。我们可以查看内容字段以及工具调用字段。\\n\\n```\\n\\n response = model_with_tools.invoke([HumanMessage(content=\\\"Hi!\\\")])\\n\\nprint(f\\\"ContentString: {response.content}\\\")\\nprint(f\\\"ToolCalls: {response.tool_calls}\\\")\\n\\n## 结果返回\\nContentString: Hello!\\nToolCalls: [] ## 没有调用任何工具\\n \\n```\\n\\n现在,让我们尝试用一些需要调用工具的输入来调用它。\\n\\n```\\n\\n response = model_with_tools.invoke([HumanMessage(content=\\\"What's the weather in SF?\\\")])\\n\\nprint(f\\\"ContentString: {response.content}\\\") ## 没有返回回答\\nprint(f\\\"ToolCalls: {response.tool_calls}\\\") ## 返回了工具调用\\n \\nContentString: \\nToolCalls: [{'name': 'tavily_search_results_json', 'args': {'query': 'weather san francisco'}, 'id': 'toolu_01VTP7DUvSfgtYxsq9x4EwMp'}]\\n \\n```\\n\\n我们可以看到,现在没有文本内容,但有一个工具调用!它想要我们调用Tavily搜索工具。\\n\\n这还没有真正调用那个工具——它只是告诉我们要调用它。为了实际调用它,我们需要创建我们的代理。\\n\\n## 创建 Agent\\n\\n既然我们已经定义了工具和LLM,我们可以创建 Agent。我们将使用LangGraph来构建代理。目前我们使用一个高级接口来构建代理,但LangGraph的 优点在于这个高级接口背后是一个**低级别、高度可控**的API,以方便您想要修改代理逻辑。\\n\\n现在,我们可以使用LLM和工具初始化代理。\\n\\n注意,我们传递的是模型,而不是 `model_with_tools`。这是因为 `create_react_agent` 会在后台调用 `.bind_tools`。\\n\\n```\\n\\n from langgraph.prebuilt import create_react_agent\\n\\nagent_executor = create_react_agent(model, tools) \\n \\n```\\n\\n### 运行 Agent\\n\\n我们现在可以在一些查询上运行代理!注意,目前这些都是**无状态查询**(它不会记住之前的交互)。注意,代理将在交互结束时返回最终状态(其中包括任何输入,我们稍后会看到如何只获取输出)。\\n\\n首先,让我们看看在不需要调用工具时,它如何响应:\\n\\n```\\n\\n response = agent_executor.invoke({\\\"messages\\\": [HumanMessage(content=\\\"hi!\\\")]})\\n\\nresponse[\\\"messages\\\"]\\n\\n## 输出结果如下\\n[HumanMessage(content='hi!', id='a820fcc5-9b87-457a-9af0-f21768143ee3'),\\n AIMessage(content='Hello!', response_metadata={'id': 'msg_01VbC493X1VEDyusgttiEr1z', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 264, 'output_tokens': 5}}, id='run-0e0ddae8-a85b-4bd6-947c-c36c857a4698-0', usage_metadata={'input_tokens': 264, 'output_tokens': 5, 'total_tokens': 269})]\\n \\n```\\n\\n为了确切了解后台发生的情况(并确保它没有调用工具),我们可以查看LangSmith跟踪。\\n\\n现在让我们在一个它应该调用工具的示例上试试。\\n\\n```\\n\\n response = agent_executor.invoke(\\n {\\\"messages\\\": [HumanMessage(content=\\\"whats the weather in sf?\\\")]}\\n)\\n\\nresponse[\\\"messages\\\"]\\n\\n## 原始结果\\n[HumanMessage(content='whats the weather in sf?', id='1d6c96bb-4ddb-415c-a579-a07d5264de0d'),\\n AIMessage(content=[{'id': 'toolu_01Y5EK4bw2LqsQXeaUv8iueF', 'input': {'query': 'weather in san francisco'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}], response_metadata={'id': 'msg_0132wQUcEduJ8UKVVVqwJzM4', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'input_tokens': 269, 'output_tokens': 61}}, id='run-26d5e5e8-d4fd-46d2-a197-87b95b10e823-0', tool_calls=[{'name': 'tavily_search_results_json', 'args': {'query': 'weather in san francisco'}, 'id': 'toolu_01Y5EK4bw2LqsQXeaUv8iueF'}], usage_metadata={'input_tokens': 269, 'output_tokens': 61, 'total_tokens': 330}),\\n ToolMessage(content='[{\\\"url\\\": \\\"https://www.weatherapi.com/\\\", \\\"content\\\": \\\"{\\\\'location\\\\': {\\\\'name\\\\': \\\\'San Francisco\\\\', \\\\'region\\\\': \\\\'California\\\\', \\\\'country\\\\': \\\\'United States of America\\\\', \\\\'lat\\\\': 37.78, \\\\'lon\\\\': -122.42, \\\\'tz_id\\\\': \\\\'America/Los_Angeles\\\\', \\\\'localtime_epoch\\\\': 1717238703, \\\\'localtime\\\\': \\\\'2024-06-01 3:45\\\\'}, \\\\'current\\\\': {\\\\'last_updated_epoch\\\\': 1717237800, \\\\'last_updated\\\\': \\\\'2024-06-01 03:30\\\\', \\\\'temp_c\\\\': 12.0, \\\\'temp_f\\\\': 53.6, \\\\'is_day\\\\': 0, \\\\'condition\\\\': {\\\\'text\\\\': \\\\'Mist\\\\', \\\\'icon\\\\': \\\\'//cdn.weatherapi.com/weather/64x64/night/143.png\\\\', \\\\'code\\\\': 1030}, \\\\'wind_mph\\\\': 5.6, \\\\'wind_kph\\\\': 9.0, \\\\'wind_degree\\\\': 310, \\\\'wind_dir\\\\': \\\\'NW\\\\', \\\\'pressure_mb\\\\': 1013.0, \\\\'pressure_in\\\\': 29.92, \\\\'precip_mm\\\\': 0.0, \\\\'precip_in\\\\': 0.0, \\\\'humidity\\\\': 88, \\\\'cloud\\\\': 100, \\\\'feelslike_c\\\\': 10.5, \\\\'feelslike_f\\\\': 50.8, \\\\'windchill_c\\\\': 9.3, \\\\'windchill_f\\\\': 48.7, \\\\'heatindex_c\\\\': 11.1, \\\\'heatindex_f\\\\': 51.9, \\\\'dewpoint_c\\\\': 8.8, \\\\'dewpoint_f\\\\': 47.8, \\\\'vis_km\\\\': 6.4, \\\\'vis_miles\\\\': 3.0, \\\\'uv\\\\': 1.0, \\\\'gust_mph\\\\': 12.5, \\\\'gust_kph\\\\': 20.1}}\\\"}, {\\\"url\\\": \\\"https://www.timeanddate.com/weather/usa/san-francisco/hourly\\\", \\\"content\\\": \\\"Sun & Moon. Weather Today Weather Hourly 14 Day Forecast Yesterday/Past Weather Climate (Averages) Currently: 59 \\\\\\\\u00b0F. Passing clouds. (Weather station: San Francisco International Airport, USA). See more current weather.\\\"}]', name='tavily_search_results_json', id='37aa1fd9-b232-4a02-bd22-bc5b9b44a22c', tool_call_id='toolu_01Y5EK4bw2LqsQXeaUv8iueF'),\\n AIMessage(content='Based on the search results, here is a summary of the current weather in San Francisco:\\\\n\\\\nThe weather in San Francisco is currently misty with a temperature of around 53°F (12°C). There is complete cloud cover and moderate winds from the northwest around 5-9 mph (9-14 km/h). Humidity is high at 88%. Visibility is around 3 miles (6.4 km). \\\\n\\\\nThe results provide an hourly forecast as well as current conditions from a couple different weather sources. Let me know if you need any additional details about the San Francisco weather!', response_metadata={'id': 'msg_01BRX9mrT19nBDdHYtR7wJ92', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 920, 'output_tokens': 132}}, id='run-d0325583-3ddc-4432-b2b2-d023eb97660f-0', usage_metadata={'input_tokens': 920, 'output_tokens': 132, 'total_tokens': 1052})] \\n \\n```\\n\\n### 输出结果整理如下\\n\\n```\\n\\n messages = [\\n\\n## Human Message\\n HumanMessage(content='whats the weather in sf?', id='1d6c96bb-4ddb-415c-a579-a07d5264de0d'),\\n AIMessage(\\n content=[\\n {\\n 'id': 'toolu_01Y5EK4bw2LqsQXeaUv8iueF',\\n 'input': {'query': 'weather in san francisco'},\\n 'name': 'tavily_search_results_json',\\n 'type': 'tool_use'\\n }\\n ],\\n response_metadata={\\n 'id': 'msg_0132wQUcEduJ8UKVVVqwJzM4',\\n 'model': 'claude-3-sonnet-20240229',\\n 'stop_reason': 'tool_use',\\n 'stop_sequence': None,\\n 'usage': {\\n 'input_tokens': 269,\\n 'output_tokens': 61\\n }\\n },\\n id='run-26d5e5e8-d4fd-46d2-a197-87b95b10e823-0',\\n tool_calls=[\\n {\\n 'name': 'tavily_search_results_json',\\n 'args': {'query': 'weather in san francisco'},\\n 'id': 'toolu_01Y5EK4bw2LqsQXeaUv8iueF'\\n }\\n ],\\n usage_metadata={\\n 'input_tokens': 269,\\n 'output_tokens': 61,\\n 'total_tokens': 330\\n }\\n ),\\n\\n## Tool Message\\n ToolMessage(\\n content='[{\\\"url\\\": \\\"https://www.weatherapi.com/\\\", \\\"content\\\": \\\"{\\\\'location\\\\': {\\\\'name\\\\': \\\\'San Francisco\\\\', \\\\'region\\\\': \\\\'California\\\\', \\\\'country\\\\': \\\\'United States of America\\\\', \\\\'lat\\\\': 37.78, \\\\'lon\\\\': -122.42, \\\\'tz_id\\\\': \\\\'America/Los_Angeles\\\\', \\\\'localtime_epoch\\\\': 1717238703, \\\\'localtime\\\\': \\\\'2024-06-01 3:45\\\\'}, \\\\'current\\\\': {\\\\'last_updated_epoch\\\\': 1717237800, \\\\'last_updated\\\\': \\\\'2024-06-01 03:30\\\\', \\\\'temp_c\\\\': 12.0, \\\\'temp_f\\\\': 53.6, \\\\'is_day\\\\': 0, \\\\'condition\\\\': {\\\\'text\\\\': \\\\'Mist\\\\', \\\\'icon\\\\': \\\\'//cdn.weatherapi.com/weather/64x64/night/143.png\\\\', \\\\'code\\\\': 1030}, \\\\'wind_mph\\\\': 5.6, \\\\'wind_kph\\\\': 9.0, \\\\'wind_degree\\\\': 310, \\\\'wind_dir\\\\': \\\\'NW\\\\', \\\\'pressure_mb\\\\': 1013.0, \\\\'pressure_in\\\\': 29.92, \\\\'precip_mm\\\\': 0.0, \\\\'precip_in\\\\': 0.0, \\\\'humidity\\\\': 88, \\\\'cloud\\\\': 100, \\\\'feelslike_c\\\\': 10.5, \\\\'feelslike_f\\\\': 50.8, \\\\'windchill_c\\\\': 9.3, \\\\'windchill_f\\\\': 48.7, \\\\'heatindex_c\\\\': 11.1, \\\\'heatindex_f\\\\': 51.9, \\\\'dewpoint_c\\\\': 8.8, \\\\'dewpoint_f\\\\': 47.8, \\\\'vis_km\\\\': 6.4, \\\\'vis_miles\\\\': 3.0, \\\\'uv\\\\': 1.0, \\\\'gust_mph\\\\': 12.5, \\\\'gust_kph\\\\': 20.1}}\\\"}, {\\\"url\\\": \\\"https://www.timeanddate.com/weather/usa/san-francisco/hourly\\\", \\\"content\\\": \\\"Sun & Moon. Weather Today Weather Hourly 14 Day Forecast Yesterday/Past Weather Climate (Averages) Currently: 59 \\\\\\\\u00b0F. Passing clouds. (Weather station: San Francisco International Airport, USA). See more current weather.\\\"}]',\\n name='tavily_search_results_json',\\n id='37aa1fd9-b232-4a02-bd22-bc5b9b44a22c',\\n tool_call_id='toolu_01Y5EK4bw2LqsQXeaUv8iueF'\\n ),\\n \\n## AI Message\\n AIMessage(\\n content='Based on the search results, here is a summary of the current weather in San Francisco:\\\\n\\\\nThe weather in San Francisco is currently misty with a temperature of around 53°F (12°C). There is complete cloud cover and moderate winds from the northwest around 5-9 mph (9-14 km/h). Humidity is high at 88%. Visibility is around 3 miles (6.4 km).\\\\n\\\\nThe results provide an hourly forecast as well as current conditions from a couple different weather sources. Let me know if you need any additional details about the San Francisco weather!',\\n response_metadata={\\n 'id': 'msg_01BRX9mrT19nBDdHYtR7wJ92',\\n 'model': 'claude-3-sonnet-20240229',\\n 'stop_reason': 'end_turn',\\n 'stop_sequence': None,\\n 'usage': {\\n 'input_tokens': 920,\\n 'output_tokens': 132\\n }\\n },\\n id='run-d0325583-3ddc-4432-b2b2-d023eb97660f-0',\\n usage_metadata={\\n 'input_tokens': 920,\\n 'output_tokens': 132,\\n 'total_tokens': 1052\\n }\\n )\\n]\\n \\n```\\n\\n## 流式消息 Streaming\\n\\n我们已经看到,代理可以通过 `.invoke` 调用来返回最终响应。如果代理正在执行多个步骤,这可能需要一些时间。为了显示中间进度,我们可以流式返回消息。\\n\\n```\\n\\n for chunk in agent_executor.stream(\\n {\\\"messages\\\": [HumanMessage(content=\\\"whats the weather in sf?\\\")]}\\n):\\n print(chunk)\\n print(\\\"----\\\")\\n\\n## 结果如下\\n## AI Message \\n{'agent': {'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_50Kb8zHmFqPYavQwF5TgcOH8', 'function': {'arguments': '{\\\\n \\\"query\\\": \\\"current weather in San Francisco\\\"\\\\n}', 'name': 'tavily_search_results_json'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 23, 'prompt_tokens': 134, 'total_tokens': 157}, 'model_name': 'gpt-4', 'system_fingerprint': None, 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-042d5feb-c2cc-4c3f-b8fd-dbc22fd0bc07-0', tool_calls=[{'name': 'tavily_search_results_json', 'args': {'query': 'current weather in San Francisco'}, 'id': 'call_50Kb8zHmFqPYavQwF5TgcOH8'}])]}}\\n----\\n\\n## Tool Message\\n{'action': {'messages': [ToolMessage(content='[{\\\"url\\\": \\\"https://www.weatherapi.com/\\\", \\\"content\\\": \\\"{\\\\'location\\\\': {\\\\'name\\\\': \\\\'San Francisco\\\\', \\\\'region\\\\': \\\\'California\\\\', \\\\'country\\\\': \\\\'United States of America\\\\', \\\\'lat\\\\': 37.78, \\\\'lon\\\\': -122.42, \\\\'tz_id\\\\': \\\\'America/Los_Angeles\\\\', \\\\'localtime_epoch\\\\': 1714426906, \\\\'localtime\\\\': \\\\'2024-04-29 14:41\\\\'}, \\\\'current\\\\': {\\\\'last_updated_epoch\\\\': 1714426200, \\\\'last_updated\\\\': \\\\'2024-04-29 14:30\\\\', \\\\'temp_c\\\\': 17.8, \\\\'temp_f\\\\': 64.0, \\\\'is_day\\\\': 1, \\\\'condition\\\\': {\\\\'text\\\\': \\\\'Sunny\\\\', \\\\'icon\\\\': \\\\'//cdn.weatherapi.com/weather/64x64/day/113.png\\\\', \\\\'code\\\\': 1000}, \\\\'wind_mph\\\\': 23.0, \\\\'wind_kph\\\\': 37.1, \\\\'wind_degree\\\\': 290, \\\\'wind_dir\\\\': \\\\'WNW\\\\', \\\\'pressure_mb\\\\': 1019.0, \\\\'pressure_in\\\\': 30.09, \\\\'precip_mm\\\\': 0.0, \\\\'precip_in\\\\': 0.0, \\\\'humidity\\\\': 50, \\\\'cloud\\\\': 0, \\\\'feelslike_c\\\\': 17.8, \\\\'feelslike_f\\\\': 64.0, \\\\'vis_km\\\\': 16.0, \\\\'vis_miles\\\\': 9.0, \\\\'uv\\\\': 5.0, \\\\'gust_mph\\\\': 27.5, \\\\'gust_kph\\\\': 44.3}}\\\"}, {\\\"url\\\": \\\"https://world-weather.info/forecast/usa/san_francisco/april-2024/\\\", \\\"content\\\": \\\"Extended weather forecast in San Francisco. Hourly Week 10 days 14 days 30 days Year. Detailed \\\\\\\\u26a1 San Francisco Weather Forecast for April 2024 - day/night \\\\\\\\ud83c\\\\\\\\udf21\\\\\\\\ufe0f temperatures, precipitations - World-Weather.info.\\\"}]', name='tavily_search_results_json', id='d88320ac-3fe1-4f73-870a-3681f15f6982', tool_call_id='call_50Kb8zHmFqPYavQwF5TgcOH8')]}}\\n----\\n\\n## AI Message\\n{'agent': {'messages': [AIMessage(content='The current weather in San Francisco, California is sunny with a temperature of 17.8°C (64.0°F). The wind is coming from the WNW at 23.0 mph. The humidity is at 50%. [source](https://www.weatherapi.com/)', response_metadata={'token_usage': {'completion_tokens': 58, 'prompt_tokens': 602, 'total_tokens': 660}, 'model_name': 'gpt-4', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-0cd2a507-ded5-4601-afe3-3807400e9989-0')]}}\\n---- \\n \\n```\\n\\n### Token Steaming\\n\\n除了流式返回消息外,流式传输 token 也是有用的。我们可以使用 `.astream_events` 方法来实现这一点。\\n\\ninfo 此 `.astream_events` 方法仅适用于Python 3.11或更高版本。\\n\\n```\\n\\n ## 关键字 async 异步处理\\nasync for event in agent_executor.astream_events(\\n {\\\"messages\\\": [HumanMessage(content=\\\"whats the weather in sf?\\\")]}, version=\\\"v1\\\"\\n):\\n kind = event[\\\"event\\\"]\\n if kind == \\\"on_chain_start\\\":\\n if (\\n event[\\\"name\\\"] == \\\"Agent\\\"\\n ): # Was assigned when creating the agent with `.with_config({\\\"run_name\\\": \\\"Agent\\\"})`\\n print(\\n f\\\"Starting agent: {event['name']} with input: {event['data'].get('input')}\\\"\\n )\\n elif kind == \\\"on_chain_end\\\":\\n if (\\n event[\\\"name\\\"] == \\\"Agent\\\"\\n ): # Was assigned when creating the agent with `.with_config({\\\"run_name\\\": \\\"Agent\\\"})`\\n print()\\n print(\\\"--\\\")\\n print(\\n f\\\"Done agent: {event['name']} with output: {event['data'].get('output')['output']}\\\"\\n )\\n if kind == \\\"on_chat_model_stream\\\":\\n content = event[\\\"data\\\"][\\\"chunk\\\"].content\\n if content:\\n # 空内容在OpenAI的上下文中意味着\\n # 模型要求调用工具。\\n # 所以我们只打印非空内容\\n print(content, end=\\\"|\\\")\\n elif kind == \\\"on_tool_start\\\":\\n print(\\\"--\\\")\\n print(\\n f\\\"Starting tool: {event['name']} with inputs: {event['data'].get('input')}\\\"\\n )\\n elif kind == \\\"on_tool_end\\\":\\n print(f\\\"Done tool: {event['name']}\\\")\\n print(f\\\"Tool output was: {event['data'].get('output')}\\\")\\n print(\\\"--\\\")\\n\\n## 结果如下\\n--\\nStarting tool: tavily_search_results_json with inputs: {'query': 'current weather in San Francisco'}\\nDone tool: tavily_search_results_json\\nTool output was: [{'url': 'https://www.weatherapi.com/', 'content': \\\"{'location': {'name': 'San Francisco', 'region': 'California', 'country': 'United States of America', 'lat': 37.78, 'lon': -122.42, 'tz_id': 'America/Los_Angeles', 'localtime_epoch': 1714427052, 'localtime': '2024-04-29 14:44'}, 'current': {'last_updated_epoch': 1714426200, 'last_updated': '2024-04-29 14:30', 'temp_c': 17.8, 'temp_f': 64.0, 'is_day': 1, 'condition': {'text': 'Sunny', 'icon': '//cdn.weatherapi.com/weather/64x64/day/113.png', 'code': 1000}, 'wind_mph': 23.0, 'wind_kph': 37.1, 'wind_degree': 290, 'wind_dir': 'WNW', 'pressure_mb': 1019.0, 'pressure_in': 30.09, 'precip_mm': 0.0, 'precip_in': 0.0, 'humidity': 50, 'cloud': 0, 'feelslike_c': 17.8, 'feelslike_f': 64.0, 'vis_km': 16.0, 'vis_miles': 9.0, 'uv': 5.0, 'gust_mph': 27.5, 'gust_kph': 44.3}}\\\"}, {'url': 'https://www.weathertab.com/en/c/e/04/united-states/california/san-francisco/', 'content': 'San Francisco Weather Forecast for Apr 2024 - Risk of Rain Graph. Rain Risk Graph: Monthly Overview. Bar heights indicate rain risk percentages. Yellow bars mark low-risk days, while black and grey bars signal higher risks. Grey-yellow bars act as buffers, advising to keep at least one day clear from the riskier grey and black days, guiding ...'}]\\n\\n\\n## Token 级别的流式输出\\n--\\nThe| current| weather| in| San| Francisco|,| California|,| USA| is| sunny| with| a| temperature| of| |17|.|8|°C| (|64|.|0|°F|).| The| wind| is| blowing| from| the| W|NW| at| a| speed| of| |37|.|1| k|ph| (|23|.|0| mph|).| The| humidity| level| is| at| |50|%.| [|Source|](|https|://|www|.weather|api|.com|/)| \\n \\n```\\n\\n## 添加记忆:memory + checkpointer + thread\\\\_id\\n\\n如前所述,此代理是无状态的。这意味着它不会记住先前的交互。要为其添加内存,我们需要传入一个 `checkpointer`。在传入 `checkpointer` 时,我们还必须在调用代理时传入 `thread_id`(以便它知道要从哪个线程/会话恢复)。\\n\\n```\\n\\n from langgraph.checkpoint.memory import MemorySaver\\n\\n## Memory Saver\\nmemory = MemorySaver()\\n\\nagent_executor = create_react_agent(model, tools, checkpointer=memory)\\n\\n## 指定 thread id \\nconfig = {\\\"configurable\\\": {\\\"thread_id\\\": \\\"abc123\\\"}}\\n\\n## 流式处理:第一个问题\\nfor chunk in agent_executor.stream(\\n {\\\"messages\\\": [HumanMessage(content=\\\"hi im bob!\\\")]}, config\\n):\\n print(chunk)\\n print(\\\"----\\\")\\n\\n## 结果如下\\n{'agent': {'messages': [AIMessage(content=\\\"Hello Bob! It's nice to meet you again.\\\", response_metadata={'id': 'msg_013C1z2ZySagEFwmU1EsysR2', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 1162, 'output_tokens': 14}}, id='run-f878acfd-d195-44e8-9166-e2796317e3f8-0', usage_metadata={'input_tokens': 1162, 'output_tokens': 14, 'total_tokens': 1176})]}}\\n----\\n\\n## 流式处理:第二个问题\\nfor chunk in agent_executor.stream(\\n {\\\"messages\\\": [HumanMessage(content=\\\"whats my name?\\\")]}, config\\n):\\n print(chunk)\\n print(\\\"----\\\")\\n\\n## 结果如下。这里因为使用了 memory,大模型记住了用户的名字\\n{'agent': {'messages': [AIMessage(content='You mentioned your name is Bob when you introduced yourself earlier. So your name is Bob.', response_metadata={'id': 'msg_01WNwnRNGwGDRw6vRdivt6i1', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 1184, 'output_tokens': 21}}, id='run-f5c0b957-8878-405a-9d4b-a7cd38efe81f-0', usage_metadata={'input_tokens': 1184, 'output_tokens': 21, 'total_tokens': 1205})]}}\\n---- \\n \\n```\\n\\n### 示例LangSmith跟踪\\n\\n如果我想开始一个新的对话,我只需要更改使用的 `thread_id`。\\n\\n```\\n\\n config = {\\\"configurable\\\": {\\\"thread_id\\\": \\\"xyz123\\\"}}\\nfor chunk in agent_executor.stream(\\n {\\\"messages\\\": [HumanMessage(content=\\\"whats my name?\\\")]}, config\\n):\\n print(chunk)\\n print(\\\"----\\\")\\n\\npythonCopy code\\n{'agent': {'messages': [AIMessage(content=\\\"I'm afraid I don't actually know your name. As an AI assistant without personal information about you, I don't have a specific name associated with our conversation.\\\", response_metadata={'id': 'msg_01NoaXNNYZKSoBncPcLkdcbo', 'model': 'claude-3-sonnet-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 267, 'output_tokens': 36}}, id='run-c9f7df3d-525a-4d8f-bbcf-a5b4a5d2e4b0-0', usage_metadata={'input_tokens': 267, 'output_tokens': 36, 'total_tokens': 303})]}}\\n----\\n\\n \\n```\\n\\n## 结论\\n\\n就这样了!在这个快速入门中,我们介绍了如何创建一个简单的代理。我们然后展示了如何流式传输响应——不仅包括中间步骤,还包括标记!我们还添加了内存,以便您可以与它进行对话。代理是一个复杂的话题,有很多东西需要学习!\\n\\n有关代理的更多信息,请查看LangGraph文档。这里有一套概念、教程和操作指南。\\n\\n原文地址:[Build an Agent | ️ LangChain](https://link.zhihu.com/?target=https%3A//python.langchain.com/v0.2/docs/tutorials/agents/)\\n\\n(文章结束)\\n\\n编辑于 2024-09-01 19:14・美国\\n\\n[LangChain](//www.zhihu.com/topic/27269779)\\n\\n[langgraph](//www.zhihu.com/topic/30114083)\\n\\n[Agent](//www.zhihu.com/topic/28352669)\\n\\n写下你的评论...\\n\\n还没有评论,发表第一个评论吧\\n\\n关于作者\\n\\n[王几行XING](//www.zhihu.com/people/brycewang1898)\\n[​](https://www.zhihu.com/kvip/purchase)\\n\\nNIPS/ICLR/ICML/顶刊 论文解读\\n\\n[​](https://zhuanlan.zhihu.com/p/96956163)\\n\\n北京大学 计算机技术硕士\\n\\n[回答\\n\\n**192**](//www.zhihu.com/people/brycewang1898/answers)\\n[文章\\n\\n**935**](//www.zhihu.com/people/brycewang1898/posts)\\n[关注者\\n\\n**13,927**](//www.zhihu.com/people/brycewang1898/followers)\\n\\n### 推荐阅读\\n\\n[# MultiWOZ:一个大型多领域用于特定任务对话模型的绿野仙踪实验型(人机对话实验)数据集\\n\\nzc111](https://zhuanlan.zhihu.com/p/47294895)\\n[# AI Agent的多轮对话:提升用户体验的关键技巧\\n\\n在前面的文章中,我们讨论了 AI Agent 的各个核心系统。今天,我想聊聊如何实现一个好用的多轮对话系统。说实话,这个话题我琢磨了很久,因为它直接影响到用户体验。 从一个槽点说起 还记得…\\n\\nEthan独立开发](https://zhuanlan.zhihu.com/p/20287999089)\\n[# [论文解读] 任务型对话领域数据集 MultiWOZ 2.0\\n\\nLooperXX](https://zhuanlan.zhihu.com/p/80369754)\\n[# [大模型多轮对话微调]——多轮对话数据组织格式\\n\\n主要是多轮对话微调数据集以及标签的构造方法,有三种常见方法。 一个多轮对话可以表示为: inputs = <user1> <assistant1> <user2> <assistant2> <user3> &l…](https://zhuanlan.zhihu.com/p/673478090)\\n\\n*想来知乎工作?请发送邮件到 jobs@zhihu.com*\\n\\n未注册手机验证后自动登录,注册即代表同意[《知乎协议》](https://www.zhihu.com/term/zhihu-terms)[《隐私保护指引》](https://www.zhihu.com/term/privacy)\\n\\n扫码下载知乎 App\\n==================================================\"}", "timestamp": "2025-11-04T12:22:12.585018", "tool_call_id": "extract_webpage:1", "name": "extract_webpage" }, { "role": "assistant", "content": "", "timestamp": "2025-11-04T12:22:17.007902", "tool_calls": [ { "id": "extract_webpage:2", "index": 0, "type": "function", "function": { "name": "extract_webpage", "arguments": "{\"url\": \"https://help.aliyun.com/zh/model-studio/qwen-function-calling\"}" } } ] }, { "role": "tool", "content": "{\"success\": true, \"url\": \"https://help.aliyun.com/zh/model-studio/qwen-function-calling\", \"content\": \"🌐 网页内容 (44282 字符):\\n📍 URL: https://help.aliyun.com/zh/model-studio/qwen-function-calling\\n==================================================\\n[AI 助理](https://www.aliyun.com/ai-assistant?displayMode=side)[备案](https://beian.aliyun.com/)[控制台](https://home.console.aliyun.com/home/dashboard/ProductAndService)\\n\\n[首页](/) [大模型服务平台百炼](/zh/model-studio/) [用户指南(模型)](/zh/model-studio/model-user-guide/) [工具调用](/zh/model-studio/tool-calls/) Function Calling\\n\\n# Function Calling\\n\\n大模型在面对实时性问题、数学计算等问题时可能效果不佳。Function Calling 通过引入外部工具,让大模型可以回答原本无法解决的问题。\\n\\n## **工作原理**\\n\\nFunction Calling 通过在应用程序和大模型之间的多步骤交互,使大模型可以参考外部工具信息进行回答。\\n\\n2. 若模型判断需要调用外部工具,会返回一个JSON格式的指令,用于告知应用程序需要执行的函数与入参。\\n\\n > 若模型判断无需调用工具,会返回自然语言格式的回复。\\n\\n工作流程示意图如下所示:\\n\\n## **快速开始**\\n\\n您需要已[获取API Key](https://help.aliyun.com/zh/model-studio/get-api-key)并[配置API Key到环境变量](https://help.aliyun.com/zh/model-studio/configure-api-key-through-environment-variables)。如果通过 OpenAI SDK 或 DashScope SDK 进行调用,需要[安装SDK](https://help.aliyun.com/zh/model-studio/install-sdk#210ee28162bs7)。\\n\\n以天气查询场景为例,介绍快速使用 Function Calling 的方法。\\n\\n## OpenAI 兼容\\n\\n```\\nfrom openai import OpenAI from datetime import datetime import json import os import random # 初始化客户端 client = OpenAI( # 若没有配置环境变量,请用阿里云百炼API Key将下行替换为:api_key=\\\"sk-xxx\\\", # 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key api_key=os.getenv(\\\"DASHSCOPE_API_KEY\\\"), # 以下是北京地域base_url,如果使用新加坡地域的模型,需要将base_url替换为:https://dashscope-intl.aliyuncs.com/compatible-mode/v1 base_url=\\\"https://dashscope.aliyuncs.com/compatible-mode/v1\\\", ) # 模拟用户问题 USER_QUESTION = \\\"北京天气咋样\\\" # 定义工具列表 tools = [ { \\\"type\\\": \\\"function\\\", \\\"function\\\": { \\\"name\\\": \\\"get_current_weather\\\", \\\"description\\\": \\\"当你想查询指定城市的天气时非常有用。\\\", \\\"parameters\\\": { \\\"type\\\": \\\"object\\\", \\\"properties\\\": { \\\"location\\\": { \\\"type\\\": \\\"string\\\", \\\"description\\\": \\\"城市或县区,比如北京市、杭州市、余杭区等。\\\", } }, \\\"required\\\": [\\\"location\\\"], }, }, }, ] # 模拟天气查询工具 def get_current_weather(arguments): weather_conditions = [\\\"晴天\\\", \\\"多云\\\", \\\"雨天\\\"] random_weather = random.choice(weather_conditions) location = arguments[\\\"location\\\"] return f\\\"{location}今天是{random_weather}。\\\" # 封装模型响应函数 def get_response(messages): completion = client.chat.completions.create( model=\\\"qwen-plus\\\", messages=messages, tools=tools, ) return completion messages = [{\\\"role\\\": \\\"user\\\", \\\"content\\\": USER_QUESTION}] response = get_response(messages) assistant_output = response.choices[0].message if assistant_output.content is None: assistant_output.content = \\\"\\\" messages.append(assistant_output) # 如果不需要调用工具,直接输出内容 if assistant_output.tool_calls is None: print(f\\\"无需调用天气查询工具,直接回复:{assistant_output.content}\\\") else: # 进入工具调用循环 while assistant_output.tool_calls is not None: tool_call = assistant_output.tool_calls[0] tool_call_id = tool_call.id func_name = tool_call.function.name arguments = json.loads(tool_call.function.arguments) print(f\\\"正在调用工具 [{func_name}],参数:{arguments}\\\") # 执行工具 tool_result = get_current_weather(arguments) # 构造工具返回信息 tool_message = { \\\"role\\\": \\\"tool\\\", \\\"tool_call_id\\\": tool_call_id, \\\"content\\\": tool_result, # 保持原始工具输出 } print(f\\\"工具返回:{tool_message['content']}\\\") messages.append(tool_message) # 再次调用模型,获取总结后的自然语言回复 response = get_response(messages) assistant_output = response.choices[0].message if assistant_output.content is None: assistant_output.content = \\\"\\\" messages.append(assistant_output) print(f\\\"助手最终回复:{assistant_output.content}\\\")\\n```\\n\\n```\\nimport OpenAI from 'openai'; // 初始化客户端 const openai = new OpenAI({ // 若没有配置环境变量,请用阿里云百炼API Key将下行替换为:apiKey: \\\"sk-xxx\\\", // 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key apiKey: process.env.DASHSCOPE_API_KEY, // 以下是北京地域base_url,如果使用新加坡地域的模型,需要将base_url替换为:https://dashscope-intl.aliyuncs.com/compatible-mode/v1 baseURL: \\\"https://dashscope.aliyuncs.com/compatible-mode/v1\\\", }); // 定义工具列表 const tools = [ { type: \\\"function\\\", function: { name: \\\"get_current_weather\\\", description: \\\"当你想查询指定城市的天气时非常有用。\\\", parameters: { type: \\\"object\\\", properties: { location: { type: \\\"string\\\", description: \\\"城市或县区,比如北京市、杭州市、余杭区等。\\\", }, }, required: [\\\"location\\\"], }, }, }, ]; // 模拟天气查询工具 const getCurrentWeather = (args) => { const weatherConditions = [\\\"晴天\\\", \\\"多云\\\", \\\"雨天\\\"]; const randomWeather = weatherConditions[Math.floor(Math.random() * weatherConditions.length)]; const location = args.location; return `${location}今天是${randomWeather}。`; }; // 封装模型响应函数 const getResponse = async (messages) => { const response = await openai.chat.completions.create({ model: \\\"qwen-plus\\\", messages: messages, tools: tools, }); return response; }; const main = async () => { const input = \\\"北京天气咋样\\\"; let messages = [ { role: \\\"user\\\", content: input, } ]; let response = await getResponse(messages); let assistantOutput = response.choices[0].message; // 确保 content 不是 null if (!assistantOutput.content) assistantOutput.content = \\\"\\\"; messages.push(assistantOutput); // 判断是否需要调用工具 if (!assistantOutput.tool_calls) { console.log(`无需调用天气查询工具,直接回复:${assistantOutput.content}`); } else { // 进入工具调用循环 while (assistantOutput.tool_calls) { const toolCall = assistantOutput.tool_calls[0]; const toolCallId = toolCall.id; const funcName = toolCall.function.name; const funcArgs = JSON.parse(toolCall.function.arguments); console.log(`正在调用工具 [${funcName}],参数:`, funcArgs); // 执行工具 const toolResult = getCurrentWeather(funcArgs); // 构造工具返回信息 const toolMessage = { role: \\\"tool\\\", tool_call_id: toolCallId, content: toolResult, }; console.log(`工具返回:${toolMessage.content}`); messages.push(toolMessage); // 再次调用模型获取自然语言总结 response = await getResponse(messages); assistantOutput = response.choices[0].message; if (!assistantOutput.content) assistantOutput.content = \\\"\\\"; messages.push(assistantOutput); } console.log(`助手最终回复:${assistantOutput.content}`); } }; // 启动程序 main().catch(console.error);\\n```\\n\\n## DashScope\\n\\n```\\nimport os from dashscope import Generation import json import random import dashscope # 若使用新加坡地域的模型,请释放下列注释 # dashscope.base_http_api_url = \\\"https://dashscope-intl.aliyuncs.com/api/v1\\\" # 1. 定义工具列表 tools = [ { \\\"type\\\": \\\"function\\\", \\\"function\\\": { \\\"name\\\": \\\"get_current_weather\\\", \\\"description\\\": \\\"当你想查询指定城市的天气时非常有用。\\\", \\\"parameters\\\": { \\\"type\\\": \\\"object\\\", \\\"properties\\\": { \\\"location\\\": { \\\"type\\\": \\\"string\\\", \\\"description\\\": \\\"城市或县区,比如北京市、杭州市、余杭区等。\\\", } }, \\\"required\\\": [\\\"location\\\"], }, }, } ] # 2. 模拟天气查询工具 def get_current_weather(arguments): weather_conditions = [\\\"晴天\\\", \\\"多云\\\", \\\"雨天\\\"] random_weather = random.choice(weather_conditions) location = arguments[\\\"location\\\"] return f\\\"{location}今天是{random_weather}。\\\" # 3. 封装模型响应函数 def get_response(messages): response = Generation.call( # 如果没有配置环境变量,请将下行替换为api_key=\\\"sk-xxx\\\" api_key=os.getenv(\\\"DASHSCOPE_API_KEY\\\"), model=\\\"qwen-plus\\\", messages=messages, tools=tools, result_format=\\\"message\\\", ) return response # 4. 初始化对话历史 messages = [ { \\\"role\\\": \\\"user\\\", \\\"content\\\": \\\"北京天气咋样\\\" } ] # 5. 第一次调用模型 response = get_response(messages) assistant_output = response.output.choices[0].message messages.append(assistant_output) # 6. 判断是否需要调用工具 if \\\"tool_calls\\\" not in assistant_output or not assistant_output[\\\"tool_calls\\\"]: print(f\\\"无需调用工具,直接回复:{assistant_output['content']}\\\") else: # 7. 进入工具调用循环 # 循环条件:只要最新的模型回复中包含工具调用请求 while \\\"tool_calls\\\" in assistant_output and assistant_output[\\\"tool_calls\\\"]: tool_call = assistant_output[\\\"tool_calls\\\"][0] # 解析工具调用的信息 func_name = tool_call[\\\"function\\\"][\\\"name\\\"] arguments = json.loads(tool_call[\\\"function\\\"][\\\"arguments\\\"]) tool_call_id = tool_call.get(\\\"id\\\") # 获取 tool_call_id print(f\\\"正在调用工具 [{func_name}],参数:{arguments}\\\") # 执行对应的工具函数 tool_result = get_current_weather(arguments) # 构造工具返回信息 tool_message = { \\\"role\\\": \\\"tool\\\", \\\"content\\\": tool_result, \\\"tool_call_id\\\": tool_call_id } print(f\\\"工具返回:{tool_message['content']}\\\") messages.append(tool_message) # 再次调用模型,让模型基于工具结果进行回复 response = get_response(messages) assistant_output = response.output.choices[0].message messages.append(assistant_output) # 8. 输出最终的自然语言回复 print(f\\\"助手最终回复:{assistant_output['content']}\\\")\\n```\\n\\n```\\nimport com.alibaba.dashscope.aigc.generation.Generation; import com.alibaba.dashscope.aigc.generation.GenerationParam; import com.alibaba.dashscope.aigc.generation.GenerationResult; import com.alibaba.dashscope.common.Message; import com.alibaba.dashscope.common.Role; import com.alibaba.dashscope.exception.InputRequiredException; import com.alibaba.dashscope.exception.NoApiKeyException; import com.alibaba.dashscope.tools.FunctionDefinition; import com.alibaba.dashscope.tools.ToolCallBase; import com.alibaba.dashscope.tools.ToolCallFunction; import com.alibaba.dashscope.tools.ToolFunction; import com.alibaba.dashscope.utils.Constants; import com.alibaba.dashscope.utils.JsonUtils; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Random; public class Main { // 若使用新加坡地域的模型,请释放下列注释 // static {Constants.baseHttpApiUrl=\\\"https://dashscope-intl.aliyuncs.com/api/v1\\\";} /** * 第一步:定义工具的本地实现。 * @param arguments 模型传入的、包含工具所需参数的JSON字符串。 * @return 工具执行后的结果字符串。 */ public static String getCurrentWeather(String arguments) { try { // 模型提供的参数是JSON格式的,需要我们手动解析。 ObjectMapper objectMapper = new ObjectMapper(); JsonNode argsNode = objectMapper.readTree(arguments); String location = argsNode.get(\\\"location\\\").asText(); // 用随机结果来模拟真实的API调用或业务逻辑。 List weatherConditions = Arrays.asList(\\\"晴天\\\", \\\"多云\\\", \\\"雨天\\\"); String randomWeather = weatherConditions.get(new Random().nextInt(weatherConditions.size())); return location + \\\"今天是\\\" + randomWeather + \\\"。\\\"; } catch (Exception e) { // 异常处理,确保程序健壮性。 return \\\"无法解析地点参数。\\\"; } } public static void main(String[] args) { try { // 第二步:向模型描述(注册)我们的工具。 String weatherParamsSchema = \\\"{\\\\\\\"type\\\\\\\":\\\\\\\"object\\\\\\\",\\\\\\\"properties\\\\\\\":{\\\\\\\"location\\\\\\\":{\\\\\\\"type\\\\\\\":\\\\\\\"string\\\\\\\",\\\\\\\"description\\\\\\\":\\\\\\\"城市或县区,比如北京市、杭州市、余杭区等。\\\\\\\"}},\\\\\\\"required\\\\\\\":[\\\\\\\"location\\\\\\\"]}\\\"; FunctionDefinition weatherFunction = FunctionDefinition.builder() .name(\\\"get_current_weather\\\") // 工具的唯一标识名,必须与本地实现对应。 .description(\\\"当你想查询指定城市的天气时非常有用。\\\") // 清晰的描述能帮助模型更好地决定何时使用该工具。 .parameters(JsonUtils.parseString(weatherParamsSchema).getAsJsonObject()) .build(); Generation gen = new Generation(); String userInput = \\\"北京天气咋样\\\"; List messages = new ArrayList<>(); messages.add(Message.builder().role(Role.USER.getValue()).content(userInput).build()); // 第四步:首次调用模型。将用户的请求和我们定义好的工具列表一同发送给模型。 GenerationParam param = GenerationParam.builder() .model(\\\"qwen-plus\\\") // 指定需要调用的模型。 .apiKey(System.getenv(\\\"DASHSCOPE_API_KEY\\\")) // 从环境变量中获取API Key。 .messages(messages) // 传入当前的对话历史。 .tools(Arrays.asList(ToolFunction.builder().function(weatherFunction).build())) // 传入可用的工具列表。 .resultFormat(GenerationParam.ResultFormat.MESSAGE) .build(); GenerationResult result = gen.call(param); Message assistantOutput = result.getOutput().getChoices().get(0).getMessage(); messages.add(assistantOutput); // 将模型的首次回复也加入到对话历史中。 // 第五步:检查模型的回复,判断它是否请求调用工具。 if (assistantOutput.getToolCalls() == null || assistantOutput.getToolCalls().isEmpty()) { // 情况A:模型没有调用工具,而是直接给出了回答。 System.out.println(\\\"无需调用天气查询工具,直接回复:\\\" + assistantOutput.getContent()); } else { // 情况B:模型决定调用工具。 // 使用 while 循环可以处理模型连续调用多次工具的场景。 while (assistantOutput.getToolCalls() != null && !assistantOutput.getToolCalls().isEmpty()) { ToolCallBase toolCall = assistantOutput.getToolCalls().get(0); // 从模型的回复中解析出工具调用的具体信息(要调用的函数名、参数)。 ToolCallFunction functionCall = (ToolCallFunction) toolCall; String funcName = functionCall.getFunction().getName(); String arguments = functionCall.getFunction().getArguments(); System.out.println(\\\"正在调用工具 [\\\" + funcName + \\\"],参数:\\\" + arguments); // 根据工具名,在本地执行对应的Java方法。 String toolResult = getCurrentWeather(arguments); // 构造一个 role 为 \\\"tool\\\" 的消息,其中包含工具的执行结果。 Message toolMessage = Message.builder() .role(\\\"tool\\\") .toolCallId(toolCall.getId()) .content(toolResult) .build(); System.out.println(\\\"工具返回:\\\" + toolMessage.getContent()); messages.add(toolMessage); // 将工具的返回结果也加入到对话历史中。 // 第六步:再次调用模型。 param.setMessages(messages); result = gen.call(param); assistantOutput = result.getOutput().getChoices().get(0).getMessage(); messages.add(assistantOutput); } // 第七步:打印模型经过总结后,生成的最终回复。 System.out.println(\\\"助手最终回复:\\\" + assistantOutput.getContent()); } } catch (NoApiKeyException | InputRequiredException e) { System.err.println(\\\"错误: \\\" + e.getMessage()); } catch (Exception e) { e.printStackTrace(); } } }\\n```\\n\\n运行后得到如下输出:\\n\\n```\\n正在调用工具 [get_current_weather],参数:{'location': '北京'} 工具返回:北京今天是多云。 助手最终回复:北京今天是多云的天气。\\n```\\n\\n## **如何使用**\\n\\nFunction Calling 支持两种传入工具信息的方式:\\n\\n* 方式一:通过 tools 参数传入(推荐)\\n\\n 请参见[如何使用](#038e24005c1vt),按照**定义工具**、**创建messages数组**、**发起 Function Calling**、**运行工具函数**、**大模型总结工具函数输出**的步骤进行调用。\\n* 方式二:通过 System Message 传入\\n\\n 通过 tools 参数传入时,服务端会根据模型自动适配合适的 prompt 模板并组装,因此推荐您优先使用 tools 参数。 如果您在使用 Qwen 模型时不期望使用 tools 参数,请参见[通过 System Message 传入工具信息](#afa52dfc56tqq)。\\n\\n以下内容以 OpenAI 兼容调用方式为例,通过 tools 参数传入工具信息,向您分步骤介绍 Function Calling 的详细用法。\\n\\n假设业务场景中会收到天气查询与时间查询两类问题。\\n\\n### **1. 定义工具**\\n\\n工具是连接大模型与外部世界的桥梁,首先需要定义工具。\\n\\n#### **1.1. 创建工具函数**\\n\\n创建两个工具函数:天气查询工具与时间查询工具。\\n\\n* **天气查询工具**\\n\\n 接收`arguments`参数,`arguments`格式为`{\\\"location\\\": \\\"查询的地点\\\"}`。工具的输出为字符串,格式为:`“{位置}今天是{天气}”`。\\n\\n > 为了便于演示,此处定义的天气查询工具并不真正查询天气,会从晴天、多云、雨天随机选择。在实际业务中可使用如[高德天气查询](https://lbs.amap.com/api/webservice/guide/api/weatherinfo)等工具进行替换。\\n* **时间查询工具**\\n\\n 时间查询工具不需要输入参数。工具的输出为字符串,格式为:`“当前时间:{查询到的时间}。”`。\\n\\n > 如果使用 Node.js,请运行`npm install date-fns`安装获取时间的工具包 date-fns:\\n\\n```\\n## 步骤1:定义工具函数 # 添加导入random模块 import random from datetime import datetime # 模拟天气查询工具。返回结果示例:“北京今天是雨天。” def get_current_weather(arguments): # 定义备选的天气条件列表 weather_conditions = [\\\"晴天\\\", \\\"多云\\\", \\\"雨天\\\"] # 随机选择一个天气条件 random_weather = random.choice(weather_conditions) # 从 JSON 中提取位置信息 location = arguments[\\\"location\\\"] # 返回格式化的天气信息 return f\\\"{location}今天是{random_weather}。\\\" # 查询当前时间的工具。返回结果示例:“当前时间:2024-04-15 17:15:18。“ def get_current_time(): # 获取当前日期和时间 current_datetime = datetime.now() # 格式化当前日期和时间 formatted_time = current_datetime.strftime('%Y-%m-%d %H:%M:%S') # 返回格式化后的当前时间 return f\\\"当前时间:{formatted_time}。\\\" # 测试工具函数并输出结果,运行后续步骤时可以去掉以下四句测试代码 print(\\\"测试工具输出:\\\") print(get_current_weather({\\\"location\\\": \\\"上海\\\"})) print(get_current_time()) print(\\\"\\\\n\\\")\\n```\\n\\n```\\n// 步骤1:定义工具函数 // 导入时间查询工具 import { format } from 'date-fns'; function getCurrentWeather(args) { // 定义备选的天气条件列表 const weatherConditions = [\\\"晴天\\\", \\\"多云\\\", \\\"雨天\\\"]; // 随机选择一个天气条件 const randomWeather = weatherConditions[Math.floor(Math.random() * weatherConditions.length)]; // 从 JSON 中提取位置信息 const location = args.location; // 返回格式化的天气信息 return `${location}今天是${randomWeather}。`; } function getCurrentTime() { // 获取当前日期和时间 const currentDatetime = new Date(); // 格式化当前日期和时间 const formattedTime = format(currentDatetime, 'yyyy-MM-dd HH:mm:ss'); // 返回格式化后的当前时间 return `当前时间:${formattedTime}。`; } // 测试工具函数并输出结果,运行后续步骤时可以去掉以下四句测试代码 console.log(\\\"测试工具输出:\\\") console.log(getCurrentWeather({location:\\\"上海\\\"})); console.log(getCurrentTime()); console.log(\\\"\\\\n\\\")\\n```\\n\\n运行工具后,得到输出:\\n\\n```\\n测试工具输出: 上海今天是多云。 当前时间:2025-01-08 20:21:45。\\n```\\n\\n#### **1.2 创建 tools 数组**\\n\\n人类在选择工具之前,需要对工具有全面的了解,包括工具的功能、何时使用以及输入参数等。大模型也需要这些信息才能更准确地选择工具。请根据以下 JSON 格式提供工具信息。\\n\\n| | |\\n| --- | --- |\\n| * `type`字段固定为`\\\"function\\\"`; * `function`字段为 Object 类型; + `name`字段为自定义的工具函数名称,建议使用与函数相同的名称,如`get_current_weather`或`get_current_time`; + `description`字段是对工具函数功能的描述,大模型会参考该字段来选择是否使用该工具函数。 + `parameters`字段是对工具函数入参的描述,类型是 Object ,大模型会参考该字段来进行入参的提取。如果工具函数不需要输入参数,则无需指定`parameters`参数。 - `type`字段固定为`\\\"object\\\"`; - `properties`字段描述了入参的名称、数据类型与描述,为 Object 类型,Key 值为入参的名称,Value 值为入参的数据类型与描述; - `required`字段指定哪些参数为必填项,为 Array 类型。 | 对于天气查询工具来说,工具描述信息的格式如下: ``` { \\\"type\\\": \\\"function\\\", \\\"function\\\": { \\\"name\\\": \\\"get_current_weather\\\", \\\"description\\\": \\\"当你想查询指定城市的天气时非常有用。\\\", \\\"parameters\\\": { \\\"type\\\": \\\"object\\\", \\\"properties\\\": { \\\"location\\\": { \\\"type\\\": \\\"string\\\", \\\"description\\\": \\\"城市或县区,比如北京市、杭州市、余杭区等。\\\" } }, \\\"required\\\": [\\\"location\\\"] } } } ``` |\\n\\n在发起 Function Calling 前,需在代码中定义工具信息数组(tools),包含每个工具的函数名、描述和参数定义。这个数组在后续发起Function Calling请求时作为参数传入。\\n\\n```\\n# 请将以下代码粘贴到步骤1代码后 ## 步骤2:创建 tools 数组 tools = [ { \\\"type\\\": \\\"function\\\", \\\"function\\\": { \\\"name\\\": \\\"get_current_time\\\", \\\"description\\\": \\\"当你想知道现在的时间时非常有用。\\\", \\\"parameters\\\": {} } }, { \\\"type\\\": \\\"function\\\", \\\"function\\\": { \\\"name\\\": \\\"get_current_weather\\\", \\\"description\\\": \\\"当你想查询指定城市的天气时非常有用。\\\", \\\"parameters\\\": { \\\"type\\\": \\\"object\\\", \\\"properties\\\": { \\\"location\\\": { \\\"type\\\": \\\"string\\\", \\\"description\\\": \\\"城市或县区,比如北京市、杭州市、余杭区等。\\\", } }, \\\"required\\\": [\\\"location\\\"] } } } ] tool_name = [tool[\\\"function\\\"][\\\"name\\\"] for tool in tools] print(f\\\"创建了{len(tools)}个工具,为:{tool_name}\\\\n\\\")\\n```\\n\\n```\\n// 请将以下代码粘贴到步骤1代码后 // 步骤2:创建 tools 数组 const tools = [ { type: \\\"function\\\", function: { name: \\\"get_current_time\\\", description: \\\"当你想知道现在的时间时非常有用。\\\", parameters: {} } }, { type: \\\"function\\\", function: { name: \\\"get_current_weather\\\", description: \\\"当你想查询指定城市的天气时非常有用。\\\", parameters: { type: \\\"object\\\", properties: { location: { type: \\\"string\\\", description: \\\"城市或县区,比如北京市、杭州市、余杭区等。\\\", } }, required: [\\\"location\\\"] } } } ]; const toolNames = tools.map(tool => tool.function.name); console.log(`创建了${tools.length}个工具,为:${toolNames.join(', ')}\\\\n`);\\n```\\n\\n### **2. 创建messages数组**\\n\\nFunction Calling 通过 messages 数组向大模型传入指令与上下文信息。发起 Function Calling 前,messages 数组需要包含 System Message 与 User Message。\\n\\n#### **System Message**\\n\\n尽管在[创建 tools 数组](#b7c8a0e72a9d0)时已经对工具的作用与何时使用工具进行了描述,但在 System Message 中强调何时调用工具通常会提高工具调用的准确率。在当前场景下,可以将 System Prompt 设置为:\\n\\n```\\n你是一个很有帮助的助手。如果用户提问关于天气的问题,请调用 ‘get_current_weather’ 函数; 如果用户提问关于时间的问题,请调用‘get_current_time’函数。 请以友好的语气回答问题。\\n```\\n\\n#### **User Message**\\n\\nUser Message 用于传入用户提问的问题。假设用户提问“上海天气”,此时的 messages 数组为:\\n\\n```\\n# 步骤3:创建messages数组 # 请将以下代码粘贴到步骤2 代码后 messages = [ { \\\"role\\\": \\\"system\\\", \\\"content\\\": \\\"\\\"\\\"你是一个很有帮助的助手。如果用户提问关于天气的问题,请调用 ‘get_current_weather’ 函数; 如果用户提问关于时间的问题,请调用‘get_current_time’函数。 请以友好的语气回答问题。\\\"\\\"\\\", }, { \\\"role\\\": \\\"user\\\", \\\"content\\\": \\\"上海天气\\\" } ] print(\\\"messages 数组创建完成\\\\n\\\")\\n```\\n\\n```\\n// 步骤3:创建messages数组 // 请将以下代码粘贴到步骤2 代码后 const messages = [ { role: \\\"system\\\", content: \\\"你是一个很有帮助的助手。如果用户提问关于天气的问题,请调用 ‘get_current_weather’ 函数; 如果用户提问关于时间的问题,请调用‘get_current_time’函数。请以友好的语气回答问题。\\\", }, { role: \\\"user\\\", content: \\\"上海天气\\\" } ]; console.log(\\\"messages 数组创建完成\\\\n\\\");\\n```\\n\\n> 由于备选工具包含天气查询与时间查询,也可提问关于当前时间的问题。\\n\\n### **3. 发起 Function Calling**\\n\\n将创建好的 `tools` 与 `messages` 传入大模型,即可发起一次 Function Calling。大模型会判断是否调用工具。若调用,则返回该工具的函数名与参数。\\n\\n> 支持的模型参见[支持的模型](#c8feada2328us),暂不支持多模态模型。\\n\\n```\\n# 步骤4:发起 function calling # 请将以下代码粘贴到步骤3 代码后 from openai import OpenAI import os client = OpenAI( # 若没有配置环境变量,请用阿里云百炼API Key将下行替换为:api_key=\\\"sk-xxx\\\", # 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key api_key=os.getenv(\\\"DASHSCOPE_API_KEY\\\"), # 以下是北京地域base_url,如果使用新加坡地域的模型,需要将base_url替换为:https://dashscope-intl.aliyuncs.com/compatible-mode/v1 base_url=\\\"https://dashscope.aliyuncs.com/compatible-mode/v1\\\", ) def function_calling(): completion = client.chat.completions.create( model=\\\"qwen-plus\\\", # 此处以qwen-plus为例,可按需更换模型名称。模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models messages=messages, tools=tools ) print(\\\"返回对象:\\\") print(completion.choices[0].message.model_dump_json()) print(\\\"\\\\n\\\") return completion print(\\\"正在发起function calling...\\\") completion = function_calling()\\n```\\n\\n```\\n// 步骤4:发起 function calling // 请将以下代码粘贴到步骤3 代码后 import OpenAI from \\\"openai\\\"; const openai = new OpenAI( { // 若没有配置环境变量,请用百炼API Key将下行替换为:apiKey: \\\"sk-xxx\\\", // 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key apiKey: process.env.DASHSCOPE_API_KEY, // 以下是北京地域base_url,如果使用新加坡地域的模型,需要将base_url替换为:https://dashscope-intl.aliyuncs.com/compatible-mode/v1 baseURL: \\\"https://dashscope.aliyuncs.com/compatible-mode/v1\\\" } ); async function functionCalling() { const completion = await openai.chat.completions.create({ model: \\\"qwen-plus\\\", // 此处以qwen-plus为例,可按需更换模型名称。模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models messages: messages, tools: tools }); console.log(\\\"返回对象:\\\"); console.log(JSON.stringify(completion.choices[0].message)); console.log(\\\"\\\\n\\\"); return completion; } const completion = await functionCalling();\\n```\\n\\n由于用户提问为上海天气,大模型指定需要使用的工具函数名称为:`\\\"get_current_weather\\\"`,函数的入参为:`\\\"{\\\\\\\"location\\\\\\\": \\\\\\\"上海\\\\\\\"}\\\"`。\\n\\n```\\n{ \\\"content\\\": \\\"\\\", \\\"refusal\\\": null, \\\"role\\\": \\\"assistant\\\", \\\"audio\\\": null, \\\"function_call\\\": null, \\\"tool_calls\\\": [ { \\\"id\\\": \\\"call_6596dafa2a6a46f7a217da\\\", \\\"function\\\": { \\\"arguments\\\": \\\"{\\\\\\\"location\\\\\\\": \\\\\\\"上海\\\\\\\"}\\\", \\\"name\\\": \\\"get_current_weather\\\" }, \\\"type\\\": \\\"function\\\", \\\"index\\\": 0 } ] }\\n```\\n\\n需要注意,如果问题被大模型判断为无需使用工具,会通过`content`参数直接回复。在输入“你好”时,`tool_calls`参数为空,返回对象格式为:\\n\\n```\\n{ \\\"content\\\": \\\"你好!有什么可以帮助你的吗?如果你有关于天气或者时间的问题,我特别擅长回答。\\\", \\\"refusal\\\": null, \\\"role\\\": \\\"assistant\\\", \\\"audio\\\": null, \\\"function_call\\\": null, \\\"tool_calls\\\": null }\\n```\\n\\n> 如果`tool_calls` 参数为空,可使程序直接返回 `content`,无需运行以下步骤。\\n\\n> 若希望每次发起 Function Calling 后大模型都可以选择指定工具,请参见[强制工具调用](#b0910669927j7)。\\n\\n### **4. 运行工具函数**\\n\\n运行工具函数是将大模型的决策转化为实际操作的关键步骤。\\n\\n> 运行工具函数的过程由您的计算环境而非大模型来完成。\\n\\n由于大模型只可以输出字符串格式的内容,因此在运行工具函数前,需要对字符串格式的工具函数与入参分别解析。\\n\\n* 工具函数\\n\\n 建立一个**工具函数名称**到**工具函数实体**的映射`function_mapper`,将返回的工具函数字符串映射到工具函数实体;\\n* 入参\\n\\n Function Calling 返回的入参为 JSON 字符串,使用工具将其解析为 JSON 对象,提取入参信息。\\n\\n完成解析后,将参数传入工具函数并执行,获取输出结果。\\n\\n```\\n# 步骤5:运行工具函数 # 请将以下代码粘贴到步骤4 代码后 import json print(\\\"正在执行工具函数...\\\") # 从返回的结果中获取函数名称和入参 function_name = completion.choices[0].message.tool_calls[0].function.name arguments_string = completion.choices[0].message.tool_calls[0].function.arguments # 使用json模块解析参数字符串 arguments = json.loads(arguments_string) # 创建一个函数映射表 function_mapper = { \\\"get_current_weather\\\": get_current_weather, \\\"get_current_time\\\": get_current_time } # 获取函数实体 function = function_mapper[function_name] # 如果入参为空,则直接调用函数 if arguments == {}: function_output = function() # 否则,传入参数后调用函数 else: function_output = function(arguments) # 打印工具的输出 print(f\\\"工具函数输出:{function_output}\\\\n\\\")\\n```\\n\\n```\\n// 步骤5:运行工具函数 // 请将以下代码粘贴到步骤4 代码后 console.log(\\\"正在执行工具函数...\\\"); const function_name = completion.choices[0].message.tool_calls[0].function.name; const arguments_string = completion.choices[0].message.tool_calls[0].function.arguments; // 使用JSON模块解析参数字符串 const args = JSON.parse(arguments_string); // 创建一个函数映射表 const functionMapper = { \\\"get_current_weather\\\": getCurrentWeather, \\\"get_current_time\\\": getCurrentTime }; // 获取函数实体 const func = functionMapper[function_name]; // 如果入参为空,则直接调用函数 let functionOutput; if (Object.keys(args).length === 0) { functionOutput = func(); } else { // 否则,传入参数后调用函数 functionOutput = func(args); } // 打印工具的输出 console.log(`工具函数输出:${functionOutput}\\\\n`);\\n```\\n\\n运行后得到如下输出:\\n\\n```\\n上海今天是多云。\\n```\\n\\n**说明** \\n\\n在实际业务场景中,许多工具的核心功能是执行具体操作(如邮件发送、文件上传等),而非数据查询,这类工具执行后不会输出字符串。为帮助大模型了解工具运行状态,建议在设计此类工具时添加如“邮件发送完成”、\\\"操作执行失败\\\"等状态描述信息。\\n\\n### **5. 大模型总结工具函数输出**\\n\\n工具函数的输出格式较为固定,如果直接返回给用户,可能会有语气生硬、不够灵活等问题。如果您希望大模型能够综合用户输入以及工具输出结果,生成自然语言风格的回复,可以将工具输出提交到模型上下文并再次向模型发出请求。\\n\\n1. 添加 Assistant Message\\n\\n [发起 Function Calling](#9f478b2e76wrk)后,通过`completion.choices[0].message`得到 Assistant Message,首先将它添加到 messages 数组中;\\n2. 添加 Tool Message\\n\\n 将工具的输出通过`{\\\"role\\\": \\\"tool\\\", \\\"content\\\": \\\"工具的输出\\\",\\\"tool_call_id\\\": completion.choices[0].message.tool_calls[0].id}`形式添加到 messages 数组。\\n\\n **说明** \\n * 请确保工具的输出为字符串格式。\\n * `tool_call_id` 是系统为每一次的工具调用请求生成的**唯一标识符**。模型可能一次性要求调用多个工具,将多个工具结果返回给模型时,`tool_call_id`可确保工具的输出结果能够与它的调用意图对应。\\n\\n```\\n# 步骤6:向大模型提交工具输出 # 请将以下代码粘贴到步骤5 代码后 messages.append(completion.choices[0].message) print(\\\"已添加assistant message\\\") messages.append({\\\"role\\\": \\\"tool\\\", \\\"content\\\": function_output, \\\"tool_call_id\\\": completion.choices[0].message.tool_calls[0].id}) print(\\\"已添加tool message\\\\n\\\")\\n```\\n\\n```\\n// 步骤6:向大模型提交工具输出 // 请将以下代码粘贴到步骤5 代码后 messages.push(completion.choices[0].message); console.log(\\\"已添加 assistant message\\\") messages.push({ \\\"role\\\": \\\"tool\\\", \\\"content\\\": functionOutput, \\\"tool_call_id\\\": completion.choices[0].message.tool_calls[0].id }); console.log(\\\"已添加 tool message\\\\n\\\");\\n```\\n\\n此时的 messages 数组为:\\n\\n```\\n[ System Message -- 指引模型调用工具的策略 User Message -- 用户的问题 Assistant Message -- 模型返回的工具调用信息 Tool Message -- 工具的输出信息(如果采用下文介绍的并行工具调用,可能有多个 Tool Message) ]\\n```\\n\\n更新 messages 数组后,运行以下代码。\\n\\n```\\n# 步骤7:大模型总结工具输出 # 请将以下代码粘贴到步骤6 代码后 print(\\\"正在总结工具输出...\\\") completion = function_calling()\\n```\\n\\n```\\n// 步骤7:大模型总结工具输出 // 请将以下代码粘贴到步骤6 代码后 console.log(\\\"正在总结工具输出...\\\"); const completion_1 = await functionCalling();\\n```\\n\\n可从`content`得到回复内容:“上海今天的天气是多云。如果您有其他问题,欢迎继续提问。”\\n\\n```\\n{ \\\"content\\\": \\\"上海今天的天气是多云。如果您有其他问题,欢迎继续提问。\\\", \\\"refusal\\\": null, \\\"role\\\": \\\"assistant\\\", \\\"audio\\\": null, \\\"function_call\\\": null, \\\"tool_calls\\\": null }\\n```\\n\\n到此,您已完成了一次完整的 Function Calling 流程。\\n\\n## **进阶用法**\\n\\n### **多轮对话**\\n\\n用户可能在第一轮提问“北京天气”,第二轮提问“上海的呢?”若模型上下文中没有第一轮的信息,模型无法判断需要调用哪个工具。建议在多轮对话场景中,每轮结束后维持 messages 数组,在此基础上添加 User Message并[发起 Function Calling](#9f478b2e76wrk)以及后续步骤。messages 结构如下所示:\\n\\n```\\n[ System Message -- 指引模型调用工具的策略 User Message -- 用户的问题 Assistant Message -- 模型返回的工具调用信息 Tool Message -- 工具的输出信息 Assistant Message -- 模型总结的工具调用信息 User Message -- 用户第二轮的问题 ]\\n```\\n\\n### **流式输出**\\n\\n为了提升用户体验和减少等待时间,可使用流式输出实时获取工具函数名称与入参信息。其中:\\n\\n* 工具函数名称:仅在第一个流式返回的对象(delta)中出现。\\n* 入参信息:以流式方式输出。\\n\\n```\\nfrom openai import OpenAI import os client = OpenAI( # 若没有配置环境变量,请用阿里云百炼API Key将下行替换为:api_key=\\\"sk-xxx\\\", # 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key api_key=os.getenv(\\\"DASHSCOPE_API_KEY\\\"), # 以下是北京地域base_url,如果使用新加坡地域的模型,需要将base_url替换为:https://dashscope-intl.aliyuncs.com/compatible-mode/v1 base_url=\\\"https://dashscope.aliyuncs.com/compatible-mode/v1\\\", ) tools = [ { \\\"type\\\": \\\"function\\\", \\\"function\\\": { \\\"name\\\": \\\"get_current_weather\\\", \\\"description\\\": \\\"当你想查询指定城市的天气时非常有用。\\\", \\\"parameters\\\": { \\\"type\\\": \\\"object\\\", \\\"properties\\\": { \\\"location\\\": { \\\"type\\\": \\\"string\\\", \\\"description\\\": \\\"城市或县区,比如北京市、杭州市、余杭区等。\\\", } }, \\\"required\\\": [\\\"location\\\"], }, }, }, ] stream = client.chat.completions.create( model=\\\"qwen-plus\\\", messages=[{\\\"role\\\": \\\"user\\\", \\\"content\\\": \\\"杭州天气?\\\"}], tools=tools, stream=True ) for chunk in stream: delta = chunk.choices[0].delta print(delta.tool_calls)\\n```\\n\\n```\\nimport { OpenAI } from \\\"openai\\\"; const openai = new OpenAI( { // 若没有配置环境变量,请用百炼API Key将下行替换为:apiKey: \\\"sk-xxx\\\", // 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key apiKey: process.env.DASHSCOPE_API_KEY, // 以下是北京地域base_url,如果使用新加坡地域的模型,需要将base_url替换为:https://dashscope-intl.aliyuncs.com/compatible-mode/v1 baseURL: \\\"https://dashscope.aliyuncs.com/compatible-mode/v1\\\" } ); const tools = [ { \\\"type\\\": \\\"function\\\", \\\"function\\\": { \\\"name\\\": \\\"getCurrentWeather\\\", \\\"description\\\": \\\"当你想查询指定城市的天气时非常有用。\\\", \\\"parameters\\\": { \\\"type\\\": \\\"object\\\", \\\"properties\\\": { \\\"location\\\": { \\\"type\\\": \\\"string\\\", \\\"description\\\": \\\"城市或县区,比如北京市、杭州市、余杭区等。\\\" } }, \\\"required\\\": [\\\"location\\\"] } } } ]; const stream = await openai.chat.completions.create({ model: \\\"qwen-plus\\\", messages: [{ role: \\\"user\\\", content: \\\"北京天气\\\" }], tools: tools, stream: true, }); for await (const chunk of stream) { const delta = chunk.choices[0].delta; console.log(delta.tool_calls); }\\n```\\n\\n运行后得到如下输出:\\n\\n```\\n[ChoiceDeltaToolCall(index=0, id='call_8f08d2b0fc0c4d8fab7123', function=ChoiceDeltaToolCallFunction(arguments='{\\\"location\\\":', name='get_current_weather'), type='function')] [ChoiceDeltaToolCall(index=0, id='', function=ChoiceDeltaToolCallFunction(arguments=' \\\"杭州\\\"}', name=None), type='function')] None\\n```\\n\\n运行以下代码拼接入参信息(`arguments`):\\n\\n```\\ntool_calls = {} for response_chunk in stream: delta_tool_calls = response_chunk.choices[0].delta.tool_calls if delta_tool_calls: for tool_call_chunk in delta_tool_calls: call_index = tool_call_chunk.index if call_index not in tool_calls: tool_calls[call_index] = tool_call_chunk else: tool_calls[call_index].function.arguments += tool_call_chunk.function.arguments print(tool_calls[0].model_dump_json())\\n```\\n\\n```\\nconst toolCalls = {}; for await (const responseChunk of stream) { const deltaToolCalls = responseChunk.choices[0]?.delta?.tool_calls; if (deltaToolCalls) { for (const toolCallChunk of deltaToolCalls) { const index = toolCallChunk.index; if (!toolCalls[index]) { toolCalls[index] = { ...toolCallChunk }; if (!toolCalls[index].function) { toolCalls[index].function = { name: '', arguments: '' }; } } else if (toolCallChunk.function?.arguments) { toolCalls[index].function.arguments += toolCallChunk.function.arguments; } } } } console.log(JSON.stringify(toolCalls[0]));\\n```\\n\\n获得如下输出:\\n\\n```\\n{\\\"index\\\":0,\\\"id\\\":\\\"call_16c72bef988a4c6c8cc662\\\",\\\"function\\\":{\\\"arguments\\\":\\\"{\\\\\\\"location\\\\\\\": \\\\\\\"杭州\\\\\\\"}\\\",\\\"name\\\":\\\"get_current_weather\\\"},\\\"type\\\":\\\"function\\\"}\\n```\\n\\n在使用大模型总结工具函数输出步骤,添加的 Assistant Message 需要符合下方格式。仅需将下方的`tool_calls`中的元素替换为以上内容即可。\\n\\n```\\n{ \\\"content\\\": \\\"\\\", \\\"refusal\\\": None, \\\"role\\\": \\\"assistant\\\", \\\"audio\\\": None, \\\"function_call\\\": None, \\\"tool_calls\\\": [ { \\\"id\\\": \\\"call_xxx\\\", \\\"function\\\": { \\\"arguments\\\": '{\\\"location\\\": \\\"xx\\\"}', \\\"name\\\": \\\"get_current_weather\\\", }, \\\"type\\\": \\\"function\\\", \\\"index\\\": 0, } ], }\\n```\\n\\n### **指定工具调用方式**\\n\\n#### **并行工具调用**\\n\\n单一城市的天气查询经过一次工具调用即可。如果输入问题需要调用多次工具,如“北京上海的天气如何”或“杭州天气,以及现在几点了”,[发起 Function Calling](#9f478b2e76wrk) 后只会返回一个工具调用信息,以提问“北京上海的天气如何”为例:\\n\\n```\\n{ \\\"content\\\": \\\"\\\", \\\"refusal\\\": null, \\\"role\\\": \\\"assistant\\\", \\\"audio\\\": null, \\\"function_call\\\": null, \\\"tool_calls\\\": [ { \\\"id\\\": \\\"call_61a2bbd82a8042289f1ff2\\\", \\\"function\\\": { \\\"arguments\\\": \\\"{\\\\\\\"location\\\\\\\": \\\\\\\"北京市\\\\\\\"}\\\", \\\"name\\\": \\\"get_current_weather\\\" }, \\\"type\\\": \\\"function\\\", \\\"index\\\": 0 } ] }\\n```\\n\\n返回结果中只有北京市的入参信息。为了解决这一问题,在[发起 Function Calling](#9f478b2e76wrk)时,可设置请求参数`parallel_tool_calls`为`true`,这样返回对象中将包含所有需要调用的工具函数与入参信息。\\n\\n**说明** \\n\\n并行工具调用适合任务之间无依赖的情况。若任务之间有依赖关系(工具A的输入与工具B的输出结果有关),请参见[快速开始](#dd5a3dca390k9),通过while循环实现串行工具调用(一次调用一个工具)。\\n\\n```\\ndef function_calling(): completion = client.chat.completions.create( model=\\\"qwen-plus\\\", # 此处以 qwen-plus 为例,可按需更换模型名称 messages=messages, tools=tools, # 新增参数 parallel_tool_calls=True ) print(\\\"返回对象:\\\") print(completion.choices[0].message.model_dump_json()) print(\\\"\\\\n\\\") return completion print(\\\"正在发起function calling...\\\") completion = function_calling()\\n```\\n\\n```\\nasync function functionCalling() { const completion = await openai.chat.completions.create({ model: \\\"qwen-plus\\\", // 此处以qwen-plus为例,可按需更换模型名称 messages: messages, tools: tools, parallel_tool_calls: true }); console.log(\\\"返回对象:\\\"); console.log(JSON.stringify(completion.choices[0].message)); console.log(\\\"\\\\n\\\"); return completion; } const completion = await functionCalling();\\n```\\n\\n在返回对象的`tool_calls`数组中包含了北京上海的入参信息:\\n\\n```\\n{ \\\"content\\\": \\\"\\\", \\\"role\\\": \\\"assistant\\\", \\\"tool_calls\\\": [ { \\\"function\\\": { \\\"name\\\": \\\"get_current_weather\\\", \\\"arguments\\\": \\\"{\\\\\\\"location\\\\\\\": \\\\\\\"北京市\\\\\\\"}\\\" }, \\\"index\\\": 0, \\\"id\\\": \\\"call_c2d8a3a24c4d4929b26ae2\\\", \\\"type\\\": \\\"function\\\" }, { \\\"function\\\": { \\\"name\\\": \\\"get_current_weather\\\", \\\"arguments\\\": \\\"{\\\\\\\"location\\\\\\\": \\\\\\\"上海市\\\\\\\"}\\\" }, \\\"index\\\": 1, \\\"id\\\": \\\"call_dc7f2f678f1944da9194cd\\\", \\\"type\\\": \\\"function\\\" } ] }\\n```\\n\\n#### **强制工具调用**\\n\\n大模型生成内容具有不确定性,有时会选择错误的工具进行调用。如果您希望对于某一类问题,大模型能够采取人为设定的策略(如强制使用某个工具、强制不使用工具),可修改`tool_choice`参数。`tool_choice`参数的默认值为`\\\"auto\\\"`,表示由大模型自主判断如何进行工具调用。\\n\\n> 大模型总结工具函数输出时,请将`tool_choice`参数去除,否则 API 仍会返回工具调用信息。\\n\\n* **强制使用某个工具**\\n\\n 如果您希望对于某一类问题,Function Calling 能强制调用某个工具,可设定`tool_choice`参数为`{\\\"type\\\": \\\"function\\\", \\\"function\\\": {\\\"name\\\": \\\"the_function_to_call\\\"}}`,大模型将不参与工具的选择,只输出入参信息。\\n\\n 假设当前场景中只包含天气查询的问题,可修改 function\\\\_calling 代码为:\\n\\n ```\\n def function_calling(): completion = client.chat.completions.create( model=\\\"qwen-plus\\\", messages=messages, tools=tools, tool_choice={\\\"type\\\": \\\"function\\\", \\\"function\\\": {\\\"name\\\": \\\"get_current_weather\\\"}} ) print(completion.model_dump_json()) function_calling()\\n ```\\n\\n ```\\n async function functionCalling() { const response = await openai.chat.completions.create({ model: \\\"qwen-plus\\\", messages: messages, tools: tools, tool_choice: {\\\"type\\\": \\\"function\\\", \\\"function\\\": {\\\"name\\\": \\\"get_current_weather\\\"}} }); console.log(\\\"返回对象:\\\"); console.log(JSON.stringify(response.choices[0].message)); console.log(\\\"\\\\n\\\"); return response; } const response = await functionCalling();\\n ```\\n\\n 无论输入什么问题,返回对象的工具函数都会是`get_current_weather`。\\n\\n > 使用该策略前请确保问题与选择的工具相关,否则可能返回不符合预期的结果。\\n* **强制不使用工具**\\n\\n 如果您希望无论输入什么问题,Function Calling 都不会进行工具调用(返回对象中包含回复内容`content`而`tool_calls`参数为空),可设定`tool_choice`参数为`\\\"none\\\"`,或不传入`tools`参数,Function Calling 返回的`tool_calls`参数将始终为空。\\n\\n 假设当前场景中的问题均无需调用工具,可修改 function\\\\_calling 代码为:\\n\\n ```\\n def function_calling(): completion = client.chat.completions.create( model=\\\"qwen-plus\\\", messages=messages, tools=tools, tool_choice=\\\"none\\\" ) print(completion.model_dump_json()) function_calling()\\n ```\\n\\n ```\\n async function functionCalling() { const completion = await openai.chat.completions.create({ model: \\\"qwen-plus\\\", messages: messages, tools: tools, tool_choice: \\\"none\\\" }); console.log(\\\"返回对象:\\\"); console.log(JSON.stringify(completion.choices[0].message)); console.log(\\\"\\\\n\\\"); return completion; } const completion = await functionCalling();\\n ```\\n\\n## **应用于生产环境**\\n\\n### **测试工具调用准确率**\\n\\n* **建立评估体系**:\\n\\n 构建贴近真实业务的测试数据集,并定义清晰的评估指标,如:工具选择准确率、参数提取准确率、端到端成功率等。\\n* **优化提示词**\\n\\n 根据测试暴露出的具体问题(选错工具、参数错误),针对性地优化系统提示词、工具描述和参数描述,是核心的调优手段。\\n* **升级模型**\\n\\n 当提示词工程调优无法提升性能时,升级到能力更强的模型版本(如 `qwen3-max-preview`)是提升指标最直接有效的方法。\\n\\n### **动态控制工具数量**\\n\\n当应用集成的工具数量超过几十甚至上百个时,将整个工具库全部提供给模型会带来以下问题:\\n\\n* **性能下降**:模型在庞大的工具集中选择正确工具的难度剧增;\\n* **成本与延迟**:大量的工具描述会消耗巨量的输入 Token,导致费用上升和响应变慢;\\n\\n解决方案是**在调用模型前,增加一个工具路由/检索层**,根据用户当前查询,从完整的工具库中快速、精准地筛选出一个小而相关的工具子集,再将其提供给模型。\\n\\n**实现工具路由的几种主流方法:**\\n\\n* **语义检索**\\n\\n 预先将所有工具的描述信息(`description`)通过 Embedding 模型转化为向量,并存入向量数据库。当用户查询时,将查询向量通过向量相似度搜索,召回最相关的 Top-K 个工具。\\n* **混合检索**\\n\\n 将语义检索的“模糊匹配”能力与传统关键词或元数据标签的“精确匹配”能力相结合。为工具添加 `tags` 或 `keywords` 字段,检索时同时进行向量搜索和关键词过滤,可以大幅提升高频或特定场景下的召回精准度。\\n* **轻量级 LLM 路由器**\\n\\n 对于更复杂的路由逻辑,可以使用一个更小、更快、更便宜的模型(如 Qwen-Flash)作为前置“路由模型”。它的任务是根据用户问题输出相关的工具名称列表。\\n\\n**实践建议**\\n\\n* **保持候选集精简**:无论使用何种方法,最终提供给主模型的工具数量建议**不超过 20 个**。这是在模型认知负荷、成本、延迟和准确率之间的最佳平衡点。\\n* **分层过滤策略**:可以构建一个漏斗式的路由策略。例如,先用成本极低的关键词/规则匹配进行第一轮筛选,过滤掉明显不相关的工具,再对剩余的工具进行语义检索,从而提高效率和质量。\\n\\n### **工具安全性原则**\\n\\n将工具执行能力开放给大模型时,必须将安全置于首位。核心原则是“最小权限”和“人类确认”。\\n\\n* **最小权限原则**:为模型提供的工具集应严格遵守最小权限原则。默认情况下,工具应是只读的(如查询天气、搜索文档),避免直接提供任何涉及状态变更或资源操作的“写”权限。\\n* **危险工具隔离**:请勿向大模型直接提供危险工具,例如执行任意代码(`code interpreter`)、操作文件系统(`fs.delete`)、执行数据库删除或更新操作(`db.drop_table`)或涉及资金流转的工具(`payment.transfer`)。\\n* **人类参与**:对于所有高权限或不可逆的操作,必须引入人工审核和确认环节。模型可以生成操作请求,但最终的执行“按钮”必须由人类用户点击。例如,模型可以准备好一封邮件,但发送操作需要用户确认。\\n\\n### **用户体验优化**\\n\\nFunction Calling 链路较长,任何一个环节出问题都可能导致用户体验下降。\\n\\n#### **处理工具运行失败**\\n\\n工具运行失败是常见情况。可采取以下策略:\\n\\n* **最大重试次数**:设置合理的重试上限(例如 3 次),避免因连续失败导致用户长时间等待或系统资源浪费。\\n* **提供兜底话术**:当重试耗尽或遇到无法解决的错误时,应向用户返回清晰、友好的提示信息,例如:“抱歉,我暂时无法查询到相关信息,可能是服务有些繁忙,请您稍后再试。”\\n\\n#### **应对处理延迟**\\n\\n较高的延迟会降低用户满意度,需要通过前端交互和后端优化来改善。\\n\\n* **设置超时时间**:为 Function Calling 的每一步设置独立且合理的超时时间。一旦超时,应立即中断操作并给出反馈。\\n* **提供即时反馈**:开始执行 Function Calling 时,建议在界面上给出提示,如“正在为您查询天气...”、“正在搜索相关信息...”,向用户实时反馈处理进度。\\n\\n## **支持的模型**\\n\\n支持的模型为:\\n\\n* [通义千问Max](https://help.aliyun.com/zh/model-studio/models#d4ccf72f23jh9)\\n* [通义千问Plus(非思考模式)](https://help.aliyun.com/zh/model-studio/models#bb0ffee88bwnk)\\n* [通义千问Flash(非思考模式)](https://help.aliyun.com/zh/model-studio/models#d617df95f1g9h)\\n* [通义千问Turbo(非思考模式)](https://help.aliyun.com/zh/model-studio/models#ff492e2c10lub)\\n* [Qwen3(非思考模式)](https://help.aliyun.com/zh/model-studio/models#9d516d17965af)\\n* [Qwen2.5](https://help.aliyun.com/zh/model-studio/models#e4831f1e9388j)\\n* [Qwen2](https://help.aliyun.com/zh/model-studio/models#5d2908c05460t)\\n* [Qwen1.5](https://help.aliyun.com/zh/model-studio/models#6440fb9f9813i)\\n* [DeepSeek](https://help.aliyun.com/zh/model-studio/models#935bd5ba5cg5d)(包括 deepseek-r1、deepseek-r1-0528与deepseek-v3)\\n\\n> [QwQ 模型](https://help.aliyun.com/zh/model-studio/deep-thinking)、开启思考模式的 Qwen3模型支持 Function Calling,但使用方法与以上模型略有不同,请参见[工具调用](https://help.aliyun.com/zh/model-studio/deep-thinking#c858c412ecx9i)。\\n\\n> 暂不支持通义千问多模态模型。\\n\\n建议您优先选择[通义千问Plus](https://help.aliyun.com/zh/model-studio/models#5ef284d4ed42p),它在效果、速度和成本上相对均衡。\\n\\n* 如果您对响应的速度与成本控制有较高要求,商业版模型建议使用[通义千问Flash](https://help.aliyun.com/zh/model-studio/models#13ff05e329blt)系列,开源版模型建议使用[Qwen3](https://help.aliyun.com/zh/model-studio/models#ced16cb6cdfsy) 系列的小参数模型;\\n* 如果您对响应的准确率有较高要求,商业版模型建议使用[模型列表与价格](https://help.aliyun.com/zh/model-studio/models#c2d5833ae4jmo)系列,开源版模型建议使用[Qwen3](https://help.aliyun.com/zh/model-studio/models#ced16cb6cdfsy) 系列的大参数模型、[DeepSeek](https://help.aliyun.com/zh/model-studio/models#935bd5ba5cg5d) 模型。\\n\\n## **计费说明**\\n\\n除了 messages 数组中的 Token 外,工具描述信息也会作为输入 Token 拼接到提示词中进行计费。\\n\\n## **通过 System Message 传入工具信息**\\n\\n推荐您参考[如何使用](#038e24005c1vt)部分,通过 tools 参数向大模型传入工具信息。如果您需要通过 System Message 传入工具信息,为了模型的最佳效果,请参考以下代码中的提示词模板:\\n\\n## OpenAI兼容\\n\\n## Python\\n\\n### **示例代码**\\n\\n```\\nimport os from openai import OpenAI import json client = OpenAI( # 若没有配置环境变量,请用百炼API Key将下行替换为:api_key=\\\"sk-xxx\\\", # 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key api_key=os.getenv(\\\"DASHSCOPE_API_KEY\\\"), # 以下是北京地域base_url,如果使用新加坡地域的模型,需要将base_url替换为:https://dashscope-intl.aliyuncs.com/compatible-mode/v1 base_url=\\\"https://dashscope.aliyuncs.com/compatible-mode/v1\\\", ) # 自定义 System prompt,可根据您的需求修改 custom_prompt = \\\"你是一个智能助手,专门负责调用各种工具来帮助用户解决问题。你可以根据用户的需求选择合适的工具并正确调用它们。\\\" tools = [ # 工具1 获取当前时刻的时间 { \\\"type\\\": \\\"function\\\", \\\"function\\\": { \\\"name\\\": \\\"get_current_time\\\", \\\"description\\\": \\\"当你想知道现在的时间时非常有用。\\\", \\\"parameters\\\": {} } }, # 工具2 获取指定城市的天气 { \\\"type\\\": \\\"function\\\", \\\"function\\\": { \\\"name\\\": \\\"get_current_weather\\\", \\\"description\\\": \\\"当你想查询指定城市的天气时非常有用。\\\", \\\"parameters\\\": { \\\"type\\\": \\\"object\\\", \\\"properties\\\": { \\\"location\\\": { \\\"type\\\": \\\"string\\\", \\\"description\\\": \\\"城市或县区,比如北京市、杭州市、余杭区等。\\\" } }, \\\"required\\\": [\\\"location\\\"] } } } ] # 遍历tools列表,为每个工具构建描述 tools_descriptions = [] for tool in tools: tool_json = json.dumps(tool, ensure_ascii=False) tools_descriptions.append(tool_json) # 将所有工具描述组合成一个字符串 tools_content = \\\"\\\\n\\\".join(tools_descriptions) system_prompt = f\\\"\\\"\\\"{custom_prompt} # Tools You may call one or more functions to assist with the user query. You are provided with function signatures within XML tags: {tools_content} For each function call, return a json object with function name and arguments within XML tags: {{\\\"name\\\": , \\\"arguments\\\": }} \\\"\\\"\\\" messages = [ {\\\"role\\\": \\\"system\\\", \\\"content\\\": system_prompt}, {\\\"role\\\": \\\"user\\\", \\\"content\\\": \\\"几点了\\\"} ] completion = client.chat.completions.create( model=\\\"qwen-plus\\\", messages=messages, ) print(completion.model_dump_json())\\n```\\n\\n## Node.js\\n\\n### 示例代码\\n\\n```\\nimport OpenAI from \\\"openai\\\"; const client = new OpenAI({ // 若没有配置环境变量,请用百炼API Key将下行替换为:apiKey: \\\"sk-xxx\\\", // 新加坡和北京地域的API Key不同。获取API Key:https://help.aliyun.com/zh/model-studio/get-api-key apiKey: process.env.DASHSCOPE_API_KEY, // 以下是北京地域base_url,如果使用新加坡地域的模型,需要将base_url替换为:https://dashscope-intl.aliyuncs.com/compatible-mode/v1 baseURL: \\\"https://dashscope.aliyuncs.com/compatible-mode/v1\\\", }); // 自定义 System prompt const customPrompt = \\\"你是一个智能助手,专门负责调用各种工具来帮助用户解决问题。你可以根据用户的需求选择合适的工具并正确调用它们。\\\"; const tools = [ // 工具1 获取当前时刻的时间 { \\\"type\\\": \\\"function\\\", \\\"function\\\": { \\\"name\\\": \\\"get_current_time\\\", \\\"description\\\": \\\"当你想知道现在的时间时非常有用。\\\", \\\"parameters\\\": {} } }, // 工具2 获取指定城市的天气 { \\\"type\\\": \\\"function\\\", \\\"function\\\": { \\\"name\\\": \\\"get_current_weather\\\", \\\"description\\\": \\\"当你想查询指定城市的天气时非常有用。\\\", \\\"parameters\\\": { \\\"type\\\": \\\"object\\\", \\\"properties\\\": { \\\"location\\\": { \\\"type\\\": \\\"string\\\", \\\"description\\\": \\\"城市或县区,比如北京市、杭州市、余杭区等。\\\" } }, \\\"required\\\": [\\\"location\\\"] } } } ]; // 遍历tools列表,为每个工具构建描述 const toolsDescriptions = []; for (const tool of tools) { const toolJson = JSON.stringify(tool, null, 2); toolsDescriptions.push(toolJson); } // 将所有工具描述组合成一个字符串 const toolsContent = toolsDescriptions.join(\\\"\\\\n\\\"); const systemPrompt = `${customPrompt} # Tools You may call one or more functions to assist with the user query. You are provided with function signatures within XML tags: ${toolsContent} For each function call, return a json object with function name and arguments within XML tags: {\\\"name\\\": , \\\"arguments\\\": } `; const messages = [ {\\\"role\\\": \\\"system\\\", \\\"content\\\": systemPrompt}, {\\\"role\\\": \\\"user\\\", \\\"content\\\": \\\"几点了\\\"} ]; async function main() { try { const completion = await client.chat.completions.create({ model: \\\"qwen-plus\\\", messages: messages, }); console.log(JSON.stringify(completion, null, 2)); } catch (error) { console.error(\\\"Error:\\\", error); } } main(); \\n```\\n\\n## DashScope\\n\\n## Python\\n\\n### **示例代码**\\n\\n```\\nimport os from dashscope import Generation import json import dashscope # 若使用新加坡地域的模型,请释放下列注释 # dashscope.base_http_api_url = \\\"https://dashscope-intl.aliyuncs.com/api/v1\\\" # 自定义 System prompt custom_prompt = \\\"你是一个智能助手,专门负责调用各种工具来帮助用户解决问题。你可以根据用户的需求选择合适的工具并正确调用它们。\\\" tools = [ # 工具1 获取当前时刻的时间 { \\\"type\\\": \\\"function\\\", \\\"function\\\": { \\\"name\\\": \\\"get_current_time\\\", \\\"description\\\": \\\"当你想知道现在的时间时非常有用。\\\", \\\"parameters\\\": {} } }, # 工具2 获取指定城市的天气 { \\\"type\\\": \\\"function\\\", \\\"function\\\": { \\\"name\\\": \\\"get_current_weather\\\", \\\"description\\\": \\\"当你想查询指定城市的天气时非常有用。\\\", \\\"parameters\\\": { \\\"type\\\": \\\"object\\\", \\\"properties\\\": { \\\"location\\\": { \\\"type\\\": \\\"string\\\", \\\"description\\\": \\\"城市或县区,比如北京市、杭州市、余杭区等。\\\" } }, \\\"required\\\": [\\\"location\\\"] } } } ] # 遍历tools列表,为每个工具构建描述 tools_descriptions = [] for tool in tools: tool_json = json.dumps(tool, ensure_ascii=False) tools_descriptions.append(tool_json) # 将所有工具描述组合成一个字符串 tools_content = \\\"\\\\n\\\".join(tools_descriptions) system_prompt = f\\\"\\\"\\\"{custom_prompt} # Tools You may call one or more functions to assist with the user query. You are provided with function signatures within XML tags: {tools_content} For each function call, return a json object with function name and arguments within XML tags: {{\\\"name\\\": , \\\"arguments\\\": }} \\\"\\\"\\\" messages = [ {\\\"role\\\": \\\"system\\\", \\\"content\\\": system_prompt}, {\\\"role\\\": \\\"user\\\", \\\"content\\\": \\\"几点了\\\"} ] response = Generation.call( # 若没有配置环境变量,请用百炼API Key将下行替换为:api_key=\\\"sk-xxx\\\", api_key=os.getenv(\\\"DASHSCOPE_API_KEY\\\"), model=\\\"qwen-plus\\\", messages=messages, result_format=\\\"message\\\", # 将输出设置为message形式 ) print(response)\\n```\\n\\n## Java\\n\\n### 示例代码\\n\\n```\\n// Copyright (c) Alibaba, Inc. and its affiliates. // version >= 2.12.0 import java.util.ArrayList; import java.util.Arrays; import java.util.List; import com.alibaba.dashscope.aigc.conversation.ConversationParam.ResultFormat; import com.alibaba.dashscope.aigc.generation.Generation; import com.alibaba.dashscope.aigc.generation.GenerationParam; import com.alibaba.dashscope.aigc.generation.GenerationResult; import com.alibaba.dashscope.common.Message; import com.alibaba.dashscope.common.Role; import com.alibaba.dashscope.exception.ApiException; import com.alibaba.dashscope.exception.InputRequiredException; import com.alibaba.dashscope.exception.NoApiKeyException; import com.alibaba.dashscope.utils.Constants; import com.alibaba.dashscope.utils.JsonUtils; public class Main { // 若使用新加坡地域的模型,请释放下列注释 // static {Constants.baseHttpApiUrl=\\\"https://dashscope-intl.aliyuncs.com/api/v1\\\";} public static void main(String[] args) { try { callToolWithCustomPrompt(); } catch (ApiException | NoApiKeyException | InputRequiredException e) { System.out.println(String.format(\\\"Exception: %s\\\", e.getMessage())); } catch (Exception e) { System.out.println(String.format(\\\"Exception: %s\\\", e.getMessage())); } System.exit(0); } public static void callToolWithCustomPrompt() throws NoApiKeyException, ApiException, InputRequiredException { // 自定义 System prompt String customPrompt = \\\"你是一个智能助手,专门负责调用各种工具来帮助用户解决问题。你可以根据用户的需求选择合适的工具并正确调用它们。\\\"; // 构建工具描述 String[] toolsDescriptions = { // 工具1 获取当前时刻的时间 \\\"{\\\\n\\\" + \\\" \\\\\\\"type\\\\\\\": \\\\\\\"function\\\\\\\",\\\\n\\\" + \\\" \\\\\\\"function\\\\\\\": {\\\\n\\\" + \\\" \\\\\\\"name\\\\\\\": \\\\\\\"get_current_time\\\\\\\",\\\\n\\\" + \\\" \\\\\\\"description\\\\\\\": \\\\\\\"当你想知道现在的时间时非常有用。\\\\\\\",\\\\n\\\" + \\\" \\\\\\\"parameters\\\\\\\": {}\\\\n\\\" + \\\" }\\\\n\\\" + \\\"}\\\", // 工具2 获取指定城市的天气 \\\"{\\\\n\\\" + \\\" \\\\\\\"type\\\\\\\": \\\\\\\"function\\\\\\\",\\\\n\\\" + \\\" \\\\\\\"function\\\\\\\": {\\\\n\\\" + \\\" \\\\\\\"name\\\\\\\": \\\\\\\"get_current_weather\\\\\\\",\\\\n\\\" + \\\" \\\\\\\"description\\\\\\\": \\\\\\\"当你想查询指定城市的天气时非常有用。\\\\\\\",\\\\n\\\" + \\\" \\\\\\\"parameters\\\\\\\": {\\\\n\\\" + \\\" \\\\\\\"type\\\\\\\": \\\\\\\"object\\\\\\\",\\\\n\\\" + \\\" \\\\\\\"properties\\\\\\\": {\\\\n\\\" + \\\" \\\\\\\"location\\\\\\\": {\\\\n\\\" + \\\" \\\\\\\"type\\\\\\\": \\\\\\\"string\\\\\\\",\\\\n\\\" + \\\" \\\\\\\"description\\\\\\\": \\\\\\\"城市或县区,比如北京市、杭州市、余杭区等。\\\\\\\"\\\\n\\\" + \\\" }\\\\n\\\" + \\\" },\\\\n\\\" + \\\" \\\\\\\"required\\\\\\\": [\\\\\\\"location\\\\\\\"]\\\\n\\\" + \\\" }\\\\n\\\" + \\\" }\\\\n\\\" + \\\"}\\\" }; // 将所有工具描述组合成一个字符串 String toolsContent = String.join(\\\"\\\\n\\\", toolsDescriptions); // 构建系统提示词 String systemPrompt = String.format(\\\"%s\\\\n\\\\n\\\" + \\\"# Tools\\\\n\\\\n\\\" + \\\"You may call one or more functions to assist with the user query.\\\\n\\\\n\\\" + \\\"You are provided with function signatures within XML tags:\\\\n\\\" + \\\"\\\\n%s\\\\n\\\\n\\\\n\\\" + \\\"For each function call, return a json object with function name and arguments within XML tags:\\\\n\\\" + \\\"\\\\n\\\" + \\\"{\\\\\\\"name\\\\\\\": , \\\\\\\"arguments\\\\\\\": }\\\\n\\\" + \\\"\\\", customPrompt, toolsContent); // 构建消息列表 Message systemMsg = Message.builder() .role(Role.SYSTEM.getValue()) .content(systemPrompt) .build(); Message userMsg = Message.builder() .role(Role.USER.getValue()) .content(\\\"几点了\\\") .build(); List messages = new ArrayList<>(Arrays.asList(systemMsg, userMsg)); // 构建请求参数 GenerationParam param = GenerationParam.builder() // 模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models .model(\\\"qwen-plus\\\") // 若没有配置环境变量,请用百炼API Key将下行替换为:.apiKey(\\\"sk-xxx\\\") .apiKey(System.getenv(\\\"DASHSCOPE_API_KEY\\\")) .messages(messages) .resultFormat(ResultFormat.MESSAGE) .build(); // 调用生成接口 Generation gen = new Generation(); GenerationResult result = gen.call(param); // 输出结果 System.out.println(JsonUtils.toJson(result)); } }\\n```\\n\\n> 运行以上代码后,可以使用 XML 解析器提取 和 之间的工具调用信息,包括函数名和入参。\\n\\n## 错误码\\n\\n如果模型调用失败并返回报错信息,请参见[错误信息](https://help.aliyun.com/zh/model-studio/error-code)进行解决。\\n\\n[上一篇:联网搜索](/zh/model-studio/web-search)[下一篇:MCP](/zh/model-studio/mcp)\\n==================================================\"}", "timestamp": "2025-11-04T12:22:18.813982", "tool_call_id": "extract_webpage:2", "name": "extract_webpage" }, { "role": "assistant", "content": "根据我查到的资料,我来给你详细解释一下多轮对话agent中查询返回内容的类型问题。\n\n## 核心结论\n\n**查询返回的内容既可能是`tool`类型,也可能是`system`类型,具体取决于agent的设计和查询的性质。**\n\n## 详细解释\n\n### 1. `tool`类型的返回\n当agent需要调用外部工具或函数来获取信息时,返回的是`tool`类型:\n\n- **典型场景**:查询天气、搜索信息、数据库查询等\n- **返回格式**:包含工具调用信息,如:\n```json\n{\n \"tool_calls\": [{\n \"name\": \"get_current_weather\",\n \"args\": {\"location\": \"北京\"},\n \"id\": \"call_xxx\"\n }]\n}\n```\n\n### 2. `system`类型的返回 \n当agent基于已有知识或内部逻辑直接回答时,返回的是`system`类型:\n\n- **典型场景**:常识问答、逻辑推理、上下文理解等\n- **返回格式**:直接的自然语言回复,如:\n```json\n{\n \"content\": \"根据对话历史,我理解您的意思是...\"\n}\n```\n\n## 实际工作流程\n\n根据阿里云和LangChain的文档,一个典型的多轮对话流程是这样的:\n\n```\n用户提问 → Agent判断 → 需要工具调用?\n ↓\n 不需要工具 需要工具\n ↓\n system回复 tool调用\n ↓\n 工具执行结果\n ↓\n system总结回复\n```\n\n## 代码示例\n\n从LangChain的示例可以看到这个区别:\n\n```python\n# 不需要工具调用时\nresponse = model_with_tools.invoke([HumanMessage(content=\"Hi!\")])\nprint(f\"ContentString: {response.content}\") # 直接回复\nprint(f\"ToolCalls: {response.tool_calls}\") # 为空\n\n# 需要工具调用时 \nresponse = model_with_tools.invoke([HumanMessage(content=\"What's the weather?\")])\nprint(f\"ContentString: {response.content}\") # 为空\nprint(f\"ToolCalls: {response.tool_calls}\") # 包含工具调用信息\n```\n\n## 总结\n\n所以,**不能简单地说查询返回的是`tool`还是`system`,而是要根据具体情况**:\n\n- 如果查询需要外部信息或操作 → `tool`类型\n- 如果查询可以基于已有知识回答 → `system`类型\n\n在多轮对话中,这两种类型往往会交替出现,形成一个完整的对话流程。", "timestamp": "2025-11-04T12:22:36.399879" }, { "role": "user", "content": "请你先切到/opt/agent路径下", "timestamp": "2025-11-06T10:09:35.833293" }, { "role": "assistant", "content": "好的,我来帮你切换到 `/opt/agent` 路径下。", "timestamp": "2025-11-06T10:09:44.250616" }, { "role": "assistant", "content": "", "timestamp": "2025-11-06T10:09:44.263402", "tool_calls": [ { "id": "terminal_session:3", "index": 0, "type": "function", "function": { "name": "terminal_session", "arguments": "{\"action\": \"open\", \"session_name\": \"main\", \"working_dir\": \"/opt/agent\"}" } } ] }, { "role": "tool", "content": "{\"success\": true, \"session\": \"main\", \"working_dir\": \"/opt/agent\", \"shell\": \"/bin/bash\", \"is_active\": true, \"total_sessions\": 2}", "timestamp": "2025-11-06T10:09:46.080071", "tool_call_id": "terminal_session:3", "name": "terminal_session" } ], "todo_list": null, "metadata": { "project_path": "/opt/agent/agents/users/jojo/project", "project_relative_path": "users/jojo/project", "thinking_mode": true, "total_messages": 13, "total_tools": 8, "status": "active" }, "token_statistics": { "total_input_tokens": 235516, "total_output_tokens": 972, "updated_at": "2025-11-06T21:45:51.600144" } }