在上一篇文章中,我们介绍了LCEL表达式Runnable组件,通过LCEL表达式可以很轻松的构建复杂的AI应用,LCEL将多个可运行组件串联起来,在执行LCEL表达式时出现了错误,如何判断是哪个组件出现了错误?又如何获取出现错误的上下文呢?

本文将会详细介绍使用LangChain提供的Callback回调机制,如何对LLM应用以及LCEL执行的各个关键节点进行监控,最后,还会介绍LangChain推出的一款在开发阶段的调试监控神器LangSmith,只需要简单的配置就可以和LangChain构建的应用进行无缝集成,自动监控整个项目的生命周期,将监控的结果自动上传到LangSmith平台,使项目监控变得非常简单。

文中所有示例代码:https://github.com/wzycoding/langchain-study

一、什么是LangSmith

LangSmith 是一个由LangChain推出,用于构建LLM应用的监控平台,如下图所示,LangSmith的功能主要包括以下三个方面:

可观测性:LangSmith可以进行数据的分析和追踪,并提供了仪表盘、告警等功能。

可评估:可以对应用运行情况进行评估,分析其性能表现。

提示词工程:支持对提示词进行管理,并且提供提示词版本控制功能

LangSmith由LangChain提供,无需本地部署,LangSmith地址如下:

https://smith.langchain.com/

登录后界面

二、LangSmith使用

首先点击Tracing Projects

系统中默认存在一个项目,点击New Project创建新项目

LangSmith支持LangChain项目和非LangChain项目,并且分别提供了将LangSmith接入到应用的方法,点击Generate API Key,生成API Key

复制保存好API KEY,并修改项目名为langchain-study,下方的配置也会自动变成对应项目名称

复制上方配置,放到项目中的.env文件中

# LangSmith配置
LANGSMITH_TRACING="true"
LANGSMITH_ENDPOINT="https://api.smith.langchain.com"
LANGSMITH_API_KEY="lsv2_pt_***************************"
LANGSMITH_PROJECT="langchain-study"

通过一个最简单的示例进行测试:

import dotenv
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI # 读取env配置
dotenv.load_dotenv() # 1.创建提示词模板
prompt = ChatPromptTemplate.from_template("{question}") # 2.构建GPT-3.5模型
llm = ChatOpenAI(model="gpt-3.5-turbo") # 3.创建输出解析器
parser = StrOutputParser() # 4.执行链
chain = prompt | llm | parser
print(chain.invoke({"question": "请以表格的形式返回三国演义实力最强的十个人,并进行简要介绍"}))

执行完成之后,在Tracing Projects页面就可以看到langchain-study项目被成功创建

点击进入项目,就可以看到刚刚那一次的调用过程,包括输入、输出、发起时间、总耗时等信息

点击All Runs可以查看各个组件的执行过程,包括Prompt生成、LLM响应、输出解析器处理等各环节的详细执行信息

点击任意组件,如ChatPromptTemplate,就会将组件的输入和输出结果进行展示

下拉选择,选择Raw Input就可以展示原始的输入,输出部分也可以以相同方式查看原始信息

支持 JSON 与 YAML 格式展示,其中 JSON 更便于观察,如下图可以清晰的观察到ChatPromptTemplate组件的原始输入输出信息

下面我们将程序改成不存在的模型gpt-3.6-turbo,重新执行程序,来模拟出现错误的情况。

llm = ChatOpenAI(model="gpt-3.6-turbo")

在LangSmith中,可以看到执行记录和错误原因

也可以详细查看错误详细原因

三、什么是Callback机制

除了使用LangSmith之外,LangChain还提供了一种回调机制,可以在 LLM 应用程序的各种阶段执行特定的钩子方法。通过这些钩子方法,我们可以轻松地进行日志输出、异常监控等任务,Callback支持以下事件的钩子方法:

