LangSmith Evaluate实战评估Agent

文章目录

现在做 Agent 测评的一个比较主流的平台:LangSmith!

你可以在上面做数据集,做 QA,做 Agent 的行为观测。今天主要是给大家介绍一下,怎么用这个平台做 Evaluate。

参数规范化

我们用这个 langsmith 平台做 Evaluate 需要的参数如下:

  • 试卷(题目+参考答案)
  • 做题的人
  • 阅卷的老师(师傅模型)
  • 考试总指挥

我们可以把这个测评理解成,让计算机帮我跑一次考试。

第一步:创建数据集、准备数据

我们其实是需要在langsmith平台上创建数据集,并导入一些数据上去。可以通过手动去上传,也可以通过官方的sdk去做。

python 复制代码
from langsmith import Client
    client = Client()

    # 数据集名称(在 LangSmith 网页上会显示为这个名)
    dataset_name = "LangSmith 学习 ------ 知识问答数据集"

    # ---- 手工准备 6 条中文 Q&A 样本 ----
    # 每条是一个 (question, reference_answer) 元组
    examples = [
        (
            "LangSmith 和 LangChain 有原生集成吗?",
            "有的,LangSmith 与 LangChain 以及 LangGraph 都有原生集成。"
        ),
        (
            "LangSmith 支持哪些类型的评估?",
            "LangSmith 支持在线评估和离线评估。在线评估可以抽样生产流量实时打分;"
            "离线评估可以基于数据集批量运行实验,支持自定义代码评估器和 LLM-as-Judge。"
        ),
        (
            "如何在 Python 中启用 LangSmith 的追踪功能?",
            "设置环境变量 LANGSMITH_TRACING=true 和 LANGSMITH_API_KEY=你的密钥即可启用追踪。"
        ),
        (
            "@traceable 装饰器的作用是什么?",
            "@traceable 装饰器用于标记一个函数,让 LangSmith 自动记录该函数的"
            "输入、输出、耗时等追踪信息,无需手动埋点。"
        ),
        (
            "LangSmith 中的 Dataset 是什么?",
            "Dataset 是评估用的样本集合,每条样本包含 inputs(输入)"
            "和 reference outputs(参考答案),用于批量测试 LLM 应用的表现。"
        ),
        (
            "如何用 LangSmith SDK 批量创建评估样本?",
            "使用 client.create_examples() 方法,传入 inputs 和 outputs 列表,"
            "以及目标 dataset_id 即可批量创建。"
        ),
    ]

你可以理解这个是在出试卷,出了 6 道题,还有相对应的参考答案!

先准备 6 道题,每道题是 (问题, 标准答案) 这样一对。这时候还只是 Python 列表,存在你电脑内存里。

我们要对出的题做去重处理,防止之前出过":

python 复制代码
# 第 138 行
existing = list(client.list_datasets(dataset_name=dataset_name))
if existing:
    print(f"数据集已存在,跳过创建。")
    return dataset_name

先问一句:"这个试卷我之前出过没?"出过就直接用旧的,不重复出题。你多跑几次 demo 不会创建一堆同名数据集。

第二步:在langsmith云端创建一个空的试卷壳子

python 复制代码
inputs_list  = [{"question": q} for q, _ in examples]   # 把问题包成 dict
outputs_list = [{"answer": a} for _, a in examples]     # 把答案包成 dict
# 创建空数据集
    # dataset_type="kv" 表示 key-value 自由格式(最常用)
    dataset = client.create_dataset(
        dataset_name=dataset_name,
        description="用于学习 LangSmith evaluate() 的测试数据集(中文)",
    )

把 6 道题一次性塞进 试卷壳子里。create_examples 传的是列表,不是一条一条传------就像你不会一道题一道题往试卷上贴,而是一次性打印整张卷子。

第三步:学生到场,回答问题

python 复制代码
 def simple_qa(question: str) -> str:
        """一个简单的 LLM 问答函数 ------ 调 qwen3-max 直接回答问题。

      
        evaluate() 的用法完全一样,换哪个应用都一样。
        """
        from openai import OpenAI
        client = OpenAI(api_key=api_key, base_url=base_url)

        completion = client.chat.completions.create(
            model=model,
            messages=[
                {
                    "role": "system",
                    "content": "你是一个 LangSmith 知识问答助手。请用简洁准确的中文回答问题。"
                },
                {"role": "user", "content": question},
            ],
        )
        return completion.choices[0].message.content

