Part 3:模型调用、记忆管理与工具调用流程(LangChain 1.0)笔记(Markdown)

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 使用流式的注意事项

文件中明确提醒的要点(很重要):

  1. Streaming 依赖链路全程支持逐块处理

    如果你中间某一步必须等完整输出(例如一次性写数据库),就会卡住流式优势。

  2. 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): ...

  • 拼接 chunkfull = 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 写回记忆,并做截断

相关推荐
mys55182 小时前
杨建允:AI搜索优化对汽车服务行业获客的影响
人工智能·aigc·geo·ai搜索优化·ai引擎优化
winfredzhang2 小时前
深度解析:利用 Python + Playwright 攻克动态网页 PPT 导出难题
python·powerpoint·截图·自动翻页
2501_936146042 小时前
鱼类识别与分类:基于freeanchor_x101-32x4d_fpn_1x_coco的三种鱼类自动检测
人工智能·分类·数据挖掘
鲨莎分不晴2 小时前
拯救暗淡图像:深度解析直方图均衡化(原理、公式与计算)
人工智能·算法·机器学习
好奇龙猫2 小时前
【人工智能学习-AI-MIT公开课-10. 学习介绍、最近邻】
人工智能·学习
风送雨2 小时前
八周Python强化计划(七)
开发语言·python
ππ很开心6662 小时前
DAY 32 函数专题2:装饰器
开发语言·python
智算菩萨2 小时前
2026马年新岁:拥抱智能时代,共谱科技华章
人工智能·科技
wdfk_prog2 小时前
[Linux]学习笔记系列 -- [fs]open
linux·笔记·学习