目录
一、通俗解释(举个🌰)
二、具体能干啥?
三、怎么用?(一句话说透)
四、小结
五、为什么Callbacks是LangChain的灵魂组件?
六、Callbacks核心API解析
1、 基础回调处理器
2、 标准实现方案对比
七、代码示例
代码一:回调函数定义和执行方式
代码二:不使用回调
代码三:使用回调函数
八、是否使用回调的区别
1、回调机制使用方式
2、执行流程差异
3、输出效果对比
4、代码结构差异
5、最佳实践建议
附:执行效果差异示意图
在 LangChain 中,回调函数就像给 AI 程序装了一个“事件监听器”或“自动小助手”。它的作用是:在 AI 执行任务的各个关键节点,自动触发你设定的操作。比如监控进度、记录日志,甚至实时显示生成结果。
一、通俗解释(举个🌰)
想象你在看厨师做菜:
- 厨师开始切菜 → 你记录开始时间(回调函数:
on_llm_start
) - 每切完一片菜 → 你数一片数量(回调函数:
on_llm_new_token
) - 菜切完了 → 你喊“上菜!”(回调函数:
on_llm_end
) - 切到手了 → 你立刻递创可贴(回调函数:
on_llm_error
)
这就是回调函数的作用:在 AI 做事的每个步骤里,帮你自动完成额外任务。
二、具体能干啥?
-
实时看进度
比如 AI 生成文字时,每输出一个词就显示到屏幕上(像打字效果)。 -
算钱省成本
自动统计 AI 用了多少“字数额度”(比如 OpenAI 按字数收费),帮你控制预算。 -
出错了能处理
如果 AI 调用网络接口失败,自动发邮件通知你,而不是让程序卡死。 -
存日志好复盘
把 AI 每次对话记录自动保存到文件,方便后期查看哪里出了问题。 - 监控与日志:回调函数用于在模型运行时捕获关键事件(如开始、结束、生成Token等),并输出调试信息。
- 应用场景:常用于调试、性能监控、流式输出处理等
三、怎么用?(一句话说透)
写一个“监听函数”,告诉 LangChain:“当 AI 开始干活/干完活/干到一半/出错时,就执行我这个函数”。
(比如:每次 AI 生成文字,就把内容实时推送到网页聊天框里)
四、小结
回调函数就是给 AI 装“监控”和“自动化工具”,让程序更智能、更可控。不需要懂代码细节,记住它能帮你自动响应事件就行。
五、为什么Callbacks是LangChain的灵魂组件?
在大语言模型应用开发中,回调机制扮演着至关重要的角色。LangChain通过Callbacks实现了:
-
全链路可观测性:实时追踪LLM调用、工具执行、任务链流转
-
执行过程干预:动态修改提示词、调整温度参数、过滤敏感内容
-
性能监控:统计token消耗、记录响应延迟、分析错误率
-
流式处理:实现逐token输出、实时进度展示等交互体验
通过下面的架构图可以看到,Callbacks贯穿整个LangChain执行生命周期:
六、Callbacks核心API解析
1、 基础回调处理器
class BaseCallbackHandler:"""处理LLM/Chain/Tool/Agent各阶段事件的抽象基类"""def on_llm_start(self, serialized, prompts, **kwargs):"""LLM开始生成时触发"""def on_llm_new_token(self, token, **kwargs):"""流式输出时每个token的生成事件"""def on_chain_end(self, outputs, **kwargs):"""任务链完成时收集最终结果"""# 共14个关键事件处理点...
2、 标准实现方案对比
配置方式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
显式回调列表 | 支持多处理器并行 | 需手动管理处理器实例 | 生产环境 |
verbose=True | 零配置快速启用 | 只能输出到stdout | 快速调试 |
自定义Handler | 完全定制处理逻辑 | 开发成本较高 | 特殊需求 |
七、代码示例
代码一:回调函数定义和执行方式
说明:该代码主要是颜色定义和执行回调函数的一种形式,不可运行。
from typing import Any, Dict, List, Unionclass BaseCallbackHandler:"""基础回调处理器,可用于处理来自langchain的回调。"""def on_llm_start(self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any) -> Any:"""当LLM开始运行时触发。"""def on_chat_model_start(self, serialized: Dict[str, Any], messages: List[List[BaseMessage]], **kwargs: Any # type: ignore) -> Any:"""当聊天模型开始运行时触发。"""def on_llm_new_token(self, token: str, **kwargs: Any) -> Any:"""生成新LLM token时触发。仅在启用流式传输时可用。"""def on_llm_end(self, response: LLMResult, **kwargs: Any) -> Any: # type: ignore"""当LLM运行结束时触发。"""def on_llm_error(self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any) -> Any:"""当LLM发生错误时触发。"""def on_chain_start(self, serialized: Dict[str, Any], inputs: Dict[str, Any], **kwargs: Any) -> Any:"""当任务链开始运行时触发。"""def on_chain_end(self, outputs: Dict[str, Any], **kwargs: Any) -> Any:"""当任务链运行结束时触发。"""def on_chain_error(self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any) -> Any:"""当任务链发生错误时触发。"""def on_tool_start(self, serialized: Dict[str, Any], input_str: str, **kwargs: Any) -> Any:"""当工具开始运行时触发。"""def on_tool_end(self, output: str, **kwargs: Any) -> Any:"""当工具运行结束时触发。"""def on_tool_error(self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any) -> Any:"""当工具发生错误时触发。"""def on_text(self, text: str, **kwargs: Any) -> Any:"""处理任意文本时触发。"""def on_agent_action(self, action: AgentAction, **kwargs: Any) -> Any: # type: ignore"""当代理执行动作时触发。"""def on_agent_finish(self, finish: AgentFinish, **kwargs: Any) -> Any: # type: ignore"""当代理结束运行时触发。"""
代码二:不使用回调
说明:该代码直接通过千问大模型进行推理,没有采用回调
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.chains import LLMChain#一、加载配置环境
load_dotenv()#二、初始化大模型
qwen=ChatOpenAI(model="qwen-max",api_key=os.getenv("DASHSCOPE_API_KEY"),openai_api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",temperature=0.0)#三、定义提示模板
prompt=ChatPromptTemplate.from_template("1+{number}=")#四、初始化链(不使用回调)
print("不使用回调函数")
chain=LLMChain(llm=qwen,prompt=prompt,)
print("返回结果:",chain.invoke({"number":2}))
运行结果
不使用回调函数
d:\computer_soft\Microsoft VS Code\python_project\my_app\AI大模型应用开发\A3\A3.2_LangChain原理\一、LangChain技术原理\2、核心组件\5、Callbacks\test.py:22: LangChainDeprecationWarning: The class `LLMChain` was deprecated in LangChain 0.1.17 and will be removed in 1.0. Use :meth:`~RunnableSequence, e.g., `prompt | llm`` instead.
chain=LLMChain(
返回结果: {'number': 2, 'text': '1+2=3'}
代码三:使用回调函数
说明:这段代码演示了LangChain中回调函数(Callbacks)和调试输出的核心用法,知识要点可概括如下:
两种启用回调的方式
方式一:显式绑定回调处理器
通过callbacks=[handler]
参数将StdOutCallbackHandler
附加到链(LLMChain
),运行时自动输出详细步骤日志。chain = LLMChain(..., callbacks=[handler]) # 手动绑定回调
方式二:启用
verbose
标志
设置verbose=True
时,LangChain默认启用StdOutCallbackHandler
,效果等同于方式一。chain2 = LLMChain(..., verbose=True) # 隐式启用回调
from langchain.callbacks import StdOutCallbackHandler
from langchain.chains import LLMChain
from langchain.chat_models import ChatOpenAI
from langchain.prompts import PromptTemplate
import os
from dotenv import load_dotenv#一、初始化配置环境
load_dotenv()# 二、初始化回调处理器
handler = StdOutCallbackHandler()# 三、初始化模型
llm = ChatOpenAI(model="qwen-max",api_key=os.getenv("DASHSCOPE_API_KEY"),openai_api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",temperature=0.0
)# 四、定义提示模板)
prompt = PromptTemplate.from_template("1 + {number} = ")# 五、初始化链(使用回调)
print("\n使用回调函数的方式")
chain = LLMChain(llm=llm, prompt=prompt, callbacks=[handler]) # 关键:添加 callbacks
# 使用verbose标志:然后,让我们使用`verbose`标志来实现相同的结果
chain2 = LLMChain(llm=llm, prompt=prompt, verbose=True)#六、输出结果
print("回调方式一:\n",chain.invoke({"number":2}))
print("回调方式二:\n",chain2.invoke({"number":2}))
运行结果
> Entering new LLMChain chain...
Prompt after formatting:
1 + 2 =
> Finished chain.
回调方式一:
{'number': 2, 'text': '1 + 2 = 3'}
> Entering new LLMChain chain...
Prompt after formatting:
1 + 2 => Finished chain.
回调方式二:
{'number': 2, 'text': '1 + 2 = 3'}
八、是否使用回调的区别
1、回调机制使用方式
以下是代码二和代码三的区别对比
对比维度 | 不使用回调 | 使用回调 |
---|---|---|
回调配置方式 | 先创建带回调的chain,后覆盖为无回调的chain | 同时展示两种回调方式:显式回调处理器 和 verbose标志 |
回调生效情况 | 最终实际未使用回调(被后续无回调chain覆盖) | 明确对比两种回调方式的效果 |
代码逻辑 | 存在逻辑矛盾:先配置回调后又覆盖 | 逻辑清晰:分别演示不同配置方式 |
2、执行流程差异
# 代码一流程
1. 创建带回调的chain(但后续被覆盖)
2. 重新创建无回调的chain(最终生效)
3. 执行无回调调用# 代码二流程
1. 创建两种配置的chain:
- 方式一:callbacks=[handler]
- 方式二:verbose=True
2. 分别执行两种调用
3、输出效果对比
回调方式 | 输出内容示例 |
---|---|
无回调 | 仅返回结果:{'number': 2, 'text': '1 + 2 = 3'} |
显式回调 | 打印LLM执行过程日志:> Entering new LLMChain chain... ... |
verbose=True | 与显式回调效果相同,底层使用默认的StdOutCallbackHandler |
4、代码结构差异
特性 | 不回调 | 使用回调 |
---|---|---|
代码注释 | 简单注释 | 分步骤详细注释(一、二、三...) |
演示目的 | 单一场景 | 对比两种回调配置方式 |
错误示例 | 包含配置覆盖错误 | 无错误示范 |
5、最佳实践建议
-
避免配置覆盖:不要重复初始化同名chain对象
-
回调选择原则:
-
需要自定义处理时使用
callbacks=[...]
-
快速调试时使用
verbose=True
(内部自动使用StdOutCallbackHandler)
-
-
生产环境推荐:使用显式回调处理器,便于扩展和自定义日志处理
附:执行效果差异示意图
代码一输出:
不使用回调函数
{'number': 2, 'text': '1 + 2 = 3'}
结果返回: {'number': 2, 'text': '1 + 2 = 3'}代码二输出:
使用回调函数的方式> Entering new LLMChain chain...
Prompt after formatting:
1 + 2 => Finished chain.
回调方式一:
{'number': 2, 'text': '1 + 2 = 3'}> Entering new LLMChain chain...
Prompt after formatting:
1 + 2 => Finished chain.
回调方式二:
{'number': 2, 'text': '1 + 2 = 3'}