给 RAG Agent 装上"仪表盘":LangSmith 全链路观测与量化评估实战

给 RAG Agent 装上"仪表盘":LangSmith 全链路观测与量化评估实战

写 LLM 应用最痛苦的是什么?盲盒感。 调用了哪个工具?每步耗时多少?流式吐了多少 token?为什么这轮回答崩了? 如果无法度量它,就无法管理它。LangSmith 就是那个把 Agent 从"黑盒"变成"透明仪表盘"的观测平台。

这篇文章,我们从一个故意写错的 LangGraph 图 开始,一路走到RAG 问答系统的自动化量化评估 ,全程基于真实可运行的代码(langsmith-demo 仓库),把 LangSmith 的四大核心能力一次讲透。


目录

  1. 为什么需要 LangSmith:LLM 开发的"盲盒感"
  2. LangChain / LangGraph / LangSmith 铁三角
  3. 核心功能四件套:Tracing / Monitoring / Datasets / Evaluators
  4. 实战一:给 LangGraph 装上"行车记录仪"(Trace 调试)
  5. 实战二:RAG 全链路接入 LangSmith
  6. 重难点详解:StateGraph 状态机、Milvus 向量库、防幻觉设计、OpenAI 兼容接入
  7. 实战三:RAG 量化评估(Dataset + Evaluators + Experiment)
  8. 知识点总表 & 一句话记忆
  9. 真实踩坑实录(都是血泪)
  10. 面试金句

1. 为什么需要 LangSmith:LLM 开发的"盲盒感"

传统后端出 Bug 不可怕:报错有堆栈、接口有日志、数据库有慢查询,一层层查总能定位。

但 LLM 应用不一样。你写一个 Agent:

js 复制代码
const answer = await agent.invoke({ question: "无理由退货要几天?" });

这一行背后可能发生了 10 次 LLM 调用、3 次工具调用、1 次向量检索。问题来了:

  • 它调用检索了吗?还是直接瞎答?
  • 每一步花了多久?哪个环节最慢?
  • 这轮回答消耗了多少 token?烧钱大户是谁?
  • 为什么上次好好的,这次就崩了?

没有观测手段时,你只能靠 console.log 和"再跑一次看看"------这就是盲盒感。

LangSmith 就是给 LLM 应用装的"行车记录仪 + 仪表盘" :每一次运行都被完整记录成一条 trace,像一棵树一样展示每一步的输入、输出、耗时、token 用量,并且能批量回放、对比、评估。

一句话记忆:LangSmith = LLM 应用的 Grafana + Sentry + 自动化测试平台。


2. LangChain / LangGraph / LangSmith 铁三角

这三个名字长得像,经常搞混。用一句话区分:

库 定位 类比
LangChain LLM 应用开发框架(模型、提示词、检索、链) 脚手架
LangGraph 有状态、有环的图编排框架(Agent 工作流) 流程图
LangSmith 全链路观测与评估平台(SaaS) 仪表盘

它们的关系:

markdown 复制代码
你的代码(LangChain/LangGraph 写的 Agent)
        │  自动埋点(零侵入)
        ▼
   LangSmith SDK(收集 trace)
        │  HTTP 上报
        ▼
  LangSmith 云端(可视化、监控、评估)

本仓库的 demo/ 和 rag-demo/ 就分别演示了 LangGraph 写图 和 LangChain+LangGraph 写 RAG,而它们全都自动把数据喂给了 LangSmith。

关键认知 :接入 LangSmith 不需要改业务代码 。只要装好 SDK、在 .env 里配好 API Key,LangChain/LangGraph 内部会自动上报。这是它最爽的一点。


3. 核心功能四件套

仓库根目录的 readme.md 把 LangSmith 的核心能力总结为四件套:

① Tracing(追踪)

每次 Agent 执行生成一条完整 trace:哪个节点、哪步检索、哪次 LLM 调用、输入输出、耗时、token。调试 Bug 的利器。

② Monitoring(监控)

Agent 上线后的后台实时监控:token 开销、耗时分布、工具调用统计。看整体表现。