这就是"学生"------你给它一个问题,它调 qwen3-max 拿到回答,返回一段文字。就这么简单。

还要做一个做适配器翻译,把问题提出来丢给大模型:

python 复制代码
    def target_function(inputs: dict) -> str:
        """适配器:从数据集的 inputs dict 里取 question,转发给 simple_qa。"""
        return simple_qa(inputs["question"])

target_function 就是干这个的。你可以理解为它是试卷分发员:从试卷上把题目读出来,念给学生听。

如果没有这一步,直接把 dict 塞给 simple_qa,它会收到一个字典而不是字符串,就报错了。

第四步:请阅卷老师上场

这里定义了两个阅卷老师,风格完全不同。

第一个老师:语义相似度(很贵但很聪明)
python 复制代码
    def semantic_similarity(inputs: dict, reference_outputs: dict, outputs: dict) -> dict:
        """LLM-as-Judge:让裁判 LLM 评估答案语义是否正确。

        评估器签名:(inputs, reference_outputs, outputs) -> {"score": ..., "key": ...}
        这是 LangSmith 推荐的签名风格(新式签名),
        也是你在上一课 evaluators_demo.py Part 2 里写的同款。
        """
        question = inputs["question"]
        reference = reference_outputs["answer"]    # 注意:数据集里参考答案的 key 是 "answer"
        model_output = outputs

        completion = judge_client.beta.chat.completions.parse(
            model=model,
            messages=[
                {
                    "role": "system",
                    "content": (
                        "你是一个语义相似度评估器。请比较「参考答案」和「待评估回答」的语义。"
                        "给出 1 到 10 的分数,1 分表示完全无关,10 分表示语义完全一致。"
                        "请以 JSON 格式输出,key 为 'similarity_score'。"
                    ),
                },
                {
                    "role": "user",
                    "content": (
                        f"问题:{question}\n"
                        f"参考答案:{reference}\n"
                        f"待评估回答:{model_output}"
                    ),
                },
            ],
            response_format=SimilarityScore,
        )
        score = completion.choices[0].message.parsed.similarity_score
        return {"score": score, "key": "语义相似度"}

这个老师不自己判断对错------它把"题目 + 标准答案 + 学生回答"三个东西一起发给另一个 LLM(裁判模型),让裁判打分。

第二个老师:长度合理性(免费但很笨)
python 复制代码
    def is_concise_enough(reference_outputs: dict, outputs: dict) -> dict:
        """简单规则评估器:检查回答长度是否在参考回答的 2.5 倍以内。

        不需要调 LLM,纯 Python 判断。
        课程原版用的是 1.5 倍,但参考答案本身比较长(多条句子),
        实际模型输出也可能较长,改成 2.5 倍更合理。
        """
        ref_len = len(reference_outputs["answer"])
        out_len = len(str(outputs))
        score = out_len < 2.5 * ref_len
        return {"score": int(score), "key": "长度合理性"}

    print("评估器已定义:")
    print("  · 语义相似度(LLM-as-Judge, 1~10 分)")
    print("  · 长度合理性(简单规则, 0/1 分)")
    print()

    return semantic_similarity, is_concise_enough

这个老师不调 LLM,就做一件事:学生的回答是不是太啰嗦了。如果学生回答的字数不超过标准答案的 2.5 倍,就算合格(1 分),否则不合格(0 分)。

第五步:考试开始!evaluate() 上场

前面三步都是在做准备------出了试卷、请了学生、请了阅卷老师。但一直没开始考试。evaluate() 就是那个说"好,现在开始"的人。

python 复制代码
# 第 329-334 行
results = evaluate(
    target_function,                                   # 学生 + 试卷分发员
    data=dataset_name,                                 # 试卷名字
    evaluators=[semantic_similarity, is_concise_enough],  # 两个阅卷老师
    experiment_prefix="qwen3-max 第一次实验",            # 这次考试叫什么名字
)

就这四行。evaluate() 拿到这三个东西之后,自己就开始干活了。它内部干的事,翻译成人话就是:

复制代码
打开试卷(从 LangSmith 云端把数据集拉下来)
对每一道题:
    1. 把题目念给学生听 → 学生写答案
    2. 把(题目、标准答案、学生答案)交给第一个老师打分
    3. 把(标准答案、学生答案)交给第二个老师打分
    4. 把两个分数传到 LangSmith 云端
