每一篇文章都短小精悍,不啰嗦。

一、功能定位:Runnable 的 "增强包装器"

RunnableBindingBase 是 LangChain 中实现装饰器模式的核心组件。它就像给原有 Runnable 套上一件 "功能外套"—— 不改变原有 Runnable 的核心逻辑,却能附加固定参数、配置或类型约束,实现对原有功能的增强或定制。

核心价值:

  1. 非侵入式扩展:无需修改原有 Runnable 的代码,就能添加固定参数或配置
  2. 复用与定制平衡:在保留原有功能的基础上,为特定场景定制行为(如固定模型参数、统一添加追踪标签)
  3. 接口透明:对使用者来说,包装后的组件和原组件用法完全一致,降低认知成本

典型场景:

  • 固定参数:给大模型调用绑定固定的temperature=0(确定性输出)或stop=["。"](终止符)
  • 统一配置:为一组 Runnable 统一添加tags=["experiment-1"](便于追踪实验)
  • 类型适配:修改输入输出类型,让不同 Runnable 之间能无缝拼接(如将str输入转为dict输入)

二、核心架构:五层增强的嵌套结构

1. 继承关系:

class RunnableBindingBase(RunnableSerializable[Input, Output]):
  • 继承RunnableSerializable,同时具备 "可运行"、"可序列化" 双重特性
  • 泛型[Input, Output]保证类型安全,输入输出类型与原 Runnable 保持一致(或可定制)

2. 核心成员变量:

这五个成员共同构成了 "增强外套" 的核心部件:

成员变量作用类比
bound被包装的底层 Runnable(核心功能提供者)基础工具(如裸机螺丝刀)
kwargs固定传递给bound的参数(调用时自动附加)工具的固定配件(如特定批头)
config固定传递给bound的配置(如默认标签、回调)工具的默认设置(如默认转速)
config_factories动态处理配置的函数列表(运行时动态修改配置)配置转换器(如根据场景调转速)
custom_input/output_type覆盖原有输入 / 输出类型(解决类型不匹配问题)接口适配器(如不同规格的接口转换头)

3. 架构示意图:

用户调用 → RunnableBindingBase → 合并参数/配置 → 调用bound → 返回结果↑                    ↑├─ 自带kwargs/config  ├─ 生成最终参数/配置└─ config_factories   └─ 传给bound执行

三、关键流程:参数与配置的 "融合 - 转发" 机制

所有方法(invoke/batch/stream等)的核心逻辑高度一致:融合参数与配置,再转发给 bound 执行。以最常用的invoke为例:

1. 同步调用流程(invoke):

