LIUPX BLOG

记录有序 文字有趣

从 0 到 1:用一个最小 Python Agent,搞懂 AI 对话程序的基本原理

如果你是第一次接触 AI 编程,这篇文章会带你用一个非常小的项目,理解”AI 对话程序”到底是怎么工作的。

这个项目不复杂,但它覆盖了最核心的一条链路:
读取配置 → 调用模型 API → 拿到回复 → 在终端输出。

项目地址:https://git.liupx.com/study/openai_demo_first
更多信息(代码、更新、使用说明)可以访问这里。


1. 这篇文章你会学到什么

读完后,你应该能回答这几个问题:

  • 什么是”OpenAI 兼容 API”
  • 为什么要在 .env 里放 API KeyBASE_URL
  • AI 回复为什么能”一个字一个字蹦出来”(流式输出)
  • temperaturetop_p 这些参数到底在控制什么
  • 一个最小 AI Agent 的执行流程是什么

2. 先建立一个最小认知模型

先别看代码,先理解 4 个核心概念。

2.1 模型(Model)

你可以把模型理解成一个超大号文本补全器
你给它上下文,它预测下一段最合理的文本。

1
2
3
4
5
输入: "今天天气真"
模型预测: "好啊" / "糟糕" / "不错" ...

输入: "Python是一门"
模型预测: "编程语言" / "很流行的语言" / "简洁的语言" ...

2.2 消息(Messages)

聊天接口不是只传一句话,而是传一个消息列表。每条消息有角色和内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌─────────────────────────────────────────────┐
│ Messages 列表 │
├─────────────────────────────────────────────┤
│ ┌─────────────────────────────────────┐ │
│ │ role: "system" │ │
│ │ content: "你是一个严谨的学习教练" │ │
│ └─────────────────────────────────────┘ │
│ ┌─────────────────────────────────────┐ │
│ │ role: "user" │ │
│ │ content: "什么是Python?" │ │
│ └─────────────────────────────────────┘ │
│ ┌─────────────────────────────────────┐ │
│ │ role: "assistant" │ │
│ │ content: "Python是一门编程语言..." │ │ ← 多轮对话时
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────┘

三种角色说明:

  • system:给模型设定角色和规则(像导演给演员的剧本要求)
  • user:用户问题(像你问老师的问题)
  • assistant:模型历史回答(多轮对话时需要带上,让模型记住上下文)

2.3 API(程序访问模型的方式)

我们不是在本地跑大模型,而是通过 HTTP 接口调用远程模型服务。

1
2
3
4
5
6
7
8
graph LR
A[你的程序] -- HTTP请求 --> B[模型服务API]
B -- 返回结果 --> A
C[.env文件] -->|提供密钥和地址| A

style A fill:#e1f5ff
style B fill:#fff4e1
style C fill:#e8f5e9

这个项目使用的是 OpenAI Python SDK,但可以连”OpenAI 兼容接口”(如 DeepSeek、阿里通义千问等)。

2.4 Token(模型处理文本的基本单位)

模型按 token 处理文本,不是按”字数”。

Token 是什么?

  • 1 个 token ≈ 0.75 个英文单词,或约 1-2 个汉字
  • 模型把文本切分成小片段(token)来理解和生成
1
2
3
4
5
6
7
8
9
10
11
示例:这句话有多少个 token?

"我爱学习编程"

可能的切分方式:
┌────────────────────────────┐
│ 我 │ 爱 │ 学习 │ 编程 │
│ token1 │ token2 │ token3 │ token4 │
└────────────────────────────┘

约 4-6 个 token(取决于具体分词器)

max_tokens 控制的是”最多生成多少 token”,不是”最多多少个汉字”。


3. 这个项目做了什么

项目里有两个入口:

它们的核心事情完全一致:调用 client.chat.completions.create(...),然后把结果打印出来。


4. 运行前准备:为什么要 .env

配置文件示例(.env.example)是:

1
2
OPENAI_API_KEY=your_api_key
BASE_URL=https://your-openai-compatible-endpoint/v1

原因很简单:

配置项 作用 为什么需要
OPENAI_API_KEY 身份凭证 没有它服务端不会让你调用
BASE_URL 接口地址 告诉 SDK 请求发到哪个模型服务

为什么用 .env 文件?

1
2
3
4
5
6
7
8
9
10
11
┌─────────────────────────────────────────┐
│ 不推荐:直接写死在代码里 │
│ api_key = "sk-xxxxx" ← 容易泄露! │
└─────────────────────────────────────────┘

┌─────────────────────────────────────────┐
│ 推荐:使用 .env 文件 │
│ 1. 安全:不会被提交到 Git │
│ 2. 灵活:不同环境用不同配置 │
│ 3. 规范:业界标准做法 │
└─────────────────────────────────────────┘

把密钥放在 .env,比直接写死在代码里安全得多,也便于切换环境。


5. 主流程拆解(最重要)

5.1 整体流程图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
flowchart TD
A[程序启动] --> B[加载 .env 文件<br/>load_dotenv]
B --> C[读取配置<br/>OPENAI_API_KEY<br/>BASE_URL]
C --> D{配置检查}
D -->|缺少API Key| E[错误提示并退出]
D -->|缺少BASE_URL| E
D -->|配置正常| F[创建客户端<br/>OpenAI client]
F --> G[组装消息<br/>messages列表]
G --> H[发起请求<br/>chat.completions.create]
H --> I{是否流式?}
I -->|是 stream=True| J[循环读取 chunk<br/>实时打印]
I -->|否 stream=False| K[等待完整结果<br/>一次性打印]
J --> L[输出完成]
K --> L

style E fill:#ffebee
style L fill:#e8f5e9
style J fill:#e3f2fd

5.2 核心代码讲解(最小可运行版本)

这是一个完整的最小示例,约 30 行代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 导入需要的库
import os
from dotenv import load_dotenv
from openai import OpenAI