Event 事件 触发时机 关联钩子方法
Chat model start 聊天模型启动 on_chat_model_start
LLM start LLM LLM模型启动 on_llm_start
LLM new token LLM LLM生成新的 token 时触发,仅在启用流式输出(streaming)模式下生效 on_llm_new_token
LLM ends LLM 或聊天模型完成运行时 on_llm_end
LLM errors LLM 或聊天模型出错 on_llm_error
Chain start 链开始执行(实际上就是每个可运行组件开始执行) on_chain_start
Chain end 链结束执行(实际上就是每个可运行组件结束执行) on_chain_end
Chain error 链执行出错 on_chain_error
Tool start 工具开始执行 on_tool_start
Tool end 工具结束执行 on_tool_end
Tool error 工具执行出错 on_tool_error
Agent action agent开始执行 on_agent_action
Agent finish agent结束执行 on_agent_finish
Retriever start 检索器开始执行 on_retriever_start
Retriever end 检索器结束执行 on_retriever_end
Retriever error 检索器执行出错 on_retriever_error
Text 每次模型输出一段文本时,就会调用这个方法 on_text
Retry 当某个组件(比如 LLM 调用或链)发生失败并触发重试机制时 on_retry

四、如何使用Callback机制

首先,使用Callback机制,需要使用到Callback handler,即回调处理器,那些各个生命周期的钩子方法,就定义在回调处理器中,回调处理器支持同步和异步,同步回调处理器继承BaseCallbackHandler类,异步回调处理器继承AsyncCallbackHandler类。

如下图在PyCharm中,定义类继承BaseCallbackHandler类,使用ctrl+o快捷键,就会出现这些可以重写的钩子方法。

那么,如何使自定义的CallbackHandler生效呢?可以在调用可执行组件的invoke()方法中,除了传递输入参数外,再传递config配置参数,config配置参数可以传递各种配置信息,其中,callbacks属性用来传递回调处理器,callbacks属性接收一个数组,数组里面包含自定义的CallbackHandler对象,代码示例如下:

from typing import Dict, Any, Optional, List
from uuid import UUID import dotenv
from langchain_core.callbacks import BaseCallbackHandler
from langchain_core.messages import BaseMessage
from langchain_core.output_parsers import StrOutputParser
from langchain_core.outputs import LLMResult
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI dotenv.load_dotenv() class CustomCallbackHandler(BaseCallbackHandler):
"""自定义回调处理类""" def on_chat_model_start(self, serialized: Dict[str, Any], messages: List[List[BaseMessage]], *, run_id: UUID,
parent_run_id: Optional[UUID] = None, tags: Optional[List[str]] = None,
metadata: Optional[Dict[str, Any]] = None, **kwargs: Any) -> Any:
print("======聊天模型结束执行======") def on_llm_end(self, response: LLMResult, *, run_id: UUID, parent_run_id: Optional[UUID] = None,
**kwargs: Any) -> Any:
print("======聊天模型结束执行======") def on_chain_start(self, serialized: Dict[str, Any], inputs: Dict[str, Any], *, run_id: UUID,
parent_run_id: Optional[UUID] = None, tags: Optional[List[str]] = None,
metadata: Optional[Dict[str, Any]] = None, **kwargs: Any) -> Any:
print(f"开始执行当前组件{kwargs['name']},run_id: {run_id}, 入参:{inputs}") def on_chain_end(self, outputs: Dict[str, Any], *, run_id: UUID, parent_run_id: Optional[UUID] = None,
**kwargs: Any) -> Any:
print(f"结束执行当前组件,run_id: {run_id}, 执行结果:{outputs}, {kwargs}") # 1.创建提示词模板
prompt = ChatPromptTemplate.from_template("{question}") # 2.构建GPT-3.5模型
llm = ChatOpenAI(model="gpt-3.5-turbo") # 3.创建输出解析器
parser = StrOutputParser() # 4.执行链
chain = prompt | llm | parser
chain.invoke({"question": "请输出静夜思的原文"},
{"callbacks": [CustomCallbackHandler()]})

在示例中,创建了一个CustomCallbackHandler类,继承了BaseCallbackHandler,分别重写了on_chain_starton_llm_endon_chain_starton_chain_end,在聊天模型开始执行和结束执行进行了信息输出,在on_chain_starton_chain_end打印了当前链执行的组件名称、运行id、输入参数、输出结果。

执行结果如下,通过输出结果可以清晰地看到每一个组件的输入和输出结果,以及LLM何时开始执行、结束执行,若需监控异常情况,可重写 on_chain_error 方法。

