维护测试监控LLM应用

维护一个生产级的 LLM 应用,我们需要做什么?

  1. 各种指标监控与统计:访问记录、响应时长、Token 用量、计费等等
  2. 调试 Prompt
  3. 测试/验证系统的相关评估指标
  4. 数据集管理(便于回归测试)
  5. Prompt 版本管理(便于升级/回滚)

针对以上需求,我们介绍两个生产级 LLM App 维护平台

  1. LangFuse: 开源 + SaaS(免费/付费),LangSmith 平替,可集成 LangChain 也可直接对接 OpenAI API;
  2. LangSmith: LangChain 的官方平台,SaaS 服务(免费/付费),非开源,企业版支持私有部署;

1、LangFuse

开源,支持 LangChain 集成或原生 OpenAI API 集成

官方网站:https://langfuse.com/

项目地址:https://github.com/langfuse

文档地址:https://langfuse.com/docs

API文档:https://api.reference.langfuse.com/

  • Python SDK: https://python.reference.langfuse.com/

  • JS SDK: https://js.reference.langfuse.com/

  1. 通过官方云服务使用:
    • 注册: cloud.langfuse.com
    • 创建 API Key
LANGFUSE_SECRET_KEY="sk-lf-..."
LANGFUSE_PUBLIC_KEY="pk-lf-..."
LANGFUSE_HOST="https://cloud.langfuse.com" # EU 服务器
# LANGFUSE_HOST="https://us.cloud.langfuse.com" # US 服务器
  1. 通过 Docker 本地部署
# Clone repository
git clone https://github.com/langfuse/langfuse.git
cd langfuse

# Run server and db
docker compose up -d
# 在自己部署的系统中生成上述两个 KEY
# 并在环境变量中指定服务地址

LANGFUSE_SECRET_KEY="sk-lf-..."
LANGFUSE_PUBLIC_KEY="pk-lf-.."
LANGFUSE_HOST="http://localhost:3000"
# !pip install langfuse==2.43.2

1.1、通过装饰器记录

from langfuse.decorators import observe, langfuse_context
from langfuse.openai import openai  # OpenAI integration
from dotenv import load_dotenv, find_dotenv

_ = load_dotenv(find_dotenv())


@observe()
def run():
    return openai.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[
            {
   
   "role": "user", "content": "对我说Hello, World!"}
        ],
    ).choices[0].message.content


print(run())
langfuse_context.flush()
Hello, World! How are you today?

1.1.1、几个基本概念

  • Trace 一般表示用户与系统的一次交互,其中记录输入、输出,也包括自定义的 metadata 比如用户名、session id 等;
  • 一个 trace 内部可以包含多个子过程,这里叫 observarions;
  • Observation 可以是多个类型:
    • Event 是最基本的单元,用于记录一个 trace 中的每个事件;
    • Span 表一个 trace 中的一个"耗时"的过程;
    • Generation 是用于记录与 AI 模型交互的 span,例如:调用 embedding 模型、调用 LLM。
  • Observation 可以嵌套使用。
    在这里插入图片描述

1.1.2、observe() 装饰器的参数

def observe(
	self,
	*,
	name: Optional[str] = None, # Trace 或 Span 的名称,默认为函数名
	as_type: Optional[Literal['generation']] = None, # 将记录定义为 Observation (LLM 调用)
	capture_input: bool = True, # 记录输入
	capture_output: bool = True, # 记录输出
	transform_to_string: Optional[Callable[[Iterable], str]] = None # 将输出转为 string
) -> Callable[[~F], ~F]:
from langfuse.decorators import observe, langfuse_context
from langfuse.openai import openai
from dotenv import load_dotenv, find_dotenv

_ = load_dotenv(find_dotenv())


@observe(name="HelloWorld")
def run():
    return openai.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[
            {
   
   "role": "user", "content": "对我说Hello, World!"}
        ],
    ).choices[0].message.content


print(run())
langfuse_context.flush()
Hello, World! How can I assist you today?

1.1.3、通过 langfuse_context 记录 User ID、Metadata 等

from langfuse.decorators import observe, langfuse_context
from langfuse.openai import openai
from dotenv import load_dotenv, find_dotenv

_ = load_dotenv(find_dotenv())


@observe()
def run():
    langfuse_context.update_current_trace(
        name="HelloWorld",
        user_id="rivers",
        tags=["test", "demo"]
    )
    return openai.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[
            {
   
   "role": "user", "content": "对我说Hello, World!"}
        ],
    ).choices[0].message.content


print(run())
langfuse_context.flush()
Hello, World! Nice to meet you.

1.2、通过 LangChain 的回调集成

from dotenv import load_dotenv, find_dotenv

_ = load_dotenv(find_dotenv())


from langchain.prompts import (
    ChatPromptTemplate,
    HumanMessagePromptTemplate,
)
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
from langchain_core.runnables import RunnablePassthrough

model = ChatOpenAI(model="gpt-3.5-turbo")

prompt = ChatPromptTemplate.from_messages([
    HumanMessagePromptTemplate.from_template("Say hello to {input}!")
])


# 定义输出解析器
parser = StrOutputParser()

chain = (
    {
   
   "input": RunnablePassthrough()}
    | prompt
    | model
    | parser
)

from langfuse.decorators import langfuse_context, observe


@observe()
def run():
    langfuse_context.update_current_trace(
        name="LangChainDemo",
        user_id="rivers",
    )

    # 获取当前 LangChain 回调处理器
    langfuse_handler = langfuse_context.get_current_langchain_handler()

    return chain.invoke(input="rivers", config={
   
   "callbacks": [langfuse_handler]})


print(run())
langfuse_context.flush()  # Langfuse 回传记录是异步的,可以通过 flush 强制更新
Hello rivers! You flow so gracefully, shaping landscapes and sustaining life. Thank you for your beauty and power.

1.3、构建一个实际应用

AGI 课堂跟课助手,根据课程内容,判断学生问题是否需要老师解答

  1. 判断该问题是否需要老师解答,回复’Y’或’N’
  2. 判断该问题是否已有同学问过

1.3.1、用 Trace 记录一个多次调用 LLM 的过程

TRACE (id: trace_id)
|
|-- SPAN: LLMCain (id: generated by Langfuse)
|   |
|   |-- GENERATION: OpenAI (id: generated by Langfuse)
|
|-- SPAN: LLMCain (id: generated by 'next_span_id')
|   |
|   |-- GENERATION: OpenAI (id: generated by Langfuse)
from dotenv import load_dotenv, find_dotenv
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI

_ = load_dotenv(find_dotenv())

# 构建 PromptTemplate
from langchain.prompts import PromptTemplate

need_answer = PromptTemplate.from_template("""
*********
你是AIGC课程的助教,你的工作是从学员的课堂交流中选择出需要老师回答的问题,加以整理以交给老师回答。

课程内容:
{outlines}
*********
学员输入:
{user_input}
*********
如果这是一个需要老师答疑的问题,回复Y,否则回复N。
只回复Y或N,不要回复其他内容。""")

check_duplicated = PromptTemplate.from_template(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SunnyRivers

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值