LangChain实战:回调函数

前言

毫无疑问,2024将是人工智能丰收年,开始寒假的我,准备先把LangChain捋一遍。

这篇文章来学习下callback机制, 之前聊过AutoGen的callback机制,我们来对比下。

回调和异步

作为js开发者,对于回调函数和异步编程非常熟悉。在事件监听、Ajax请求和定时器中,我们常会使用回调函数和打理异步任务。我们通过代码来熟悉下python的方式。

python 复制代码
# Python asyncio模块是用于异步编程的标准库,实现了协程、事件循环和异步I/O等功能
import asyncio
async def rectangleArea(w, h, callback):
    print("开始计算矩形的面积...")
    # 等待0.5秒
    await asyncio.sleep(0.5)
     = x * y
    print("计算结束")
   
async def circleArea():
   print("开始圆形计算")
   await asyncio.sleep(1)
   print("完成圆形计算")
    
# async 和 在js里一样, 是函数修饰符,内部可以使用await
async def main():
    print("主线程开始...")
    task1 = asyncio.create_task(rectangleArea(3, 4, print_result))
    task2 = asyncio.create_task(circleArea())
    
    await task1
    await task2
    print("主线程结束...")
    
asyncio.run(main())

当代码执行到sleep时,task会暂停,并开始执行另一个任务,这就是异步,跟js 里的async await 有些区别。

LangChain的Callback机制

LangChain在打理AI应用时,有太多需要通过CallbackHandler来实现,比如日志记录、监控、数据流处理等。

我们来看一个需求,要求在LangChain执行完一个LLM工作后,将输出写入output.log文件

ini 复制代码
from loguru import logger
# langchain callback 机制提供了各种callbackHandler,这里是File,处理文件加调
from langchain.callbacks import FileCallbackHandler
# 最基本的LLM工作Chain,
from langchain.chain from LLMChain
# Prompt模板
from langchain.prompts import PromptTemplate

logFile = "output.log"
logger.add(logfile, colorize=True, enqueue=True)
handler = FileCallbackHandler(logfile)

llm = OpenAI()
prompt = PromptTemplate.from_template("1 + {number} = ")
chain = LLMChain(llm=llm, prompt=prompt, callbacks=[handler], verbose=True)
answer = chain.run(number=2)
logger.info(answer)

上面是一个简单的callback例子。在之前熟悉的LLMChain实例化过程中,我们传入了callbacks参数,它是一个数组,里面是我们定义的文件回调处理。当大模型交互完成后,将结果写入logger。

自定义回调函数

我们来看一个老喻干货店客服中的例子。

python 复制代码
# Python asyncio模块是用于异步编程的标准库,实现了协程、事件循环和异步I/O等功能
import asyncio
# 从typing模块导入Any Dict  List 类型
from typing import Any, Dict, List
# ChatOpenAI
from langchain.chat_models import ChatOpenAI
# 从schema 中引入LLMResult、HumanMessage
from langchain.schema import LLMResult, HumanMessage

from langchain.callbacks.base import AsyncCallbackHandler, BaseCallbackHandler

上述代码引入了AsyncCallbackHandler 异步任务处理器、BaseCallbackHandler LangChain回调基类,等下可以自定义

python 复制代码
基于 BaseCallbackHandler 创建异步任务处理类
class MyDryFoodShopSyncHandler(BaseCallbackHandler): 
    # 当llm 接收到新token时 触发
    def on_llm_new_token(self, token: str, **kwargs) -> None: 
    print(f"干货数据: token: {token}")
python 复制代码
# 创建异步回调处理器
class MyDryFoodAsyncHandler(AsyncCallbackHandler): 
    # 在llm 开始工作前
    async def on_llm_start(
        self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any
    ) -> None: 
    print("正在获取干货数据...") 
    await asyncio.sleep(0.5) # 模拟异步操作 
    print("干货数据获取完毕。提供建议...") 
    async def on_llm_end(self, response: LLMResult, **kwargs: Any) -> None: 
    print("整理干货建议...") 
    await asyncio.sleep(0.5) # 模拟异步操作 
    print("祝你买货愉快!")
ini 复制代码
# 异步函数
async def main(): 
    drayfood_shop_chat = ChatOpenAI( 
        max_tokens=100, 
        streaming=True, 
        callbacks=[MyDryFoodShopSyncHandler(), MyDryFoodAsyncHandler()], ) # 异步生成聊天回复 
        await drayfood_shop_chat.agenerate([[HumanMessage(content="哪种干货最适合炖鸡?只简单说3种,不超过60字")]])
        # 运行主异步函数
        asyncio.run(main())

