当前位置: 首页 > news >正文

[智能体-237]:LCEL 多节点各自独立调用工具实现方案

核心结论:

bind_tools依附 LLM,哪个节点用 LLM,就在当前节点的 LLM 单独绑定本节点专属工具;不同链路节点可以挂载不同工具集,各自隔离、互不干扰。 类比:CPU 不同运算流水线(不同节点)按需挂载不同外设驱动,A 流水线挂计算器网卡、B 流水线挂数据库网卡。

一、底层原理

  1. bind_tools()生成全新 LLM 实例,互不污染原始 base_llm;
  2. LCEL 每个节点是独立 Runnable,每个节点内的 LLM 可以单独绑定专属 tool_list
  3. 工具执行器(查表执行 tool)按节点隔离 / 全局共用二选一;
  4. 节点 A LLM 绑定工具 A 组、节点 B LLM 绑定工具 B 组,各自只认识自己的工具。

二、完整示例:双节点链路,各自绑定不同工具

链路:输入预处理 → 节点A(LLM+计算器工具) → 节点B(LLM+字符串工具) → 结果组装

python

运行

from langchain_openai import ChatOpenAI from langchain_core.tools import tool from langchain_core.messages import HumanMessage,AIMessage,ToolMessage from langchain_core.prompts import ChatPromptTemplate from langchain_core.runnables import RunnableLambda # 1. 定义两套隔离工具 ## 节点A专用:数学计算工具 @tool def calc(a:float,b:float,opt:str): """加减乘除运算""" if opt=="+":return a+b elif opt=="-":return a-b elif opt=="*":return a*b elif opt=="/":return a/b ## 节点B专用:字符串工具 @tool def str_count(s:str): """统计字符长度""" return len(s) # 全局原始裸LLM,不绑定任何工具 base_llm = ChatOpenAI(model="gpt-3.5-turbo",temperature=0) # 2. 两个节点分别绑定各自工具,生成独立带工具LLM llm_nodeA = base_llm.bind_tools([calc]) # A节点只认识calc llm_nodeB = base_llm.bind_tools([str_count])# B节点只认识str_count # 工具注册表(全局统一执行) tool_dict = {"calc":calc,"str_count":str_count} # 通用工具执行封装Runnable:收到AIMessage就执行tool,回灌消息二次推理 def tool_run(msg:AIMessage): if not msg.tool_calls: return msg.content msg_list = [] for item in msg.tool_calls: fn = tool_dict[item["name"]] res = fn.invoke(item["args"]) msg_list.append(ToolMessage(str(res),tool_call_id=item["id"])) # 二次调用当前节点绑定过tools的LLM final = llm_nodeA.invoke([HumanMessage(""),msg]+msg_list) if "calc" in [i["name"] for i in msg.tool_calls] else llm_nodeB.invoke([HumanMessage(""),msg]+msg_list) return final.content # 定义所有chain的所有节点共享的可执行工具节点!!! tool_exec = RunnableLambda(tool_run) # 3. 构造两段独立prompt+节点链路 promptA = ChatPromptTemplate.from_messages([("user","请计算:{query}")]) nodeA = promptA | llm_nodeA | tool_exec # 把工具节点插入到chainA中 promptB = ChatPromptTemplate.from_messages([("user","统计字符:{resA}")]) nodeB = promptB | llm_nodeB | tool_exec # 把工具节点插入到chainB中 # 完整串联全链路 full_chain = {"resA":nodeA} | nodeB # 调用:先算10+20,再统计结果字符串长度 res = full_chain.invoke({"query":"10+20"}) print(res)

运行逻辑:

  1. 节点 A LLM 仅绑定 calc,自动调用加法得到30
  2. 结果流入节点 B,节点 B LLM 只绑定 str_count,统计"30"字符长度 = 2。

三、三种工程落地模式

模式 1:各节点独立绑定独立工具(隔离最强,推荐多业务场景)

  • 原始 base_llm 不变;
  • 每个节点内部:llm_n = base_llm.bind_tools(tools_n)
  • 优点:工具域隔离,A 节点不能调用 B 节点工具;
  • 适用:分段业务、不同环节权限 / 工具不同。

模式 2:全局统一工具 LLM,全节点共用一套工具

全链路所有 LLM 节点共用同一个llm_all=base_llm.bind_tools(all_tools); 所有节点都能调用全部工具,最简开发。

python

运行

llm_all = base_llm.bind_tools([calc,str_count]) nodeA = promptA|llm_all|tool_exec nodeB = promptB|llm_all|tool_exec

模式 3:动态按需临时绑定(运行时切换工具)

Runnable.bind(tools=xxx)动态在链路局部临时挂载工具,不污染全局 LLM:

python

运行

