LangChain Agent评估与可观测性体系实战

本文是「LangChain Agent开发实战系列」的第6篇,系列文章包含:

一、前言

在前面的文章中,我们学习了如何开发、部署和优化LangChain Agent。但有一个问题一直困扰着很多开发者:如何衡量Agent的好坏?

传统软件有明确的测试标准,通过就好,不通过就坏。但Agent不一样,它的输出是"开放性"的,很难用简单的对错来判断。

没有评估,就没有优化。 如果你不知道当前的Agent表现如何,就无法判断优化是否有效。

今天这篇文章,我们就来聊聊如何建立一套完整的Agent评估与可观测性体系,让你对Agent的表现了如指掌。

二、为什么需要评估Agent?

2.1 Agent评估的重要性

评估Agent不是"锦上添花",而是"必不可少":

  1. 质量保证:确保Agent输出的质量符合预期
  2. 优化依据:知道哪里好哪里不好,才能针对性优化
  3. 版本对比:新版本是否比旧版本好,用数据说话
  4. 成本控制:知道每个查询的成本,才能优化成本
  5. 用户满意度:最终目标是让用户满意

2.2 传统软件测试 vs Agent评估

Agent评估和传统软件测试有很大的不同:

维度 传统软件测试 Agent评估
输出确定性 输入相同,输出一定相同 输入相同,输出可能不同
判断标准 对错分明 好坏是程度问题
测试用例 容易设计 难以设计全面
自动化程度 高度自动化 难以完全自动化
评估维度 功能、性能、安全 质量、相关性、有用性
成本 相对较低 相对较高

核心区别:传统软件是"确定性系统",Agent是"概率性系统"。

三、评估指标体系

要评估Agent,首先要建立一套完整的指标体系。

3.1 质量指标

质量指标是最核心的,直接关系到用户体验。

1. 准确性(Accuracy)

回答是否正确,是否符合事实。

评估方法

  • 与标准答案对比
  • 事实核查
  • 人工评分
2. 相关性(Relevance)

回答是否与问题相关,有没有答非所问。

评估方法

  • 语义相似度计算
  • 人工判断相关性等级
3. 完整性(Completeness)

回答是否完整,有没有遗漏重要信息。

评估方法

  • 检查关键点是否都覆盖
  • 人工评分(1-5分)
4. 一致性(Consistency)

相同或相似的问题,回答是否一致。

评估方法

  • 多次提问相同问题,对比答案
  • 相似问题聚类,检查答案一致性
5. 有用性(Helpfulness)

回答对用户是否真的有帮助。

评估方法

  • 用户反馈
  • 人工评分

3.2 性能指标

性能指标关系到系统的响应速度和稳定性。

指标 说明 目标
响应时间 从请求到返回完整结果的时间 P95 < 5秒
首字延迟 从请求到返回第一个字的时间 < 1秒(流式输出)
吞吐量 每秒能处理的请求数 根据业务需求
成功率 成功返回结果的比例 > 99.5%
错误率 出错的比例 < 0.5%

3.3 成本指标

成本指标关系到运营成本。

指标 说明
单次查询Token消耗 输入+输出的总Token数
单次查询成本 单次查询的费用
日/月总Token消耗 一段时间内的总Token消耗
日/月总成本 一段时间内的总费用
人均Token消耗 平均每个用户的Token消耗

3.4 用户体验指标

最终还是要看用户买不买账。

指标 说明
用户满意度 用户对回答的满意程度
采纳率 用户采纳/使用Agent回答的比例
留存率 用户是否会继续使用
反馈率 用户主动反馈的比例
问题解决率 一次对话就解决问题的比例

四、评估方法

4.1 人工评估

人工评估是最准确的,但也是最昂贵的。

适用场景
  • 核心功能的质量把关
  • 新版本上线前的最终验证
  • 自动评估结果的校准
评估流程
  1. 设计评估问卷(评分标准、维度)
  2. 准备测试用例集
  3. 招募评估人员
  4. 执行评估
  5. 统计分析结果
评分量表示例
复制代码
有用性评分(1-5分):
1分 - 完全没用,答非所问
2分 - 有点相关,但帮助不大
3分 - 基本有用,但不够完善
4分 - 很有用,基本解决问题
5分 - 非常有用,超出预期

