01. bind 函数用途与使用技巧
在使用 LangChain 开发时,某些场景我们希望在一个 Runnable 队列中调用另一个 Runnable 并传递常量参数,这些参数既非前序 Runnable 的输出,也不是用户输入,而是组件自身的部分参数。此时可以使用 Runnable.bind()
传递默认参数。
典型应用场景
- 创建
ChatOpenAI
大语言模型构建两条链:- 第 1 条链设置
temperature=0.7
(确定性输出) - 第 2 条链设置
temperature=1.2
(创意性输出)
- 第 1 条链设置
- 通过
LLM.bind(temperature=0.7)
和LLM.bind(temperature=1.2)
设置不同参数
bind()
用于修改 Runnable 底层的默认调用参数,调用时自动传递无需手动输入。适合在构建链时已知参数值的场景。
1.1 动态添加默认调用参数
通过绑定运行时参数实现 LLM 的多场景复用:
import dotenv
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
dotenv.load_dotenv()
prompt = ChatPromptTemplate.from_messages([
(
"system",
"你正在执行一项测试,请重复用户传递的内容,除了重复其他均不要操作"
),
("human", "{query}")
])
llm = ChatOpenAI(model="gpt-4o")
chain = prompt | llm.bind(stop="world") | StrOutputParser()
content = chain.invoke({"query": "Hello world"})
print(content)
输出:
Hello
资料推荐
1.2 解决 RunnableLambda 多参传递问题
在 LangChain 中,如果要将一个函数变成 Runnable 组件,可以通过 RunnableLambda 函数进行包装。但是封装后,所有的 Runnable 组件的 invoke 函数,调用时,只能传递一个参数(类型不限制),如果原本的函数支持多个参数,并且是必填参数,就会出现报错。
例如:
import random
from langchain_core.runnables import RunnableLambda
def get_weather(location: str, unit: str) -> str:
"""根据传入的位置+温度单位获取对应的天气信息"""
print("location:", location)
print("unit:", unit)
return f"{location}天气为{random.randint(24, 40)}{unit}"
get_weather_runnable = RunnableLambda(get_weather)
resp = get_weather_runnable.invoke({"location": "北京", "unit": "摄氏度"})
print(resp)
上述代码在执行 invoke 时虽然传递了字典,并且包含了 location 和 unit 两个参数,但是这个参数只会作为唯一的一个值,传递给 get_weather 函数的 location 参数,所以实际上 get_weather 函数接收的参数如下:
{
"location": {"location": "北京", "unit": "摄氏度"},
"unit": None
}
而使用 bind() 函数绑定添加其他默认调用参数,从而巧妙实现 RunnableLambda 组件实现接收多个参数,修改示例:
import random
from langchain_core.runnables import RunnableLambda
def get_weather(location: str, unit: str) -> str:
"""根据传入的位置+温度单位获取对应的天气信息"""
print("location:", location)
print("unit:", unit)
return f"{location}天气为{random.randint(24, 40)}{unit}"
get_weather_runnable = RunnableLambda(get_weather).bind(unit="摄氏度")
resp = get_weather_runnable.invoke("北京")
print(resp)
输出内容:
location: {'location': '北京', 'unit': '摄氏度'}
unit: 摄氏度
{'location': '北京', 'unit': '摄氏度'}天气为31摄氏度
资料推荐
02 bind 函数运行流程解析
通过上述的案例,可以知道 .bind() 函数是在构建应用的时候添加上对应的默认调用参数,而在 Runnable.bind() 函数的底层,本质上是往 Runnable 的 kwargs 属性添加对应的字段,并生成一个新的 Runnable,当 Runnable 组件执行调用时(invoke、ainvoke、stream、astream、batch、abatch等),会自动将 kwargs 字段里的所有参数合并并覆盖默认调用参数。
从而完成动态添加默认调用参数的效果,Runnable.bind() 的实现原理:
- 在构建时向 Runnable 的 kwargs 属性添加字段
- 调用时自动合并覆盖默认参数
- 生成携带新参数的新 Runnable 实例
运行流程为:
注意事项
虽然 .bind() 是所有 Runnable 共有的方法,但是并不是所有的 Runnable 组件都支持绑定默认调用参数,部分组件底层并没有默认调用参数的概念,例如 PromptTemplate 底层的 invoke 方法,并没有使用到 .bind() 的逻辑。
# langchain_core/prompts/base.py -> BasePromptTemplate
def invoke(
self, input: Dict, config: Optional[RunnableConfig] = None
) -> PromptValue:
config = ensure_config(config)
if self.metadata:
config["metadata"] = {**config["metadata"], **self.metadata}
if self.tags:
config["tags"] = config["tags"] + self.tags
return self._call_with_config(
self._format_prompt_with_error_handling,
input,
config,
run_type="prompt",
)
"