全部做完 → 返回结果

你不用写 for 循环,不用管"第几道题了",不用管"分数怎么存"。evaluate() 全包了。

跑完后打开 LangSmith 网页,你会看到一张表:每一行是一道题,右边两列分别是两个老师打的分。

第六步:换个学生再考一次

python

python 复制代码
# 第 373 行
alt_model = "qwen-plus"    # 换一个更便宜的模型

Part 5 做的事情和 Part 4 一模一样 ,唯一区别是学生从 qwen3-max 换成了 qwen-plus

python

python 复制代码
# 第 398-407 行
evaluate(
    alt_target,                                    # ← 新学生
    data=dataset_name,                             # ← 同一张试卷
    evaluators=[semantic_similarity, is_concise_enough],  # ← 同样的老师
    experiment_prefix=f"{alt_model} 对比实验",
    metadata={"对比说明": "qwen-plus vs qwen3-max..."},
)

试卷没变、老师没变、只换了学生。这样你就能公平对比:同一个题,qwen3-max 答得好还是 qwen-plus 答得好?分数说话。

metadata 就是给这次考试贴个便签条,写上"这是 qwen-plus 的那场",方便你以后在网页上几十个实验里快速找到它。

整个Demo完整代码分享

下面是一个完整的代码,你可以去跑一下:

python 复制代码
# -*- coding: utf-8 -*-
"""
Module 2 · Experiments(实验)学习 Demo
=======================================



上一课你写了评估器函数(correct_label / compare_semantic_similarity),
但都是手动喂一两条数据看结果。这课的核心是:把评估器和数据集交给
langsmith.evaluate(),让它帮你自动遍历整个数据集、批量调用应用、逐个打分。

一句话:evaluate() 就是个智能的 for 循环。

    evaluate(
        target_function,    # ← 你要测试的应用
        data=dataset_name,  # ← 你在 LangSmith 云端建好的数据集
        evaluators=[...],   # ← 你上一课写的评估器
    )

它做的事情:
  1. 从 LangSmith 云端拉取数据集(N 条 Q&A)
  2. 对每条数据,执行 target_function(question) → 拿到模型输出
  3. 把每条 (inputs, 参考输出, 模型输出) 传给每个 evaluator → 拿到分数
  4. 把所有分数上传到 LangSmith 云端 → 你去网页看聚合结果

本 Demo 分六部分:
  Part 0: 环境配置(连 LangSmith + 百炼)
  Part 1: 程序化创建数据集(在 LangSmith 云端建一个有 N 条样本的数据集)
  Part 2: 定义"被测试的应用"(一个简单的 LLM Q&A 函数)
  Part 3: 定义评估器(语义相似度)
  Part 4: 第一次 evaluate() ------ 核心!
  Part 5: 对比实验 ------ 换个应用版本再跑一次,看分数差异
  Part 6: evaluate() 的各种参数(重复、并发、元数据、数据集切片)

⚠️ 注意:本 Demo 需要联网 + API Key。
运行前确保 .env 里的 DASHSCOPE_API_KEY 和 LANGSMITH_API_KEY 已配置。

运行方式:
  python module_2/experiments_demo.py              # 跑全部
  python module_2/experiments_demo.py 1 2 3 4      # 只跑指定的 Part
"""

import os
import sys
import time
from pathlib import Path

ENV_PATH = Path(__file__).resolve().parent.parent / ".env"


# ============================================================
# Part 0 · 环境配置
# ============================================================
def setup_env():
    """装载环境变量,初始化 LangSmith Client 和百炼 Client。"""
    from dotenv import load_dotenv
    load_dotenv(dotenv_path=ENV_PATH, override=True)

    api_key = os.getenv("DASHSCOPE_API_KEY") or os.getenv("OPENAI_API_KEY")
    base_url = os.getenv(
        "JUDGE_BASE_URL", "https://dashscope.aliyuncs.com/compatible-mode/v1"
    )
    model = os.getenv("JUDGE_MODEL", "qwen3-max")
    ls_api_key = os.getenv("LANGSMITH_API_KEY")

    if not api_key:
        print("未找到 API Key,请在 .env 里配置 DASHSCOPE_API_KEY")
        sys.exit(1)
    if not ls_api_key:
        print("未找到 LANGSMITH_API_KEY,请在 .env 里配置")
        sys.exit(1)

    return api_key, base_url, model


