Langfuse:LLM 应用可观测利器

1,323 阅读15分钟

随着大语言模型(LLM)应用的日益普及,开发者们在享受其强大能力的同时,也面临着前所未有的挑战:如何有效地监控、调试和优化这些复杂且动态的AI系统?传统的软件监控工具往往力不从心,因为 LLM 应用的内部运作充满了不确定性,例如令牌使用、响应质量、延迟以及成本波动等。在这种背景下,一个专为 LLM 应用设计的可观测性平台变得至关重要。今天,我们将深入探讨 Langfuse,一个开源的 LLM 工程平台,它如何帮助我们解决这些痛点,并构建更健壮、高效且可解释的 AI 系统。

什么是Langfuse

Langfuse是一个开源的可观测性平台,专为大语言模型(LLM)应用而设计。它提供了一套全面的工具,帮助开发者深入了解LLM应用的运行状况,从而实现对LLM请求的跟踪、调试和优化。无论使用的是LangChain、LlamaIndex等主流框架,还是自定义的RAG(检索增强生成)管道,Langfuse都能以最小的配置成本无缝集成,并揭示LLM应用内部的复杂性。

image.png

Langfuse的核心价值在于其对LLM应用全生命周期的详细可见性,主要体现在以下几个方面:

  • 追踪(Traces): 记录LLM应用中完整的请求生命周期,从用户查询到最终响应的每一个步骤,包括API调用、上下文、提示、并行处理等。这对于理解多跳推理、工具使用和检索调用的行为至关重要。
  • 指标(Metrics): 提供关键的性能、成本和质量指标。可以监控模型延迟、API调用成本(例如OpenAI的token使用)、错误率和重试次数等,从而量化应用的运行效率和经济效益。
  • 评估(Assessments): 支持对LLM响应质量进行自动化评估和人工评估。Langfuse可以与“LLM即裁判”的评估方法集成,并支持基于LLM的自动评分,评估指标包括准确性、有毒性和连贯性等。此外,它也提供了手动评估界面,方便人工审核和反馈。
  • 实验跟踪(Experiment Tracking): 允许开发者对不同配置进行A/B测试,并比较它们在性能、成本和质量方面的表现,从而实现数据驱动的优化。

简而言之,Langfuse填补了传统监控工具在LLM应用领域留下的空白,它为开发者提供了一个清晰的“窗口”,可以透视AI工作流的内部,帮助我们发现问题、衡量关键指标,并充满信心地进行扩展。

Langfuse的重要性

image.png

传统的软件开发中,我们习惯于使用日志和应用性能监控(APM)工具来追踪系统行为。然而,LLM应用的特性使得这些传统方法显得力不从心。LLM应用并非简单的确定性逻辑,其内部运作充满了复杂性和不确定性。例如,一个看似简单的客户支持聊天机器人,在幕后可能涉及:

  • 从向量数据库中检索相关文档
  • 构建包含上下文的复杂提示
  • 对不同推理步骤进行多次LLM调用
  • 通过各种过滤器处理响应
  • 生成最终答案

每一个步骤都可能引入不可控的变量,如token使用量、响应质量、延迟以及因提示复杂度和模型行为而波动的成本。当聊天机器人给出不佳的响应时,我们很难直接判断是检索到的文档不相关、提示构建不当,还是模型产生了幻觉。缺乏适当的可观测性,调试LLM应用就像在盲目摸索,不仅效率低下,还可能导致成本失控。

Langfuse正是为了解决这些挑战而生。它作为一个可观测性层,能够捕捉语言模型应用的完整执行上下文,从而提供传统工具无法比拟的深度洞察:

1. 成本透明化与优化

LLM的API调用成本是许多开发者面临的痛点。Langfuse能够自动计算支持模型的成本(如OpenAI、Anthropic),并允许按用户、会话、特定模型版本、不同功能或工作流程,甚至地理区域来细分成本。这意味着可以精确地找出哪些提示、用户或功能正在驱动你的开支,从而有针对性地进行优化。例如,通过Langfuse,可以发现某些公司报告触发了极长的提示(12,000+ token),导致成本飙升,进而优化分块策略,将平均提示长度减少40%,实现显著的成本节约。