③ Datasets(数据集)

把"用户提问 + 标准答案"统一存成测试样本(比如仓库里的 12 条退货/物流/会员问答对)。这是自动评估的"考卷"。

④ Evaluators(评估器)

定义打分规则,让 LLM 当"考官",对 Agent 的回答批量打分:有没有幻觉?回答够不够好?检索到的内容相关吗?这是量化优化 Agent 的依据。

一句话记忆:Tracing 看过程,Monitoring 看整体,Datasets 出考卷,Evaluators 打分数。


4. 实战一:给 LangGraph 装上"行车记录仪"

仓库的 demo/ 是最小演示:一个 故意抛错 的 LangGraph 图。

4.1 代码解剖

js 复制代码
// demo/src/trigger-error.mjs
import "dotenv/config";  // ① 加载 .env,LangSmith 配置自动生效
import { Annotation, END, START, StateGraph } from "@langchain/langgraph";

// ② 定义状态:像"背包"一样在节点间传递数据
const StateAnnotation = Annotation.Root({
  text: Annotation({
    reducer: (_prev, next) => next,  // 后写的覆盖先写的
    default: () => "",
  })
});

const stepOk = (state) => ({ text: `${state.text}[ok]` }); // 正常节点
const stepThrow = () => {                                   // 故意抛错的节点
  throw new Error("DemoError:节点内故意跑错:(trigger-error.mjs)")
}

// ③ 建图:START → step_ok → step_throw → END
const graph = new StateGraph(StateAnnotation)
  .addNode("step_ok", stepOk)
  .addNode("step_throw", stepThrow)
  .addEdge(START, "step_ok")
  .addEdge("step_ok", "step_throw")
  .addEdge("step_throw", END)
  .compile();

try {
  await graph.invoke({ text: "start" });
  console.log("不应执行");
} catch(err) {
  console.error("已捕获:", err?.message ?? err);
  process.exitCode = 1;
}

4.2 为什么用 LangGraph 而不用普通函数链?

因为 Agent 的工作流不是一条直线 :可能要循环(问→答→再问)、要分支(判断要不要检索)、要多人协作。LangGraph 用图来表达这种复杂流程,每个节点是一个函数,状态在节点间流动。

4.3 重难点详解:StateGraph 的状态机制

这是 LangGraph 最容易懵的地方,用背包比喻:

把 State 想象成一个背包 。每个节点执行完,把新东西塞进背包(返回值 merge 进状态);下一个节点从背包里取自己需要的东西。reducer 就是"塞东西时发生冲突怎么处理"的规则。

js 复制代码
reducer: (_prev, next) => next,  // 新值直接覆盖旧值
reducer: (prev, next) => [...prev, ...next], // 数组就拼接

本仓库 RAG Agent 里的状态有三个字段:

js 复制代码
const GraphState = Annotation.Root({
  question: Annotation,   // 用户问题(进)
  context: Annotation,    // 检索到的文档片段(中间)
  answer: Annotation      // 最终回答(出)
});

节点 retrieve 往背包塞 context,节点 generate 从背包取 question + context,再塞 answer。就这么简单。

4.4 这个 demo 的看点:错误也能被 trace

节点故意抛错,LangGraph 的 trace 照样把错误记录在案。去 LangSmith 控制台打开这条 trace,你会看到:

  • step_ok 节点:正常输出
  • step_throw 节点:红色错误标记 + 完整报错信息
  • 整条链路的耗时、每一步的输入输出

这就是"行车记录仪":即使翻车了,记录仪也拍下了翻车全过程 。比裸奔时只看到一个 Error 强太多。


5. 实战二:RAG 全链路接入 LangSmith

rag-demo/ 是一个完整的 RAG(检索增强生成)问答系统:先往向量库灌知识,再用 LangGraph 组织"检索→生成",所有运行都自动 trace 到 LangSmith。

5.1 项目结构一览