4.2 自动评估

自动评估成本低、速度快,可以大规模执行。

1. 基于规则的评估
python 复制代码
def evaluate_by_rules(question: str, answer: str) -> dict:
    """基于规则的简单评估"""
    scores = {}
    
    # 长度检查
    if len(answer) < 10:
        scores["length"] = 0  # 太短了
    elif len(answer) > 1000:
        scores["length"] = 0.5  # 太长了
    else:
        scores["length"] = 1
    
    # 关键词检查
    keywords = extract_keywords(question)
    hit_count = sum(1 for kw in keywords if kw in answer)
    scores["keyword_hit"] = hit_count / len(keywords) if keywords else 1
    
    return scores
2. 基于LLM的评估

用一个更强的LLM来评估另一个LLM的输出:

python 复制代码
from langchain.chat_models import ChatOpenAI
from langchain.prompts import PromptTemplate

evaluator_llm = ChatOpenAI(model="gpt-4", temperature=0)

eval_template = """
请评估以下回答的质量。

问题:{question}
回答:{answer}

请从以下几个维度评分(1-5分):
1. 准确性:回答是否准确,有没有事实错误
2. 相关性:回答是否与问题相关
3. 完整性:回答是否完整,有没有遗漏
4. 有用性:回答对用户是否有帮助

请以JSON格式输出评分结果。
"""

def evaluate_by_llm(question: str, answer: str) -> dict:
    """用LLM评估回答质量"""
    prompt = eval_template.format(
        question=question,
        answer=answer
    )
    result = evaluator_llm.predict(prompt)
    return parse_json(result)

优点

  • 评估维度丰富
  • 接近人类判断
  • 可扩展性强

缺点

  • 成本较高
  • 评估结果可能不稳定
  • 有"评估偏差"

4.3 半自动化评估

结合人工和自动的优点:

  1. 自动初筛:用自动评估快速筛选出明显有问题的
  2. 人工复核:对边界案例和重要案例进行人工评估
  3. 模型校准:用人工评估结果校准自动评估模型

4.4 A/B测试

当有多个版本的Agent时,用A/B测试来比较哪个更好:

复制代码
用户 → 流量分配 → A版本(旧版)
                → B版本(新版)
                → 收集指标对比

注意事项

  • 流量分配要随机
  • 样本量要足够大
  • 实验时间要足够长
  • 只改变一个变量

五、评估数据集构建

5.1 测试集设计原则

  1. 代表性:覆盖真实用户的各种问题类型
  2. 多样性:包含简单、中等、困难等不同难度
  3. 平衡性:各类型问题比例合理
  4. 可重复性:测试集要稳定,便于对比
  5. 可扩展性:方便后续添加新的测试用例

5.2 数据来源

  1. 真实用户查询:从生产环境日志中采样(最有价值)
  2. 人工构造:根据业务场景人工设计
  3. 数据增强:对现有问题进行改写、翻译等
  4. 公开数据集:使用公开的评测数据集

5.3 标注方法

1. 单标注

每个样本由一个人标注,速度快但可能有偏差。

2. 多标注

每个样本由多个人标注,取平均值或多数投票:

python 复制代码
def aggregate_labels(labels: list[float]) -> float:
    """聚合多个标注结果"""
    # 去掉最高分和最低分,取平均
    if len(labels) >= 3:
        sorted_labels = sorted(labels)
        labels = sorted_labels[1:-1]
    return sum(labels) / len(labels)
3. 标注一致性

计算标注人员之间的一致性,确保标注质量:

python 复制代码
# Cohen's Kappa系数、Fleiss' Kappa系数等

5.4 数据集管理

python 复制代码
# 测试集格式示例
test_set = [
    {
        "id": "q001",
        "question": "如何重置密码?",
        "category": "账户操作",
        "difficulty": "easy",
        "reference_answer": "可以在设置页面点击'忘记密码'...",
        "keywords": ["重置", "密码", "忘记密码", "设置"],
        "tags": ["新手引导", "账户安全"]
    },
    # ... 更多测试用例
]

六、LangChain评估工具

6.1 LangChain Evaluators 简介