2. 性能瓶颈识别

LLM应用的响应速度直接影响用户体验。Langfuse捕获了从数据检索、提示创建到LLM分析的每一个阶段,使得延迟测量和细粒度的性能调优成为可能。当的应用感觉缓慢时,Langfuse可以准确地显示时间花费在哪里,是向量搜索、提示构建,还是LLM推理本身?通过识别并优化最慢的组件,可以显著提升应用的整体性能。

3. 大规模质量监控

确保LLM输出的质量是构建可靠AI应用的关键。Langfuse允许运行基于模型的评估,LLM可以根据准确性、有毒性或幻觉等因素对特定会话、追踪或LLM调用进行评分。这意味着可以设置自动化质量评分,在生产环境中持续运行,而不是依赖手动审查。这不仅提高了效率,还能在用户发现问题之前及时捕获并解决问题。

4. 提示词工程优化

提示词(Prompt)的微小改动都可能对LLM的输出质量和成本产生巨大影响。Langfuse能够追踪哪些提示在不同场景中表现最佳,并支持对提示变体进行A/B测试,衡量它们对质量和成本的影响。通过系统地测试和优化提示,可以找到每个用例的最佳版本,从而提升模型表现并控制成本。

5. 用户体验洞察

Langfuse允许实时监控成本和延迟指标,并按用户、会话、地理位置和模型版本进行分类。通过了解不同用户群体如何与AI互动,可以优先考虑改进方向,并识别个性化的机会。例如,可以为高频用户和普通用户制定不同的优化策略,以提供更优质的服务。

总之,Langfuse为LLM应用开发者提供了一个强大的工具集,将AI开发从“成本高昂的猜测游戏”转变为“数据驱动的优化机器”。它让LLM应用的可观测性不再是可选项,而是构建可靠、高效和可解释AI系统的必需品。

Langfuse本地部署与RAG系统集成

了解Langfuse的重要性后,接下来通过一个实际案例,手把手教你如何在本地免费部署Langfuse,并将其集成到一个RAG系统中,实现对LLM应用的全链路可观测性。

image.png

1. Langfuse本地部署

Langfuse支持本地部署,这对于开发者进行测试和开发非常方便,且完全免费。以下是详细的步骤:

前提条件:

  • 确保机器上已安装Git和Docker,并且Docker服务处于运行状态。

部署步骤:

  1. 克隆Langfuse仓库: 打开终端或命令行工具,执行以下命令:

    git clone https://github.com/langfuse/langfuse.git
    
  2. 进入Langfuse目录:

    cd langfuse
    
  3. 启动Docker服务: 在Langfuse目录下,执行Docker Compose命令启动服务。此步骤可能需要几分钟,具体取决于网络和机器性能。

    docker compose up
    
  4. 访问Langfuse UI: 服务成功启动后,可以通过浏览器访问 http://localhost:3000http://0.0.0.0:3000 来打开Langfuse的用户界面。

首次使用配置:

首次访问Langfuse UI时,需要进行一些初始化配置:

  1. 创建组织: 点击右上角的“新建组织”按钮,输入一个组织名称并创建。
  2. 创建项目: 在“项目”标签页中,输入你的项目名称并创建。每个组织可以拥有多个项目。
  3. 获取API密钥: 在“设置追踪”部分,会看到Langfuse的公钥(Public Key)、私钥(Secret Key)和主机URL。请务必妥善保存这些密钥,它们只显示一次。 建议将它们保存在项目的.env文件中,以便后续代码中使用。

2. Python环境准备与Langfuse集成验证

接下来,我们准备Python开发环境,并验证Langfuse是否成功集成。

1. 创建虚拟环境并安装依赖:

```bash
# 推荐使用uv包管理器,如果未安装,请先安装:pip install uv
uv venv venv
source .venv/bin/activate  # Linux/macOS
# 或者 .venv\Scripts\activate # Windows
uv pip install langfuse python-dotenv openai
```

2. 创建.env文件:

在项目根目录下创建.env文件,并将之前获取的Langfuse API密钥和主机URL填入其中:

```config
LANGFUSE_PUBLIC_KEY=YOUR_PUBLIC_KEY
LANGFUSE_SECRET_KEY=YOUR_SECRET_KEY
LANGFUSE_HOST=http://localhost:3000 # 如果是本地部署,通常是这个地址
OPENAI_API_KEY=YOUR_OPENAI_API_KEY # 如果使用OpenAI模型,也需要配置
```

3. 创建main.py文件并验证连接:

```python
from langfuse import Langfuse
from dotenv import load_dotenv
import os

# 加载环境变量
load_dotenv()

# 从环境变量中获取密钥和主机URL
secret_key = os.getenv("LANGFUSE_SECRET_KEY")
public_key = os.getenv("LANGFUSE_PUBLIC_KEY")
host = os.getenv("LANGFUSE_HOST")

# 初始化Langfuse
langfuse = Langfuse(
    secret_key=secret_key,
    public_key=public_key,
    host=host
)

# 验证Langfuse连接
print(f"Langfuse Auth Check : {langfuse.auth_check()}")

# 创建一个示例追踪
langfuse.trace(name="trial-trace", input="Hello Langfuse!", output="Langfuse is working!")

print("Sample trace sent to Langfuse.")
```

4. 运行main.py

    python main.py

如果一切顺利,在终端看到Langfuse Auth Check : True,并且在Langfuse UI的Dashboard中看到名为trial-trace的示例追踪。如果“设置追踪”部分在2-3分钟内没有显示为活跃状态,请检查密钥是否正确,或尝试删除组织和项目后重新配置。

3. RAG系统集成Langfuse

现在,我们将Langfuse集成到一个RAG系统中。这里我们以一个基于LangChain的RAG系统为例,展示如何利用Langfuse追踪RAG的各个环节。

安装RAG相关依赖:

pip install langchain langchain_openai faiss-cpu pypdf ragas

RAG系统示例(rag_with_langfuse.py):

import os
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import CharacterTextSplitter
from langchain.vectorstores import FAISS
from langchain.chains import RetrievalQA
from langchain_openai import OpenAIEmbeddings
from langchain_openai import OpenAI as OpenAILLM
from dotenv import load_dotenv
from langfuse import Langfuse
from langfuse.callback import CallbackHandler # 用于LangChain集成

# 加载环境变量
load_dotenv()

# 初始化Langfuse
langfuse = Langfuse(
    public_key=os.environ.get("LANGFUSE_PUBLIC_KEY"),
    secret_key=os.environ.get("LANGFUSE_SECRET_KEY"),
    host=os.environ.get("LANGFUSE_HOST", "https://cloud.langfuse.com")
)

# 设置LangChain追踪(可选,但推荐)
os.environ["LANGCHAIN_TRACING_V2"] = "true"