bash 复制代码
rag-demo/
├── .env                  # 密钥与配置(LangSmith / Milvus / 通义千问)
├── data/                 # 知识库原始文档(会员、支付、售后、物流...)
├── src/
│   ├── milvus_insert.mjs # ① 灌库:文档→切块→向量→写入 Milvus
│   ├── rag_agent.mjs     # ② RAG Agent:LangGraph 编排检索+生成
│   ├── cli.mjs           # ③ 命令行问答入口
│   └── eval/             # ④ 量化评估三件套
│       ├── build_dataset.mjs  # 建数据集
│       ├── evaluators.mjs     # 定义评估器
│       └── run_eval.mjs       # 跑评估

5.2 .env 配置:一条链路三套密钥

env 复制代码
# ---- LangSmith 观测 ----
LANGSMITH_API_KEY=lsv2_pt_xxxx
LANGSMITH_PROJECT=rag-demo
LANGSMITH_TRACING_V2=true

# ---- Milvus 向量库 ----
MILVUS_URI=http://localhost:19530
MILVUS_COLLECTION=rag_docs

# ---- 通义千问(OpenAI 兼容) ----
OPENAI_API_KEY=sk-xxxx
OPENAI_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
EMBEDDING_MODEL=text-embedding-v3
MODEL_NAME=qwen-plus

三个重点:

  1. LANGSMITH_TRACING_V2=true 是总开关 。LangChain core 的 isTracingEnabled 会检查它(LANGSMITH_TRACING_V2 / LANGSMITH_TRACING / LANGCHAIN_TRACING_V2 任一为 true 即开启)。代码一行不改,全局自动 trace。
  2. 通义千问走 OpenAI 兼容协议 :baseURL 指到阿里云 DashScope,apiKey 用百炼的 key,model 用 qwen-plus。意味着 @langchain/openai 全家桶(ChatOpenAI / OpenAIEmbeddings)直接可用,不用换 SDK。
  3. Embedding 和 Chat 用同一套 baseURL ,但模型不同:text-embedding-v3(向量化)vs qwen-plus(对话)。

5.3 灌库:milvus_insert.mjs 详解

这一步把知识文档变成向量存进 Milvus,让 Agent 之后"查得到"。

js 复制代码
// 关键链路:读文件 → 切块 → 向量化 → 建集合建索引 → 插入
const chunks = await loadChunks();                       // ① 读 data/ 下所有 md/txt
const vectors = await embeddings.embedDocuments(         // ③ 每块文本 → 1536维向量
  chunks.map(c => c.pageContent)
);
const dim = vectors[0].length;                           // 向量维度

await client.createCollection({                          // ④ 建集合(表)
  collection_name: COLLECTION,
  fields: [
    { name: "langchain_primaryid", is_primary_key: true, data_type: DataType.Int64, autoID: true },
    { name: "langchain_vector", data_type: DataType.FloatVector, dim },
    { name: "langchain_text", data_type: DataType.VarChar, max_length: 8000 },
    { name: "source", data_type: DataType.VarChar, max_length: 256 }
  ]
});

await client.createIndex({                               // ⑤ 建索引(加速检索)
  field_name: "langchain_vector",
  index_type: IndexType.IVF_FLAT,                        // 聚类加速
  metric_type: MetricType.L2,                            // 欧氏距离
  params: { nlist: 128 }
});
重难点详解 A:为什么要"切块"(Chunking)?

LLM 有上下文窗口限制(比如 8k token),而且"长文档整篇塞进去"既浪费 token 又不精准。所以要把文档切成 500 字左右的小块:

js 复制代码
const splitter = new RecursiveCharacterTextSplitter({
  chunkSize: 500,      // 每块目标长度
  chunkOverlap: 50,    // 相邻块重叠 50 字,防止语义被拦腰截断
});

用裁缝比喻:整匹布没法直接给人穿,要按身材裁成块 。overlap 就像裁缝留的"缝份",保证拼接处不丢内容。

重难点详解 B:索引 IVF_FLAT 和度量 L2 是什么?
  • IVF_FLAT :把向量空间先聚类成 128 个桶(nlist: 128),检索时只搜最近的几个桶,用精度换速度。类比图书馆先按类目分区,找书不用一本本翻。
  • L2(欧氏距离) :衡量两个向量"离得多远",距离越小越相似。另外还有余弦距离(COSINE)、内积(IP)等,按业务选。
重难点详解 C:向量数据库 vs 传统数据库?
维度 传统数据库(MySQL) 向量数据库(Milvus)
存的 结构化数据(行+列) 高维向量(如 1536 维)
查的 精确匹配 / 范围 语义相似("最像的 K 条")
核心操作 WHERE name = '张三' ORDER BY 距离 LIMIT 4

"苹果"和"手机"在文字上毫无共同点,但向量距离很近------这就是向量库能实现语义搜索的原因。

5.4 RAG Agent:rag_agent.mjs 详解

这是整个项目的灵魂,LangGraph 编排了两个节点。

js 复制代码
// ① 模型层:Embedding + Chat(通义千问,OpenAI 兼容)
const embeddings = new OpenAIEmbeddings({
  apiKey: process.env.OPENAI_API_KEY,
  configuration: { baseURL: process.env.OPENAI_BASE_URL },
  model: process.env.EMBEDDING_MODEL ?? "text-embedding-v3",
});
const llm = new ChatOpenAI({
  apiKey: process.env.OPENAI_API_KEY,
  configuration: { baseURL: process.env.OPENAI_BASE_URL },
  model: process.env.MODEL_NAME ?? "qwen-plus",
  temperature: 0,   // 客服场景要确定性,不发挥
});

// ② 检索器:从 Milvus 现有集合加载,取最相似的 4 条
const vectorStore = await Milvus.fromExistingCollection(embeddings, {
  collectionName: process.env.MILVUS_COLLECTION ?? "rag_docs",
  url: process.env.MILVUS_URI ?? "http://localhost:19530",
});
const retriever = vectorStore.asRetriever({ k: 4 });

// ③ 提示词:防幻觉!明确"没提到的就说不知道"
const prompt = ChatPromptTemplate.fromMessages([
  ["system", `你是客服助手。仅根据下面[上下文]回答:上下文没有的信息请明确说不知道,不要编造。\n\n上下文: {context}`],
  ["human", "{question}"]
]);
const chain = RunnableSequence.from([prompt, llm, new StringOutputParser()]);

// ④ 状态:问题进,答案出,上下文中间传
const GraphState = Annotation.Root({
  question: Annotation,
  context: Annotation,
  answer: Annotation
});

// ⑤ 节点1:检索 ------ 用问题去向量库捞 4 条最相关的片段
async function retrieve(state) {
  const docs = await retriever.invoke(state.question);
  return { context: docs };
}

// ⑥ 节点2:生成 ------ 把片段拼进上下文,让 LLM 基于事实回答
async function generate(state) {
  const contextText = state.context.map(d => d.pageContent).join("\n\n");
  const answer = await chain.invoke({
    context: contextText,
    question: state.question
  });
  return { answer };
}

// ⑦ 组装图:START → retrieve → generate → END
const workflow = new StateGraph(GraphState)
  .addNode("retrieve", retrieve)
  .addNode("generate", generate)
  .addEdge(START, "retrieve")
  .addEdge("retrieve", "generate")
  .addEdge("generate", END);

export const ragApp = workflow.compile();

// ⑧ 对外接口
export async function ask(question) {
  const result = await ragApp.invoke({ question });
  return { answer: result.answer, context: result.context ?? [] };
}
重难点详解 D:为什么 temperature: 0?

客服场景要"给什么答什么",不能自由发挥。temperature 越低回答越确定;0 表示贪心解码,每次基本一致。写诗写小说才需要高温度。

重难点详解 E:防幻觉 Prompt ------ RAG 的灵魂

RAG 最大的敌人是幻觉:LLM 一本正经地编造知识库没有的内容。解决办法一半在代码一半在提示词:

提示词明确说:"上下文没有的信息请明确说不知道,不要编造。"

实测效果:问"会员积分怎么获取?",Agent 答出消费积分、签到奖励、生日福利后,会主动补一句"其他积分获取方式(如活动赠送、邀请好友)在提供的上下文中未提及,因此不知道"。这就是防幻觉机制生效了。