# 1. 加载环境变量
load_dotenv()

# 2. 读取配置
api_key = os.getenv("OPENAI_API_KEY")
base_url = os.getenv("BASE_URL")

# 3. 创建客户端
client = OpenAI(api_key=api_key, base_url=base_url)

# 4. 组装消息
messages = [
{"role": "user", "content": "帮我设计一个早起计划"}
]

# 5. 发起请求(流式)
stream = client.chat.completions.create(
model="deepseek-chat", # 指定模型
messages=messages, # 消息列表
stream=True, # 开启流式输出
)

# 6. 实时打印结果
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)

你会发现,AI 应用的第一版往往就这几步。


6. 为什么会”流式输出”

6.1 原理解析

main.py 中,请求里用了:

1
stream=True

这意味着服务端会把回答切成多个小片段返回。

非流式 vs 流式对比:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌────────────────────────────────────────────────────────────┐
│ 非流式输出 (stream=False) │
├────────────────────────────────────────────────────────────┤
│ 等待... 等待... 等待... │
│ [10秒后] │
│ "完整的一段话一次性显示出来" │
│ │
│ 用户体验:等待时间较长,不知道是否在处理 │
└────────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────────┐
│ 流式输出 (stream=True) │
├────────────────────────────────────────────────────────────┤
│ "完" -> "整" -> "的" -> "一" -> "段" -> "话" │
│ 一个字一个字实时显示出来 │
│ │
│ 用户体验:即时反馈,更流畅,像真人对话 │
└────────────────────────────────────────────────────────────┘

6.2 代码实现

流式处理的关键代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 开启流式返回
stream = client.chat.completions.create(
model="deepseek-chat",
messages=[{"role": "user", "content": "你好"}],
stream=True, # ← 关键参数
)

# 循环读取每个片段(chunk)
for chunk in stream:
# 检查是否有内容
if not chunk.choices:
continue

# 获取增量文本
delta = chunk.choices[0].delta.content
if delta:
print(delta, end="", flush=True) # 实时打印

所以你看到的效果就是:

  • 不是等整段回答全部生成完才显示
  • 而是边生成边显示,体验更像实时对话

7. 参数到底在调什么(面向新手版)

main_cli.py 支持几个常见参数:

7.1 参数速查表

参数 作用 取值范围 建议值
--model 使用哪个模型 字符串 deepseek-chat
--system 给模型设定角色 任意文本 根据场景设置
--temperature 随机性/发散程度 0.0 ~ 2.0 0.2~0.8
--top-p 核采样控制 0.0 ~ 1.0 0.9 左右
--max-tokens 最多生成多少token 正整数 视需求而定
--no-stream 关闭流式输出 标志位 需要完整结果时使用

7.2 Temperature 详解

Temperature 是什么?

Temperature 控制模型输出的随机性和多样性。想象一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Temperature = 0  (完全确定性)
┌────────────────────────────────┐
│ 像"背诵课文" │
│ 每次回答几乎完全相同 │
│ 适合:需要精确、稳定的答案 │
└────────────────────────────────┘

Temperature = 0.7 (适中随机)
┌────────────────────────────────┐
│ 像"正常交谈" │
│ 有一定变化,但保持逻辑一致 │
│ 适合:日常对话、创意任务 │
└────────────────────────────────┘

Temperature = 1.5 (高随机性)
┌────────────────────────────────┐
│ 像"头脑风暴" │
│ 非常有创意,但可能不太连贯 │
│ 适合:需要大量创意的场景 │
└────────────────────────────────┘

7.3 实际效果对比

使用不同的 temperature 调用同一个问题:

1
2
3
4
5
6
7
# 低温度:0.2(更稳定、更保守)
python src/agent/main_cli.py "用一句话介绍Python" --temperature 0.2
# 输出:Python是一门简洁高效、易学易用的高级编程语言。

# 高温度:0.9(更多样、更发散)
python src/agent/main_cli.py "用一句话介绍Python" --temperature 0.9
# 输出:Python是一门简洁高效、易学易用的通用编程语言。

7.4 新手建议

  1. 先只改 prompt--system
  2. temperature 先从 0.20.8 小范围试
  3. 不要一开始同时乱调多个参数,不然很难判断变化原因
  4. 需要稳定答案时用低 temperature,需要创意时用高 temperature

8. 常见报错怎么理解

8.1 “未检测到 OPENAI_API_KEY”

现象:

1
未检测到 OPENAI_API_KEY。请在 .env 文件中配置后重试。

原因和解决:

  • .env 文件不存在或没有配置 OPENAI_API_KEY
  • .env 文件不在项目根目录
  • 检查步骤:确认 .env 文件存在,内容格式正确

8.2 “未检测到 BASE_URL”

现象:

1
未检测到 BASE_URL .env 文件中配置后重试。

原因和解决:

  • 接口地址缺失,SDK 不知道请求发去哪
  • 确保在 .env 中配置了 BASE_URL

8.3 连接失败 / 429 / 配额不足

现象:

1
调用失败:HTTPSConnectionPool(...) / 429 Client Error

可能原因:

问题类型 检查项
服务地址不可达 BASE_URL 是否正确,网络是否通畅
网络或代理问题 是否需要配置代理,防火墙是否拦截
额度不足或限流 账户是否有足够配额,是否触限流

排查顺序:

  1. 先检查配置(.env 文件)
  2. 再检查网络(是否能访问 API 地址)
  3. 最后看平台账单和配额

9. 给零基础同学的 3 个小实验

直接用 main_cli.py 做:

实验 1:同一个问题,换 system prompt

1
2
3
4
5
# 温和的老师版本
python src/agent/main_cli.py "给我一份一周学习计划" --system "你是温和的老师,说话要鼓励学生"

# 严格的教练版本
python src/agent/main_cli.py "给我一份一周学习计划" --system "你是严格的教练,要求学生必须执行"

观察点: 同样的问题,不同的人设,回答风格有什么不同?