# ============================================================
# Part 1 · 程序化创建数据集
# ============================================================
def part1():
    """在 LangSmith 云端创建一个中文问答数据集。

    数据集就像考试的"试卷"------每条样本包含:
      inputs:  {"question": "用户问的问题"}
      outputs: {"answer": "参考答案 / 标准答案"}

    创建方式有两种:
    A) 在 LangSmith 网页 UI 上手动创建(dataset_upload.ipynb 演示了这种方式)
    B) 用 LangSmith SDK 程序化创建(本 demo 演示这种方式)

    第二条路径对你的 BSP Agent 项目特别有用:
    你可以写个脚本,把摄像头模组的 FAQ、芯片 datasheet Q&A
    批量转成评估数据集,不用在网页上一个一个贴。
    """
    from dotenv import load_dotenv
    load_dotenv(dotenv_path=ENV_PATH, override=True)

    from langsmith import Client
    client = Client()

    # 数据集名称(在 LangSmith 网页上会显示为这个名)
    dataset_name = "LangSmith 学习 ------ 知识问答数据集"

    # ---- 手工准备 6 条中文 Q&A 样本 ----
    # 每条是一个 (question, reference_answer) 元组
    examples = [
        (
            "LangSmith 和 LangChain 有原生集成吗?",
            "有的,LangSmith 与 LangChain 以及 LangGraph 都有原生集成。"
        ),
        (
            "LangSmith 支持哪些类型的评估?",
            "LangSmith 支持在线评估和离线评估。在线评估可以抽样生产流量实时打分;"
            "离线评估可以基于数据集批量运行实验,支持自定义代码评估器和 LLM-as-Judge。"
        ),
        (
            "如何在 Python 中启用 LangSmith 的追踪功能?",
            "设置环境变量 LANGSMITH_TRACING=true 和 LANGSMITH_API_KEY=你的密钥即可启用追踪。"
        ),
        (
            "@traceable 装饰器的作用是什么?",
            "@traceable 装饰器用于标记一个函数,让 LangSmith 自动记录该函数的"
            "输入、输出、耗时等追踪信息,无需手动埋点。"
        ),
        (
            "LangSmith 中的 Dataset 是什么?",
            "Dataset 是评估用的样本集合,每条样本包含 inputs(输入)"
            "和 reference outputs(参考答案),用于批量测试 LLM 应用的表现。"
        ),
        (
            "如何用 LangSmith SDK 批量创建评估样本?",
            "使用 client.create_examples() 方法,传入 inputs 和 outputs 列表,"
            "以及目标 dataset_id 即可批量创建。"
        ),
    ]

    # 检查数据集是否已存在(避免重复创建)
    existing = list(client.list_datasets(dataset_name=dataset_name))
    if existing:
        print(f"数据集 '{dataset_name}' 已存在,跳过创建。")
        return dataset_name

    # 创建空数据集
    # dataset_type="kv" 表示 key-value 自由格式(最常用)
    dataset = client.create_dataset(
        dataset_name=dataset_name,
        description="用于学习 LangSmith evaluate() 的测试数据集(中文)",
    )
    print(f"数据集已创建: {dataset.name} (id: {dataset.id})")

    # 往数据集里批量写入样本
    inputs_list = [{"question": q} for q, _ in examples]
    outputs_list = [{"answer": a} for _, a in examples]

    client.create_examples(
        inputs=inputs_list,
        outputs=outputs_list,
        dataset_id=dataset.id,
    )
    print(f"已写入 {len(examples)} 条样本")

    return dataset_name


