LangChain Callbacks 与事件驱动架构
一、概念速查
核心概念
| 概念 | 定义 |
|---|---|
| Callback | LangChain 执行过程中触发的生命周期事件的回调函数 |
| BaseCallbackHandler | 所有回调处理器的抽象基类,定义各事件的接口 |
| CallbackManager | 事件分发中心,管理一组 CallbackHandler 的注册与调用 |
| 事件驱动架构 | 执行流程中异步/同步地产生事件,由注册的处理器消费的模式 |
BaseCallbackHandler 核心接口
| 事件方法 | 触发时机 | 典型场景 |
|---|---|---|
on_llm_start |
LLM 调用开始时 | 记录请求 prompt、计费 |
on_llm_end |
LLM 调用完成时 | 记录响应 token 数、延迟 |
on_llm_error |
LLM 调用抛出异常时 | 告警、重试逻辑 |
on_chat_model_start |
ChatModel 调用开始时 | 同上,仅 ChatModel 路径 |
on_chain_start |
RunnableChain 开始时 | 追踪链路入口 |
on_chain_end |
RunnableChain 完成时 | 记录链路输出 |
on_chain_error |
RunnableChain 异常时 | 链路级告警 |
on_tool_start |
Tool 调用开始时 | 审计工具参数 |
on_tool_end |
Tool 调用完成时 | 记录工具输出 |
on_tool_error |
Tool 抛出异常时 | 工具级告警 |
on_retry |
Retry 机制触发重试时 | 监控重试频率 |
on_text |
输出文本时 | 日志记录 |
on_agent_action |
Agent 决定执行一个 Action 时 | Agent 步骤追踪 |
on_agent_finish |
Agent 完成全部步骤时 | Agent 最终输出 |
内置处理器
| 处理器 | 作用 | 依赖 |
|---|---|---|
StdOutCallbackHandler |
将事件输出到 stdout | langchain-core |
FileCallbackHandler |
将事件写入文件 | langchain-core |
ConsoleCallbackHandler |
LangChain CLI 控制台输出 | langchain-cli |
二、底层原理
2.1 事件分发机制
CallbackManager 内部维护一个 handlers: list[BaseCallbackHandler] 列表。当一个事件被触发时(例如 LLM 开始调用),CallbackManager.on_llm_start() 会遍历所有注册的 handler,依次调用其 on_llm_start() 方法:
#mermaid-svg-RqQmiM3h9RZk0O1n{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RqQmiM3h9RZk0O1n .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RqQmiM3h9RZk0O1n .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RqQmiM3h9RZk0O1n .error-icon{fill:#552222;}#mermaid-svg-RqQmiM3h9RZk0O1n .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RqQmiM3h9RZk0O1n .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RqQmiM3h9RZk0O1n .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RqQmiM3h9RZk0O1n .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RqQmiM3h9RZk0O1n .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RqQmiM3h9RZk0O1n .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RqQmiM3h9RZk0O1n .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RqQmiM3h9RZk0O1n .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RqQmiM3h9RZk0O1n .marker.cross{stroke:#333333;}#mermaid-svg-RqQmiM3h9RZk0O1n svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RqQmiM3h9RZk0O1n p{margin:0;}#mermaid-svg-RqQmiM3h9RZk0O1n .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RqQmiM3h9RZk0O1n .cluster-label text{fill:#333;}#mermaid-svg-RqQmiM3h9RZk0O1n .cluster-label span{color:#333;}#mermaid-svg-RqQmiM3h9RZk0O1n .cluster-label span p{background-color:transparent;}#mermaid-svg-RqQmiM3h9RZk0O1n .label text,#mermaid-svg-RqQmiM3h9RZk0O1n span{fill:#333;color:#333;}#mermaid-svg-RqQmiM3h9RZk0O1n .node rect,#mermaid-svg-RqQmiM3h9RZk0O1n .node circle,#mermaid-svg-RqQmiM3h9RZk0O1n .node ellipse,#mermaid-svg-RqQmiM3h9RZk0O1n .node polygon,#mermaid-svg-RqQmiM3h9RZk0O1n .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RqQmiM3h9RZk0O1n .rough-node .label text,#mermaid-svg-RqQmiM3h9RZk0O1n .node .label text,#mermaid-svg-RqQmiM3h9RZk0O1n .image-shape .label,#mermaid-svg-RqQmiM3h9RZk0O1n .icon-shape .label{text-anchor:middle;}#mermaid-svg-RqQmiM3h9RZk0O1n .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RqQmiM3h9RZk0O1n .rough-node .label,#mermaid-svg-RqQmiM3h9RZk0O1n .node .label,#mermaid-svg-RqQmiM3h9RZk0O1n .image-shape .label,#mermaid-svg-RqQmiM3h9RZk0O1n .icon-shape .label{text-align:center;}#mermaid-svg-RqQmiM3h9RZk0O1n .node.clickable{cursor:pointer;}#mermaid-svg-RqQmiM3h9RZk0O1n .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RqQmiM3h9RZk0O1n .arrowheadPath{fill:#333333;}#mermaid-svg-RqQmiM3h9RZk0O1n .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RqQmiM3h9RZk0O1n .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RqQmiM3h9RZk0O1n .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RqQmiM3h9RZk0O1n .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RqQmiM3h9RZk0O1n .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RqQmiM3h9RZk0O1n .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RqQmiM3h9RZk0O1n .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RqQmiM3h9RZk0O1n .cluster text{fill:#333;}#mermaid-svg-RqQmiM3h9RZk0O1n .cluster span{color:#333;}#mermaid-svg-RqQmiM3h9RZk0O1n div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RqQmiM3h9RZk0O1n .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RqQmiM3h9RZk0O1n rect.text{fill:none;stroke-width:0;}#mermaid-svg-RqQmiM3h9RZk0O1n .icon-shape,#mermaid-svg-RqQmiM3h9RZk0O1n .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RqQmiM3h9RZk0O1n .icon-shape p,#mermaid-svg-RqQmiM3h9RZk0O1n .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RqQmiM3h9RZk0O1n .icon-shape .label rect,#mermaid-svg-RqQmiM3h9RZk0O1n .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RqQmiM3h9RZk0O1n .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RqQmiM3h9RZk0O1n .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RqQmiM3h9RZk0O1n :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} RunnableChain.invoke()
CallbackManager.on_chain_start()
Handler1.on_chain_start()
Handler2.on_chain_start()
Handler3.on_chain_start()
LLM.invoke()
CallbackManager.on_llm_start()
Handler1.on_llm_start()
Handler2.on_llm_start()
Handler3.on_llm_start()
CallbackManager.on_llm_end()
Handler1.on_llm_end()
Handler2.on_llm_end()
Handler3.on_llm_end()
CallbackManager.on_chain_end()
Handler1.on_chain_end()
Handler2.on_chain_end()
Handler3.on_chain_end()
每个 Runnable(Chain、LLM、Tool、Retriever 等)都持有自己的 CallbackManager 实例。当外层 Runnable 调用内层 Runnable 时,callback 继承链会自动传递------子 Runnable 会继承父 Runnable 的 callback handlers,同时可以追加自己的 handler。
2.2 继承链机制
LangChain 通过 RunnableConfig 中的 callbacks 字段传递 handler。当一个 chain 包含一个 LLM 调用时:
python
# langchain>=0.3.0
from langchain_core.runnables import RunnableConfig
config = RunnableConfig(callbacks=[my_handler])
chain.invoke({"input": "hello"}, config=config)
# 内部 LLM 自动继承 config 中的 callbacks
继承链的实现路径:
Runnable的invoke()从RunnableConfig提取callbacks- 如果
RunnableConfig没有显式传callbacks,从 parent run 继承 CallbackManager新建时合并 parent 的 handlers 与当前配置的 handlers- 事件冒泡:内层事件先通知本地 handler,再通知 parent handler
#mermaid-svg-h6jIuDYyQBMtPoSa{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-h6jIuDYyQBMtPoSa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-h6jIuDYyQBMtPoSa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-h6jIuDYyQBMtPoSa .error-icon{fill:#552222;}#mermaid-svg-h6jIuDYyQBMtPoSa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-h6jIuDYyQBMtPoSa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-h6jIuDYyQBMtPoSa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-h6jIuDYyQBMtPoSa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-h6jIuDYyQBMtPoSa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-h6jIuDYyQBMtPoSa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-h6jIuDYyQBMtPoSa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-h6jIuDYyQBMtPoSa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-h6jIuDYyQBMtPoSa .marker.cross{stroke:#333333;}#mermaid-svg-h6jIuDYyQBMtPoSa svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-h6jIuDYyQBMtPoSa p{margin:0;}#mermaid-svg-h6jIuDYyQBMtPoSa .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-h6jIuDYyQBMtPoSa .cluster-label text{fill:#333;}#mermaid-svg-h6jIuDYyQBMtPoSa .cluster-label span{color:#333;}#mermaid-svg-h6jIuDYyQBMtPoSa .cluster-label span p{background-color:transparent;}#mermaid-svg-h6jIuDYyQBMtPoSa .label text,#mermaid-svg-h6jIuDYyQBMtPoSa span{fill:#333;color:#333;}#mermaid-svg-h6jIuDYyQBMtPoSa .node rect,#mermaid-svg-h6jIuDYyQBMtPoSa .node circle,#mermaid-svg-h6jIuDYyQBMtPoSa .node ellipse,#mermaid-svg-h6jIuDYyQBMtPoSa .node polygon,#mermaid-svg-h6jIuDYyQBMtPoSa .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-h6jIuDYyQBMtPoSa .rough-node .label text,#mermaid-svg-h6jIuDYyQBMtPoSa .node .label text,#mermaid-svg-h6jIuDYyQBMtPoSa .image-shape .label,#mermaid-svg-h6jIuDYyQBMtPoSa .icon-shape .label{text-anchor:middle;}#mermaid-svg-h6jIuDYyQBMtPoSa .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-h6jIuDYyQBMtPoSa .rough-node .label,#mermaid-svg-h6jIuDYyQBMtPoSa .node .label,#mermaid-svg-h6jIuDYyQBMtPoSa .image-shape .label,#mermaid-svg-h6jIuDYyQBMtPoSa .icon-shape .label{text-align:center;}#mermaid-svg-h6jIuDYyQBMtPoSa .node.clickable{cursor:pointer;}#mermaid-svg-h6jIuDYyQBMtPoSa .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-h6jIuDYyQBMtPoSa .arrowheadPath{fill:#333333;}#mermaid-svg-h6jIuDYyQBMtPoSa .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-h6jIuDYyQBMtPoSa .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-h6jIuDYyQBMtPoSa .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-h6jIuDYyQBMtPoSa .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-h6jIuDYyQBMtPoSa .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-h6jIuDYyQBMtPoSa .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-h6jIuDYyQBMtPoSa .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-h6jIuDYyQBMtPoSa .cluster text{fill:#333;}#mermaid-svg-h6jIuDYyQBMtPoSa .cluster span{color:#333;}#mermaid-svg-h6jIuDYyQBMtPoSa div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-h6jIuDYyQBMtPoSa .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-h6jIuDYyQBMtPoSa rect.text{fill:none;stroke-width:0;}#mermaid-svg-h6jIuDYyQBMtPoSa .icon-shape,#mermaid-svg-h6jIuDYyQBMtPoSa .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-h6jIuDYyQBMtPoSa .icon-shape p,#mermaid-svg-h6jIuDYyQBMtPoSa .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-h6jIuDYyQBMtPoSa .icon-shape .label rect,#mermaid-svg-h6jIuDYyQBMtPoSa .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-h6jIuDYyQBMtPoSa .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-h6jIuDYyQBMtPoSa .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-h6jIuDYyQBMtPoSa :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} parent_config.callbacks
Chain CallbackManager
LLM CallbackManager
LLM.on_llm_start
本地Handler
继承自Chain的Handler
Chain.on_chain_start
继承自...的Handler
2.3 同步与异步双通道
BaseCallbackHandler 同时定义同步和异步接口:
python
# langchain>=0.3.0
from langchain_core.callbacks import BaseCallbackHandler
class MyHandler(BaseCallbackHandler):
def on_llm_start(self, serialized, prompts, **kwargs):
# 同步路径
pass
async def on_llm_start(self, serialized, prompts, **kwargs):
# 异步路径
pass
CallbackManager 会在同步执行链(invoke)中只调用同步方法,在异步执行链(ainvoke)中只调用异步方法。如果处理器只实现了同步方法,异步路径会自动降级为同步调用。
2.4 构造函数传参 vs 运行时传参
传参方式有两种:
python
# langchain>=0.3.0
from langchain_openai import ChatOpenAI
from langchain_core.callbacks import StdOutCallbackHandler
# 方式一:构造时传入(生命周期绑定)
model = ChatOpenAI(
model="gpt-4o-mini",
callbacks=[StdOutCallbackHandler()],
)
# 方式二:运行时传入(每次调用独立)
model.invoke("你好", config={"callbacks": [StdOutCallbackHandler()]})
方式一适用于固定 logger,方式二适用于请求级别的追踪 ID 注入。
三、架构设计原则
3.1 处理器应当无状态或幂等
Callback handler 可能在多线程环境下被共享。如果 handler 持有状态(如累加 token 计数器),需要确保线程安全或使用 run_inline 隔离。
推荐模式:每个请求创建一个新的 handler 实例,在 on_chain_start 中注入 run_id:
python
# langchain>=0.3.0
import json
from uuid import UUID
from pathlib import Path
from langchain_core.callbacks import BaseCallbackHandler
class AuditHandler(BaseCallbackHandler):
"""审计日志处理器:记录每次 LLM 调用的入参和出参"""
def __init__(self, log_dir: str = "audit_logs"):
self.log_dir = Path(log_dir)
self.log_dir.mkdir(exist_ok=True)
def on_llm_start(self, serialized, prompts, **kwargs):
run_id = kwargs.get("run_id")
entry = {"event": "llm_start", "run_id": str(run_id), "prompts": prompts}
(self.log_dir / f"{run_id}.jsonl").write_text(
json.dumps(entry, ensure_ascii=False) + "\n", encoding="utf-8"
)
def on_llm_end(self, response, **kwargs):
run_id = kwargs.get("run_id")
entry = {"event": "llm_end", "run_id": str(run_id), "llm_output": response.llm_output}
with open(self.log_dir / f"{run_id}.jsonl", "a", encoding="utf-8") as f:
f.write(json.dumps(entry, ensure_ascii=False) + "\n")
def on_llm_error(self, error, **kwargs):
run_id = kwargs.get("run_id")
entry = {"event": "llm_error", "run_id": str(run_id), "error": repr(error)}
with open(self.log_dir / f"{run_id}.jsonl", "a", encoding="utf-8") as f:
f.write(json.dumps(entry, ensure_ascii=False) + "\n")
3.2 不要阻塞主流程
Callback handler 与主执行流程是同步关系------handler 的执行时间直接累加到 invoke() 的总耗时。如果需要异步持久化(写入数据库、发送 HTTP 请求),使用队列解耦:
python
# langchain>=0.3.0
import threading
from queue import Queue
from langchain_core.callbacks import BaseCallbackHandler
class AsyncLogHandler(BaseCallbackHandler):
"""写日志到远端,通过队列解耦不阻塞主流程"""
def __init__(self):
self.queue = Queue()
self._worker = threading.Thread(target=self._consumer, daemon=True)
self._worker.start()
def on_llm_start(self, serialized, prompts, **kwargs):
self.queue.put(("llm_start", serialized, prompts, kwargs.get("run_id")))
def on_llm_end(self, response, **kwargs):
self.queue.put(("llm_end", response.llm_output, kwargs.get("run_id")))
def _consumer(self):
while True:
event = self.queue.get()
# 实际写入远端存储
...
3.3 使用 tags 细粒度过滤
在生产环境中可能注册多个 handler,每个 handler 只关心特定链路:
python
# langchain>=0.3.0
chain = prompt | model | parser
chain.invoke(
{"input": "hello"},
config={
"tags": ["critical", "user-facing"],
"callbacks": [alert_handler],
},
)
在 handler 内检查 tags:
python
# langchain>=0.3.0
class AlertHandler(BaseCallbackHandler):
def on_chain_error(self, error, **kwargs):
tags = kwargs.get("tags", [])
if "critical" in tags:
send_alert(error) # 仅关键链路触发告警
3.4 典型场景组合
| 场景 | 推荐处理器 | 关键事件 |
|---|---|---|
| 调试开发 | StdOutCallbackHandler |
全部事件 |
| 生产日志 | 自定义 JsonLogHandler |
on_llm_start/end/error |
| 合规审计 | 自定义 AuditHandler |
on_tool_start/end(记录工具参数+输出) |
| 性能监控 | 自定义 MetricHandler |
on_llm_end(计算耗时 + token 数) |
| 异常告警 | 自定义 AlertHandler |
on_llm_error, on_chain_error, on_tool_error |
| 调用追踪 | LangSmithTracer(远端 handler) |
全部事件 |
3.5 与 LangSmith 的关系
LangSmith 的追踪功能本质上是注册了一个远端 CallbackHandler。当设置 LANGCHAIN_TRACING_V2=true 时,LangChain 自动将 LangSmithTracer(继承自 BaseCallbackHandler)注入到 CallbackManager 中:
#mermaid-svg-wAOXh3YD73NqjYqM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wAOXh3YD73NqjYqM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wAOXh3YD73NqjYqM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wAOXh3YD73NqjYqM .error-icon{fill:#552222;}#mermaid-svg-wAOXh3YD73NqjYqM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wAOXh3YD73NqjYqM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wAOXh3YD73NqjYqM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wAOXh3YD73NqjYqM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wAOXh3YD73NqjYqM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wAOXh3YD73NqjYqM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wAOXh3YD73NqjYqM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wAOXh3YD73NqjYqM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wAOXh3YD73NqjYqM .marker.cross{stroke:#333333;}#mermaid-svg-wAOXh3YD73NqjYqM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wAOXh3YD73NqjYqM p{margin:0;}#mermaid-svg-wAOXh3YD73NqjYqM .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-wAOXh3YD73NqjYqM .cluster-label text{fill:#333;}#mermaid-svg-wAOXh3YD73NqjYqM .cluster-label span{color:#333;}#mermaid-svg-wAOXh3YD73NqjYqM .cluster-label span p{background-color:transparent;}#mermaid-svg-wAOXh3YD73NqjYqM .label text,#mermaid-svg-wAOXh3YD73NqjYqM span{fill:#333;color:#333;}#mermaid-svg-wAOXh3YD73NqjYqM .node rect,#mermaid-svg-wAOXh3YD73NqjYqM .node circle,#mermaid-svg-wAOXh3YD73NqjYqM .node ellipse,#mermaid-svg-wAOXh3YD73NqjYqM .node polygon,#mermaid-svg-wAOXh3YD73NqjYqM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wAOXh3YD73NqjYqM .rough-node .label text,#mermaid-svg-wAOXh3YD73NqjYqM .node .label text,#mermaid-svg-wAOXh3YD73NqjYqM .image-shape .label,#mermaid-svg-wAOXh3YD73NqjYqM .icon-shape .label{text-anchor:middle;}#mermaid-svg-wAOXh3YD73NqjYqM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wAOXh3YD73NqjYqM .rough-node .label,#mermaid-svg-wAOXh3YD73NqjYqM .node .label,#mermaid-svg-wAOXh3YD73NqjYqM .image-shape .label,#mermaid-svg-wAOXh3YD73NqjYqM .icon-shape .label{text-align:center;}#mermaid-svg-wAOXh3YD73NqjYqM .node.clickable{cursor:pointer;}#mermaid-svg-wAOXh3YD73NqjYqM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wAOXh3YD73NqjYqM .arrowheadPath{fill:#333333;}#mermaid-svg-wAOXh3YD73NqjYqM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wAOXh3YD73NqjYqM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wAOXh3YD73NqjYqM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wAOXh3YD73NqjYqM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wAOXh3YD73NqjYqM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wAOXh3YD73NqjYqM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wAOXh3YD73NqjYqM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wAOXh3YD73NqjYqM .cluster text{fill:#333;}#mermaid-svg-wAOXh3YD73NqjYqM .cluster span{color:#333;}#mermaid-svg-wAOXh3YD73NqjYqM div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-wAOXh3YD73NqjYqM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wAOXh3YD73NqjYqM rect.text{fill:none;stroke-width:0;}#mermaid-svg-wAOXh3YD73NqjYqM .icon-shape,#mermaid-svg-wAOXh3YD73NqjYqM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wAOXh3YD73NqjYqM .icon-shape p,#mermaid-svg-wAOXh3YD73NqjYqM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wAOXh3YD73NqjYqM .icon-shape .label rect,#mermaid-svg-wAOXh3YD73NqjYqM .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wAOXh3YD73NqjYqM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wAOXh3YD73NqjYqM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wAOXh3YD73NqjYqM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} LangChain 应用
CallbackManager
StdOutCallbackHandler
自定义AuditHandler
LangSmithTracer
(远端CallbackHandler)
序列化 + POST
LangSmith API
(云端存储/可视化)
LangSmithTracer 在各事件回调中收集运行数据(prompt、response、token 计数、延迟、错误),序列化为 protobuf 或 JSON,异步 POST 到 LangSmith 后端。用户可在 LangSmith Web UI 中查看 trace 树、对比实验、分析成本。本质上,LangSmith 就是一个运行在远端的、自带存储和可视化能力的超级 CallbackHandler。
四、快速参考
完整导入路径
python
from langchain_core.callbacks import BaseCallbackHandler # langchain>=0.3.0
from langchain_core.callbacks import CallbackManager # langchain>=0.3.0
from langchain_core.callbacks import StdOutCallbackHandler # langchain>=0.3.0
from langchain_core.callbacks import FileCallbackHandler # langchain>=0.3.0
from langchain_core.callbacks import CallbackManagerForLLMRun
from langchain_core.runnables import RunnableConfig
最小自定义 handler 模板
python
# langchain>=0.3.0
from langchain_core.callbacks import BaseCallbackHandler
class MyHandler(BaseCallbackHandler):
def on_llm_start(self, serialized, prompts, **kwargs):
# serialized: dict, LLM 配置的序列化表示
# prompts: list[str], 传入的 prompt 列表
# kwargs: run_id, parent_run_id, tags, metadata 等
pass
def on_llm_end(self, response, **kwargs):
# response: LLMResult, 包含 generations 和 llm_output
# llm_output 中通常包含 token_usage
pass
def on_tool_start(self, serialized, input_str, **kwargs):
# input_str: str, 工具的输入(已转为字符串)
pass
def on_tool_end(self, output, **kwargs):
# output: str, 工具的输出
pass
注意事项
- Handler 中的异常不会中断主流程(LangChain 默认
raise_error=False),但可以通过配置CallbackManager(raise_error=True)改变 - 同一个 handler 实例不要在多线程 invoke 中共享可变状态
on_retry事件在重试发生前触发,可用来记录重试原因和次数run_id是UUID类型,可作为链路追踪的全局标识符