开始执行当前组件RunnableSequence,run_id: 6eaf8cba-87d8-4e8f-8ec3-ea67a2b2cc6c, 入参:{'question': '请输出静夜思的原文'}
开始执行当前组件ChatPromptTemplate,run_id: 5f452385-74e3-40bf-a7b7-2dcbb921b801, 入参:{'question': '请输出静夜思的原文'}
结束执行当前组件,run_id: 5f452385-74e3-40bf-a7b7-2dcbb921b801, 执行结果:messages=[HumanMessage(content='请输出静夜思的原文')], {'tags': ['seq:step:1']}
======聊天模型结束执行======
======聊天模型结束执行======
开始执行当前组件StrOutputParser,run_id: 7c1043c0-aa7c-4969-9822-f8464b312922, 入参:content='《静夜思》是唐代诗人李白创作的一首诗,原文如下:\n\n**静夜思**\n\n床前明月光, \n疑是地上霜。 \n举头望明月, \n低头思故乡。' response_metadata={'token_usage': {'completion_tokens': 76, 'prompt_tokens': 17, 'total_tokens': 93, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_b28b39ffa8', 'finish_reason': 'stop', 'logprobs': None} id='run-80f91d8a-c8d0-47c5-8255-806272bdbfcf-0' usage_metadata={'input_tokens': 17, 'output_tokens': 76, 'total_tokens': 93}
结束执行当前组件,run_id: 7c1043c0-aa7c-4969-9822-f8464b312922, 执行结果:《静夜思》是唐代诗人李白创作的一首诗,原文如下: **静夜思** 床前明月光,
疑是地上霜。
举头望明月,
低头思故乡。, {'tags': ['seq:step:3']}
结束执行当前组件,run_id: 6eaf8cba-87d8-4e8f-8ec3-ea67a2b2cc6c, 执行结果:《静夜思》是唐代诗人李白创作的一首诗,原文如下: **静夜思** 床前明月光,
疑是地上霜。
举头望明月,
低头思故乡。, {'tags': []}

五、总结

本文介绍了什么是LangSmith,以及如何创建LangSmith应用、无缝集成到LangChain当中,通过LangSmith可以清晰的监控到AI应用每一步的执行过程,包括执行时间、原始输入输出、花费金额、使用token数等详细信息。

随后我们还介绍了LangChain提供的Callback机制,利用BaseCallbackHandler提供的钩子方法,可以轻松地监控各个关键执行流程,有读者可能会疑惑:既然有了 LangSmith,为何还需要 Callback 机制?在实际开发中,LangSmith 更适合在开发调试阶段使用,而在生产环境下,出于数据隐私和安全考量,我们通常不会将敏感数据上传到LangSmith平台。这时,Callback 机制就能将执行信息接入到本地或自定义的监控系统,实现同样的可观测性。

相信通过本文你已经掌握如何对AI应用进行监控,后续将继续深入介绍LangChain的核心模块和高级用法,敬请期待。

LangChain框架入门07:AI应用监控神器LangSmith的更多相关文章

  1. Taurus.MVC 微服务框架 入门开发教程:项目部署:7、微服务节点的监控与告警。

    系统目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 开源地址:https://github.com/cyq1162/Taurus.MVC 本系列第一篇:Tauru ...

  2. DWR3.0框架入门(2) —— DWR的服务器推送

    DWR3.0框架入门(2) —— DWR的服务器推送 DWR 在开始本节内容之前,先来了解一下什么是服务器推送技术和DWR的推送方式.   1.服务器推送技术和DWR的推送方式   传统模式的 Web ...

  3. Java自带RPC实现,RMI框架入门

    Java自带RPC实现,RMI框架入门 首先RMI(Remote Method Invocation)是Java特有的一种RPC实现,它能够使部署在不同主机上的Java对象进行通信与方法调用,它是一种 ...

  4. JavaScript基础入门07

    目录 JavaScript 基础入门07 BOM window对象 Navigator对象 Screen 对象 Location对象 History 对象 JavaScript 基础入门07 BOM ...

  5. Go-Micro框架入门教程(一)---框架结构

    Go语言微服务系列文章,使用golang实现微服务,这里选用的是go-micro框架,本文主要是对该框架的一个架构简单介绍. 1. 概述 go-micro是go语言下的一个很好的微服务框架. 1.服务 ...

  6. Newbe.Claptrap 框架入门,第一步 —— 开发环境准备

    Newbe.Claptrap 框架依托于一些关键性的基础组件和一些可选的辅助组件.本篇我们来介绍一下如何准备一个开发环境. Newbe.Claptrap 是一个用于轻松应对并发问题的分布式开发框架.如 ...

  7. Taurus.MVC 微服务框架 入门开发教程:项目集成:5、统一的日志管理。

    系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 本系列第一篇:Taurus.MVC V3.0.3 微服务开源框架发布:让.NET 架构在大并发的演进过程更简单 ...

  8. Taurus.MVC 微服务框架 入门开发教程:项目集成:6、微服务间的调用方式:Rpc.StartTaskAsync。

    系统目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 开源地址:https://github.com/cyq1162/Taurus.MVC 本系列第一篇:Tauru ...

  9. CI框架入门1

    CI框架入门: 1.url的特点             2.目录结构/布局             3.MVC分别在哪里,如何依葫芦画瓢             4.安全性             ...

  10. 【原创】NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战

    概述 本文演示的是一个Android客户端程序,通过UDP协议与两个典型的NIO框架服务端,实现跨平台双向通信的完整Demo. 当前由于NIO框架的流行,使得开发大并发.高性能的互联网服务端成为可能. ...

随机推荐

  1. WindowsPE文件格式入门11.资源表

    https://www.bpsend.net/thread-411-1-1.html 资源表 资源的管理方式采用windows资源管理器目录的管理方式,一般有三层目录. 根目录 结构体IMAGE_RE ...

  2. .Net 组件库先混淆签名,再打包成.nupkg包

    目前,我们项目组打算做增量升级的功能,这涉及到dll库增量改变,只有修改过代码的dll才需要有文件的变化,否则文件和对应的版本是不会改变的. 我们之前的打包的项目有一个小缺陷: 在整个应用打包输出的时 ...

  3. Linux常用命令介绍-文件管理

    MV命令 - 移动或改名文件 mv命令来自英文单词move的缩写,中文译为"移动",其功能与英文含义相同,能够对文件进行剪切和重命名操作.这是一个被高频使用的文件管理命令,需要留意 ...

  4. Leangoo助力医药行业项目降本增效

    医药行业痛点诸多,制药研发周期长.生物技术创新协同难.医疗器械研发生产衔接不畅.医疗保健服务流程繁琐.Leangoo 可化解困境,促各领域信息共享.流程优化.协同增效,提升效率与质量,推动医药行业整体 ...

  5. Hypermesh_LsDyna划分网格

    1.创建角点 2.创建单元(按F6) 3.划分网格(按F12) 4.清除临时节点(Geom → temp nodes → clear all) 5.设置单元集(PD单元)(*SET_SHELL_LIS ...

  6. Docker Compose部署随机图API

    Docker Compose部署随机图API 平时我们部署博客的时候,为了考虑美观会考虑使用随机图来作为文章的封面,现在有很多大佬愿意提供随机图API,通过API我们可以很方便地部署随机图,不必自己寻 ...

  7. P1155 [NOIP 2008 提高组] 双栈排序

    将一组数据分成两组,且组内不能冲突.考虑使用二分图. 我们来思考什么样的两个数不能存在于一个栈中.因为最后要求我们升序排序输出,所以在一个栈中的数字必定是降序. 那么当 \(i<j\) 时并且 ...

  8. 实测提速 60%!Maven Daemon 全面加速 SeaTunnel 编译打包效率

    作者 | 张东浩 在大规模数据集成项目中,构建效率尤为关键.本文实测了 Apache SeaTunnel 项目在使用传统 Maven 与新一代构建工具 Maven Daemon(mvnd)下的打包效率 ...

  9. 协程本质是函数加状态机——零基础深入浅出 C++20 协程

    前言 之前写过一篇 C++20 协程入门的文章:<使用 C++ 20 协程降低异步网络编程复杂度>,谈到了协程在消除异步编程 callback hell 方面的重要贡献--使代码更清晰,易 ...

  10. Oracle 客户端深度指南:SQL Developer 与 PL/SQL Developer 全面安装使用教程

    作为拥有10年Oracle开发经验的资深工程师,我将为您提供一份专业级的客户端工具指南.无论您是初学者还是进阶开发者,本教程都将帮助您高效使用Oracle生态中最强大的两个工具. 一.工具对比与选择建 ...