示例代码:
import time
from langchain_core.runnables import RunnableLambda, RunnableConfig
# 🚀 自定义回调函数,打印 config 里的 tags 和 metadata
def custom_callback(input_text, config: dict):
print(f"\n[回调] 执行任务: {input_text}")
# 使用字典访问方式获取 tags 和 metadata
print(f"[回调] 任务标签 (tags): {config.get('tags', '未提供')}")
print(f"[回调] 任务元数据 (metadata): {config.get('metadata', '未提供')}\n")
# 🚀 任务 1:转换为小写
def to_lowercase(text: str, config: RunnableConfig):
custom_callback(text, config) # 调用回调函数,打印 config 信息
print("[Step 1] 转换为小写:", text.lower())
return text.lower()
# 🚀 任务 2:去掉空格
def remove_spaces(text: str, config: RunnableConfig):
custom_callback(text, config)
print("[Step 2] 去掉空格:", text.replace(" ", ""))
return text.replace(" ", "")
# 🚀 任务 3:反转字符串
def reverse_text(text: str, config: RunnableConfig):
custom_callback(text, config)
print("[Step 3] 反转字符串:", text[::-1])
return text[::-1]
# 🚀 创建 Runnables
step1_runnable = RunnableLambda(to_lowercase)
step2_runnable = RunnableLambda(remove_spaces)
step3_runnable = RunnableLambda(reverse_text)
# 🚀 任务流水线
def text_pipeline(text: str, config: RunnableConfig):
step1_result = step1_runnable.invoke(text, config=config)
step2_result = step2_runnable.invoke(step1_result, config=config)
step3_result = step3_runnable.invoke(step2_result, config=config)
return step3_result
pipeline_runnable = RunnableLambda(text_pipeline)
# 🚀 定义 RunnableConfig,包含 tags 和 metadata
config = RunnableConfig(
tags=["text-processing", "pipeline"], # 任务标签
metadata={"author": "user", "version": "1.0"} # 任务额外信息
)
# 🚀 执行任务
result = pipeline_runnable.invoke("Hello World", config=config)
# 🚀 输出最终结果
print("\n最终结果:", result)
输出:
[回调] 执行任务: Hello World
[回调] 任务标签 (tags): ['text-processing', 'pipeline']
[回调] 任务元数据 (metadata): {'author': 'user', 'version': '1.0'}
[Step 1] 转换为小写: hello world
[回调] 执行任务: hello world
[回调] 任务标签 (tags): ['text-processing', 'pipeline']
[回调] 任务元数据 (metadata): {'author': 'user', 'version': '1.0'}
[Step 2] 去掉空格: helloworld
[回调] 执行任务: helloworld
[回调] 任务标签 (tags): ['text-processing', 'pipeline']
[回调] 任务元数据 (metadata): {'author': 'user', 'version': '1.0'}
[Step 3] 反转字符串: dlrowolleh
最终结果: dlrowolleh
RunnableConfig 是什么?
RunnableConfig
是 LangChain 里的运行时配置(Runtime Configuration),用来控制和管理 Runnable
的执行。
它的作用类似于:
- 配置执行参数(比如超时时间、并发数)
- 传递日志、回调(可以监控
Runnable
的运行情况) - 添加元数据(比如给每个调用打上标签,方便追踪)
- 影响子
Runnable
的行为(如果Runnable
由多个子Runnable
组成,RunnableConfig
会让子Runnable
继承父Runnable
的设置)
🌟 你可以把 RunnableConfig
理解成执行任务时的一张「运行指令卡」,它规定了任务的运行方式和额外信息。
RunnableConfig 的作用
1. 让 Runnable
知道怎么运行
比如:
- 设置超时时间(防止任务执行太久)
- 限制并发数(防止任务同时执行太多次)
- 启用调试模式(可以看到详细的日志)
2. 让 Runnable
记录运行信息
比如:
- 添加
tags
标签(方便后续查询任务记录) - 添加
metadata
元数据(保存额外的信息,比如任务来源)
3. 让 Runnable
可以执行回调
比如:
- 在任务开始、结束、出错时执行回调
- 可以实时监控任务的执行情况
如果不用 RunnableConfig
,会有什么副作用?
副作用 1:无法管理 Runnable
的运行
- 没有超时控制,导致任务可能一直运行
- 没有并发限制,可能会过载系统
- 没有日志,无法调试任务运行情况
副作用 2:无法追踪任务执行
- 任务执行时不会带
tags
,后续查询不到记录 - 任务执行的信息不会被存入
metadata
- 任务出错时不会触发回调,无法自动处理错误
副作用 3:子 Runnable
不能继承配置
- 任务的子步骤不会继承超时、回调等设定,导致行为不一致
什么场景下需要用 RunnableConfig
?
✅ 场景 1:给任务设置超时,防止死循环
from langchain_core.runnables import RunnableLambda, RunnableConfig
import time
# 定义一个带有超时控制的任务
from langchain_core.runnables import RunnableLambda, RunnableConfig
import time
# 定义一个带有超时控制的任务
def slow_task(input_text, config):
# 从 "configurable" 子字典中获取 timeout 参数
timeout = config.get("configurable", {}).get("timeout", 5)
print('timeout===', timeout)
start_time = time.time()
# 假设任务执行时间5s
while time.time() - start_time < 5:
time.sleep(1) # 模拟任务执行中
print("任务正在执行...")
if time.time() - start_time >= timeout:
raise TimeoutError("TimeoutError任务超时!")
return input_text.upper()
task_runnable = RunnableLambda(slow_task)
# 设置配置,传递超时参数到 "configurable" 字段
config = RunnableConfig(configurable={"timeout": 2})
try:
result = task_runnable.invoke("hello", config=config)
print('result===',result)
except TimeoutError as e:
print('出错了===',e)
输出:
- 如果没有
RunnableConfig
,任务会执行 10 秒,可能卡死。 - 加上
timeout=2
后,任务会在 2 秒后超时终止。
✅ 场景 2:给任务打标签,方便后续查询
config = RunnableConfig(tags=["text-processing", "uppercase"])
result = task_runnable.invoke("hello", config=config)
- 如果不加
tags
,以后想找这个任务的执行记录会很难。 - 加上
tags=["text-processing", "uppercase"]
后,可以用这个标签查询任务记录。
✅ 场景 3:子任务继承 RunnableConfig
def process_step1(text):
return text.lower()
def process_step2(text):
return text.replace(" ", "-")
step1_runnable = RunnableLambda(process_step1)
step2_runnable = RunnableLambda(process_step2)
def full_pipeline(text, config: RunnableConfig):
step1_result = step1_runnable.invoke(text, config=config) # 继承 config
step2_result = step2_runnable.invoke(step1_result, config=config) # 继承 config
return step2_result
pipeline_runnable = RunnableLambda(full_pipeline)
config = RunnableConfig(tags=["pipeline"], metadata={"source": "user-input"})
result = pipeline_runnable.invoke("Hello World", config=config)
print(result) # 输出:hello-world
- 如果不传
config
,step1_runnable
和step2_runnable
不会继承tags
和metadata
,导致后续查询不到相关信息。 - 加上
config
,所有步骤都会带上tags=["pipeline"]
和metadata={"source": "user-input"}
。
总结
问题 | 不用 RunnableConfig | 用 RunnableConfig |
---|---|---|
超时控制 | 任务可能卡死 | 任务超时会终止 |
日志记录 | 无法查看任务状态 | 可以监控任务 |
任务追踪 | 任务信息无法查询 | 可以添加 tags 和 metadata |
子任务管理 | 子任务不会继承配置 | 子任务可以继承 config |
错误处理 | 无法执行回调 | 任务失败时可触发回调 |
一句话总结
RunnableConfig
让Runnable
运行得更安全、更可控,并且方便调试、追踪和管理任务。
如果你的Runnable
需要超时控制、日志、回调、任务追踪等功能,就应该用RunnableConfig
!
完整代码示例:RunnableConfig
的作用
下面是一个完整的 Python 代码示例,展示 RunnableConfig
在超时控制、日志追踪、任务标签、子任务管理等方面的作用。
📌 代码功能
- 创建一个文本处理任务:
- 第一步:将文本转换为小写
- 第二步:去掉所有空格
- 第三步:反转字符串
- 使用
RunnableConfig
来:- 设置超时,防止任务卡死
- 添加
tags
,方便任务查询 - 添加
metadata
,记录额外信息 - 自动传播
config
到子任务
✅ 完整代码
import time
from langchain_core.runnables import RunnableLambda, RunnableConfig
# 🚀 自定义回调函数,打印 config 里的 tags 和 metadata
def custom_callback(input_text, config: dict):
print(f"\n[回调] 执行任务: {input_text}")
# 使用字典访问方式获取 tags 和 metadata
print(f"[回调] 任务标签 (tags): {config.get('tags', '未提供')}")
print(f"[回调] 任务元数据 (metadata): {config.get('metadata', '未提供')}\n")
# 🚀 任务 1:转换为小写
def to_lowercase(text: str, config: RunnableConfig):
custom_callback(text, config) # 调用回调函数,打印 config 信息
print("[Step 1] 转换为小写:", text.lower())
return text.lower()
# 🚀 任务 2:去掉空格
def remove_spaces(text: str, config: RunnableConfig):
custom_callback(text, config)
print("[Step 2] 去掉空格:", text.replace(" ", ""))
return text.replace(" ", "")
# 🚀 任务 3:反转字符串
def reverse_text(text: str, config: RunnableConfig):
custom_callback(text, config)
print("[Step 3] 反转字符串:", text[::-1])
return text[::-1]
# 🚀 创建 Runnables
step1_runnable = RunnableLambda(to_lowercase)
step2_runnable = RunnableLambda(remove_spaces)
step3_runnable = RunnableLambda(reverse_text)
# 🚀 任务流水线
def text_pipeline(text: str, config: RunnableConfig):
step1_result = step1_runnable.invoke(text, config=config)
step2_result = step2_runnable.invoke(step1_result, config=config)
step3_result = step3_runnable.invoke(step2_result, config=config)
return step3_result
pipeline_runnable = RunnableLambda(text_pipeline)
# 🚀 定义 RunnableConfig,包含 tags 和 metadata
config = RunnableConfig(
tags=["text-processing", "pipeline"], # 任务标签
metadata={"author": "user", "version": "1.0"} # 任务额外信息
)
# 🚀 执行任务
result = pipeline_runnable.invoke("Hello World", config=config)
# 🚀 输出最终结果
print("\n最终结果:", result)
📌 运行结果
[回调] 执行任务: Hello World
[回调] 任务标签 (tags): ['text-processing', 'pipeline']
[回调] 任务元数据 (metadata): {'author': 'user', 'version': '1.0'}
[Step 1] 转换为小写: hello world
[回调] 执行任务: hello world
[回调] 任务标签 (tags): ['text-processing', 'pipeline']
[回调] 任务元数据 (metadata): {'author': 'user', 'version': '1.0'}
[Step 2] 去掉空格: helloworld
[回调] 执行任务: helloworld
[回调] 任务标签 (tags): ['text-processing', 'pipeline']
[回调] 任务元数据 (metadata): {'author': 'user', 'version': '1.0'}
[Step 3] 反转字符串: dlrowolleh
最终结果: dlrowolleh
📌 代码解析
-
RunnableLambda(to_lowercase)
- 创建可执行对象
step1_runnable
,执行文本转换为小写的任务。
- 创建可执行对象
-
RunnableConfig(configurable={"timeout":2}, tags=[...], metadata={...})
timeout=5
:如果任务执行超过 2 秒,会自动超时。tags=["text-processing", "pipeline"]
:标记任务为 “文本处理” 和 “流水线”。metadata={"author": "user", "version": "1.0"}
:记录额外信息,如作者和版本。
-
任务的
config
会自动传播step1_runnable.invoke(text, config=config)
继承了RunnableConfig
step2_runnable.invoke(step1_result, config=config)
也继承了RunnableConfig
step3_runnable.invoke(step2_result, config=config)
也继承了RunnableConfig
❓ 如果不使用 RunnableConfig
,会发生什么?
❌ 没有 timeout
:
- 任务可能会卡死,没有时间限制。
❌ 没有 tags
:
- 任务执行记录不会有标签,难以查询。
❌ 没有 metadata
:
- 任务不会保存额外信息(比如作者、版本等)。
❌ 子任务无法继承配置:
- 每个
Runnable
需要手动传递参数,增加复杂度。
✅ 总结
功能 | 不用 RunnableConfig | 用 RunnableConfig |
---|---|---|
超时控制 | 任务可能卡死 | 任务超时会终止 |
任务标签 | 任务无法分类 | tags=["text-processing", "pipeline"] |
任务追踪 | 没有 metadata 记录 | metadata={"author": "user", "version": "1.0"} |
子任务继承配置 | 每个子任务都要手动配置 | 任务会自动继承 config |
📢 结论
如果你的任务涉及多个子步骤、需要超时控制、日志追踪、任务分类等,
RunnableConfig
是必不可少的!