在上一篇文章中,我们介绍了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_startLLM start LLMLLM模型启动on_llm_startLLM new token LLMLLM生成新的 token 时触发,仅在启用流式输出(streaming)模式下生效on_llm_new_tokenLLM endsLLM 或聊天模型完成运行时on_llm_endLLM errorsLLM 或聊天模型出错on_llm_errorChain start链开始执行(实际上就是每个可运行组件开始执行)on_chain_startChain end链结束执行(实际上就是每个可运行组件结束执行)on_chain_endChain error链执行出错on_chain_errorTool start工具开始执行on_tool_startTool end工具结束执行on_tool_endTool error工具执行出错on_tool_errorAgent actionagent开始执行on_agent_actionAgent finishagent结束执行on_agent_finishRetriever start检索器开始执行on_retriever_startRetriever end检索器结束执行on_retriever_endRetriever error检索器执行出错on_retriever_errorText每次模型输出一段文本时,就会调用这个方法on_textRetry当某个组件(比如 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_start、on_llm_end、on_chain_start、on_chain_end,在聊天模型开始执行和结束执行进行了信息输出,在on_chain_start、on_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的核心模块和高级用法,敬请期待。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |