系列目标:30 天从 LangChain 入门到企业级部署
今日任务:理解 Callback 作用 → 掌握自定义 Callback → 实现“调用日志 + 耗时统计 + 敏感词拦截”三大实战功能!
🔍 一、为什么需要 Callback?
当你在生产环境部署 AI 应用时,会面临这些问题:
- 调试困难:AI 返回错误,但不知道中间哪步出问题
- 无法监控:不知道 LLM 调用耗时、token 消耗
- 安全盲区:用户输入了敏感信息,却没被记录
- 成本失控:不知道哪个功能最“烧 token”
Callback 就是 LangChain 提供的“全链路监听器” —— 它能在以下时机触发回调:
- LLM 开始/结束调用
- Chain 开始/结束执行
- Tool 被调用
- Agent 做出决策
✅ 今天,我们就用 Callback 打造一个“可观测、可审计、可拦截”的 AI 系统!
🧱 二、LangChain Callback 核心事件
表格
| 事件 | 触发时机 |
|---|---|
on_llm_start / on_llm_end | LLM 调用开始/结束 |
on_chain_start / on_chain_end | Chain 执行开始/结束 |
on_tool_start / on_tool_end | Tool 被调用开始/结束 |
on_agent_action | Agent 决定调用某个 Tool |
on_agent_finish | Agent 输出最终答案 |
💡 所有 Runnable(Chain/Agent/Tool/LLM)都支持 Callback!
🛠️ 三、动手实践 1:基础日志 Callback
步骤 1:创建自定义 Callback Handler
# day14_callbacks.py
import time
from langchain_core.callbacks import BaseCallbackHandler
class SimpleLoggerCallback(BaseCallbackHandler):
def on_llm_start(self, serialized, prompts, **kwargs):
print(f"🧠 LLM 调用开始,prompt 长度: {len(prompts[0])}")
def on_llm_end(self, response, **kwargs):
tokens = response.llm_output.get("token_usage", {})
total = tokens.get("total_tokens", "N/A")
print(f"✅ LLM 调用结束,总 token: {total}")
def on_chain_start(self, serialized, inputs, **kwargs):
print(f"🔗 Chain 开始执行,输入: {inputs}")
def on_chain_end(self, outputs, **kwargs):
print(f"🔚 Chain 执行结束,输出: {outputs}")
步骤 2:在 Chain 中使用 Callback
from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate
llm = ChatOllama(model="qwen:7b", temperature=0)
prompt = ChatPromptTemplate.from_template("翻译成英文:{text}")
chain = prompt | llm
# 调用时传入 callbacks
response = chain.invoke(
{"text": "你好,世界!"},
config={"callbacks": [SimpleLoggerCallback()]}
)
print("最终结果:", response.content)
▶️ 输出示例:
🔗 Chain 开始执行,输入: {'text': '你好,世界!'}
🧠 LLM 调用开始,prompt 长度: 42
✅ LLM 调用结束,总 token: 35
🔚 Chain 执行结束,输出: AIMessage(content='Hello, world!')
最终结果: Hello, world!
✅ 成功捕获全流程!
🛠️ 四、动手实践 2:性能监控 Callback(记录耗时)
class TimingCallback(BaseCallbackHandler):
def __init__(self):
self.start_time = None
def on_chain_start(self, serialized, inputs, **kwargs):
self.start_time = time.time()
print("⏱️ 开始计时...")
def on_chain_end(self, outputs, **kwargs):
elapsed = time.time() - self.start_time
print(f"⏱️ 总耗时: {elapsed:.2f} 秒")
💡 可扩展为 Prometheus 指标上报,用于 Grafana 监控。
🛠️ 五、动手实践 3:安全审计 Callback(敏感词拦截)
class SecurityAuditCallback(BaseCallbackHandler):
SENSITIVE_WORDS = ["密码", "身份证", "银行卡"]
def on_chain_start(self, serialized, inputs, **kwargs):
text = str(inputs)
for word in self.SENSITIVE_WORDS:
if word in text:
raise ValueError(f"⚠️ 检测到敏感词:{word}!请求已拦截。")
def on_tool_start(self, serialized, input_str, **kwargs):
# 同样检查 Tool 输入
for word in self.SENSITIVE_WORDS:
if word in input_str:
raise ValueError(f"⚠️ Tool 调用含敏感词:{word}!")
🔒 安全第一:在生产环境中,这是防止数据泄露的关键防线!
🤖 六、在 Agent 中使用 Callback
Agent 的 Callback 使用方式完全相同:
from langchain.agents import AgentExecutor, create_react_agent
from langchain_community.tools import DuckDuckGoSearchRun
tools = [DuckDuckGoSearchRun()]
agent = create_react_agent(llm, tools, create_react_agent.get_default_prompt())
executor = AgentExecutor(agent=agent, tools=tools, verbose=False)
# 同时注册多个 Callback!
callbacks = [
SimpleLoggerCallback(),
TimingCallback(),
SecurityAuditCallback()
]
try:
response = executor.invoke(
{"input": "杭州今天的天气如何?"},
config={"callbacks": callbacks}
)
print("Agent 最终回答:", response["output"])
except ValueError as e:
print(e)
✅ 所有事件都会被监听,包括 Tool 调用!
📊 七、高级用法:全局 Callback(自动生效)
不想每次手动传 callbacks?可以用 全局回调:
from langchain.globals import set_debug, set_verbose
from langchain.callbacks import StdOutCallbackHandler
# 全局打印详细日志(开发用)
set_debug(True)
# 或注册全局 handler
import langchain
langchain.callbacks.manager.tracing_v2_callback_var.set(
[StdOutCallbackHandler()]
)
⚠️ 注意:全局 Callback 会影响所有 LangChain 调用,生产环境慎用。
⚠️ 八、注意事项 & 最佳实践
表格
| 场景 | 建议 |
|---|---|
| 高并发服务 | Callback 逻辑要轻量,避免阻塞 |
| 日志存储 | 将日志写入文件或 ELK,而非 print |
| 敏感信息脱敏 | 在 on_llm_end 中对 output 做脱敏处理 |
| 成本分析 | 从 response.llm_output["token_usage"] 提取 token 数 |
| 异常处理 | Callback 内部异常会中断主流程,务必 try-catch |
💡 生产架构建议:
- 开发环境:开启
verbose=True+StdOutCallbackHandler- 生产环境:自定义 Callback 写入日志系统 + 监控告警
📦 九、配套代码结构
langchain-30-days/
└── day14/
└── callback_handlers.py # 日志 + 耗时 + 安全审计三大 Callback
📝 十、今日小结
- ✅ 理解了 Callback 在可观测性中的核心价值
- ✅ 学会了继承
BaseCallbackHandler自定义监听逻辑 - ✅ 实现了日志、性能监控、安全审计三大实用功能
- ✅ 掌握了在 Chain 和 Agent 中使用 Callback 的方法
- ✅ 知道了全局 Callback 与局部 Callback 的区别
🎯 明日预告:Day 15 —— 异步与流式响应!让 AI 边思考边输出,打造丝滑用户体验!