LangChain内置了一套评估框架(Evaluators),可以方便地对Chain和Agent进行评估。

6.2 内置评估器

1. 字符串评估器(String Evaluators)
python 复制代码
from langchain.evaluation import load_evaluator

# 加载评估器
evaluator = load_evaluator("string_distance")

# 评估
result = evaluator.evaluate_strings(
    prediction="你好,我是AI助手",
    reference="你好,我是人工智能助手"
)

print(result)
# {'score': 0.85, 'reasoning': '...'}
2. 标准准确性评估器
python 复制代码
from langchain.evaluation import load_evaluator

evaluator = load_evaluator("labeled_criteria", criteria="correctness")

result = evaluator.evaluate_strings(
    prediction="巴黎是法国的首都",
    reference="法国的首都是巴黎",
    input="法国的首都是哪个城市?"
)
3. 标准相关性评估器
python 复制代码
evaluator = load_evaluator("criteria", criteria="relevance")

result = evaluator.evaluate_strings(
    prediction="巴黎是法国的首都,也是法国最大的城市...",
    input="法国的首都是哪个城市?"
)

6.3 自定义评估器

你也可以创建自己的评估器:

python 复制代码
from langchain.evaluation import StringEvaluator

class MyCustomEvaluator(StringEvaluator):
    """自定义评估器"""
    
    @property
    def evaluation_name(self) -> str:
        return "my_custom_eval"
    
    def _evaluate_strings(
        self,
        prediction: str,
        reference: str = None,
        input: str = None,
        **kwargs
    ) -> dict:
        # 自定义评估逻辑
        score = calculate_score(prediction, reference, input)
        
        return {
            "score": score,
            "reasoning": "评估理由..."
        }

6.4 对Agent进行评估

python 复制代码
from langchain.agents import AgentType, initialize_agent
from langchain.evaluation import load_evaluator, AgentTrajectoryEvaluator

# 创建Agent
agent = initialize_agent(
    tools,
    llm,
    agent=AgentType.OPENAI_FUNCTIONS,
    verbose=True
)

# 加载Agent轨迹评估器
evaluator = load_evaluator("trajectory")

# 评估
result = evaluator.evaluate_agent_trajectory(
    prediction=agent.run("用户问题"),
    input="用户问题",
    agent_trajectory=agent_trajectory
)

七、可观测性体系

评估是"离线"的,可观测性是"在线"的。生产环境需要实时监控Agent的表现。

7.1 日志系统

1. 记录什么
python 复制代码
# 完整的请求日志
{
    "request_id": "uuid",
    "timestamp": "2024-01-01T12:00:00Z",
    "user_id": "user123",
    "session_id": "session456",
    "query": "用户的问题",
    "answer": "Agent的回答",
    "model": "gpt-3.5-turbo",
    "tokens": {
        "input": 100,
        "output": 200,
        "total": 300
    },
    "cost": 0.0005,
    "latency_ms": 2500,
    "status": "success",
    "error": null,
    "tools_used": ["search", "calculator"],
    "steps": 3,
    "user_feedback": 5  # 用户评分(如果有)
}
2. 日志级别
python 复制代码
import logging

logger = logging.getLogger(__name__)

# DEBUG:详细的调试信息
logger.debug("详细的调试信息...")

# INFO:正常的请求日志
logger.info("请求处理完成")

# WARNING:需要注意但不影响使用
logger.warning("Token使用量接近阈值")

# ERROR:错误
logger.error("LLM调用失败", exc_info=True)

# CRITICAL:严重错误
logger.critical("系统不可用")

7.2 指标监控

核心指标
python 复制代码
from prometheus_client import Counter, Histogram, Gauge

# 请求计数
requests_total = Counter('agent_requests_total', 'Total requests')

# 延迟直方图
request_duration = Histogram(
    'agent_request_duration_seconds',
    'Request duration in seconds',
    buckets=[0.5, 1, 2, 5, 10, 30]
)

# Token使用量
tokens_total = Counter('agent_tokens_total', 'Total tokens used')

# 错误计数
errors_total = Counter('agent_errors_total', 'Total errors')

# 当前活跃用户数
active_users = Gauge('agent_active_users', 'Active users')

7.3 链路追踪

