现在做 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() 拿到这三个东西之后,自己就开始干活了。它内部干的事,翻译成人话就是:
markdown
打开试卷(从 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() 就是个自动跑流程的人,你把试卷、学生、老师交给它,它帮你从头跑到尾,分数传到云端,你去网页看成绩单。