上一课已经能保存和恢复聊天。这节改善使用体验:不再等待完整回答,而是收到一段就显示一段。
之前学的 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
继续使用原来的对话编号,然后输入:
请结合我的阅读偏好,安排一个七天阅读计划,每天写两句话。
观察:
- 回答是否逐段出现。
- 触发摘要时,是否没有混入摘要正文。
- 退出并重启后,是否仍能继续这段对话。
注意:流式输出不保证模型生成得更快,只是让你更早看到已经生成的部分。 如果本轮先做摘要,正式回答出现前仍然需要等待摘要完成。