面试常问:RAG 怎么防止幻觉?答:①检索质量(召回相关片段)②提示词约束(不知道就说不知道)③评估兜底(下文讲的 groundedness 评估器检测幻觉)。

重难点详解 F:RunnableSequence 是什么?
js 复制代码
RunnableSequence.from([prompt, llm, new StringOutputParser()])

一条流水线 :prompt 把模板+变量拼成消息 → llm 调用模型 → StringOutputParser 把复杂输出解析成纯字符串。链式调用,前者的输出是后者的输入。

重难点详解 G:LangGraph 和 RunnableSequence 的区别?
  • RunnableSequence :一条固定直线,无状态,适合"模板→模型→解析"。
  • LangGraph :一张图,有状态、有环、可分支,适合 Agent 级编排。

本仓库两样都用:小链路用 Sequence,大流程用 Graph。

5.5 命令行提问

js 复制代码
// cli.mjs
const questions = args.length > 0 ? [args.join(" ")] : ["无理由退货要在几天内?"];
const { answer } = await ask(question);

跑一下:node src/cli.mjs "满多少元包邮",Agent 就会基于知识库回答"满 99 元包邮(部分大件/冷链除外)"------顺便,这次调用也自动出现在 LangSmith 的 trace 里了。


6. 实战三:RAG 量化评估(重点中的重点)

跑通 RAG 只是第一步。怎么证明它回答得好? 靠感觉不行,要量化。LangSmith 的评估体系是四步:

复制代码
① 建数据集(考卷)→ ② 定义评估器(考官)→ ③ 跑评估(考生答题)→ ④ 看结果(打分)

6.1 第一步:建数据集 build_dataset.mjs

12 条"问题 + 标准答案":

js 复制代码
const EXAMPLES = [
  { inputs: { question: "无理由退货要在几天内申请?" },
    outputs: { answer: "自签收之日起 7 天内支持无理由退货。" } },
  { inputs: { question: "满多少元包邮?" },
    outputs: { answer: "满 99 元包邮(部分大件/冷链除外)。" } },
  // ... 共 12 条
];
js 复制代码
// 已存在就读,不存在就建
try {
  dataset = await client.readDataset({ datasetName: DATASET_NAME });
} catch(err) {
  dataset = await client.createDataset(DATASET_NAME, { description: "RAG Agent 回归评估集" });
}
await client.createExamples(EXAMPLES.map(e => ({
  dataset_id: dataset.id,
  inputs: e.inputs,
  outputs: e.outputs,
})));

这是"考卷":inputs 是题目,outputs 是标准答案。之后 Agent 每次用这份卷子考试,分数可横向对比(回归测试)。

6.2 第二步:定义评估器 evaluators.mjs

用 LLM-as-a-Judge(LLM 当考官) 模式,让一个 LLM 给 Agent 的回答打分。这里用了 openevals 库的三个官方 RAG 评估器:

js 复制代码
import { createLLMAsJudge, RAG_GROUNDEDNESS_PROMPT, RAG_HELPFULNESS_PROMPT, RAG_RETRIEVAL_RELEVANCE_PROMPT } from "openevals";
import { ChatOpenAI } from "@langchain/openai";

// 考官 = 通义千问
const judge = new ChatOpenAI({
  apiKey: process.env.OPENAI_API_KEY,
  configuration: { baseURL: process.env.OPENAI_BASE_URL },
  model: process.env.MODEL_NAME ?? "qwen-plus",
  temperature: 0,
});

// 考官一号:幻觉检测(回答有没有依据)
const ragGroundnessJudge = createLLMAsJudge({
  prompt: RAG_GROUNDEDNESS_PROMPT,
  feedbackKey: "rag_groundness",
  judge,
  continuous: true,   // 输出 0~1 连续分,而不是只有 0/1
});

三个评估器分别查什么?