实验 2:对比流式与非流式

1
2
3
4
5
# 流式输出(默认)
python src/agent/main_cli.py "解释什么是提示词工程"

# 非流式输出
python src/agent/main_cli.py "解释什么是提示词工程" --no-stream

观察点: 两种方式在用户体验上有什么区别?

实验 3:调 temperature 看风格变化

1
2
3
4
5
# 低温度
python src/agent/main_cli.py "给我3个自我介绍版本" --temperature 0.2

# 高温度
python src/agent/main_cli.py "给我3个自我介绍版本" --temperature 0.9

观察点: 回答的多样性有什么变化?


10. 运行效果示例

以下是实际运行效果:

1
2
3
4
5
6
7
8
$ python src/agent/main.py
Assistant: 为你设计一个科学的早起计划,帮助你在不牺牲健康的前提下,逐步养成早起习惯...

### **第一步:明确早起的目的(关键!)**
早起本身不是目的,获得高质量的早晨时间才是...

### **第二步:制定渐进式早起方案(核心步骤)**
**切勿突然提前1-2小时起床!** 这会导致睡眠不足...

11. 一句话总结

这个项目本质上是在教你一件事:
AI 应用的第一步,不是复杂算法,而是先打通调用链路并理解输入/输出机制。

当你真正理解这条链路后,再去学多轮记忆、RAG、Agent 工具调用,会轻松很多。


12. 建议的下一步学习顺序

1
2
3
4
5
6
7
8
9
10
11
graph LR
A[当前阶段<br/>单次调用] --> B[多轮对话<br/>保存历史messages]
B --> C[提示词工程<br/>设计稳定的Prompt]
C --> D[RAG<br/>引入外部知识库]
D --> E[复杂Agent<br/>工具调用与编排]