对于复杂的Agent,需要追踪每一步的执行情况:

复制代码
用户请求
  ├── 意图识别 (20ms)
  ├── 工具选择 (100ms)
  ├── 调用搜索工具 (500ms)
  ├── LLM推理 (1500ms)
  └── 返回结果

可以使用OpenTelemetry等工具实现全链路追踪。

7.4 可视化看板

搭建Grafana看板,实时监控关键指标:

  • 总览面板:请求量、成功率、平均延迟
  • 质量面板:用户满意度、问题解决率
  • 成本面板:Token消耗、日/月费用
  • 错误面板:错误率、错误类型分布
  • 用户面板:活跃用户、留存率

八、实战:搭建完整的评估体系

8.1 需求分析

我们需要搭建一套评估体系,能够:

  1. 定期自动评估Agent的质量
  2. 实时监控生产环境的表现
  3. 发现异常及时告警
  4. 生成评估报告

8.2 架构设计

复制代码
测试数据集 → 评估引擎 → 评估结果 → 报告生成
                          ↓
生产日志 → 实时监控 → 告警系统

8.3 代码实现

1. 评估引擎
python 复制代码
import json
from datetime import datetime
from typing import List, Dict
from langchain.chat_models import ChatOpenAI
from langchain.evaluation import load_evaluator

class AgentEvaluator:
    """Agent评估引擎"""
    
    def __init__(self, test_set_path: str):
        self.test_set = self._load_test_set(test_set_path)
        self.llm_evaluator = load_evaluator("labeled_criteria", criteria="helpfulness")
        self.results = []
    
    def _load_test_set(self, path: str) -> List[Dict]:
        """加载测试集"""
        with open(path, 'r') as f:
            return json.load(f)
    
    def evaluate_agent(self, agent) -> Dict:
        """评估Agent"""
        print(f"开始评估,共{len(self.test_set)}个测试用例")
        
        scores = []
        for i, test_case in enumerate(self.test_set):
            print(f"评估第{i+1}/{len(self.test_set)}个...")
            
            # 运行Agent
            prediction = agent.run(test_case["question"])
            
            # 评估
            eval_result = self.llm_evaluator.evaluate_strings(
                prediction=prediction,
                reference=test_case.get("reference_answer"),
                input=test_case["question"]
            )
            
            # 记录结果
            result = {
                "test_case_id": test_case["id"],
                "question": test_case["question"],
                "prediction": prediction,
                "score": eval_result["score"],
                "reasoning": eval_result.get("reasoning", ""),
                "category": test_case.get("category"),
                "difficulty": test_case.get("difficulty")
            }
            self.results.append(result)
            scores.append(eval_result["score"])
        
        # 统计汇总
        summary = {
            "total": len(scores),
            "avg_score": sum(scores) / len(scores) if scores else 0,
            "max_score": max(scores) if scores else 0,
            "min_score": min(scores) if scores else 0,
            "pass_rate": sum(1 for s in scores if s >= 3) / len(scores) if scores else 0,
            "by_category": self._group_by_category(),
            "by_difficulty": self._group_by_difficulty(),
            "timestamp": datetime.now().isoformat()
        }
        
        return summary
    
    def _group_by_category(self) -> Dict[str, Dict]:
        """按类别分组统计"""
        categories = {}
        for r in self.results:
            cat = r.get("category", "other")
            if cat not in categories:
                categories[cat] = {"count": 0, "scores": []}
            categories[cat]["count"] += 1
            categories[cat]["scores"].append(r["score"])
        
        # 计算平均分
        for cat in categories:
            scores = categories[cat]["scores"]
            categories[cat]["avg_score"] = sum(scores) / len(scores)
        
        return categories
    
    def save_report(self, path: str):
        """保存评估报告"""
        report = {
            "summary": self.evaluate_agent(None),  # 注意:实际使用时需要传入agent
            "details": self.results
        }
        with open(path, 'w') as f:
            json.dump(report, f, ensure_ascii=False, indent=2)
2. 实时监控
python 复制代码
import time
from prometheus_client import start_http_server, Counter, Histogram