# 仅当前Runnable临时附加工具配置 temp_llm = base_llm.bind(tools=[calc])

四、关键细节:为什么能做到节点隔离

  1. bind_tools不修改原对象,每次返回新实例,各个节点的 llm 是不同内存对象、kwargs 里 tools 配置互相独立;
  2. 模型侧:每次请求 HTTP body 里tools数组随各自 llm 配置变化,A 请求带 calc、B 请求带 str_count;
  3. 执行侧:工具字典全局统一,或者按节点拆分 tool_map。

五、复杂 DAG 场景(RunnableMap 并行多分支,每分支独立工具)

python

运行

from langchain_core.runnables import RunnableMap # 两条并行分支,各自LLM+专属工具 branch_calc = promptA|llm_nodeA|tool_exec branch_str = promptB|llm_nodeB|tool_exec dag_chain = RunnableMap({ "计算结果":branch_calc, "字符统计":branch_str })

并行两个分支独立使用各自绑定的工具,互不干涉。

六、总结

  1. LCEL 实现多节点差异化工具核心:每个节点内部单独对 base_llm 执行 bind_tools,生成本节点私有 LLM
  2. bind_tools 生成新实例是隔离的关键,原始裸 LLM 复用;
  3. 工具决策在各节点 LLM,工具执行统一由后置 RunnableLambda 完成;
  4. 类比硬件:同一块 CPU (base_llm),不同流水线节点动态挂载不同外设驱动(tools)。
http://www.jsqmd.com/news/939651/

相关文章:

  • 告别DLL依赖!用MinGW编译独立运行的C++程序(静态链接libgcc、libstdc++、libwinpthread实战)
  • 让文献管理成为视觉盛宴:Zotero-Style插件的优雅革命
  • 别再只清理聊天记录了!深度清理微信电脑版(v3.9.9.43)收藏夹的保姆级指南
  • 2026年6月优质的线上获客企业推荐,建材抖音投流获客/门窗抖音投流获客/建材线上获客,线上获客公司怎么选择 - 品牌推荐师
  • STM32F103C8T6做的CMSIS-DAP调试器第三版:带SWO输出、USB串口和HID模式的完整软硬件包
  • 避坑指南:eCognition ESP2插件安装、配置与‘不出峰值’问题全解决
  • AutoDL上传大文件太慢?试试我的压缩+AutoPanel传输提速法(实测2.9G文件3分钟)
  • Scanpy vs Seurat 深度对比:Python 与 R 的单细胞分析框架谁更强?
  • 「ECG信号处理——(33)基于LSTM-RNN的睡眠呼吸暂停检测」2026年06月02日
  • UE4 Sequence实战:手把手教你用粒子特效打造酷炫的火焰激活动画(含摄像机追踪技巧)
  • Playwright爬虫进阶:巧用Route拦截修改请求与响应,绕过反爬就这么简单
  • Linux中常用的的命令
  • AI 营销的核心不在种草,而在 GEO 构建的信任体系
  • PMBOK8新架构:绩效域取代过程组
  • 线上召回率暴跌?一次关于 Sentence Transformers 提示词注入绕过向量检索边界的惊险排查与防护
  • 告别小打小闹!用LargeST数据集(8600个传感器,5年数据)实战交通流量预测
  • Flutter小程序跨端方案:打破技术边界实现代码复用新范式
  • Linux嵌入式SPI主从通信验证工程:C语言实现+spidev驱动调用+一键编译
  • 基于主成分分析(PCA)的EPFs(PCA-EPFs)方法在边缘保留特征在高光谱图像分类中的应用研究(Matlab代码实现)
  • 超节点、灵衢、CANN,华为给出了智算时代的新选择
  • 从DDR4到PCIe 5.0:聊聊Allegro中那些容易被忽略的‘隐性’信号延迟(以Via Z轴延迟为例)
  • 收藏!7个文理兼收的AI高薪岗位,小白也能轻松入门
  • 【ACM稳定出版检索】2026年人工智能与智慧生活国际学术会议 (ICAISL 2026)
  • 计算机毕业设计之基于hadoop的网易云音乐推荐系统的设计与实现
  • 发票、合同、身份证——OCR在金融行业到底替代了多少人工
  • 别再乱调参数了!OpenCV Canny边缘检测的threshold1和threshold2到底怎么设?附实战调参技巧
  • 实战指南:基于快马平台开发智能程控lm317电源,实现实验室精密供电
  • Mixly图形化编程一键接入Blinker物联网的点灯科技扩展包(含ESP32示例与完整开发文件)
  • 不止点灯!用FreeRTOS在GD32F407上实现多任务串口打印与按键响应
  • 保姆级教程:用OpenIPC和WFB-NG在Jetson Orin Nano上搭建低延迟无人机图传(含RTL8812AU驱动避坑)