def build_and_run_rag(pdf_path: str, query: str):
    # 创建主追踪,用于跟踪整个RAG过程
    main_trace = langfuse.trace(
        name="rag_pdf_process",
        user_id="user-001",
        metadata={"file": pdf_path}
    )

    # 1. 文档加载
    print("Loading PDF...")
    loader = PyPDFLoader(pdf_path)
    pages = loader.load()

    # 2. 文档分块与追踪
    document_splitting_span = main_trace.span(
        name="document_splitting",
        input={"page_count": len(pages)}
    )
    splitter = CharacterTextSplitter(
        chunk_size=200,
        chunk_overlap=20
    )
    chunks = splitter.split_documents(pages)
    document_splitting_span.update(
        output={"chunk_count": len(chunks)}
    )
    document_splitting_span.end()
    print(f"Split into {len(chunks)} chunks.")

    # 3. 向量化与追踪
    vectorization_span = main_trace.span(
        name="vectorization",
        input={"chunk_count": len(chunks)}
    )
    embedding = OpenAIEmbeddings()
    vectorstore = FAISS.from_documents(chunks, embedding)
    retriever = vectorstore.as_retriever(
        search_kwargs={"k": 3}
    )
    vectorization_span.end()
    print("Documents vectorized and indexed.")

    # 4. 构建RAG链与追踪
    chain_setup_span = main_trace.span(name="chain_setup")
    llm = OpenAILLM(
        model_name="gpt-4o",
        max_tokens=256,
        temperature=0
    )
    qa_chain = RetrievalQA.from_chain_type(
        llm=llm,
        chain_type="stuff",
        retriever=retriever,
        return_source_documents=True
    )
    chain_setup_span.end()
    print("RAG chain built.")

    # 5. 执行查询与追踪
    print(f"Running query: {query}")
    # 使用LangChain的CallbackHandler自动追踪LLM调用
    langchain_handler = CallbackHandler(trace=main_trace)
    
    try:
        result = qa_chain({"query": query}, callbacks=[langchain_handler])
        answer = result["result"]
        source_documents = result["source_documents"]

        # 记录最终结果到主追踪
        main_trace.update(
            output={"answer": answer, "source_count": len(source_documents)},
            status="success"
        )
        print("Query executed successfully.")
        print("Answer:", answer)
        print("Source Documents:", [doc.metadata.get('source') for doc in source_documents])

    except Exception as e:
        main_trace.update(status="error", status_message=str(e))
        print(f"An error occurred: {e}")

# 假设有一个名为 'model.pdf' 的PDF文件在 'data/documents/' 目录下
# 需要手动创建 'data/documents/' 目录并将PDF文件放入其中
# 或者修改为实际PDF文件路径
# 例如:创建一个空的model.pdf文件用于测试
# touch data/documents/model.pdf

# 调用函数运行RAG系统
# build_and_run_rag("data/documents/model.pdf", "What are the main topics covered in the PDF?")

# 注意:上述代码中的PDF文件路径和查询需要根据实际情况进行调整。
# 运行此代码后,可以在Langfuse UI中看到完整的RAG流程追踪,包括文档加载、分块、向量化、RAG链构建和查询执行的详细信息,以及每个步骤的耗时、输入输出和潜在错误。

说明:

  • langfuse.trace() 创建一个主追踪(Trace),它代表了整个RAG处理过程。所有后续的子操作(Span)都将关联到这个主追踪。
  • main_trace.span() 创建一个跨度(Span),用于追踪RAG系统中的特定步骤,例如document_splitting(文档分块)、vectorization(向量化)和chain_setup(RAG链构建)。通过span.update()span.end(),我们可以记录每个步骤的输入、输出和持续时间。
  • CallbackHandler(trace=main_trace) LangChain提供了方便的CallbackHandler,可以自动将LangChain内部的LLM调用、检索等操作作为子跨度记录到Langfuse中,无需手动创建generationspan
  • 错误处理: try...except块用于捕获异常,并将错误信息记录到Langfuse中,帮助我们快速定位问题。

通过上述集成,你可以在Langfuse的Dashboard中清晰地看到RAG系统从文档处理到最终答案生成的全链路流程图,每个节点都展示了详细的元数据、耗时和成本信息。这极大地简化了LLM应用的调试、性能分析和优化工作。

结合RAGAS进行质量评估

除了可观测性,Langfuse还可以与RAGAS等评估框架结合,对RAG系统的输出质量进行量化评估。RAGAS专注于评估RAG系统的“访问质量”和“生成质量”,其核心指标包括:

  • 忠实度(Faithfulness): 衡量生成答案对检索文档信息的忠实程度,避免模型“幻觉”。
  • 答案相关性(Answer Relevancy): 评估生成答案与用户原始查询的相关性。

image.png

通过将RAGAS的评估结果集成到Langfuse中,可以更全面地了解RAG系统的工作方式和效果,并根据评估分数持续改进模型和流程。