class AgentMonitor:
    """Agent监控器"""
    
    def __init__(self):
        # 定义指标
        self.requests_total = Counter('agent_requests_total', 'Total requests')
        self.request_duration = Histogram(
            'agent_request_duration_seconds',
            'Request duration',
            buckets=[0.1, 0.5, 1, 2, 5, 10, 30]
        )
        self.tokens_total = Counter('agent_tokens_total', 'Total tokens')
        self.errors_total = Counter('agent_errors_total', 'Total errors')
        self.user_satisfaction = Histogram(
            'agent_user_satisfaction',
            'User satisfaction score',
            buckets=[1, 2, 3, 4, 5]
        )
        
        # 启动指标服务
        start_http_server(8000)
    
    def record_request(self, duration: float, tokens: int, success: bool, satisfaction: int = None):
        """记录一次请求"""
        self.requests_total.inc()
        self.request_duration.observe(duration)
        self.tokens_total.inc(tokens)
        
        if not success:
            self.errors_total.inc()
        
        if satisfaction:
            self.user_satisfaction.observe(satisfaction)
3. 告警系统
python 复制代码
class AlertManager:
    """告警管理器"""
    
    def __init__(self):
        self.thresholds = {
            "error_rate": 0.05,  # 错误率超过5%告警
            "latency_p95": 10,   # P95延迟超过10秒告警
            "cost_per_day": 1000 # 日成本超过1000元告警
        }
    
    def check_alerts(self, metrics: dict) -> list:
        """检查是否需要告警"""
        alerts = []
        
        # 检查错误率
        if metrics.get("error_rate", 0) > self.thresholds["error_rate"]:
            alerts.append({
                "level": "critical",
                "message": f"错误率过高:{metrics['error_rate']:.2%}",
                "metric": "error_rate"
            })
        
        # 检查延迟
        if metrics.get("latency_p95", 0) > self.thresholds["latency_p95"]:
            alerts.append({
                "level": "warning",
                "message": f"P95延迟过高:{metrics['latency_p95']:.2f}s",
                "metric": "latency_p95"
            })
        
        # 检查成本
        if metrics.get("cost_per_day", 0) > self.thresholds["cost_per_day"]:
            alerts.append({
                "level": "warning",
                "message": f"日成本过高:¥{metrics['cost_per_day']:.2f}",
                "metric": "cost_per_day"
            })
        
        return alerts

九、最佳实践

9.1 评估流程建议

  1. 建立基线:先评估当前版本,建立基准线
  2. 小步迭代:每次只改一个变量,评估效果
  3. 持续评估:定期评估,及时发现退化
  4. 人工+自动结合:自动评估日常监控,人工评估重点把关
  5. 数据驱动:所有优化决策都要有数据支撑

9.2 常见误区

误区1:只看平均分

平均分很重要,但不够。还要看:

  • 分布情况(是普遍好还是两极分化?)
  • 最差案例(木桶效应)
  • 不同类型问题的表现
误区2:评估集太小

测试用例太少,结果不具备统计意义。建议至少100+个测试用例。

误区3:只评估质量,不评估成本

质量再好,成本太高也用不起。要在质量和成本之间找平衡。

误区4:一劳永逸

评估不是一次性的,要持续进行。Agent在变,用户需求也在变。

9.3 持续优化循环

复制代码
评估 → 发现问题 → 分析原因 → 优化改进 → 再评估 → ...

这是一个持续迭代的过程,没有最好,只有更好。

十、总结

本文我们详细介绍了LangChain Agent的评估与可观测性体系,主要内容包括:

  1. 评估指标体系:质量、性能、成本、用户体验四大维度
  2. 评估方法:人工评估、自动评估、半自动化、A/B测试
  3. 数据集构建:测试集设计、数据来源、标注方法
  4. LangChain评估工具:Evaluators的使用
  5. 可观测性体系:日志、指标、链路追踪、可视化
  6. 实战项目:从零搭建完整的评估监控体系
  7. 最佳实践:评估流程、常见误区、持续优化

记住一句话:没有评估,就没有优化。

想要把Agent做好,评估体系是必不可少的。从今天开始,建立你的Agent评估体系吧!

在下一篇文章中,我们将通过几个真实的企业级案例,来看看LangChain在实际业务中是如何应用的,敬请期待!