style A fill:#e8f5e9
style B fill:#e3f2fd
style C fill:#fff3e0
style D fill:#fce4ec
style E fill:#f3e5f5
  1. 让程序支持多轮对话(保存历史 messages
  2. 设计更稳定的提示词模板(Prompt Template)
  3. 引入外部知识库(RAG)
  4. 再考虑更复杂的 Agent 编排

先小步快跑,持续可运行,比一次做”大而全”更重要。


更多信息与项目最新内容请访问:https://git.liupx.com/study/openai_demo_first

从零理解 LangChain:AI 智能体与工具调用的实战指南

面向有编程基础的 AI 初学者,通过一个天气查询 Demo,深入理解 LangChain Agent 的工作原理


1. 引言 - 为什么需要 LangChain?

你一定听说过 ChatGPT、Claude 这些大语言模型。它们很强大——可以写代码、作诗、回答问题。但它们也有一些明显的局限:

  • 无法获取实时信息:问”今天北京天气怎么样?”,它只能回答训练数据截止日期前的信息
  • 没有执行能力:让它”帮我查一下数据库”,它做不到
  • 缺少持久记忆:聊完天关闭窗口,下次对话它就不记得你了
  • 无法精确控制输出:想要结构化的数据(如 JSON),它可能格式混乱

LangChain 就是为了解决这些问题而生的。你可以把它想象成:

大语言模型的”操作系统”——让 LLM 能够调用工具、记忆对话、与外部世界交互

一个形象的比喻:

角色 能力
大语言模型 (LLM) “大脑”——理解和推理
LangChain “手脚”——执行操作
Tools “工具箱”——各种具体功能

本文通过一个天气查询智能体 Demo,带你深入理解 LangChain 的核心概念——特别是 Agent(智能体)工具调用 的工作原理。

2. 什么是 Agent(智能体)?

核心概念:大脑 + 工具

Agent(智能体) 是 LangChain 的核心概念。用一个公式表达:

1
Agent = LLM(大脑) + Tools(工具) + 规划能力

想象一个现实中的场景:

你问助手:”帮我查一下今天北京的天气,然后告诉我适不适合跑步”

一个聪明的助手会:

  1. 理解意图 → 你想知道天气
  2. 规划步骤 → 先查天气,再判断是否适合跑步
  3. 调用工具 → 使用天气查询工具
  4. 综合回答 → 结合结果给出建议

LangChain Agent 就在做同样的事情!

Agent 的工作流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
┌─────────────────────────────────────────────────────────┐
│ Agent 工作流程 │
├─────────────────────────────────────────────────────────┤
│ │
│ 用户输入: "北京天气怎么样?" │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ LLM 分析意图 │ → 理解用户想要查询天气 │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ 决策判断 │ → 需要调用 get_weather_for_location │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 执行工具调用 │ → get_weather_for_location("北京") │
│ └──────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 获取工具结果 │ → "晴朗,约 12C" │
│ └──────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 生成自然语言回复 │ → "北京今天晴朗,温度12度..." │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘

为什么要这样设计?

你可能会问:为什么不直接让程序判断调用哪个工具,而要让 LLM 来决策?

答案在于灵活性

传统程序 LangChain Agent
用 if-else 预定义所有情况 LLM 理解意图后动态决策
新增场景需要改代码 只需描述新工具,LLM 自动学会使用
无法处理模糊表达 能理解”外面”、”我这里”等自然语言

举个例子:

1
2
3
4
5
6
7
8
9
# 传统方式:硬编码判断
if "天气" in user_input and "北京" in user_input:
return get_weather("Beijing")
# 遇到 "外面冷不冷?" 就无能为力了

# Agent 方式:LLM 理解后决策
# 用户: "外面冷不冷?"
# Agent: get_user_location() → get_weather("Shanghai")
# 回复: "上海目前15度,有点凉,建议加件外套"

3. 工具调用(Tool Calling)原理

工具调用是 Agent 的核心能力。让我们深入理解它是如何工作的。

什么是工具调用?

工具调用(Function Calling) 是大语言模型的一项特殊能力:模型不仅能生成文本,还能生成”函数调用指令”。

简单来说:

  • 普通对话:用户提问 → 模型生成文本回答
  • 工具调用:用户提问 → 模型生成”调用某个函数”的指令 → 程序执行函数 → 模型基于结果生成回答

完整的交互流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
┌─────────────────────────────────────────────────────────────────┐
│ 工具调用完整流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 步骤1: 用户提问 │
│ ┌──────────────────────────────────────┐ │
│ │ "北京今天天气怎么样?" │ │
│ └──────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 步骤2: LangChain 将可用工具的描述发送给 LLM │
│ ┌──────────────────────────────────────┐ │
│ │ 可用工具: │ │
│ │ - get_weather_for_location(city) │ │
│ │ 描述: 根据城市名获取天气 │ │
│ └──────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 步骤3: LLM 决策并生成函数调用 │
│ ┌──────────────────────────────────────┐ │
│ { │ │
│ "name": "get_weather_for_location", │ │
│ "arguments": {"city": "北京"} │ │
│ } │ │
│ └──────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 步骤4: LangChain 执行函数 │
│ ┌──────────────────────────────────────┐ │
│ │ 执行: get_weather_for_location("北京") │ │
│ │ 返回: "晴朗,约 12C" │ │
│ └──────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 步骤5: 将函数结果返回给 LLM 生成最终回复 │
│ ┌──────────────────────────────────────┐ │
│ │ "北京今天天气晴朗,温度约12摄氏度, │ │
│ │ 非常适合外出活动!" │ │
│ └──────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

代码示例:定义工具

在 LangChain 中,定义工具非常简单——只需要一个函数加一个装饰器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from langchain.tools import tool

@tool # ← @tool 装饰器自动生成工具的 JSON Schema 描述
def get_weather_for_location(city: str) -> str:
"""根据城市名获取天气。

这个文档字符串会被发送给 LLM,帮助模型理解工具用途!
"""
# 实际应用中,这里会调用真实的天气 API
weather_data = {
"北京": "晴朗,约 12C",
"上海": "多云伴轻风,约 15C",
"深圳": "温暖偏湿,约 24C",
}
return weather_data.get(city, "暂无数据")

关键技术点

  1. @tool 装饰器的魔法

    • 自动解析函数签名生成 JSON Schema
    • 提取函数文档字符串发送给 LLM
    • 让 LLM 理解”这个工具是做什么的”、”需要什么参数”
  2. LLM 的决策能力

    • 根据用户输入判断是否需要调用工具
    • 自动提取和填充工具参数
    • 可能链式调用多个工具
  3. LangChain 的执行层

    • 接收 LLM 的调用指令
    • 执行实际函数
    • 将结果返回给 LLM 继续处理

4. 实战 Demo:天气查询智能体

现在让我们看一个完整的项目——一个能够查询天气、理解用户位置的智能聊天助手。

项目架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
┌──────────────────────────────────────────────────────────┐
│ 项目架构图 │
├──────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ main.py │ │
│ │ (CLI 入口,参数解析) │ │
│ └─────────────────────┬──────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ runner.py │ │
│ │ (运行流程控制、流式输出、多轮对话) │ │
│ └─────────────────────┬──────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ factory.py │ │
│ │ (Agent 构建器) │ │
│ └───────────────┬─────────────┬──────────────────────┘ │
│ │ │ │
│ ┌───────▼─────┐ ┌───▼──────┐ ┌─────────────┐ │
│ │ llm.py │ │ tools.py │ │ memory.py │ │
│ │ (模型初始化) │ │(工具定义) │ │(会话记忆) │ │
│ └─────────────┘ └──────────┘ └─────────────┘ │
│ │
└──────────────────────────────────────────────────────────┘

核心代码解析

1. Agent 构建 (factory.py)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy

def build_advanced_agent():
"""构建高级版 agent(工具 + 结构化输出 + 记忆)"""
model = build_deepseek_model() # 初始化 DeepSeek 模型
checkpointer = build_checkpointer() # 初始化记忆组件

return create_agent(
model=model, # LLM "大脑"
system_prompt=SYSTEM_PROMPT, # 系统提示词
tools=[ # 可用工具列表
get_user_location, # 获取用户位置
get_weather_for_location, # 查询天气
],
context_schema=Context, # 上下文结构定义
response_format=ToolStrategy(ResponseFormat), # 结构化输出
checkpointer=checkpointer, # 记忆检查点
)

参数解读:

参数 作用 类比
model 提供推理能力 代理人的”大脑”
tools 可调用的工具 代理人的”工具箱”
system_prompt 设定角色和行为 给代理人的”工作指南”
checkpointer 保存对话历史 代理人的”笔记本”

2. 工具定义 (tools.py)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from langchain.tools import tool

@tool
def get_weather_for_location(city: str) -> str:
"""根据城市名获取天气。"""
resolved_city = resolve_city_name(city)
if resolved_city:
return WEATHER_BY_CITY[resolved_city]
return f"暂无 {city} 的实时数据"

@tool
def get_user_location(runtime) -> str:
"""获取当前用户所在位置。"""
return USER_LOCATION_BY_ID.get(runtime.context.user_id, "San Francisco")

3. 运行流程 (runner.py)

1
2
3
4
5
6
7
8
9
10
11
def run_advanced_demo(question: str, thread_id: str, user_id: str):
"""运行高级模式 demo"""
agent = build_advanced_agent()

# 配置会话ID,用于记忆
config = {"configurable": {"thread_id": thread_id}}

# 流式输出,实时展示生成过程
for chunk in agent.stream({"messages": [question]}, config):
# 处理并显示每个生成片段
...

运行效果

基础模式(单工具调用):

1
$ python src/agent/main.py --mode basic --question "北京天气怎么样?"

输出:

1
2
正在查询天气...
北京今天晴朗,温度约12摄氏度。

高级模式(多轮对话 + 结构化输出):

1
$ python src/agent/main.py --mode advanced --question "北京天气怎么样?"

输出:

1
2
3
4
5
6
7
8
9
🤖 北京今天天气晴朗,温度约12度。非常适合外出活动!

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📊 结构化输出:
punny_response: "北京今天晴朗宜人,12度正好不冷不热!"
weather_conditions: "晴朗,约 12C"
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

继续聊天(输入 exit 退出) > 明天呢?

(后续对话会自动记住之前讨论的是北京)

5. 高级特性

这个 Demo 不仅展示了基础的工具调用,还实现了几个高级特性。

5.1 结构化输出

问题:自然语言输出格式不稳定,难以被程序解析。

解决方案:使用 Pydantic 定义输出结构,强制 LLM 返回格式化数据。

1
2
3
4
5
6
from pydantic import BaseModel

class ResponseFormat(BaseModel):
"""定义输出的结构"""
punny_response: str # 幽默风格的回复
weather_conditions: str | None = None # 天气详情(可选)

使用方式

1
2
3
4
5
6
7
8
from langchain.agents.structured_output import ToolStrategy

return create_agent(
model=model,
tools=tools,
response_format=ToolStrategy(ResponseFormat), # 强制结构化输出
...
)

输出对比:

普通输出 结构化输出
“北京今天晴朗,温度约12度” {"punny_response": "北京今天晴朗宜人...", "weather_conditions": "晴朗,约 12C"}
格式不确定,程序难以解析 格式固定,程序可直接使用

5.2 会话记忆

问题:默认情况下,LLM 每次对话都是独立的,无法记住之前的内容。

场景示例

1
2
3
用户: 北京天气怎么样?
Agent: 北京今天晴朗,约12度。
用户: 明天呢? ← Agent 不知道这是在问"北京明天的天气"

解决方案:使用 checkpointer 保存对话状态。

1
2
3
4
5
6
7
8
9
from langchain.checkpoints.memory import InMemorySaver

def build_checkpointer():
"""创建内存检查点,保存对话历史"""
return InMemorySaver()

# 使用时指定 thread_id,不同 thread 隔离
config = {"configurable": {"thread_id": "user-session-123"}}
agent.invoke({"messages": [question]}, config)

工作原理

1
2
3
4
5
6
7
8
9
InMemorySaver (内存存储)

├── thread_id="user-001" (用户A的会话)
│ ├── 第1轮: ["北京天气怎么样?", "北京今天晴朗..."]
│ ├── 第2轮: ["明天呢?", "北京明天预计..."] ← 自动记住上下文
│ └── ...

└── thread_id="user-002" (用户B的会话,完全独立)
└── ...

5.3 城市名智能解析

问题:用户输入城市名的方式千奇百怪:

  • “北京”、”Beijing”、”Peking”
  • “beijing天气”、”北京呢?”
  • 甚至 “京市”、”那边的城市”

解决方案:多层解析策略 + LLM 兜底

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def resolve_city_name(city: str) -> str | None:
"""智能解析城市名,支持多种输入方式"""

# 第1层:标准名直接匹配(最快)
if city in WEATHER_BY_CITY:
return city

# 第2层:别名精确匹配
for canonical, aliases in CITY_ALIASES.items():
if normalize(city) in [normalize(a) for a in aliases]:
return canonical

# 第3层:包含匹配(处理 "beijing天气" 这类)
for canonical, aliases in CITY_ALIASES.items():
if any(normalize(a) in normalize(city) for a in aliases):
return canonical

# 第4层:LLM 兜底(处理模糊输入,但成本较高)
return translate_city_with_llm(city, tuple(WEATHER_BY_CITY.keys()))

策略对比:

策略 速度 成本 适用场景
字典匹配 极快 免费 标准输入
别名匹配 极快 免费 常见变体
包含匹配 免费 混合输入
LLM兜底 较慢 API费用 模糊/未见过输入

设计理念:先用规则快速处理常见情况,最后才用 LLM 处理复杂情况,兼顾效率和智能。

6. 总结与延伸

核心概念回顾

通过这个天气查询 Demo,我们学习了 LangChain 的核心概念:

概念 理解要点
LLM 提供理解和推理能力的”大脑”
Agent LLM + 工具 + 规划能力 = 智能体
Tool Calling LLM 生成函数调用指令,程序执行后返回结果
Memory 保存对话历史,实现多轮上下文
Structured Output 强制 LLM 返回固定格式的数据

Agent 的核心价值

Agent 相比传统编程方式的核心价值在于:

  1. 理解意图而非匹配模式

    • 传统:if "天气" in input: → 硬编码规则
    • Agent:LLM 理解语义后动态决策
  2. 扩展简单

    • 传统:新增场景需要修改代码逻辑
    • Agent:只需添加新工具,LLM 自动学会使用
  3. 处理模糊输入

    • 传统:无法理解”外面”、”我这里”等上下文表达
    • Agent:结合记忆和用户位置工具,智能推理

快速上手指南

如果你想运行这个 Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1. 克隆项目
git clone https://git.liupx.com/study/langchain_demo_first
cd langchain_demo_first

# 2. 创建虚拟环境
python3 -m venv .venv
source .venv/bin/activate

# 3. 安装依赖
pip install -r requirements.txt

# 4. 配置环境变量
cp .env.example .env
# 编辑 .env,填入你的 DEEPSEEK_API_KEY

# 5. 运行
python src/agent/main.py --mode advanced --question "北京天气怎么样?"

下一步学习

如果你想继续深入 LangChain,推荐的学习路径:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
基础阶段(当前)
├── Agent 概念 ✓
├── 工具调用 ✓
└── 记忆机制 ✓

进阶阶段
├── LangGraph:复杂工作流编排
├── 自定义工具开发
├── 不同类型的 Memory
└── 错误处理与重试

实战阶段
├── RAG(检索增强生成)
├── 多 Agent 协作
├── Agent 与数据库交互
└── 生产环境部署

参考资源

7. 参考资料

相关文档

本项目文档

源码索引

文件 功能
main.py CLI 入口,参数解析
factory.py Agent 构建
tools.py 工具定义,城市名解析
runner.py 运行流程,流式输出
llm.py DeepSeek 模型初始化
memory.py 会话记忆实现

关于作者:本文基于 langchain_demo_first 项目编写,这是一个适合 LangChain 入门的实战项目。欢迎 Star 和 Fork!

LangSmith 可观测性实战:给 AI 装上”监控摄像头”

系列文章第二篇,深入理解 LangSmith 的可观测性原理,让你的 Agent 调试不再”盲人摸象”


1. 引言 - 为什么需要可观测性?

在上一篇文章《从零理解 LangChain》中,我们构建了一个天气查询 Agent。它能理解用户意图、调用工具、保持对话记忆——一切看起来都很完美。

但当你真正开始使用时,会遇到这些问题:

1
2
3
4
5
6
用户: "帮我查一下上海今天天气"
Agent: (沉默...)...
(30秒后...)
Agent: "抱歉,我暂时无法回答"

你: 😱 - "到底哪里出错了??"

没有可观测性时,Agent 就像一个黑盒子

1
2
3
4
5
6
7
8
9
10
11
12
13
┌─────────────────────────────────────┐
│ Agent 黑盒子 │
├─────────────────────────────────────┤
│ │
│ 输入 ──────────────────► 输出 │
│ │
│ ❓ 中间发生了什么? │
│ ❓ 哪个工具被调用了? │
│ ❓ 为什么花了这么长时间? │
│ ❓ LLM 收到了什么 Prompt? │
│ ❓ 工具返回了什么结果? │
│ │
└─────────────────────────────────────┘

这就是 LangSmith 要解决的问题。

你可以把 LangSmith 想象成:

给 AI Agent 装上的”监控摄像头”——记录每一个决策、每一次调用、每一秒耗时

2. LangSmith 是什么?

核心概念:AI 应用的”监控录像”

LangSmith 是 LangChain 官方推出的可观测性平台,它让你能够:

能力 说明
完整追踪 记录 Agent 执行的每一步
性能监控 统计 Token 使用、API 调用延迟
可视化调试 图形化展示执行流程
会话回放 重新运行历史对话,验证修改效果
异常诊断 捕获并分析错误发生的位置和原因

一个形象的类比:

1
2
3
4
5
6
7
8
9
10
11
12
13
传统调试 vs LangSmith

┌─────────────────────┐ ┌─────────────────────────────┐
│ 传统调试方式 │ │ 使用 LangSmith │
├─────────────────────┤ ├─────────────────────────────┤
│ │ │ │
│ print("调试信息") │ │ 📸 自动录制完整执行过程 │
│ print("工具调用") │ │ 📊 Token/延迟自动统计 │
│ print("结果...") │ │ 🔍 点击查看任意步骤详情 │
│ print("...") │ │ 🎬 一键回放历史会话 │
│ │ │ │
│ 😰 代码到处是日志 │ │ ✨ 零侵入,自动追踪 │
└─────────────────────┘ └─────────────────────────────┘

LangSmith 的工作原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
┌─────────────────────────────────────────────────────────────────┐
│ LangSmith 追踪原理 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 你的代码 │
│ ┌──────────────────────────────────────────────┐ │
│ │ agent.invoke({"messages": "北京天气怎么样?"}) │ │
│ └──────────────────────┬───────────────────────┘ │
│ │ │
│ ▼ │
│ LangChain 自动注入追踪层 │
│ ┌──────────────────────────────────────────────┐ │
│ │ 每个步骤开始 → 记录时间戳、输入参数 │ │
│ │ ↓ │ │
│ │ 执行 LLM 调用 → 记录 Prompt、响应、Token │ │
│ │ ↓ │ │
│ │ 执行工具调用 → 记录工具名、参数、返回值 │ │
│ │ ↓ │ │
│ │ 每个步骤结束 → 记录输出、耗时 │ │
│ └──────────────────────┬───────────────────────┘ │
│ │ │
│ ▼ │
│ 数据上传到 LangSmith 云端 │
│ ┌──────────────────────────────────────────────┐ │
│ │ https://smith.langchain.com/ │ │
│ │ │ │
│ │ 项目: langchain-langsmith-demo │ │
│ │ ├── 追行记录 2024-01-15 10:23 │ │
│ │ │ ├── LLM 调用 (1.2s, 156 tokens) │ │
│ │ │ ├── 工具调用 get_weather (0.1s) │ │
│ │ │ └── 最终响应生成 (0.8s) │ │
│ │ └── ... │ │
│ └──────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

3. 快速开始:三步集成 LangSmith

步骤 1:获取 LangSmith API Key

  1. 访问 https://smith.langchain.com/
  2. 注册/登录账号
  3. 进入 Settings → API Keys
  4. 创建新的 API Key(格式:lsv2_...

步骤 2:配置环境变量

在你的 .env 文件中添加:

1
2
3
4
# LangSmith 可观测性配置
LANGCHAIN_API_KEY=lsv2_xxx... # 你的 LangSmith API Key
LANGCHAIN_TRACING_V2=true # 启用 V2 追踪
LANGCHAIN_PROJECT=langchain-langsmith-demo # 项目名称

步骤 3:自动生效

关键点:不需要修改任何代码!

LangChain 会自动检测环境变量,启用追踪:

1
2
3
4
5
6
# 你的代码完全不需要改动
from src.agent.factory import build_advanced_agent

agent = build_advanced_agent()
response = agent.invoke({"messages": "北京天气怎么样?"})
# ↑ 这一步的完整追踪会自动上传到 LangSmith

原理揭秘:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# LangChain 内部(简化版)
if os.getenv("LANGCHAIN_TRACING_V2") == "true":
# 自动包装每个函数调用
def traced_wrapper(original_func):
def wrapper(*args, **kwargs):
# 开始追踪
trace_id = start_trace()
start_time = time.time()

# 执行原函数
result = original_func(*args, **kwargs)

# 结束追踪并上传
duration = time.time() - start_time
upload_trace(trace_id, {
"input": args,
"output": result,
"duration": duration
})
return result
return wrapper

# 自动应用到 LLM、工具调用等
_original_invoke = traced_wrapper(_original_invoke)

4. LangGraph Studio:可视化调试界面

什么是 LangGraph Studio?

LangGraph Studio 是 LangSmith 的本地开发伴侣,它让你能够:

  • 在本地运行和调试 Agent
  • 实时查看执行流程
  • 可视化展示状态变化
  • 修改后立即看到效果

启动 Studio

1
2
3
4
5
# 确保已安装(需要 Python >= 3.11)
pip install --upgrade "langgraph-cli[inmem]"

# 启动 Studio 服务器
langgraph dev

启动后你会看到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌────────────────────────────────────────────────────────────┐
│ LangGraph Studio 已启动 │
├────────────────────────────────────────────────────────────┤
│ │
│ 🌐 API 端点: │
│ http://127.0.0.1:2024 │
│ │
│ 🎨 Studio 界面: │
│ https://smith.langchain.com/studio/?baseUrl=... │
│ │
│ 📊 可用的 Graph: │
│ • agent - 高级 Agent(带记忆) │
│ • basic_agent - 基础 Agent(单工具) │
│ │
└────────────────────────────────────────────────────────────┘

Studio 界面一览

打开 Studio 界面后,你会看到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
┌─────────────────────────────────────────────────────────────────┐
│ LangGraph Studio 界面 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────────────────────────┐ │
│ │ Graph 列表 │ │ 执行追踪详情 │ │
│ │ │ ├─────────────────────────────────────┤ │
│ │ ▶ agent │ │ Thread: user-001 │ │
│ │ basic_agent │ │ │ │
│ │ │ │ ┌─ Step 1: LLM 调用 ────────┐ │ │
│ │ │ │ │ 时间: 2024-01-15 10:23:01 │ │ │
│ │ │ │ │ 耗时: 1.2s │ │ │
│ │ │ │ │ Tokens: 156 input / 42 out │ │ │
│ │ │ │ │ │ │ │
│ │ │ │ │ 输入: │ │ │
│ │ │ │ │ "北京天气怎么样?" │ │ │
│ │ │ │ │ │ │ │
│ │ │ │ │ 输出: │ │ │
│ │ │ │ │ [工具调用] │ │ │
│ │ │ │ │ get_weather_for_location │ │ │
│ │ │ │ │ {"city": "北京"} │ │ │
│ │ │ │ └────────────────────────────┘ │ │
│ │ │ │ │ │
│ │ │ │ ┌─ Step 2: 工具调用 ───────┐ │ │
│ │ │ │ │ 耗时: 0.1s │ │ │
│ │ │ │ │ 结果: "晴朗,约 12C" │ │ │
│ │ │ │ └────────────────────────────┘ │ │
│ └─────────────────┘ └─────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

5. 追踪数据的解读

完整的执行链路

当你在 LangSmith 中查看一次 Agent 执行,会看到类似这样的追踪:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
┌─────────────────────────────────────────────────────────────────┐
│ Agent 执行追踪示例 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 📅 2024-01-15 10:23:45 │
│ 🧵 Thread: user-001 │
│ ⏱️ 总耗时: 2.1s │
│ 💰 总 Tokens: 234 │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Step 1: agent:LLM (理解意图并决策) │ │
│ │ ├─ 耗时: 1.2s │ │
│ │ ├─ Tokens: 156 in / 42 out │ │
│ │ ├─ 输入: "北京天气怎么样?" │ │
│ │ └─ 输出: [工具调用] get_weather_for_location("北京") │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Step 2: agent:tools:get_weather_for_location │ │
│ │ ├─ 耗时: 0.1s │ │
│ │ ├─ 参数: {"city": "北京"} │ │
│ │ └─ 结果: "晴朗,约 12C" │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Step 3: agent:LLM (生成最终回复) │ │
│ │ ├─ 耗时: 0.8s │ │
│ │ ├─ Tokens: 36 in / 89 out │ │
│ │ ├─ 输入: (工具结果 "晴朗,约 12C") │ │
│ │ └─ 输出: "北京今天天气晴朗,温度约12度..." │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

关键指标解读

指标 说明 用途
Latency 单个步骤的耗时 定位性能瓶颈
Tokens 输入/输出 Token 数量 估算成本、优化 Prompt
Cost API 调用成本(估算) 监控花费
Error 错误信息(如果有) 快速定位问题

6. 实战:调试一个真实问题

让我们通过一个实际例子,展示 LangSmith 如何帮助调试。

问题场景

用户反馈:Agent 有时返回”抱歉,我暂时无法回答”

没有 LangSmith 时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 你能做的:
def run_agent(question):
try:
response = agent.invoke({"messages": [question]})
return response
except Exception as e:
print(f"出错了: {e}")
# 😰 但问题可能不是异常,而是 LLM 决策导致的

# 实际问题可能出在:
# - 工具参数解析错误?
# - 城市名解析失败?
# - Prompt 写得不清楚?
# - LLM 能力不足?

使用 LangSmith 后

  1. 在 LangSmith 中找到失败的那次执行
  2. 展开每个步骤查看详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
问题定位过程:

┌─────────────────────────────────────────────────────┐
│ Step 1: LLM 调用 │
├─────────────────────────────────────────────────────┤
│ 输入: "查一下三藩市天气" │
│ │
│ 输出: [工具调用] │
│ get_weather_for_location({"city": "三藩市"}) │
│ │
│ ✅ LLM 理解正确,知道要调用天气工具 │
└─────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────┐
│ Step 2: 工具调用 get_weather_for_location │
├─────────────────────────────────────────────────────┤
│ 参数: {"city": "三藩市"} │
│ │
│ 结果: "暂无 三藩市 的实时数据" │
│ │
│ ❌ 找到问题了! │
│ 城市别名解析不支持"三藩市"这个称呼 │
└─────────────────────────────────────────────────────┘
  1. 修复问题
1
2
3
4
5
# 在 CITY_ALIASES 中添加别名
CITY_ALIASES = {
"San Francisco": ["sf", "san francisco", "旧金山", "三藩市"], # ← 新增
...
}
  1. 验证修复:在 Studio 中回放同一条对话,确认现在能正确处理

7. langgraph.json 配置详解

langgraph.json 是 LangGraph Studio 的配置文件:

1
2
3
4
5
6
7
8
{
"dependencies": ["."],
"graphs": {
"agent": "./graphs.py:agent",
"basic_agent": "./graphs.py:basic_agent"
},
"env": ".env"
}

字段解释:

字段 说明
dependencies Python 包依赖(. 表示当前目录)
graphs 定义的 Graph 列表,格式为 文件路径:变量名
env 环境变量文件路径

graphs.py 的作用:

1
2
3
4
5
6
7
8
9
10
11
12
"""
graphs.py 导出已编译的 agent,供 Studio 使用
"""

from src.agent.factory import build_advanced_agent, build_basic_agent

# 基础版 agent
basic_agent = build_basic_agent()

# 高级版 agent
# 注意:不传入 checkpointer,由 Studio 自动处理
agent = build_advanced_agent(with_checkpointer=False)

8. 高级功能

8.1 会话回放(Replay)

LangSmith 允许你重新运行历史对话:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
场景:你修改了 Prompt 或工具实现

┌─────────────────────────────────────────────────────────┐
│ LangSmith 回放功能 │
├─────────────────────────────────────────────────────────┤
│ │
│ 历史执行 #1234 │
│ ├── 步骤 1: LLM 调用 │
│ ├── 步骤 2: 工具调用 │
│ └── 步骤 3: 最终回复 │
│ │
│ [🔄 Replay 按钮] │
│ │ │
│ ▼ │
│ 用最新代码重新运行同一条输入 │
│ → 对比新旧结果,验证修改效果 │
│ │
└─────────────────────────────────────────────────────────┘

8.2 项目管理

你可以为不同环境创建不同项目:

1
2
3
4
5
6
7
8
# 开发环境
LANGCHAIN_PROJECT=langchain-dev

# 测试环境
LANGCHAIN_PROJECT=langchain-test

# 生产环境
LANGCHAIN_PROJECT=langchain-prod

8.3 性能优化建议

通过 LangSmith 追踪,你可以发现:

问题 追踪中的表现 优化方案
Prompt 太长 输入 Token 过高 精简 Prompt,去除冗余内容
串行调用 总耗时 = 各步骤耗时之和 改为并行调用
重复调用同一工具 同一工具被多次调用 添加结果缓存
LLM 响应慢 单次 LLM 调用耗时过长 换更快的模型或优化 Prompt

9. 总结

核心概念回顾

概念 一句话理解
LangSmith AI 应用的”监控摄像头”
Trace(追踪) 一次完整的执行记录
Span(跨度) 执行中的一个步骤
LangGraph Studio 本地可视化调试工具

LangSmith 的核心价值

  1. 调试不再”盲人摸象”

    • 看见每一步的输入输出
    • 定位错误的精确位置
  2. 性能优化有据可依

    • 识别耗时最长的步骤
    • 计算 Token 成本
  3. 团队协作效率提升

    • 分享追踪链接给同事
    • 复现和定位问题
  4. 持续改进的依据

    • 对比不同版本的效果
    • 回放验证修改

与上一篇文章的关联

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌─────────────────────────────────────────────────────────┐
│ 系列文章知识体系 │
├─────────────────────────────────────────────────────────┤
│ │
│ 第一篇:LangChain Agent 原理 │
│ ├── Agent = LLM + Tools + 规划 │
│ ├── 工具调用(Function Calling) │
│ ├── 对话记忆(Memory) │
│ └── 结构化输出(Structured Output) │
│ │ │
│ ▼ │
│ 第二篇:LangSmith 可观测性(本文) │
│ ├── 为什么需要可观测性 │
│ ├── LangSmith 工作原理 │
│ ├── LangGraph Studio 可视化调试 │
│ └── 追踪数据解读与实战调试 │
│ │ │
│ ▼ │
│ 下一阶段(待续) │
│ ├── 更复杂的 Agent 编排 │
│ ├── RAG(检索增强生成) │
│ └── 多 Agent 协作 │
│ │
└─────────────────────────────────────────────────────────┘

10. 快速上手

运行本项目的 LangSmith Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 1. 克隆项目
git clone https://git.liupx.com/study/langchain_langsmith_demo
cd langchain_langsmith_demo

# 2. 创建虚拟环境
python3 -m venv .venv
source .venv/bin/activate

# 3. 安装依赖
pip install langchain langgraph python-dotenv openai
pip install --upgrade "langgraph-cli[inmem]"

# 4. 配置环境变量
cp .env.example .env
# 编辑 .env,填入 DEEPSEEK_API_KEY 和 LANGCHAIN_API_KEY

# 5. 启动 LangGraph Studio
langgraph dev

# 6. 访问 Studio 界面
# https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024

查看追踪数据

启动 Studio 并执行一些查询后,访问 https://smith.langchain.com/ 查看完整的追踪记录。

11. 参考资料

相关文档

本项目文档

源码索引

文件 功能
langgraph.json Studio 配置文件
graphs.py Agent 导出(Studio用)
src/agent/llm.py DeepSeek 模型配置
.env.example 环境变量示例(含LangSmith)

系列文章导航

关于作者:本文基于 langchain_langsmith_demo 项目编写,延续上一篇文章的风格,专注于 LangSmith 可观测性的原理与实践。欢迎 Star 和 Fork!

0%