# ============================================================
# Part 2 · 定义"被测试的应用"
# ============================================================
def part2():
    """
    定义你要测试的应用(target function)。

    课程 notebook 里是一个完整的 RAG 应用(向量检索 + LLM 生成)。
    这里简化成直接调用 qwen3-max 回答问题,逻辑更纯粹,学习重点不变:
    evaluate() 不关心你的应用内部有多复杂,只关心 input → output 这个契约。

    关键概念 ------ 适配器函数 (target_function):
      数据集里的每条样本是 {"question": "..."} 这样的 dict,
      但你的应用 `simple_qa("...")` 接受的是 str。
      适配器负责从 dict 里取出 question 字段,转发给应用。
    """
    api_key, base_url, model = setup_env()

    print("=" * 64)
    print(f"Part 2 · 定义被测试的应用(模型: {model})")
    print("=" * 64)

    # ---- 真实的被测应用 ----
    def simple_qa(question: str) -> str:
        """一个简单的 LLM 问答函数 ------ 调 qwen3-max 直接回答问题。

        课程用的是完整 RAG(搜文档 → 拼上下文 → 调 LLM),
        这里简化掉检索环节,直接让 LLM 凭自己的知识回答。
        evaluate() 的用法完全一样,换哪个应用都一样。
        """
        from openai import OpenAI
        client = OpenAI(api_key=api_key, base_url=base_url)

        completion = client.chat.completions.create(
            model=model,
            messages=[
                {
                    "role": "system",
                    "content": "你是一个 LangSmith 知识问答助手。请用简洁准确的中文回答问题。"
                },
                {"role": "user", "content": question},
            ],
        )
        return completion.choices[0].message.content

    # ---- 适配器 ----
    # 这是 evaluate() 和你的应用之间的"翻译层":
    # evaluate() 传进来的是 Example.inputs(一个 dict),
    # 适配器把它转成你应用能接受的参数。
    def target_function(inputs: dict) -> str:
        """适配器:从数据集的 inputs dict 里取 question,转发给 simple_qa。"""
        return simple_qa(inputs["question"])

    # ---- 手工验证一下应用能正常运行 ----
    test_q = "LangSmith 是什么?用一句话回答。"
    answer = simple_qa(test_q)
    print(f"应用测试: Q={test_q}")
    print(f"         A={answer[:80]}...")
    print()

    return target_function, simple_qa, model


# ============================================================
# Part 3 · 定义评估器(复用上一课 Part 2 的 LLM-as-Judge)
# ============================================================
def part3():
    """
    定义评估器。

    这里定义两个评估器:
    1. semantic_similarity ------ 用裁判 LLM 打语义相似度分(1~10)
    2. is_concise_enough  ------ 检查回答长度是否在合理范围内(简单规则)
    """
    api_key, base_url, model = setup_env()

    from openai import OpenAI
    from pydantic import BaseModel, Field

    judge_client = OpenAI(api_key=api_key, base_url=base_url)

    class SimilarityScore(BaseModel):
        similarity_score: int = Field(
            description="语义相似度分数,1 到 10 之间,1 表示完全无关,10 表示语义完全一致"
        )

    def semantic_similarity(inputs: dict, reference_outputs: dict, outputs: dict) -> dict:
        """LLM-as-Judge:让裁判 LLM 评估答案语义是否正确。

        评估器签名:(inputs, reference_outputs, outputs) -> {"score": ..., "key": ...}
        这是 LangSmith 推荐的签名风格(新式签名),
        也是你在上一课 evaluators_demo.py Part 2 里写的同款。
        """
        question = inputs["question"]
        reference = reference_outputs["answer"]    # 注意:数据集里参考答案的 key 是 "answer"
        model_output = outputs

        completion = judge_client.beta.chat.completions.parse(
            model=model,
            messages=[
                {
                    "role": "system",
                    "content": (
                        "你是一个语义相似度评估器。请比较「参考答案」和「待评估回答」的语义。"
                        "给出 1 到 10 的分数,1 分表示完全无关,10 分表示语义完全一致。"
                        "请以 JSON 格式输出,key 为 'similarity_score'。"
                    ),
                },
                {
                    "role": "user",
                    "content": (
                        f"问题:{question}\n"
                        f"参考答案:{reference}\n"
                        f"待评估回答:{model_output}"
                    ),
                },
            ],
            response_format=SimilarityScore,
        )
        score = completion.choices[0].message.parsed.similarity_score
        return {"score": score, "key": "语义相似度"}

    def is_concise_enough(reference_outputs: dict, outputs: dict) -> dict:
        """简单规则评估器:检查回答长度是否在参考回答的 2.5 倍以内。

        不需要调 LLM,纯 Python 判断。
        课程原版用的是 1.5 倍,但参考答案本身比较长(多条句子),
        实际模型输出也可能较长,改成 2.5 倍更合理。
        """
        ref_len = len(reference_outputs["answer"])
        out_len = len(str(outputs))
        score = out_len < 2.5 * ref_len
        return {"score": int(score), "key": "长度合理性"}

    print("评估器已定义:")
    print("  · 语义相似度(LLM-as-Judge, 1~10 分)")
    print("  · 长度合理性(简单规则, 0/1 分)")
    print()

    return semantic_similarity, is_concise_enough


