第四单元——第17课:让回答边生成、边显示

上一课已经能保存和恢复聊天。这节改善使用体验:不再等待完整回答,而是收到一段就显示一段。

之前学的 updates 是观察节点更新;这次的 messages 是接收模型生成的文字片段。官方说明

1. 复制上一课文件

另存为:

复制代码
C:\Users\Administrator\langchain-study\unit4_lesson17_stream_chat.py

数据库、状态定义、摘要逻辑都保留,只改以下部分。

2. 给正式回答加一个标签

你的 chat 函数中有两次模型调用:

  • summary_response = model.invoke(...):生成内部摘要。
  • response = model.invoke(...):生成用户需要的回答。

只把第二处改成:

复制代码
    response = model.invoke(
        [
            system_message,
            *messages,
        ],
        config={"tags": ["user_answer"]},
    )

user_answer 是我们自己取的标签,用来识别"这是正式回答"。

否则,直接展示所有模型输出时,内部摘要也可能出现在聊天界面中。

同时,先注释掉 chat 函数内部的调试 print(...),避免它们插进正在输出的回答里。保留函数外每轮问题的输出。

3. 替换聊天循环里的调用部分

找到 while True 中,从:

复制代码
        result = graph.invoke(

到:

复制代码
        turn += 1

这一段,替换为:

复制代码
        print("助手:", end="", flush=True)
        printed = False

        for chunk in graph.stream(
            {
                "messages": [
                    HumanMessage(content=question)
                ]
            },
            config=config,
            context=Context(user_id=user_id),
            stream_mode="messages",
            version="v2",
        ):
            # 只处理模型消息事件
            if chunk["type"] != "messages":
                continue

            message_chunk, metadata = chunk["data"]

            # 只展示正式回答,不展示内部摘要
            if "user_answer" not in metadata.get("tags", []):
                continue

            # 本课使用普通文本回答
            text = message_chunk.content

            if isinstance(text, str) and text:
                print(text, end="", flush=True)
                printed = True

        # 流结束后,读取已经更新并保存的完整状态
        result = graph.get_state(config).values

        # 某些分支直接返回固定提示,没有调用模型
        if not printed and result.get("messages"):
            print(
                result["messages"][-1].content,
                end="",
                flush=True,
            )

        print()
        print("当前状态消息数:", len(result.get("messages", [])))
        print()

        turn += 1

原来前面的这行继续保留,因此每轮问题仍然会显示:

复制代码
        print(f"\n本次运行第 {turn} 轮问题:{question}")

4. 理解三个关键点

① stream_mode="messages"

接收模型输出的片段。一个片段可能包含一个字、多个字或标点,不能简单理解成"一次一个汉字"。

这里显式使用 version="v2",所以通过:

复制代码
message_chunk, metadata = chunk["data"]

拿到内容和相关信息。

② end="" 和 flush=True

复制代码
print(text, end="", flush=True)
  • end="":每个片段后不换行,让它们接成一句话。
  • flush=True:立即刷新到终端,不等待缓冲。

③ 为什么节点里还是 model.invoke()?

外层通过 LangGraph 的消息流运行时,支持流式输出的模型调用可以产生消息事件,节点内部仍能拿到完整回答。

因此保留原来的:

复制代码
return {
    "summary": summary,
    "messages": [
        *delete_messages,
        response,
    ],
}

屏幕上逐段显示,状态里保存完整消息。 不要再把每个片段逐个加入 messages。

5. 运行测试

复制代码
python unit4_lesson17_stream_chat.py

继续使用原来的对话编号,然后输入:

复制代码
请结合我的阅读偏好,安排一个七天阅读计划,每天写两句话。

观察:

  1. 回答是否逐段出现。
  2. 触发摘要时,是否没有混入摘要正文。
  3. 退出并重启后,是否仍能继续这段对话。

注意:流式输出不保证模型生成得更快,只是让你更早看到已经生成的部分。 如果本轮先做摘要,正式回答出现前仍然需要等待摘要完成。

相关推荐
北风toto1 小时前
Linux 服务器如何判断 CPU 是否支持 x86-64-v2 指令集
java·linux·服务器
1024奇点1 小时前
【仓颉语言入门 · 第二篇 第 8 课】
开发语言·数据库·ide·mysql·开源
结局无敌1 小时前
React Native for OpenHarmony 实战:三方库 react-native-paper 的鸿蒙化适配指南
javascript·人工智能·深度学习·react native·react.js·ecmascript·harmonyos
Shulex1 小时前
2026 AI 客服系统选型指南:大模型RAG GEO 2.0 时代下的买方决策卡位战
人工智能·智能客服·跨境电商
XMAIPC_Robot1 小时前
ARM+FPGA+AI船舶/工业自动化控制器EMC测试等级解析(IEC60533/IEC60945)
arm开发·人工智能·fpga开发·zynq·arm+fpga·rk3588+fpga
MetWeave1 小时前
从零跑一个气象数据管道:抓取、清洗、对账、节奏
后端·python
程序员-Benothing1 小时前
Linux抓包分析教程:tcpdump、Wireshark、tshark实战
linux·运维·服务器
小姚也要变强1 小时前
机器学习基本概念
人工智能·机器学习
weixin_446260851 小时前
TraceGuard:基于执行轨迹的智能体事后溯源审计框架
人工智能