第四单元——第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. 退出并重启后,是否仍能继续这段对话。

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

相关推荐
赛博山人2 分钟前
字节跳动下线扣子企业版,团队已并入豆包
人工智能
isboyjc3 分钟前
Lomo Agent:面向创作型、持续协作 Agent 的架构设计与工程实践
人工智能·架构·agent
小蒋观天下9 分钟前
码率、I帧间隔、智能编码冲突:H.265+导致AI取流解码异常
人工智能
橙橙笔记25 分钟前
具身智能的学习 - 完成一个机械臂 第1节
python·学习·机械臂·具身智能
小小张说故事33 分钟前
Python 的 class 语句到底执行了什么?拆开 __prepare__、metaclass.__new__ 和那个被讲反的 __set_name__ 顺序
python
GISer_Jing1 小时前
如何学习Agent
后端·langchain
roman_日积跬步-终至千里1 小时前
AI 能让一个人成为团队吗?从数据中台到 OPC,重新理解软件工程能力
大数据·人工智能·软件工程
讯捷蓝达1 小时前
戴尔R720xd服务器卡死在 “Configuring Memory...Done“,换了内存还是卡?问题根本不在内存
运维·服务器
在所不辞兄1 小时前
为什么神经网络在训练集损失已接近零后继续训练,测试性能仍可能继续提升,而不是单纯地越来越过拟合?
人工智能·深度学习·神经网络·机器学习·智能仿真
AI砖家2 小时前
ChatGPT Images 2.6 正式支持 GIF 生成:一张图片也能动起来,AI 动图创作时代来了!
人工智能·chatgpt