在 LangChain 框架中,Runnable
接口不仅定义了组件交互的标准行为,还通过完善的类型系统和配置机制确保复杂 AI 流程的可靠性。本文将深入解析 Runnable 的输入输出类型规范、架构检查能力及运行时配置管理,帮助开发者构建类型安全、可调试的 AI 应用。
Runnable 接口的类型系统为组件交互建立了明确的 "契约":
与传统面向对象接口不同,Runnable 的类型可以是任意 Python 对象,由组件自身定义,极大提升了灵活性。
以下是 LangChain 关键组件的输入输出类型对照表:
组件类型 | 输入类型 | 输出类型 |
---|---|---|
Prompt |
dict (包含提示词变量) |
PromptValue (格式化后的提示词) |
ChatModel |
str / 聊天消息列表 /PromptValue |
ChatMessage 列表 |
LLM |
str / 聊天消息列表 /PromptValue |
str (生成的文本) |
OutputParser |
LLM/ChatModel 的输出 | 解析后的特定格式(如 JSON/Dict) |
Retriever |
str (查询关键词) |
Document 列表(检索结果) |
Tool |
str /dict (取决于工具协议) |
工具特定输出(如 API 响应) |
python
运行
from langchain.chat_models import ChatOpenAI
from langchain.schema import HumanMessage
# 输入:聊天消息列表
messages = [
HumanMessage(content="请推荐三个AI框架"),
]
# 初始化ChatModel(输入类型为消息列表,输出为ChatMessage)
chat_model = ChatOpenAI(temperature=0.5)
# 调用执行(输出为包含AI回复的ChatMessage)
response = chat_model.invoke(messages)
print(f"回复类型: {type(response)}") # 输出:
print(f"回复内容: {response.content}")
LangChain 会尝试根据组件定义自动推断输入输出类型,但在以下场景可能失效:
(prompt1 | model1) & (prompt2 | model2)
的联合类型RunnableLambda
封装的函数python
运行
from langchain.runnables import RunnableLambda
# 定义一个将数字加1的函数(输入/输出均为int)
def add_one(x):
return x + 1
# 自动推断时,x可能被误判为Any类型
runnable = RunnableLambda(add_one)
# 手动指定输入输出类型
typed_runnable = runnable.with_types(
input_type=int,
output_type=int
)
# 类型检查(MyPy会报错如果输入非int)
result = typed_runnable.invoke("错误输入") # MyPy提示: Argument 1 to "invoke" has incompatible type "str"; expected "int"
架构检查能力允许开发者在运行时获取 Runnable 的类型元数据,主要应用于:
LangChain 提供两组 API 获取组件模式:
python
运行
from langchain.llms import OpenAI
from pydantic import BaseModel
# 初始化LLM
llm = OpenAI(temperature=0.7)
# 获取输入Pydantic模式(返回Pydantic模型类)
InputModel = llm.get_input_schema()
print(f"输入模型字段: {InputModel.__fields__.keys()}")
# 输出: dict_keys(['prompt', 'stop', 'temperature', ...])
# 获取输出Pydantic模式
OutputModel = llm.get_output_schema()
print(f"输出模型类型: {OutputModel}")
# 输出:
# 验证输入合法性
valid_input = InputModel(prompt="测试提示词", temperature=0.5)
python
运行
# 获取输入JSON Schema(符合JSON Schema规范)
input_jsonschema = llm.get_input_jsonschema()
print("输入模式的title:", input_jsonschema["title"]) # 输出: LLMInput
print("temperature字段描述:", input_jsonschema["properties"]["temperature"]["description"])
# 输出: What sampling temperature to use.
# 获取输出JSON Schema
output_jsonschema = llm.get_output_jsonschema()
对于可配置的 Runnable,还可获取其配置选项的模式:
python
运行
from langchain.chains import LLMChain
# 创建LLMChain(包含配置选项)
chain = LLMChain(llm=OpenAI(), prompt=prompt)
# 获取配置的Pydantic模式
ConfigModel = chain.config_schema()
print(f"配置模型字段: {ConfigModel.__fields__.keys()}")
# 输出包含: run_name, tags, metadata, max_concurrency, ...
# 获取配置的JSON Schema
config_jsonschema = chain.get_config_jsonschema()
print("max_concurrency的默认值:", config_jsonschema["properties"]["max_concurrency"]["default"])
# 输出: 10
RunnableConfig
是控制 Runnable 运行时行为的关键字典,包含以下核心属性:
python
运行
# 设置运行名称、标签与元数据
config = {
"run_name": "production_chain", # 运行标识(不被子调用继承)
"tags": ["production", "v1.0"], # 标签(被子调用继承)
"metadata": {
"user_id": "12345",
"request_id": "req-789"
} # 元数据(被子调用继承)
}
# 在调用时传入配置
result = chain.invoke(
{"topic": "配置示例"},
config=config
)
python
运行
config = {
"max_concurrency": 5, # 最大并行数(批处理时生效)
"recursion_limit": 10, # 最大递归深度(防止无限循环)
"configurable": {
"llm__temperature": 0.9 # 动态修改LLM的temperature参数
}
}
python
运行
from langchain.callbacks import ConsoleCallbackHandler
config = {
"callbacks": [
ConsoleCallbackHandler() # 控制台输出回调
]
}
# 调用时传入回调配置
result = chain.invoke(
{"question": "配置回调"},
config=config
)
# 控制台将实时输出调用过程
当多个 Runnable 组合成链时,配置会自动传播到所有子调用,确保全局配置一致性。
python
运行
# 组合提示词、模型与解析器
chain = prompt | chat_model | output_parser
# 调用时传入配置,子组件自动继承
result = chain.invoke(
{"question": "配置传播测试"},
config={
"run_name": "combined_chain",
"tags": ["debug"]
}
)
# 在子组件(如chat_model)中可访问配置
def custom_llm_invoke(inputs, config):
print(f"子调用配置: {config['tags']}") # 输出: ['debug']
return chat_model.ainvoke(inputs, config=config)
在 Python 3.11 + 环境中,配置可自动传播;但在 3.10 及以下版本的异步代码中需手动传递:
python
运行
# Python 3.10及以下异步环境的手动传播
import asyncio
from langchain.runnables import RunnableLambda
async def manual_propagation(inputs, config):
# 显式传递config到子调用
return await sub_runnable.ainvoke(inputs, config=config)
# 创建Runnable时保留config参数
runnable = RunnableLambda(manual_propagation)
# 调用时传入配置
asyncio.run(
runnable.ainvoke(
{"data": "需要传播配置"},
config={"run_id": "manual-001"}
)
)
当配置在不同层级出现冲突时,遵循以下优先级顺序(从高到低):
invoke(config=...)
)python
运行
# 父链设置max_concurrency=5
parent_config = {"max_concurrency": 5}
# 子调用中覆盖为3
child_result = chain.invoke(
input_data,
config={
**parent_config,
"max_concurrency": 3 # 子调用优先
}
)
python
运行
# 在LangSmith中追踪运行(需安装langsmith)
from langchain import hub
from langsmith import Client
# 注册链到LangSmith
chain = hub.pull("langchain-ai/chat-langchain")
# 调用时添加详细配置
result = chain.invoke(
{"question": "LangChain是什么"},
config={
"run_name": "langsmith_demo",
"tags": ["demo", "quickstart"],
"metadata": {
"model_version": "gpt-3.5-turbo-0613",
"user_role": "developer"
},
"callbacks": [Client().make_chain_tracker()] # LangSmith追踪回调
}
)
# 在LangSmith界面中可按标签筛选、查看运行详情
python
运行
# 从环境变量获取配置(生产环境常用模式)
import os
config = {
"max_concurrency": int(os.getenv("MAX_CONCURRENCY", "10")),
"configurable": {
"llm__temperature": float(os.getenv("LLM_TEMPERATURE", "0.2")),
"retriever__search_kwargs__k": int(os.getenv("SEARCH_K", "5"))
},
"callbacks": [
# 生产环境使用日志回调而非控制台
SomeProductionLoggerCallback()
]
}
# 应用配置到链
result = my_chain.invoke(inputs, config=config)
Runnable 的类型系统与配置机制是 LangChain 框架灵活性与可维护性的重要支撑:
with_types
可灵活应对复杂场景如果本文对你有帮助,别忘了点赞收藏,关注我,一起探索更高效的开发方式~