# ============================================================
# Part 4 · 第一次 evaluate() ------ 核心!
# ============================================================
def part4(target_function, dataset_name, semantic_similarity, is_concise_enough):
    """
    用 evaluate() 把"应用 + 数据集 + 评估器"串起来,跑一次完整实验。

    这是整个 experiments.ipynb 的精髓 ------ 就这一个函数调用。
    """
    from langsmith import evaluate

    print("=" * 64)
    print("Part 4 · 第一次 evaluate() 实验")
    print("=" * 64)
    print(f"数据集: {dataset_name}")
    print("评估器: 语义相似度 + 长度合理性")
    print()

    print("正在运行实验...")
    print("(evaluate() 会逐条从云端拉数据 → 调你的应用 → 传给评估器打分 → 上传结果)")
    print()

    results = evaluate(
        target_function,
        data=dataset_name,
        evaluators=[semantic_similarity, is_concise_enough],
        experiment_prefix="qwen3-max 第一次实验",
    )

    # results 是一个 ExperimentResults 对象,包含每条样本的评估细节
    print(f"\n实验完成!共评估了 {len(list(results))} 条样本。")
    print(f"去 LangSmith 网页查看结果: https://smith.langchain.com/")
    print()
    print("在网页上你会看到:")
    print("  · 每行是一个数据集样本,右侧显示 '语义相似度' 和 '长度合理性' 两列分数")
    print("  · 顶部自动聚合出平均分")
    print("  · 点击具体样本还能展开看到裁判 LLM 的推理过程")

    return results


# ============================================================
# Part 5 · 对比实验 ------ 换个应用版本再跑一次
# ============================================================
def part5(dataset_name, semantic_similarity, is_concise_enough):
    """
    对比实验:用另一个模型(qwen-plus)跑同一个数据集,看分数差异。

    这是 LangSmith 实验体系的核心价值:
    同一个数据集 + 同一套评估器 = 不同应用版本之间的公平对比。

    对于你的 BSP Agent 项目:
    - 同样的 SDK 输入 + 同样的评估标准
    - 对比不同版本的 Agent 输出
    - 哪个版本编译通过率更高、驱动代码质量更好 → 一目了然
    """
    from langsmith import evaluate
    from openai import OpenAI

    api_key, base_url, _ = setup_env()

    print("=" * 64)
    print("Part 5 · 对比实验")
    print("=" * 64)

    # 换一个更便宜的模型
    alt_model = "qwen-plus"

    def alt_simple_qa(question: str) -> str:
        client = OpenAI(api_key=api_key, base_url=base_url)
        completion = client.chat.completions.create(
            model=alt_model,
            messages=[
                {
                    "role": "system",
                    "content": "你是一个 LangSmith 知识问答助手。请用简洁准确的中文回答问题。"
                },
                {"role": "user", "content": question},
            ],
        )
        return completion.choices[0].message.content

    def alt_target(inputs: dict) -> str:
        return alt_simple_qa(inputs["question"])

    print(f"对比组: {alt_model} vs 上一轮 qwen3-max")
    print(f"数据集相同: {dataset_name}")
    print(f"评估器相同: 语义相似度 + 长度合理性")
    print()

    print("正在运行对比实验...")
    evaluate(
        alt_target,
        data=dataset_name,
        evaluators=[semantic_similarity, is_concise_enough],
        experiment_prefix=f"{alt_model} 对比实验",
        metadata={
            "对比说明": "qwen-plus vs qwen3-max,相同数据集和评估器",
            "模型": alt_model,
        },
    )

    print("\n对比实验完成!")
    print("去 LangSmith 网页,你会看到两个实验的结果,可以并排对比分数。")
    print("这是 LangSmith 最实用的功能之一 ------ 换模型/换提示词后,立刻看到分数变化。")


