【可能是全网最丝滑的LangChain教程】二十二、LangChain进阶之Callbacks(完结篇)

news2025/1/11 23:36:32

这是LangChain进阶教程的最后一篇,Let’s get it!!!

01 Callback介绍

在LangChain中,Callback 是一种非常重要的机制,它允许用户监听和处理在执行链式任务 (Chain) 过程中的各种事件。这包括但不限于开始执行、结束执行、异常处理等。Callback 可以帮助开发者追踪执行过程、调试问题、监控性能指标或者进行日志记录等。

基本概念

  • Callback 是一个可以接收事件通知的对象。
  • Callback 可以在不同的执行阶段被触发,例如在语言模型 (LLM) 开始运行、结束运行、生成文本等时刻。
  • Callback 可以是同步或异步的。

核心组件

  • BaseCallbackHandler: 所有回调处理器的基础类。
  • CallbackManager: 负责管理多个 CallbackHandler 实例,控制它们的触发顺序和方式。(已标记废弃,不建议使用这个

常用回调处理器

  • StreamingStdOutCallbackHandler: 将流式输出直接打印到标准输出。
  • FileCallbackHandler: 将回调信息写入文件。
  • CustomCallbackHandler: 用户可以自定义自己的回调处理器。

如何使用 Callbacks

  • 通常通过 CallbackManager 来添加和管理 CallbackHandler 实例。(已标记废弃,不建议使用这个
  • 当创建 Chain 或者其他组件时,可以通过 callbacks 参数传入一个 CallbackManager 实例。

02 基础使用

这里以 StdOutCallbackHandler 为例:

from langchain_core.callbacks import StdOutCallbackHandler
from langchain.chains import LLMChain
from langchain_core.prompts import PromptTemplate

handler = StdOutCallbackHandler()
prompt = PromptTemplate.from_template("1 + {number} = ")

# 初始化chain的时候设置callback
chain = LLMChain(llm=llm_model, prompt=prompt, callbacks=[handler])
chain.invoke({"number":2})

# 或者直接设置verbose=True,内部依然调用StdOutCallbackHandler
# chain = LLMChain(llm=llm_model, prompt=prompt, verbose=True)
# chain.invoke({"number":2})


# 或者在调用时设置
# chain = LLMChain(llm=llm_model, prompt=prompt)
# chain.invoke({"number":2}, {"callbacks":[handler]})


# 或者通过config参数设置
# config = {
#     'callbacks' : [handler]
# }
# chain = LLMChain(llm=llm_model, prompt=prompt)
# chain.invoke({"number":2}, config=config)


# 4种方式打印回调如下:
"""
> Entering new LLMChain chain...
Prompt after formatting:
1 + 2 = 

> Finished chain.
"""

上面代码中,我们使用的是 invoke 来执行 chain 的调用,如果我们使用 ainvoke,则建议使用 AsyncCallbackHandler 来实现回调。

Custom callback handlers

要创建自定义回调处理程序,我们需要确定我们希望回调处理程序处理的事件,以及我们希望回调处理程序在触发事件时执行的操作。然后,我们需要做的就是将回调处理程序附加到对象上,作为构造器回调或请求回调。

示例代码如下:

import asyncio
from typing import Any, Dict, List, Optional

from langchain.callbacks.base import AsyncCallbackHandler, BaseCallbackHandler
from langchain_core.messages import HumanMessage
from langchain_core.outputs import LLMResult


class MyCustomSyncHandler(BaseCallbackHandler):
    def on_llm_new_token(self, token: str, **kwargs) -> None:
        print(f"Sync handler being called in a `thread_pool_executor`: token: {token}")


class MyCustomAsyncHandler(AsyncCallbackHandler):
    """Async callback handler that can be used to handle callbacks from langchain."""

    async def on_llm_start(
        self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any
    ) -> None:
        """Run when chain starts running."""
        print("zzzz....")
        await asyncio.sleep(0.3)
        class_name = serialized["name"]
        print("Hi! I just woke up. Your llm is starting")

    async def on_llm_end(self, response: LLMResult, **kwargs: Any) -> None:
        """Run when chain ends running."""
        print("zzzz....")
        await asyncio.sleep(0.3)
        print("Hi! I just woke up. Your llm is ending")


# To enable streaming, we pass in `streaming=True` to the ChatModel constructor
# Additionally, we pass in a list with our custom handler
chat_model = ChatTongyi(dashscope_api_key='sk-da184735f2454123ab213cea8d39e9ce',streaming=True,callbacks=[MyCustomSyncHandler(), MyCustomAsyncHandler()],)


await chat_model.agenerate([[HumanMessage(content="Tell me a joke")]])

File logging

from langchain_core.callbacks import FileCallbackHandler, StdOutCallbackHandler
from langchain_core.prompts import PromptTemplate
from loguru import logger

logfile = "output.log"

logger.add(logfile, colorize=True, enqueue=True)
handler_1 = FileCallbackHandler(logfile)
handler_2 = StdOutCallbackHandler()

prompt = PromptTemplate.from_template("1 + {number} = ")

# this chain will both print to stdout (because verbose=True) and write to 'output.log'
# if verbose=False, the FileCallbackHandler will still write to 'output.log'
chain = prompt | llm_model

response = chain.invoke({"number": 2}, {"callbacks": [handler_1, handler_2]})
logger.info(response)

我们的日志文件打开后是乱码的,需要用第三方工具做一次转换

在这里插入图片描述

转换代码如下:

from ansi2html import Ansi2HTMLConverter
from IPython.display import HTML, display

with open("output.log", "r") as f:
    content = f.read()

conv = Ansi2HTMLConverter()
html = conv.convert(content, full=True)

display(HTML(html))

用 display 查看,最终效果如下(线上可用):

在这里插入图片描述

Tags

我们可以通过向 call()/run()/apply() 方法传递 tags 参数来向回调添加标签。这对于过滤日志很有用,例如,如果我们想记录对特定 LLMChain 发出的所有请求,可以添加一个标签,然后按该标签过滤您的日志。

示例代码如下:

from langchain.callbacks.base import AsyncCallbackHandler, BaseCallbackHandler
from typing import Any, Dict, List

from langchain.chains.llm import LLMChain
from langchain_community.llms.tongyi import Tongyi
from langchain_core.callbacks import BaseCallbackHandler
from langchain_core.prompts import PromptTemplate

class MyCustomHandler(BaseCallbackHandler):
    def on_llm_start(
        self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any
    ) -> Any:
        print(f"自定义回调,on_llm_start")
        
    def on_llm_end(
        self, serialized: Dict[str, Any], **kwargs: Any
    ) -> Any:
        print(f"自定义回调,on_llm_end")
        
    def on_llm_new_token(
        self, token: str, **kwargs: Any
    ) -> Any:
        print(f"自定义回调,on_llm_new_token")
    
    def on_chain_start(
        self,
        serialized: Dict[str, Any],
        inputs: Dict[str, Any],
        **kwargs: Any,
    ) -> Any:
        print(f"自定义回调,on_chain_start, {kwargs}")
        
    def on_chain_end(
        self,
        outputs: Dict[str, Any],
        **kwargs: Any,
    ) -> Any:
        print(f"自定义回调,on_chain_end, {kwargs}")
        
handler = MyCustomHandler()
prompt = PromptTemplate.from_template("1 + {number} = ")

chain = LLMChain(llm=llm_model, prompt=prompt,verbose=False, callbacks=[handler],tags=['custom-------'])
chain.invoke({"number":2})

"""
自定义回调,on_chain_start, {'run_id': UUID('91086ade-0121-4565-946e-8fadbb870f27'), 'parent_run_id': None, 'tags': ['custom-------'], 'metadata': {}, 'name': 'LLMChain'}
自定义回调,on_chain_end, {'run_id': UUID('91086ade-0121-4565-946e-8fadbb870f27'), 'parent_run_id': None, 'tags': ['custom-------']}
"""

Token counting

如果我们正在使用openai的相关模型,我们就能使用这个令牌计数的功能。

示例代码如下:

import asyncio

from langchain_community.callbacks import get_openai_callback
from langchain_openai import OpenAI

llm = OpenAI(temperature=0)
with get_openai_callback() as cb:
    llm.invoke("What is the square root of 4?")

total_tokens = cb.total_tokens
assert total_tokens > 0

with get_openai_callback() as cb:
    llm.invoke("What is the square root of 4?")
    llm.invoke("What is the square root of 4?")

assert cb.total_tokens == total_tokens * 2

# You can kick off concurrent runs from within the context manager
with get_openai_callback() as cb:
    await asyncio.gather(
        *[llm.agenerate(["What is the square root of 4?"]) for _ in range(3)]
    )

assert cb.total_tokens == total_tokens * 3

# The context manager is concurrency safe
task = asyncio.create_task(llm.agenerate(["What is the square root of 4?"]))
with get_openai_callback() as cb:
    await llm.agenerate(["What is the square root of 4?"])

await task
assert cb.total_tokens == total_tokens

03 总结与回顾

从2024年3月3日第一篇 LangChain 相关文章的发布,到现在 LangChain 最后一篇文章的发表,期间跨度已近半年,总共输出22篇文章。

从初级使用教程(7篇),到进阶理解教程(15篇),我们从知道 LangChain 到使用 LangChain 再到理解 LangChain,一步一步循序渐进的走来。

最初写这些文章的目的是为了“武装自己”,但是在看到各大平台上的读者留言后,渐渐的想把这些文章写好写透,因为我能从这些留言上看到“以前自己的影子”。

这生活已经很难了,难能可贵的是您能在百忙之中抽空阅读这些文章。如果能给到您一点小小的帮助,也是我非常喜闻乐见的😁

WX搜索公众号关注
在这里插入图片描述

以上内容依据官方文档编写,官方地址:https://python.langchain.com/docs/modules/callbacks

这真的是“进阶系列”的最后一篇文章了哦,我们下个系列再见。Love & Peace~

在这里插入图片描述

  • 【LangChain进阶教程】十四、LangChain进阶之Memory
  • 【LangChain进阶教程】十三、LangChain进阶之Chains
  • 【LangChain进阶教程】十二、LangChain进阶之Agents
  • 【LangChain进阶教程】十一、LangChain进阶之Tools

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2077883.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构(邓俊辉)学习笔记】串 03——KMP算法:记忆法

文章目录 1. 重复匹配的前缀2. 不变性3. 记忆力4. 预知力 1. 重复匹配的前缀 关于串匹配,包括蛮力算法在内,至少有30多种知名的算法,而接下来,就将介绍其中最为经典的 KMP 算法。这个算法之所以著名,不仅是由于它出自包…

Autosar(Davinci) --- ADT和IDT如何Mapping

前言 这里我们讲一下ADT如何与IDT进行Mapping 一、ADT为什么要与IDT进行Mapping 二、ADT和IDT如何Mapping 鼠标右键【type Mapping Sets】,选择【New Data type Mapping Set...】 打开之后,我们起一个名字【DemoTypeMapping】 然后选择【Data Type Maps】来将ADT与IDT进行m…

SpringBoot+Grafana+Prometheus+Docker-Compose 快速部署与JVM监控的快速入门的简单案例

1. Java项目 1.1 项目结构 1.2 pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"htt…

ThingsGateway:一款基于.NET8开源的跨平台高性能边缘采集网关

前言 今天大姚给大家分享一款基于.NET8开源的跨平台高性能边缘采集网关&#xff0c;提供底层PLC通讯库&#xff0c;通讯调试软件等&#xff0c;单机采集数据点位可达百万&#xff1a;ThingsGateway。 项目技术栈 后端技术栈&#xff1a;支持.NET 6/7/8&#xff0c;Sqlsugar&am…

爬虫使用优质代理:确保高效稳定的数据采集之道

爬虫使用优质代理的最佳实践 在进行网络爬虫时&#xff0c;使用优质代理就像是为你的爬虫装上了强劲的发动机&#xff0c;能够大幅提升数据抓取的效率和成功率。然而&#xff0c;选择和使用优质代理并非易事&#xff0c;今天我们就来探讨如何在爬虫中有效使用优质代理。 1. 什…

vue3组件封装系列-表格及分页-第二弹

第二弹来了&#xff0c;不知道有多少人是看过我的第一篇文章的&#xff0c;今天本来是没想更新的&#xff0c;但是现在项目正在验收期准备上线&#xff0c;闲着还不如来发发文。虽然这两天可能会高产&#xff0c;下一次高产就不知道是什么时候了。话不多说&#xff0c;先上图。…

OpenGuass under Ubuntu_22.04 install tutorial

今天开始短学期课程&#xff1a;数据库课程设计。今天9点左右在SL1108开课&#xff0c;听陈老师讲授了本次短学期课程的要求以及任务安排&#xff0c;随后讲解了国产数据库的三层架构的逻辑。配置了大半天才弄好&#xff0c;放一张成功的图片&#xff0c;下面开始记录成功的步骤…

数据融合的超速引擎——SeaTunnel

概览 SeaTunnel是一个由Apache软件基金会孵化的数据集成工具&#xff0c;专为应对大规模数据的快速处理而设计。它以高效的数据处理能力和简洁的架构&#xff0c;帮助企业在数据仓库构建、实时数据处理和数据迁移等场景下&#xff0c;实现数据流的无缝整合。SeaTunnel的设计理…

LDO工作原理与仿真

LDO工作原理与仿真 目录 LDO工作原理与仿真一、LDO内部电路组成1. 基准电压源&#xff08;Reference Voltage Source&#xff09;2. 误差放大器&#xff08;Error Amplifier&#xff09;3. 功率调整元件&#xff08;Power Adjustment Element&#xff09;4. 分压取样电路&#…

用于不平衡分类的 Bagging 和随机森林

用于不平衡分类的 Bagging 和随机森林 Bagging 是一种集成算法&#xff0c;它在训练数据集的不同子集上拟合多个模型&#xff0c;然后结合所有模型的预测。 [随机森林]是 bagging 的扩展&#xff0c;它也会随机选择每个数据样本中使用的特征子集。bagging 和随机森林都已被证…

【Word与WPS如何冻结首行首列及窗口】

1.Word如何冻结首行首列及窗口 microsoft word 中锁定表头是一项实用的功能&#xff0c;可让您在滚动文档时保持表头可见。这在处理大型文档或包含大量数据的表格时非常有用。php小编柚子将为您详细介绍 word 锁定表头位置的方法&#xff0c;帮助您轻松掌握这项实用技巧。 1.…

实体书商城小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;小说分类管理&#xff0c;小说信息管理&#xff0c;订单管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;小说信息&#xff0c;小说资讯&#xff0…

Qt_两种创建组件的方式-通过图形化-通过代码

文章目录 一、通过图形化的方式&#xff0c;在界面上创建一个控件&#xff0c;显示hello world1.打开UI设计界⾯2.拖拽控件⾄ ui 界⾯窗⼝并修改内容3.构建并运行 二、通过代码的方式&#xff0c;通过编写代码&#xff0c;在界面上创建控件&#xff0c;显示hello world在Widget…

手撕python之基本数据类型以及变量

​​​​​​1.基础概念 python就是将不同的数据划分成了不同的类型 就像我们生活中的数据有数字、字符等数据一样 小知识点&#xff1a; 注释&#xff1a;# 全体注释&#xff1a;AltF3 取消注释&#xff1a;AltF4 2.数值类型 数值类型概括 数值类型分为三种&#xff…

Cesium 展示——动态洪水淹没效果

文章目录 需求分析1. 引入插件2. 定义变量3. 开始绘制3.1 绘制点3.2 绘制线3.3 绘制面3.4 开始分析(第一种)3.5 开始分析(第二种)3.6 方法调用4. 整体代码其他需求 从低处到高处实现洪水淹没效果 分析 本篇文章对方法进行单独抽离,因此支持拿来即用,注意传参就可 1. …

宠物系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;商品信息管理&#xff0c;店主管理&#xff0c;猫狗查询管理&#xff0c;猫狗宠物社区&#xff0c;管理员管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&…

宝塔 出现 请使用正确的入口登录面板

目录 前言1. 问题所示2. 原理分析3. 解决方法 前言 记录实战中所有的问题导向、原理分析以及解决方法 1. 问题所示 在登录宝塔的时候&#xff0c;出现如下问题 请使用正确的入口登录面板 错误原因&#xff1a;当前新安装的已经开启了安全入口登录&#xff0c;新装机器都会随…

使用redis模拟cookie-session,例子:实现验证码功能

目录 在前后端分离架构中不建议使用cookie-session机制实现端状态识别 所以我们可以使用redis来模拟session-cookie机制 下面我们通过实现验证码的功能来举例 第一步&#xff1a;了解前端要我们返回的数据变量名字&#xff0c;变量类型 1.封装code,data成一个result类&…

多模态技术应用场景探析,景联文科技多模态数据测试平台推动多模态大模型技术突破

多模态技术应用场景探析&#xff0c;景联文科技多模态数据测试平台推动多模态大模型技术突破 在大语言模型背景下&#xff0c;多模态技术的发展已成为一个重要趋势。 Sora是OpenAI推出的多模态大模型&#xff0c;具备高级视频生成与编辑功能&#xff0c;支持长视频、多视角、多…

使用OpenCV库来捕获摄像头视频流,并按指定格式保存

今天我们来使用OpenCV库来捕获摄像头视频流&#xff0c;并将其保存为AVI格式的视频文件&#xff0c; 代码的主要功能包括&#xff1a; 初始化摄像头捕获对象。设置视频编解码器和输出文件路径。循环读取视频帧&#xff0c;处理并保存到文件中。显示处理后的视频帧。按下q键退…