评估器 中文 考察点 查什么问题
rag_groundness 幻觉检测 回答是否有上下文依据 Agent 有没有编造知识库没有的内容
rag_helpfullness 帮助性 回答是否准确解决问题 答非所问?漏了关键信息?
rag_retrieval_relevance 检索相关性 检索回的片段和问题是否相关 向量检索是不是捞了无关内容

重难点详解 H:LLM-as-a-Judge 怎么打分?

createLLMAsJudge 会把"标准答案、Agent 回答、检索上下文"一起发给考官 LLM,让考官按官方提示词模板输出一个分数(continuous: true 时是 0~1 的连续值)。整个过程不需要人工标注,用 AI 批改 AI 的作业。

面试亮点话术:"我们用 LLM-as-a-Judge 做评估,把人工抽查的成本降到接近零,而且评估维度可定制。"

6.3 第三步:跑评估 run_eval.mjs

js 复制代码
import { evaluate } from "langsmith/evaluation";
import { ask } from "../rag_agent.mjs";
import { ragEvaluators } from "./evaluators.mjs";

const result = await evaluate(runRagAgent, {
  data: DATASET_NAME,               // 用哪份考卷
  evaluators: ragEvaluators,        // 哪些考官
  client,
  experimentPrefix: `rag-openevals-${process.env.MODEL_NAME ?? "qwen"}`,
  maxConcurrency: 2                 // 并发控制,防止把 LLM 打爆
});

runRagAgent 就是考生:拿数据集里的每个问题去跑真实的 RAG Agent,把回答和检索上下文交出来给考官打分。

运行后输出:

ruby 复制代码
Starting evaluation of experiment: rag-openevals-qwen-plus-7d4a3a76
View results at https://smith.langchain.com/o/xxx/datasets/xxx/compare?selectedSessions=xxx

6.4 重难点详解 I:Experiment 是什么?

一次评估 = 一次 experiment(实验)。它把"考卷 + 考生回答 + 考官分数"打包存档。

为什么要用 experiment? 因为你可以改代码后重跑,两次实验横向对比 :比如把 k=4 改成 k=6,或者换了提示词,看幻觉分、帮助性分是升是降。这就是回归测试------优化 Agent 不再靠玄学。

6.5 重难点详解 J:三份分数的闭环价值

三个评估器不是各看各的,它们能帮你定位问题在哪一环:

复制代码
检索相关性低  →  问题在检索层(换 chunk 策略、调 k、换索引)
幻觉分低      →  问题在生成层(加强提示词约束、提高检索质量)
帮助性低      →  问题在回答层(prompt 指令、模型选择)

一句话记忆:检索相关性查"有没有捞对",幻觉查"有没有瞎编",帮助性查"有没有答好"。


7. 知识点总表

知识点 一句话解释 实战位置
Tracing 每次运行生成完整调用树 demo/ + 全项目
Monitoring 后台实时监控 token/耗时 LangSmith 控制台
Datasets 问题+标准答案的"考卷" eval/build_dataset.mjs
Evaluators LLM 当考官打分 eval/evaluators.mjs
Experiment 一次评估存档,可横向对比 eval/run_eval.mjs
StateGraph 有状态图编排,节点间传背包 rag_agent.mjs
Annotation.Root 定义状态结构 rag_agent.mjs
reducer 状态合并规则 demo/trigger-error.mjs
RecursiveCharacterTextSplitter 递归切块,500字+50重叠 milvus_insert.mjs
IVF_FLAT 聚类索引,加速检索 milvus_insert.mjs
L2 度量 欧氏距离衡量相似度 milvus_insert.mjs
OpenAIEmbeddings 文本→向量 milvus_insert.mjs
ChatOpenAI 对话模型客户端 rag_agent.mjs
OpenAI 兼容 baseURL 通义千问等国产模型接入 .env
RunnableSequence 线性流水线 rag_agent.mjs
LLM-as-a-Judge 用 LLM 评估 LLM evaluators.mjs
openevals 官方评估器库 evaluators.mjs
防幻觉 Prompt "没有的信息说不知道" rag_agent.mjs
temperature: 0 客服场景要确定性 rag_agent.mjs

8. 真实踩坑实录(都是血泪)

