0. 课程说明(文件前半部分的内容概览)
-
该 Notebook 是某套课程体验课内容的节选,前面包含:
-
课程介绍/宣传页(图片)
-
部分课程成果演示(若干项目方向:语音客服、自动化内容创作、多 Agent 数据分析、自动数据集构建、GraphRAG 等)
-
-
与技术笔记直接相关的核心内容从:
- "LangChain 1.0入门实战 - Part 3" 开始。
1. LangChain 1.0:基础模型消息格式与模型调用流程
1.1 LangChain 1.0 的核心变化(从"工作流语言"转向"API + 消息标准")
-
在 LangChain 1.0 中,实践重心更偏向:
-
直接使用 LangChain API 完成模型调用
-
通过 消息列表(message list) 管理多轮对话(也就是"记忆")
-
支撑更复杂的 Agent / 调度(本文件未深入展开工具调用细节,但消息规范是基础)
-
1.2 Message 是最基本交互单元
Message 的意义
-
Message = 模型输入/输出的统一载体
-
每轮对话可以由一条或多条 Message 组成
-
Message 除了文本外,还可以承载更多信息(多模态/元数据/追踪信息)
Message 通用字段(概念表)
-
Role(角色) :消息来源/类型
常见:
system(系统提示)、user(用户输入)、assistant(模型输出) -
Content(内容):文本/多模态内容本体
-
Metadata(元数据):可选信息,如 message_id、token 用量、标签、耗时等
LangChain 1.0 的一个关键点:跨模型统一 Message 标准(不同厂商模型尽量用同一套消息结构),便于替换底座、调试、追踪(例如 LangSmith 之类的链路追踪工具)。
1.3 代码:环境变量与模型初始化(DeepSeek 示例)
加载 API Key(dotenv)
python
import os
from dotenv import load_dotenv
load_dotenv(override=True)
DeepSeek_API_KEY = os.getenv("DEEPSEEK_API_KEY")
初始化模型
python
from langchain_deepseek import ChatDeepSeek
model = ChatDeepSeek(model="deepseek-chat")
1.4 代码:构造消息并调用模型(invoke)
三种最常用消息类型
python
from langchain.messages import HumanMessage, AIMessage, SystemMessage
-
SystemMessage:定义"助手是谁/要遵守什么规则"(系统提示词)
-
HumanMessage:用户输入
-
AIMessage:模型回复(也可用于"把历史答案塞回去"形成上下文)
单轮对话(system + user)
python
system_msg = SystemMessage("你叫小智,是一名助人为乐的助手。")
human_msg = HumanMessage("你好,好久不见,请介绍下你自己。")
messages = [system_msg, human_msg]
response = model.invoke(messages)
多轮对话(通过拼接历史消息形成"记忆")
python
messages = [
SystemMessage("你叫小智,是一名助人为乐的助手。"),
HumanMessage("你好,我叫陈明,好久不见,请介绍下你自己。"),
AIMessage("你好呀!我是小智,一个乐于助人的智能助手。..."),
HumanMessage("你好,请问你还记得我叫什么名字么?"),
]
response = model.invoke(messages)
print(response.content)
要点:
-
所谓"对话记忆",最朴素的形式就是:把历史消息按顺序放进 list 里继续喂给模型。
-
Few-shot 也同理:你可以把示例问答写成几条
HumanMessage/AIMessage放在前面。
1.5 返回结果是什么?(AIMessage 的关键字段)
invoke 的返回通常也是一个 Message(例如 AIMessage),不仅有 content,还常包含模型调用的统计信息与追踪信息,例如:
-
content:模型自然语言回复 -
additional_kwargs:补充字段(例如 refusal 等) -
response_metadata:底层调用信息(token、模型名、finish_reason、run id...) -
usage_metadata:统一 token 计量信息(input/output/total 等)
实践意义:
-
做产品化/上线时,这些字段用于:
-
成本统计(token)
-
质量分析(finish_reason)
-
链路追踪(run id / fingerprint)
-
安全策略或拒答原因记录(refusal)
-
1.6 直接传字符串也可以
如果你只想简单问一句,不自己手动构造 HumanMessage,可以直接:
python
question = "你好,请你介绍一下你自己。"
result = model.invoke(question)
print(result.content)
2. 流式响应(Streaming)与批处理(Batch)
2.1 为什么需要流式响应
流式生成(Streaming Generation)的优势:
-
更快看到首 token:用户体验更好
-
长回答更友好:不用等整段生成完成
-
可实时更新 UI:聊天机器人/网页端非常常用
2.2 stream():逐块返回 AIMessageChunk
最简单的流式打印
python
for chunk in model.stream("你好,好久不见"):
print(chunk.text, end="|", flush=True)
-
model.stream(...)返回一个可迭代对象 -
每次迭代产生一个 AIMessageChunk
-
你可以把 chunk 当成"增量输出的一小段"
2.3 Chunk 拼接:用 + 合并成完整回复
python
full = None
for chunk in model.stream("你好,好久不见"):
full = chunk if full is None else full + chunk
print(full.text)
-
LangChain 内置"chunk 相加"的机制
-
最终
full代表完整内容
查看更底层的内容块:
python
print(full.content_blocks)
2.4 使用流式的注意事项
文件中明确提醒的要点(很重要):
-
Streaming 依赖链路全程支持逐块处理
如果你中间某一步必须等完整输出(例如一次性写数据库),就会卡住流式优势。
-
LangChain 1.0 有"自动流式(Auto-streaming)"倾向:
在一些更复杂结构(例如 Agent)中,整体链路处于 streaming 模式时,即使节点里写的是
invoke(),也可能被框架自动流式化(此处文件是概念说明)。
2.5 批处理:batch() 并行处理多请求
当你有多条互不依赖的请求(多问题、多文档)时,批处理能显著提速:
python
responses = model.batch([
"请介绍下你自己。",
"请问什么是机器学习?",
"你知道机器学习和深度学习区别么?"
])
for response in responses:
print(response)
文件里强调的 batch 特性
-
batch()是 客户端并行(并不是调用模型厂商的 batch API) -
默认是 全部完成后一次性返回结果列表
-
适用场景:
-
批量摘要
-
批量问答
-
数据预处理/分类等
-
2.6 流式批处理:batch_as_completed()
如果你希望"谁先算完先返回谁",用:
python
for response in model.batch_as_completed([
"请介绍下你自己。",
"请问什么是机器学习?",
"你知道机器学习和深度学习区别么?"
]):
print(response)
注意点:
-
返回顺序可能与输入顺序不同
-
通常每个返回项会带有原始输入的
index,需要的话可按 index 重排
2.7 并发控制 config(批处理常用)
文件给了常见参数表,核心是:
-
max_concurrency:最大并行数(控制压测/限流) -
timeout:单请求超时 -
callbacks:回调(日志/监控) -
metadata:额外上下文信息(追踪)
示例:
python
model.batch(
list_of_inputs,
config={
"max_concurrency": 5,
}
)
3. 结构化输出(Structured Output)
3.1 为什么需要结构化输出
把模型输出约束成固定 Schema 的好处:
-
避免自然语言歧义
-
便于解析与落库
-
下游流程稳定(API 调用/任务编排/自动化链路更可靠)
3.2 LangChain 1.0 常见 Schema 方式(文件列举)
-
Pydantic(最强:校验、描述、嵌套结构、默认值等)
-
TypedDict(轻量约束)
-
JSON Schema(跨语言通用)
文件建议生产优先用 Pydantic。
3.3 示例:用 Pydantic 定义电影信息结构
python
from pydantic import BaseModel, Field
class Movie(BaseModel):
"""A movie with details."""
title: str = Field(..., description="The title of the movie")
year: int = Field(..., description="The year the movie was released")
director: str = Field(..., description="The director of the movie")
rating: float = Field(..., description="The movie's rating out of 10")
用 with_structured_output 约束输出:
python
model_with_structure = model.with_structured_output(Movie)
response = model_with_structure.invoke("Provide details about the movie Inception")
print(response)
-
返回值通常会是一个符合
Movie结构的对象(或等价结构) -
字段类型不匹配时会触发校验问题(这正是结构化输出的价值:让错误显性化)
3.4 include_raw=True:同时拿到原始回复
如果你既想要解析后的结构,又想保留模型原文(便于调试/回放):
python
model_with_structure = model.with_structured_output(Movie, include_raw=True)
resp = model_with_structure.invoke("Provide details about the movie Inception")
resp
4. 入门实践:搭建"流式响应的多轮问答机器人"(Gradio)
这一节是全文件最"工程化"的部分:把 消息队列(记忆)+ 流式输出 串成一个可交互 Demo。
4.1 目标
-
一个网页聊天界面(Gradio)
-
用户输入后:
-
将用户问题追加到消息历史
-
调用
model.stream(messages_list)流式生成 -
边生成边更新 UI 上的机器人回复
-
-
支持清空历史
-
支持对话轮数截断(避免无限增长导致 token 爆炸)
4.2 安装与导入
python
# !pip install gradio
import gradio as gr
from langchain_deepseek import ChatDeepSeek
from langchain.messages import HumanMessage, AIMessage, SystemMessage
4.3 初始化模型与 System Prompt
python
model = ChatDeepSeek(model="deepseek-chat")
system_message = SystemMessage(
content="你叫小智,是一名乐于助人的智能助手。请在对话中保持友好、有耐心、温和的语气。"
)
关键点:
-
system prompt 决定了"人格/风格/规则"
-
后续每轮都需要把它放在上下文里(最稳妥做法是:messages_list 为空时重新补上)
4.4 Gradio UI:Chatbot + Textbox + Buttons + State
UI 搭建的核心组件:
-
gr.Chatbot(...):显示对话 -
gr.Textbox(...):输入框 -
gr.Button(...):发送/清空 -
gr.State([]):保存"消息历史"(也就是记忆)
文件中的关键结构(简化理解版):
python
state = gr.State([]) # 用于保存 messages_list(LangChain Message对象列表)
4.5 核心:流式响应函数(async generator)
函数签名(文件中):
python
async def respond(user_msg: str, chat_hist: list, messages_list: list):
它做了几件非常标准的事:
Step 1:空输入直接返回
python
if not user_msg.strip():
yield "", chat_hist, messages_list
return
Step 2:构造消息上下文(记忆管理的关键)
python
if not messages_list:
messages_list = [system_message]
messages_list.append(HumanMessage(content=user_msg))
-
messages_list就是"对话记忆" -
每次用户发言就 append 一个
HumanMessage -
system message 确保在对话开头存在
同时更新 UI 的 chat 历史,并先占位机器人回复:
python
chat_hist = chat_hist + [(user_msg, None)]
yield "", chat_hist, messages_list
Step 3:流式生成 + 实时刷新 UI
python
partial = ""
for chunk in model.stream(messages_list):
if chunk.content:
partial += chunk.content
chat_hist[-1] = (user_msg, partial)
yield "", chat_hist, messages_list
-
partial是"已经生成的回复前缀" -
每个 chunk 来了就刷新一次界面
-
这就是"流式体验"的来源
Step 4:把完整回复写回记忆,并做截断
python
messages_list.append(AIMessage(content=partial))
messages_list = messages_list[-50:]
yield "", chat_hist, messages_list
非常重要的工程点:
-
必须把最终 AI 回复写回 messages_list,否则下一轮模型"不记得自己说过什么"
-
messages_list = messages_list[-50:]是一种简易的记忆裁剪策略:-
防止消息无限增长
-
控制 token 成本
-
生产中通常会更精细:按 token 数裁剪、做摘要记忆、向量库检索记忆等(本文件是入门 demo)
-
4.6 清空历史
python
def clear_history():
return [], "", []
4.7 事件绑定:submit / click / clear
python
msg.submit(respond, [msg, chatbot, state], [msg, chatbot, state])
submit.click(respond, [msg, chatbot, state], [msg, chatbot, state])
clear.click(clear_history, outputs=[chatbot, msg, state])
5. 本文件的"记忆管理 / 工具调用"到底讲了什么?
5.1 记忆管理(本文件覆盖:✅)
-
记忆的最小实现:messages_list(Message 列表)
-
多轮对话:每轮追加 HumanMessage + AIMessage
-
通过裁剪控制成本:保留最近 N 轮(示例为 50)
5.2 工具调用(本文件覆盖:⚠️偏少)
-
文件标题提到了"工具调用流程",但正文示例并未展示:
-
bind_tools -
tool/function calling 的 message 结构
-
tool 执行与 ToolMessage 回填等
-
-
结合文件内容能得到的结论是:
工具调用的前提仍是统一 Message 标准与可追踪的调用链路,而本篇重点在把"消息/流式/结构化输出"打牢。
6. 最小可复用总结(你可以直接当作开发 checklist)
-
单轮调用 :
model.invoke("...")或model.invoke([SystemMessage, HumanMessage]) -
多轮对话(记忆) :维护
messages_list,不断 append -
流式输出 :
for chunk in model.stream(messages_list): ... -
拼接 chunk :
full = full + chunk -
批处理 :
model.batch([...]);想谁先完成先返回用batch_as_completed -
并发控制 :
config={"max_concurrency": N, ...} -
结构化输出 :
model.with_structured_output(PydanticModel) -
工程化 Demo(聊天 UI):
-
UI state 存 messages_list
-
先 yield 用户消息,再流式 yield 机器人消息
-
最后把完整 AIMessage 写回记忆,并做截断
-