当用户在我的干货店里提出关于营养汤相关的问题时,我们的AI客服,每当新的Token生成时,会有打印。在与OpenAI进行交互前后,又有打印,并最后祝客户买货愉快。

计算Tokens 开销及成本控制

python 复制代码
from langchain import OpenAI
# 聊天chain
from langchain.chains import ConversationChain
# memory
from langchain.chains.conversation.memory import ConversationBufferMemory

# 初始化大语言模型
llm = OpenAI(
    temperature=0.5,
    model_name="gpt-3.5-turbo-instruct")

# 初始化对话链
conversation = ConversationChain(
    llm=llm,
    memory=ConversationBufferMemory()
)

# 第一天的对话
# 回合1
conversation("我家明天要开party,我需要一些干海货。")
print("第一次对话后的记忆:", conversation.memory.buffer)

# 回合2
conversation("爷爷喜欢虾干,一两一只的。")
print("第二次对话后的记忆:", conversation.memory.buffer)

# 回合3 (第二天的对话)
conversation("我又来了,还记得我昨天为什么要买干海货吗?")
print("/n第三次对话后时提示:/n",conversation.prompt.template)
print("/n第三次对话后的记忆:/n", conversation.memory.buffer)

如果我们需要确切计算tokens开销,就需要用到calblack。

python 复制代码
from langchain import OpenAI
from langchain.chains import ConversationChain
from langchain.chains.conversation.memory import ConversationBufferMemory
from langchain.callbacks import get_openai_callback

# 初始化大语言模型
llm = OpenAI(temperature=0.5, model_name="gpt-3.5-turbo-instruct")

# 初始化对话链
conversation = ConversationChain(
    llm=llm,
    memory=ConversationBufferMemory()
)

# 使用context manager进行token counting
with get_openai_callback() as cb:
    # 第一天的对话
    # 回合1
    conversation("我家明天要开party,我需要一些干海货。")
    print("第一次对话后的记忆:", conversation.memory.buffer)

    # 回合2
    conversation("爷爷喜欢虾干,一两一只的。")
    print("第二次对话后的记忆:", conversation.memory.buffer)

    # 回合3 (第二天的对话)
    conversation("我又来了,还记得我昨天为什么要买干海货吗?")
    print("/n第三次对话后时提示:/n",conversation.prompt.template)
    print("/n第三次对话后的记忆:/n", conversation.memory.buffer)

# 输出使用的tokens
print("\n总计使用的tokens:", cb.total_tokens)

get_openai_callback 可以监控ConversationChain 的开销。正好我们可以计算在这些对话中使用的总 Tokens 数。

makefile 复制代码
总计使用的tokens: 1023

总结

通过callback, 我们可以去处理一些token开销,或LLM 任务log等的工作,收获还是可以的。

相关推荐
测试员周周4 小时前
【Appium 系列】第16节-WebView-H5上下文切换 — 混合应用的自动化难点
运维·开发语言·人工智能·功能测试·appium·自动化·测试用例
K姐研究社6 小时前
怎么用AI制作电商口播视频,开拍APP一键生成
人工智能·音视频
LaughingZhu6 小时前
Product Hunt 每日热榜 | 2026-05-21
前端·人工智能·经验分享·chatgpt·html
传说故事7 小时前
【论文阅读】MotuBrain: An Advanced World Action Model for Robot Control
论文阅读·人工智能·具身智能·wam
北京耐用通信7 小时前
全域适配工业场景耐达讯自动化Modbus TCP 转 PROFIBUS 网关轻松实现以太网与现场总线互通
网络·人工智能·网络协议·自动化·信息与通信
火山引擎开发者社区7 小时前
TRAE × 火山引擎 Supabase:为你的 AI 应用装上“数据引擎”
人工智能
小a彤8 小时前
GE 在 CANN 五层架构中的位置
人工智能·深度学习·transformer
前端若水8 小时前
会话管理:创建、切换、删除对话历史
前端·人工智能·python·react.js
Upsy-Daisy8 小时前
AI Agent 项目学习笔记(八):Tool Calling 工具调用机制总览
人工智能·笔记·学习
企学宝8 小时前
企学宝5月专题课程丨《OpenClaw AI 智能体实战营:从零基础部署到全场景自动化落地》
人工智能·ai·企业培训