坑 1:键名不匹配,靠默认值"碰巧"跑通

代码里写 process.env.LANGCHAIN_API_KEY,但 .env 里只有 LANGSMITH_API_KEY。结果没报错?因为 LangSmith Client 传 undefined 时会自动 fallback 读环境变量。看起来能用,其实是隐性炸弹。

教训:配置键名必须和代码严格对齐。统一后:

js 复制代码
const client = new Client({ apiKey: process.env.LANGSMITH_API_KEY });

坑 2:Milvus 容器崩溃(Exited 134)

etcdserver: authentication is not enabled ------ 根因是 bind-mount 数据卷残留旧配置 。docker compose down -v 只清 named volume,清不掉 bind-mount 。把 volumes/{etcd,minio,milvus} 备份改名后重启才恢复。

教训:用 bind-mount 的数据卷,出问题要手动清目录。

坑 3:普通脚本不打印 trace 链接

跑脚本时控制台没有"View current trace"链接,以为没连上 LangSmith。实际用 API 一查:run 早就上报了,只是脚本环境不打印这个提示(那是交互式环境的行为)。

教训 :判断是否连接成功,用 client.listRuns() 实查,别靠肉眼。

坑 4:Milvus 地址要剥掉协议头

js 复制代码
const MILVUS_ADDRESS = process.env.MILVUS_URI.replace(/^https?:\/\//, "") ?? "localhost:19530";

MilvusClient 会自动加协议前缀,传入带协议的地址反而报错。


9. 面试金句

  1. "LangSmith 是 LLM 应用的可观测性与评估平台,一句话概括就是 LLM 应用的 Grafana + Sentry + 自动化测试。"
  2. "接入零侵入:配好环境变量,LangChain/LangGraph 内部自动上报 trace,不需要改业务代码。"
  3. "RAG 防幻觉三板斧:检索质量、提示词约束(不知道就说不知道)、评估兜底(groundedness 评估器检测)。"
  4. "评估体系四件套:Datasets 出考卷、Evaluators 当考官、Experiment 存档、横向对比做回归。"
  5. "我们用 LLM-as-a-Judge 做自动化评估,评估维度可定制,人工成本趋近于零。"

10. 写在最后

从 demo/ 里一个故意报错的图,到 rag-demo/ 里跑通的全链路 RAG,再到 eval/ 里的自动化评估------这条路径恰好是一个 LLM 应用从调试、上线到优化的完整生命周期。

  • Tracing 让你看得见(调试)
  • Monitoring 让你管得住(上线)
  • Datasets + Evaluators 让你优化得动(迭代)

你无法度量它,就无法管理它。给 Agent 装好仪表盘,剩下的就是持续迭代。

仓库代码都在这:langsmith-demo/,clone 下来配好 .env 就能跑。动手试一次,比看十篇文章都管用。

相关推荐
RaaS1001 小时前
Node.js Brotli RCE 漏洞自查指南:fetch、undici、axios 都在影响面,三分钟确认你的版本
人工智能·深度学习·算法
Behavior1 小时前
OpenAI发布GPT-6.1 Terra:能自主操作电脑!成本砍半、速度快3倍
chatgpt·aigc·openai
LEE1 小时前
别把 Agent 架构画成组织架构图——子代理该不该拆,用任务级 Eval 说话
前端·后端·ai编程
梦醒江南1 小时前
RAG 证据链验收:别只测答案像不像,要证明它从哪里来
前端·agent·ai编程
郝琳1 小时前
Claude Code 额度优化实践,缓存写入单价是读取的 40 倍,四条可落地的会话习惯
ai编程
红尘伴蝶舞1 小时前
加密拦截器让抓包失效,我只能从 4KB 的 logcat 碎片里拼回 JSON
android·kotlin·ai编程
码哥字节2 小时前
大厂程序员写 AI 编程半年来,我的 3 次认知推翻与 1 次生存顿悟
ai编程·claude
CVNot2 小时前
AI让程序员失去了什么?
ai编程
没事写点啥2 小时前
WeKnora 回答溯源
前端·后端·ai编程