def invoke(self, input: Input, config: Optional[RunnableConfig] = None, **kwargs: Any) -> Output:return self.bound.invoke(input,self._merge_configs(config),  # 合并配置**{**self.kwargs, **kwargs},  # 合并参数)
  • 参数合并self.kwargs(固定参数)与调用时传入的kwargs(动态参数)合并,后者优先级更高
  • 配置合并:通过_merge_configs处理配置,最终传给bound

2. 配置合并的核心逻辑(_merge_configs):

def _merge_configs(self, *configs: Optional[RunnableConfig]) -> RunnableConfig:# 1. 先合并自身config和调用时传入的configconfig = merge_configs(self.config, *configs)# 2. 再应用所有config_factories动态修改配置return merge_configs(config, *(f(config) for f in self.config_factories))
  • 合并优先级:调用时传入的config > config_factories处理结果 > 自身config
  • 动态处理config_factories是函数列表,可根据当前配置动态生成新配置(如根据输入长度调整超时时间)

3. 批量处理流程(batch):

def batch(self, inputs: list[Input], config: Optional[Union[RunnableConfig, list[RunnableConfig]]] = None, ...) -> list[Output]:if isinstance(config, list):# 为每个输入单独合并配置configs = [self._merge_configs(conf) for conf in config]else:# 所有输入共用同一合并后的配置configs = [self._merge_configs(config) for _ in range(len(inputs))]return self.bound.batch(inputs, configs, return_exceptions=return_exceptions,**{**self.kwargs, **kwargs})

  • 支持两种批量模式:每个输入单独配置,或所有输入共用配置
  • 保持与bound.batch一致的接口,仅在配置和参数层做增强

四、技术细节:支撑灵活扩展的关键设计

1. 类型系统的 "覆盖与继承":

@property
@override
def InputType(self) -> type[Input]:return cast("type[Input]", self.custom_input_type) if self.custom_input_type else self.bound.InputType

优先级:如果设置了custom_input_type,则覆盖bound.InputType,否则继承

  • 价值:解决不同 Runnable 之间的类型不匹配问题。例如:原 Runnable 要求dict输入,但上游输出是str,可通过custom_input_type=str实现适配

2. 参数与配置的 "优先级合并":

  • 参数合并{** self.kwargs, **kwargs} → 调用时传入的kwargs会覆盖self.kwargs中的同名参数
    • 例:self.kwargs={"temperature": 0},调用时传temperature=1,最终生效的是1
  • 配置合并merge_configs函数保证:
    • 基础配置(self.config)→ 动态配置(config_factories生成)→ 调用时配置(config),后者覆盖前者
    • 复杂结构(如callbacksmetadata)会递归合并,而非简单覆盖

3. 序列化与透明性保障:

@classmethod
@override
def is_lc_serializable(cls) -> bool:return True  # 支持序列化,保证包装后的组件可保存/传输

序列化时会包含boundkwargsconfig等信息,重新加载后仍能保持增强特性

  • get_name方法直接返回bound.get_name(),保证在追踪日志中显示的是核心组件名称,降低调试成本

4. 接口全适配:

代码中实现了invoke/ainvoke/batch/abatch/stream/astream等所有Runnable接口,且逻辑高度一致:

# 异步流式处理示例
async def astream(self, input: Input, config: Optional[RunnableConfig] = None, **kwargs: Any) -> AsyncIterator[Output]:async for item in self.bound.astream(input, self._merge_configs(config),**{**self.kwargs, **kwargs}):yield item

保证无论用哪种方式调用,增强逻辑(参数 / 配置合并)都能生效

  • 对使用者完全透明,无需关心内部是如何包装的

五、实际场景:设计逻辑的落地价值

场景 1:固定大模型参数

from langchain_openai import ChatOpenAI# 原始模型(无固定参数)
llm = ChatOpenAI(model="gpt-3.5-turbo")# 包装后:固定temperature=0(确定性输出)和stop=["。"](遇句号停止)
fixed_llm = llm.bind(temperature=0, stop=["。"])# 调用时,会自动带上这两个参数
fixed_llm.invoke("写三句关于春天的话。")
# 输出会是:
# 春天是万物复苏的季节。
# 春风拂过,带来了花香。
# 田野里的小草探出了脑袋。

这里的bind方法就是基于RunnableBindingBase实现的,temperature=0stop=["。"]被存入self.kwargs

场景 2:统一添加追踪标签

# 包装后:所有调用都会带上tags=["experiment-2"]
tracked_llm = llm.with_config(config={"tags": ["experiment-2"]})# 调用时,配置会自动合并
tracked_llm.invoke("你好")
# 在LangSmith追踪中,该调用会被标记为"experiment-2"

with_config方法基于RunnableBindingBasetags被存入self.config,调用时自动附加到追踪信息中

场景 3:动态调整配置

# 定义一个根据输入长度调整超时时间的config_factory
def adjust_timeout(config: RunnableConfig) -> RunnableConfig:input_len = config.get("metadata", {}).get("input_len", 0)return {"timeout": max(5, input_len // 10)}  # 输入越长,超时时间越长# 包装时添加该factory
dynamic_llm = llm.with_config(config_factories=[adjust_timeout])# 调用时传入输入长度metadata
dynamic_llm.invoke("长文本..." * 100, config={"metadata": {"input_len": 1000}})
# 最终超时时间会被调整为100(1000//10=100)

config_factories实现了配置的动态生成,让组件能根据场景自适应

六、设计逻辑总结:装饰器模式的完美实践

RunnableBindingBase 的设计深刻体现了开放 - 封闭原则:对扩展开放(可通过包装添加新功能),对修改封闭(无需改动原有 Runnable)。

核心设计亮点:

  1. 最小知识原则:使用者只需知道原 Runnable 的接口,无需了解包装细节
  2. 单一职责bound负责核心逻辑,RunnableBindingBase专注于参数 / 配置增强
  3. 组合优于继承:通过包装而非继承实现扩展,避免类爆炸问题(如LLMWithTemperatureLLMWithTags等大量子类)

这个组件就像一个 "万能转接器",让不同的 Runnable 能在不修改自身的前提下,轻松适配各种场景需求,是构建灵活、可维护 AI 应用的关键基石。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/89383.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/89383.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/89383.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

为 Git branch 命令添加描述功能

写在最前面的使用方式 查看 所有分支的备注 git branch.notes创建分支并为分支添加备注 git co -b feat/oauth -m 第三方用户登录对分支描述的添加与清除 添加 git branch.note --add 清除 git branch.note --clear 📝 为 Git branch 命令添加描述功能 &#x…

LeetCode|Day18|20. 有效的括号|Python刷题笔记

LeetCode|Day18|20. 有效的括号|Python刷题笔记 🗓️ 本文属于【LeetCode 简单题百日计划】系列 👉 点击查看系列总目录 >> 📌 题目简介 题号:20. 有效的括号 难度:简单 题目…

使⽤Pytorch构建⼀个神经⽹络

关于torch.nn:使⽤Pytorch来构建神经⽹络, 主要的⼯具都在torch.nn包中.nn依赖于autograd来定义模型, 并对其⾃动求导.构建神经⽹络的典型流程:定义⼀个拥有可学习参数的神经⽹络遍历训练数据集处理输⼊数据使其流经神经⽹络计算损失值将⽹络参数的梯度进⾏反向传播以⼀定的规则…

网络爬虫的详细知识点

基本介绍 什么是网络爬虫 网络爬虫(Web Crawler)是一种自动化程序,用于从互联网上抓取、解析和存储网页数据。其核心功能是模拟人类浏览行为,通过HTTP/HTTPS协议访问目标网站,提取文本、链接、图片或其他结构化信息&…

AndroidX中ComponentActivity与原生 Activity 的区别

一、AndroidX 与原生 Activity 的区别 1. 概念与背景 原生 Activity:指 Android 早期(API 1 起)就存在于 android.app 包下的 Activity 类(如 android.app.Activity),是 Android 最初的 Activity 实现&…

Spring AI 使用 Elasticsearch 作为向量数据库

前言 嗨,大家好,我是雪荷,最近在公司开发 AI 知识库,同时学到了一些 AI 开发相关的技术,这期先与大家分享一下如何用 ES 当做向量数据库。 安装ES 第一步我们先安装 Elasticsearch,这里建议 Elasticsear…

TypeScript 配置全解析:tsconfig.json、tsconfig.app.json 与 tsconfig.node.json 的深度指南

前言在现代前端和后端开发中,TypeScript 已经成为许多开发者的首选语言。然而,TypeScript 的配置文件(特别是多个配置文件协同工作时)常常让开发者感到困惑。本文将深入探讨 tsconfig.json、tsconfig.app.json 和 tsconfig.node.j…

读书笔记(学会说话)

1、一个人只有会说话,才会有好人缘,做事才会顺利。会说话的人容易成功。善于说话的人易成功,而不善说话的人往往寸步难行。我们要把话说得好听,同时更要把事做得漂亮。或许一句话,一件事,就可能使人生的旅途…

私有服务器AI智能体搭建-大模型选择优缺点、扩展性、可开发

以下是主流 AI 框架与模型的对比分析,涵盖其优缺点、扩展性、可开发性等方面。 文章目录一、AI 框架对比二、主流大模型对比三、扩展性对比总结四、可开发性对比总结五、选择建议(按场景)六、未来趋势一、AI 框架对比 框架优点缺点扩展性可开…

OpenCV直线段检测算法类cv::line_descriptor::LSDDetector

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 该类用于实现 LSD (Line Segment Detector) 直线段检测算法。LSD 是一种快速、准确的直线检测方法,能够在不依赖边缘检测的前提下直接从…

Go语言流程控制(if / for)

分支结构package mainimport ("fmt""strconv" )/* 1.顺序结构 2.分支结构 3.循环结构 *//* if 条件1 {// 条件1为真时执行的代码 } else if 条件2 {// 条件1为假但条件2为真时执行的代码 } else {// 所有条件均为假时执行的代码 }一种特殊的条件分支结构if…

wx小程序设置沉浸式导航文字高度问题

第一步:在app.json中设置"navigationStyle": "custom"第二步骤:文件的home.js中// pages/test/test.js Page({/*** 页面的初始数据*/data: {statusBarHeight: 0,navBarHeight: 44 // 自定义导航内容区高度(单位px)},/*** 生命周期函…

C++算法竞赛篇:DevC++ 如何进行debug调试

C算法竞赛篇:DevC 如何进行debug调试前言一、准备工作:编译生成可执行程序二、核心步骤:设置断点与启动调试1. 设置断点2. 启动调试模式三、调试操作:逐步执行与变量监控1. 逐步执行代码2. 监控变量值变化四、调试结束前言 在算法…

语音大模型速览(三)- cosyvoice2

CosyVoice 2: Scalable Streaming Speech Synthesis with Large Language Models 论文链接:https://arxiv.org/pdf/2412.10117代码链接:https://github.com/FunAudioLLM/CosyVoice 一句话总结 CosyVoice 2 是一款改进的流式语音合成模型,其…

-lstdc++与-static-libstdc++的用法和差异

CMakeLists.txt 里写了: target_link_libraries(${PROJECT_NAME} PRIVATEgccstdc ) target_link_options(${PROJECT_NAME} PRIVATE -static-libstdc)看起来像是“链接了两次 C 标准库”,其实它们的作用完全不同:1. target_link_libraries(...…

Redis学习其二(事务,SpringBoot整合,持久化RDB和AOF)

文章目录5,事务5.1Redis 事务不保证原子性的原因5.2事务操作过程5.3监控6,SpringBoot整合Redis6.1Redis客户端6.1.1Jedis简单使用6.1.2Lettuce&Jedis6.2配置相关6.3使用6.3.1使用RedisTemplate6.3.2Redis工具类7,持久化RDB7.1RDB持久化原理7.2触发机制save命令flushall命令…

springboot项目部署到K8S

java后台 创建harbor镜像拉取Secret:kubectl create secret docker-registry harbor-regcred \--docker-server \ #harbor仓库地址--docker-username \ #harbor 账号--docker-password \ #harbor密码-n productionDockerfile FROM *harbor地址*/library/custom-jdk…

【FPGA开发】一文轻松入门Modelsim的基本操作

Modelsim仿真的步骤 (1)创建新的工程。 (2)在弹出的窗口中,确定项目名和工作路径,库保持为work不变(如有需要可以根据需求进行更改)。 (3)添加已经存在的文件(rtl代码和t…

服务攻防-Java组件安全FastJson高版本JNDI不出网C3P0编码绕WAF写入文件CI链

服务攻防-Java组件安全&FastJson&高版本JNDI&不出网C3P0&编码绕WAF&写入文件CI链26天 原创 朝阳 Sec朝阳 2025年07月18日 09:23 湖北 标题已修改 演示环境: https://github.com/lemono0/FastJsonParty FastJson全版本Docker漏洞环境(涵盖1.…

【Python】DRF核心组件详解:Mixin与Generic视图

在 Django REST Framework (DRF) 中,mixins.CreateModelMixin、mixins.ListModelMixin、GenericAPIView 和 GenericViewSet 是构建 API 视图的核心组件。以下是对这些组件的主要方法及其职责的简要说明,内容清晰且结构化:1. mixins.CreateMod…