RAGAS评估示例:

# ... (接上文的RAG系统代码)

from datasets import Dataset
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy

def evaluate_rag_system(qa_chain, langfuse_instance):
    # 准备评估数据集
    # 实际应用中,这些数据应来自真实的用户查询和人工标注的理想答案及上下文
    data = {
        "question": [
            "What is the capital of France?",
            "Who wrote 'To Kill a Mockingbird'?"
        ],
        "answer": [
            "The capital of France is Paris.",
            "Harper Lee wrote 'To Kill a Mockingbird'."
        ],
        "contexts": [
            ["Paris is the capital and most populous city of France."],
            ["Nelle Harper Lee was an American novelist best known for her 1960 novel To Kill a Mockingbird."]
        ],
        "ground_truths": [
            "Paris",
            "Harper Lee"
        ]
    }
    dataset = Dataset.from_dict(data)

    # 运行RAGAS评估
    print("Running RAGAS evaluation...")
    result = evaluate(
        dataset,
        metrics=[faithfulness, answer_relevancy],
        llm=qa_chain.llm, # 使用RAG链中的LLM进行评估
        embeddings=OpenAIEmbeddings()
    )

    # 打印评估结果
    print("RAGAS Evaluation Results:")
    print(result)

    # 将评估结果发送到Langfuse(需要手动集成,或使用LangChain的CallbackHandler)
    # 这里仅作示例,实际集成可能需要更复杂的逻辑来关联评估结果与特定追踪
    # 例如,可以为每次评估创建一个新的trace,或者将评估结果作为metadata添加到现有trace中
    eval_trace = langfuse_instance.trace(name="ragas-evaluation", metadata=result.to_dict())
    eval_trace.end(status="success")
    print("RAGAS evaluation results sent to Langfuse.")

# 假设qa_chain已经构建好
# evaluate_rag_system(qa_chain, langfuse)

# 注意:RAGAS评估通常需要一个包含问题、答案、上下文和真实答案的评估数据集。
# 在实际项目中,需要构建这样的数据集来量化RAG系统的性能。

说明:

  • 评估数据集: RAGAS评估需要一个结构化的数据集,包含question(用户查询)、answer(RAG系统生成的答案)、contexts(RAG系统检索到的相关文档片段)和ground_truths(人工标注的正确答案)。
  • ragas.evaluate() 调用RAGAS的评估函数,传入数据集和需要评估的指标(如faithfulnessanswer_relevancy)。
  • 集成到Langfuse: 评估结果可以作为元数据(metadata)记录到Langfuse的追踪中,或者为每次评估创建一个独立的追踪,以便在Langfuse UI中进行统一管理和分析。

通过Langfuse和RAGAS的结合,不仅能够“看到”LLM应用的内部运作,还能“量化”其输出质量,形成一个完整的“观测-评估-优化”闭环,从而持续提升AI应用性能。

总结与展望

Langfuse作为一款专为LLM应用设计的开源可观测性平台,为开发者提供了前所未有的透明度和控制力。它不仅帮助我们解决了LLM应用开发中成本失控、性能瓶颈和质量难以把控等核心痛点,更通过其强大的追踪、指标、评估和实验跟踪功能,将AI开发从传统的“黑盒”模式带入了数据驱动的“白盒”模式。

通过本文的介绍和实操指南,相信你已经对Langfuse有了全面的认识,并掌握了如何在本地部署Langfuse以及将其集成到RAG系统中的方法。结合RAGAS等评估工具,将能够构建一个完整的“观测-评估-优化”闭环,持续提升LLM应用性能和用户体验。

在AI技术飞速发展的今天,LLM应用的可观测性不再是可选项,而是构建成功、可靠且具有竞争力的AI产品的基石。掌握Langfuse这样的利器,将在AI浪潮中占据先机,从容应对未来的挑战。

希望本文能为你在LLM应用之路上提供有益的帮助!如果有任何疑问或经验分享,欢迎在评论区留言交流。