# ============================================================
# Part 6 · evaluate() 的各种参数
# ============================================================
def part6(target_function, dataset_name, semantic_similarity, is_concise_enough):
    """
    演示 evaluate() 的各种参数,让你全面了解它的能力。

    每个参数都有对应注释,标了"课程原例"的来自 notebook。
    """
    from langsmith import evaluate

    print("=" * 64)
    print("Part 6 · evaluate() 参数全览")
    print("=" * 64)

    # ---- 6.1 num_repetitions: 重复实验 ----
    # 每道题跑多次,消除 LLM 随机性带来的评分波动
    # 对于非确定性模型(temperature > 0),这个参数很有价值
    print("[6.1] num_repetitions:重复实验(跑 2 次取平均)------ 课程原例")
    evaluate(
        target_function,
        data=dataset_name,
        evaluators=[semantic_similarity],
        experiment_prefix="重复 2 次",
        num_repetitions=2,
    )
    print("  → 每条样本会跑 2 次,分数取平均\n")

    # ---- 6.2 max_concurrency: 并发 ----
    # 多线程并发执行,加快实验速度
    # 注意:如果裁判 LLM 有 QPS 限制,并发数太高可能被限流
    print("[6.2] max_concurrency:并发执行(2 线程)------ 课程原例")
    evaluate(
        target_function,
        data=dataset_name,
        evaluators=[semantic_similarity],
        experiment_prefix="并发 2",
        max_concurrency=2,
    )
    print("  → 最多 2 个线程同时跑,数据集大的时候明显加速\n")

    # ---- 6.3 metadata: 元数据 ----
    # 给实验打标签,方便在网页上搜索和筛选
    print("[6.3] metadata:添加实验元数据 ------ 课程原例")
    evaluate(
        target_function,
        data=dataset_name,
        evaluators=[semantic_similarity],
        experiment_prefix="带元数据",
        metadata={
            "model": "qwen3-max",
            "基础模型": "通义千问3",
            "评估类型": "语义相似度",
            "备注": "用于学习 LangSmith 实验功能",
        },
    )
    print("  → 元数据会显示在 LangSmith 网页的实验详情里\n")

    # ---- 6.4 数据集拆分(splits) ----
    # 只评估数据集里特定 split 的样本
    # 适用场景:先测"简单题",再测"困难题"
    print("[6.4] 数据集拆分示例")
    from langsmith import Client
    client = Client()
    try:
        # 尝试只评估特定 split(你的数据集不一定有这个 split,所以 try)
        result = evaluate(
            target_function,
            data=client.list_examples(
                dataset_name=dataset_name,
                splits=["base"]
            ),
            evaluators=[semantic_similarity],
            experiment_prefix="base split",
        )
        print("  → 只评估了 'base' 拆分里的样本")
    except Exception:
        print("  → 数据集当前没有拆分,跳过此示例")
        print("    你可以去 LangSmith 网页把部分样本标记为一个 split,然后重试\n")

    # ---- 6.5 as_of: 数据集版本 ----
    # 数据集的每次修改都会生成一个版本快照,as_of 可以指定用哪个版本
    # 适用场景:回归测试 ------ 和上次评估用完全一致的数据集版本
    print("[6.5] as_of:数据集版本快照 ------ 课程原例(需有命名版本)")
    try:
        evaluate(
            target_function,
            data=client.list_examples(
                dataset_name=dataset_name,
                as_of="initial dataset"
            ),
            evaluators=[semantic_similarity],
            experiment_prefix="初始版本",
        )
        print("  → 使用 'initial dataset' 版本的样本")
    except Exception:
        print("  → 数据集当前没有命名版本,跳过此示例")
        print("    数据集的第一个 commit 默认名为 'initial dataset'\n")

    # ---- 6.6 指定特定样本 ID ----
    # 只评估指定的几条样本
    # 适用场景:调试 ------ 只测出问题的那几条,不用全量跑
    print("[6.6] 指定特定样本 ID ------ 课程原例(需要实际 example_ids)")
    examples = list(client.list_examples(dataset_name=dataset_name))
    if examples:
        ids = [e.id for e in examples[:2]]  # 取前 2 条做演示
        evaluate(
            target_function,
            data=client.list_examples(
                dataset_name=dataset_name,
                example_ids=ids,
            ),
            evaluators=[semantic_similarity],
            experiment_prefix="指定 2 条样本",
        )
        print(f"  → 只评估了 {len(ids)} 条指定样本")
    print()

    print("参数演示完毕!总结一下 evaluate() 的参数:")
    print("  必需:  target_function, data, evaluators")
    print("  常用:  experiment_prefix(实验名前缀), metadata(元数据)")
    print("  进阶:  num_repetitions(重复), max_concurrency(并发)")
    print("  数据:  splits(拆分), as_of(版本), example_ids(指定样本)")


