(备注:所有字体标红的笔记模块暂时没有用到或者没来得及看)
2026-02-24
Runable与LCEL(一下的五种链式调用仅代表常见场景)
Runable
是什么:是langchain核心抽象接口(定义在langchain_core.runnables)统一组件的调用方式,支持LCEL组合,可以适配同步/异步、流式、批量等场景,是构建工作流的基础。通俗一点就是为所有可执行组件都提供统一的接口,案例如下
python
from langchain.chat_models import init_chat_model
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from loguru import logger
import os
# 创建聊天提示模板,包含系统角色设定和用户问题输入
chat_prompt = ChatPromptTemplate.from_messages([
("system", "你是一个{role},请简短回答我提出的问题"),
("human", "请回答:{question}")
])
# 使用具体参数实例化提示模板并记录日志
prompt = chat_prompt.invoke({"role": "AI助手", "question": "什么是LangChain,简洁回答100字以内"})
logger.info(prompt)
# 初始化模型
model = init_chat_model(
model="qwen-plus",
model_provider="openai",
api_key=os.getenv("aliQwen-api"),
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
# 调用模型获取原始响应并记录日志
result = model.invoke(prompt)
logger.info(f"********>模型原始输出:\n{result}")
# 创建字符串输出解析器,用于处理模型输出
parser = StrOutputParser ()
# 解析模型输出为结构化结果并记录日志
response = parser.invoke(result)
logger.info(f"解析后的结构化结果:\n{response}")
# 记录解析结果的数据类型
logger.info(f"结果类型: {type(response)}")
解决什么问题:
1、在使用runnable之前,不同的AI组件各有各的调用方式。比如,提示模板用.format(),语言模型用.generate(),解析器用.parse()。开发者需要手动处理这些差异,代码既冗长又难以维护,使用这种可以解决接口混乱问题
2、使用runnable实现了无缝的组合,通过lcel, 可以结合管道符|把多个runnable像流水线一样串联起来
python
chain = chat_prompt | model | parser
3、支持多种执行模式,AI应用场景中,有的时候需要获取单个答案invoke,有的时候需要批量处理数据batch,有的时候需要流式处理stream,runnable原生支持这些模式,IT工作中不需要为不同的场景编排不同的代码
为什么需要:
1、降低学习成本:只需要学习一套接口就可以操作所有AI组件
2、提升开发效率:runnable的可组合性让快速迭代成为了可能,可以先搭建简单的测试核心功能,然后逐步增加组件,不需要重写整个系统
3、增强系统可靠性:内置有企业级功能增强
4、实现了更好的可观测性:可以通过astream_events等方法追踪每个步骤的执行情况
LCEL
是什么:组合runnable组件的声明式语法,通俗说类似于Linux的管道符|
解决什么问题:
1、代码冗长且难以维护
2、可读性差
3、缺乏灵活性:传统链式调用的结构固定难以动态调整
4、调试困难:所有步骤耦合在一起,出现异常时难以快速定位异常位置
为什么需要:
灵活性和可组合性,可以拼接多个runnable为复杂工作流,支持条件分支和并行执行
RunnableSequence-顺序链
RunnableSequence按照顺序链接多个可执行对象,其中的一个对象的输出作为下一个对象的输入,代码案例如下
python
"""
顺序链
LangChain 的一个典型链条由Prompt、Model、OutputParser (可没有)组成,
然后可以通过 链(Chain) 把它们顺序组合起来,让一个任务的输出成为下一个任务的输入
意思等价于Linux里面的管道符
"""
from langchain.chat_models import init_chat_model
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from loguru import logger
import os
# 创建聊天提示模板,包含系统角色设定和用户问题输入
chat_prompt = ChatPromptTemplate.from_messages([
("system", "你是一个{role},请简短回答我提出的问题"),
("human", "请回答:{question}")
])
# 使用具体参数实例化提示模板并记录日志
prompt = chat_prompt.invoke({"role": "AI助手", "question": "什么是LangChain,简洁回答100字以内"})
logger.info(prompt)
# 初始化模型
model = init_chat_model(
model="qwen-plus",
model_provider="openai",
api_key=os.getenv("aliQwen-api"),
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
# 调用模型获取原始响应并记录日志
result = model.invoke(prompt)
logger.info(f"********>模型原始输出:\n{result}")
# 创建字符串输出解析器,用于处理模型输出
parser = StrOutputParser ()
# 解析模型输出为结构化结果并记录日志
response = parser.invoke(result)
logger.info(f"解析后的结构化结果:\n{response}")
# 记录解析结果的数据类型
logger.info(f"结果类型: {type(response)}")
print()
print("*" * 60)
print()
# 构建处理链:提示模板 -> 模型 -> 输出解析器
chain = chat_prompt | model | parser
# 执行处理链并记录最终结果及数据类型
result_chain = chain.invoke({"role": "AI助手", "question": "什么是LangChain,简洁回答100字以内"})
logger.info(f"Chain执行结果:\n {result_chain}")
logger.info(f"Chain执行结果类型: {type(result_chain)}")
print()
print(type(chain))
RunnableBranch-分支链
用条件分支判断 (条件,Runnable) 对列表和默认分支进行初始化。就是if-else if-else
在langchain中提供类RunnableBranch来完成LCEL中的条件分支判断,可以根据输入的不同采用不同的处理逻辑
python
'''
分支链
在langchain中提供类RunnableBranch来完成LCEL中的条件分支判断,可以根据输入的不同采用不同的处理逻辑
具体实例如下
会根据用户输入中是否包含英文、韩文等关键词,来选择对应的提示词进行处理。根据判断结果
再执行不同的逻辑分支
'''
from langchain.chat_models import init_chat_model
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from loguru import logger
from langchain_core.runnables import RunnableBranch
import os
# 创建提示词
english_prompt = ChatPromptTemplate.from_messages([
('system', '你是一个英语翻译,你叫小英'),
('human', '{query}')
])
japanese_prompt = ChatPromptTemplate.from_messages([
("system", "你是一个日语翻译专家,你叫小日"),
("human", "{query}")
])
korean_prompt = ChatPromptTemplate.from_messages([
('system', '你是一个韩语翻译,你叫小韩'),
('human', '{query}')
])
def determine_language(inputs):
'''判断语言种类'''
query = inputs['query']
if '日语' in query:
return 'japanese'
elif '韩语' in query:
return 'korean'
return 'english'
# 初始化模型
model = init_chat_model(
model='qwen-plus',
model_provider='openai',
# 硬编码写死key
api_key=os.getenv('aliQwen-api'), # 平台提供的API-KEY
base_url='https://dashscope.aliyuncs.com/compatible-mode/v1'
)
# 创建字符输出解析器,用于处理模型输出
parser = StrOutputParser()
# 创建一个可执行的分支链,根据输入文本语言类型选择响应的处理流程
# 返回值:RunnableBranch对象,可根据输入动态选择执行路径的可运行链
chain = RunnableBranch(
(lambda x: determine_language(x)=='japanese',japanese_prompt|model|parser),
(lambda x: determine_language(x)=='korean',korean_prompt|model|parser),
(english_prompt|model|parser)
)
# 测试查询
test_queries = [
{'query': '请你用韩语翻译这句话:"见到你很高兴"'},
{'query': '请你用日语翻译这句话:"见到你很高兴"'},
{'query': '请你用英语翻译这句话:"见到你很高兴"'}
]
for query_input in test_queries:
# 判断使用哪个词
lang = determine_language(query_input)
logger.debug(f'检测到语言类型:{lang}')
# 根据语言类型选择对应的提示词并格式化
if lang == 'english':
chatPromptTemplate = english_prompt
elif lang == 'korean':
chatPromptTemplate = korean_prompt
else:
chatPromptTemplate = japanese_prompt
# print(query_input) # {'query': '请你用英语翻译这句话:"见到你很高兴"'}
# 格式化提示词并打印
formatted_messages = chatPromptTemplate.format_messages(**query_input)
logger.info(f"格式化后的提示词:{formatted_messages}")
for msg in formatted_messages:
logger.info(f"[{msg.type}]: {msg.content}")
# 执行链
result = chain.invoke(query_input)
logger.info(f"输出结果: {result}\n")
RunnableSerializable-串行链
子链叠加串行,假如我们需要多次调用大模型,将多个步骤串联起来实现功能,案例如下
python
'''
RunnableSerializable-串行链
子链叠加串联,假如我们需要多次调用大模型,需要多个步骤串联起来实现功能
'''
from langchain.chat_models import init_chat_model
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from loguru import logger
import os
# 初始化模型
model = init_chat_model(
model='qwen-plus',
model_provider='openai',
# 硬编码写死key
api_key=os.getenv('aliQwen-api'), # 平台提供的API-KEY
base_url='https://dashscope.aliyuncs.com/compatible-mode/v1'
)
# 子链1提示词
prompt1 = ChatPromptTemplate.from_messages([
('system', '你是一个知识渊博的计算机专家,请用中文简短回答我提出的问题'),
('human', '请简短介绍什么是{topic}')
])
# 子链1解析器
parser1 = StrOutputParser()
# 子链1:生成内容
chain1 = prompt1 | model | parser1
result1 = chain1.invoke({'topic':'langchain'})
logger.info(result1)
# 子链2提示词
prompt2 = ChatPromptTemplate.from_messages([
('system', '你是一个翻译助手,请将用户输入内容翻译为英文'),
('human', '{input}')
])
# 子链2解析器
parser2 = StrOutputParser()
# 子链1:翻译内容
chain2 = prompt2 | model | parser2
# 组合一个复合chain,使用lambda函数将chain1执行结果content内容添加input键作为参数传递给chain2
full_chain = chain1 | (lambda content:{'input':content})|chain2
full_chain1 = chain1 |chain2
# 调用复合链
result = full_chain.invoke({'topic':'langchain'})
result1 = full_chain1.invoke({'topic':'langchain'})
logger.info(result)
print('*'*100)
logger.info(result1)
RunnableParallel-并行链
在 Langchain 中,创建并行链(Parallel Chains),是指同时运行多个子链(Chain),
并在它们都完成后汇总结果。
python
'''
RunnableParallel-并行链
在langchain中,创建并行链(Parallel Chains),是指同时运行多个子链(chain),并在他们都完成后汇总结果
**作用**,同时执行多个runnable,合并结果
'''
from langchain.chat_models import init_chat_model
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableParallel
from loguru import logger
import os
# 初始化模型
model = init_chat_model(
model='qwen-plus',
model_provider='openai',
# 硬编码写死key
api_key=os.getenv('aliQwen-api'), # 平台提供的API-KEY
base_url='https://dashscope.aliyuncs.com/compatible-mode/v1'
)
# 并行链1提示词
prompt1 = ChatPromptTemplate.from_messages([
('system', '你是一个知识渊博的计算机专家,请用中文简短回答我提出的问题'),
('human', '请简短介绍什么是{topic}')
])
# 并行链1解析器
parser1 = StrOutputParser()
# 并行链1:生成中文结果
chain1 = prompt1 | model | parser1
# 并行链2提示词
prompt2 = ChatPromptTemplate.from_messages([
('system', '你是一个知识渊博的计算机专家,请用英文简短回答我提出的问题'),
('human', '请简短介绍什么是{topic}')
])
# 子链2解析器
parser2 = StrOutputParser()
# 子链1:生成英文结果
chain2 = prompt2 | model | parser2
# 创建并行链,用于同时处理多个语言处理链
parallel_chain = RunnableParallel({
'chinese': chain1,
'english': chain2
})
# 调用复合链
result = parallel_chain.invoke({'topic': 'langchain'})
logger.info(result)
RunnableLambda-函数链
函数转可执行链,
将普通Python函数融入Runnable流程
记忆缓存(和Runnable相关)
是什么:上下文记忆缓存,存储之前对话的内容,从而提供连贯和个性化回复
能解决什么问题:前面说的话和后面的话语相关联,即前言搭后语
为什么要使用:解决固有的上下文遗忘问题,实现连贯且智能
内存版本:(生产上用不上,自行查找文档)
持久化版本:此处以RedisStack为例
|------|-----------------------------|--------------|
| 组件 | RedisStack | 原生Redis |
| 数据结构 | 增加JSON、图、时间序列、概率结构等高级类型 | 字符串、列表、集合、哈希 |
| 查询能力 | 支持全文搜索、向量搜索、图查询、JSON查询 | 仅限键值查询 |
| 使用场景 | 实时推荐、时序分析、知识图谱、文档数据库、AI向量检索 | 缓存、消息队列、计数器等 |
| 开发体验 | 提供RedisInsight和对象映射库,开发效率更高 | 命令行操作,手动拼接逻辑 |
Redis核心组件:RedisSearch、RedisJSON、RedisGraph、RedisBloom
RedisStack = 原生Redis + 搜索 + 图 + 时间序列 + JSON + 概率结构 + 可视化工具 + 开发框架支持
代码案例
环境验证
python
# pip install redis==7.2.0
# 尝试导入 redis 包
import redis
# 验证包版本(无报错即为导入成功)
print(redis.__version__)
# 极简 redis 导入测试脚本
try:
# 导入 redis 包
import redis
print("✅ redis 包导入成功!")
print(f"✅ redis 包版本:{redis.__version__}")
except ModuleNotFoundError:
print("❌ 未找到 redis 包,请先安装!")
except Exception as e:
print(f"❌ redis 包导入异常:{e}")
"""
7.2.0
✅ redis 包导入成功!
✅ redis 包版本:7.2.0
"""
python
from langchain_community.chat_message_histories import RedisChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableConfig
from langchain.chat_models import init_chat_model
import os
import redis # 导入原生Redis库
from loguru import logger
REDIS_URL = 'redis://117.72.80.97:6379'
# 创建原生Redis客户端,decode_responses 控制Redis返回数据的类型:False返回字节串,True返回字符串
redis_client = redis.Redis.from_url(REDIS_URL,decode_responses=True)
# 初始化模型
model = init_chat_model(
model='qwen-plus',
model_provider='openai',
api_key=os.getenv('aliQwen-api'), # 平台提供的API-KEY
temperature=0.0,
base_url='https://dashscope.aliyuncs.com/compatible-mode/v1'
)
# 创建提示词模板
prompt = ChatPromptTemplate.from_messages([
MessagesPlaceholder('history'),
('human', '{question}')
])
def get_session_history(session_id: str) ->RedisChatMessageHistory:
'''创建或者获取历史会话(使用Redis)'''
# 创建Redis历史对象
history = RedisChatMessageHistory(
session_id=session_id,
url=REDIS_URL,
# ttl=3600 # 注释:关闭自动过期,避免重启后数据被清洗
)
return history
# 创建带历史的链
chain = RunnableWithMessageHistory(
prompt | model | StrOutputParser(),
get_session_history,
input_messages_key='question',
history_messages_key='history'
)
# 配置
# session_id 就是登录大模型的各自账号,类似登录手机号码,各不相同
config = RunnableConfig(configurable={'session_id': 'user-001'})
# 主循环
print('开始对话(输入"quit"退出)')
while True:
question = input('\n输入问题:')
if question.lower() in ['quit', 'exit', 'q']:
break
response = chain.invoke({'question': question},config)
logger.info(f'AI回答{response}')
# 等同于redis-cli的save命令,强制写入dump.rdb
redis_client.save()
Tools (Function Calling)工具调用
是什么:通过tool工具机制可以使得模型具有'调用外部函数'的能力,是的其可以与外部系统、API或者自定义函数进行交互,从而完成仅靠文本生成无法完成的任务(LLM本身不执行函数,只是知道调用哪个函数和如何调用),简单来说就是LLM的外部工具类
能解决什么问题:可以解决大模型不具备访问数据库、调用API的能力,不执行代码或者文件操作,无法实时访问互联网或者动态数据的问题
为什么要使用:比如希望通过大模型获取某地实时的天气,大模型是基于过往数据的预训练,对于之后的数据则不知道,可以结合tool工具获取最新的数据,可以使用工具访问数据库,调用API,执行文件和代码
|---------------|-------------------|-----------------------------------------------|
| 属性 | 类型 | 描述 |
| name | str | 必选,在提供给LLM或者Agent工具中必须是唯一的 |
| description | str | 可选但建议,描述工具的功能,LLM或Agent将使用此描述作为上下文工具的使用 |
| args_schema | PydanticBaseModel | 可选但建议,可用于提供更多信息(例如few-shot实例)或验证预期参数 |
| return_direct | boolean | 仅对Agent相关,当为True时,在调用给工具后,Agent将停止并将结果直接返回给用户 |
[Tool常用属性]
