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等的工作,收获还是可以的。

相关推荐
机器之心3 分钟前
刚刚,苹果基础模型团队负责人庞若鸣被Meta挖走!加入超级智能团队、年薪千万美元
人工智能
PetterHillWater28 分钟前
基于Trae智能复杂项目重构实践
后端·aigc
G.E.N.42 分钟前
开源!RAG竞技场(2):标准RAG算法
大数据·人工智能·深度学习·神经网络·算法·llm·rag
西西弗Sisyphus1 小时前
如果让计算机理解人类语言- Word2Vec(Word to Vector,2013)
人工智能·word·word2vec
前端双越老师1 小时前
30 行代码 langChain.js 开发你的第一个 Agent
人工智能·node.js·agent
东坡肘子2 小时前
高温与奇怪的天象 | 肘子的 Swift 周报 #092
人工智能·swiftui·swift
AI大模型2 小时前
COZE实战部署(四)—— coze实战部署
程序员·llm·coze
AI大模型2 小时前
COZE实战部署(三)—— 更多实例的展示
llm·agent·coze
KaneLogger2 小时前
视频转文字,别再反复拖进度条了
前端·javascript·人工智能