# ============================================================
# 入口
# ============================================================
if __name__ == "__main__":
    parts = sys.argv[1:] or ["1", "2", "3", "4", "5", "6"]

    print()
    print("╔" + "═" * 62 + "╗")
    print("║  LangSmith Evaluate 实验 · 学习 Demo                  ║")
    print("╚" + "═" * 62 + "╝")
    print()

    # 这些变量在不同 Part 间共享
    dataset_name = None
    target_function = None
    semantic_similarity = None
    is_concise_enough = None

    if "1" in parts:
        dataset_name = part1()

    if "2" in parts:
        target_function, _, model = part2()

    if "3" in parts:
        semantic_similarity, is_concise_enough = part3()

    if "4" in parts:
        # 如果前面没跑 Part 1-3,先自动准备
        if dataset_name is None:
            dataset_name = part1()
        if target_function is None:
            target_function, _, _ = part2()
        if semantic_similarity is None:
            semantic_similarity, is_concise_enough = part3()
        part4(target_function, dataset_name, semantic_similarity, is_concise_enough)

    if "5" in parts:
        if dataset_name is None:
            dataset_name = part1()
        if semantic_similarity is None:
            semantic_similarity, is_concise_enough = part3()
        part5(dataset_name, semantic_similarity, is_concise_enough)

    if "6" in parts:
        if dataset_name is None:
            dataset_name = part1()
        if target_function is None:
            target_function, _, _ = part2()
        if semantic_similarity is None:
            semantic_similarity, is_concise_enough = part3()
        part6(target_function, dataset_name, semantic_similarity, is_concise_enough)

    print()
    print("═══ 全部实验完成 ═══")
    print()
    print("现在去 LangSmith 网页 https://smith.langchain.com/")
    print("你会在 Datasets & Testing 下看到刚跑的实验,")
    print("每个实验的每条样本都有对应的评估分数。")
    print()
    print("核心收获:")
    print("1. evaluate() 就是一个自动化的 for 循环 + 打分 + 上报")
    print("2. 你的应用和评估器各是各的,通过函数签名解耦")
    print("3. 换模型/换提示词 → 同数据集同评估器再跑一次 → 直接对比分数")
    print("   这就是 LangSmith 最核心的评估实验闭环")

Part 1 出试卷 → Part 2 请学生 → Part 3 请老师 → Part 4 开始考试 → Part 5 换个学生再考一次 → Part 6 演示考试的各种选项。

evaluate() 就是个自动跑流程的人,你把试卷、学生、老师交给它,它帮你从头跑到尾,分数传到云端,你去网页看成绩单。

相关推荐
weixin_468466851 小时前
DeepSeek-V4-Flash正式版深度解析:当“轻量模型”用后训练撬动Agent能力革命
人工智能·大模型·agent·deepseek·deepseek-v4
donoot1 小时前
《大话文渊慧典》:六
人工智能·深度学习·aigc·ppstructure·文渊慧典·大话系列
阿部多瑞 ABU1 小时前
正反馈的死亡螺旋:数字资本时代泛二次元文化-情感-金融复合体的系统性分析
大数据·人工智能·金融
起司喵喵1 小时前
推荐一款基于 Python 和 Rust 开发的跨平台 GUI 自动化库!
python·rust·自动化
过期的秋刀鱼!2 小时前
学习曲线-过拟合和欠拟合要做什么以及原因
人工智能·python·深度学习·算法·机器学习·模型评估
用户3126874877202 小时前
AI Agent 开发实战(九):Grill Me 反问式规划
llm·ai编程
haerapi2 小时前
别把页面塞进三个枚举:用“数据、过程、消息”重建 UI 状态
人工智能
非优秀程序员2 小时前
Netdata 接入 RTX5090显卡(集群),实现自动化监测及预警邮件发送
人工智能
罗小罗同学2 小时前
Nat. Biomed. Eng最新发表的医学AI基础模型CLEAR,可以将诊断决策与临床概念一一匹配,不再是传统黑箱模型
人工智能·医学图像处理·医工交叉·医学ai