LangChain 消息处理全解析:缓存、过滤、合并与流式输出实战

LangChain 消息处理全解析:缓存、过滤、合并与流式输出实战

文章目录

在这里插入图片描述


一、消息内存缓存

核心概念

通过 InMemoryChatMessageHistory 将对话历史存储在内存中,使模型能"记住"之前的对话内容。

关键组件

组件作用
InMemoryChatMessageHistory内存中的聊天记录存储器
RunnableWithMessageHistory将模型包装为支持历史记录的可运行对象
memory_store(字典)session_id 为 key 管理多个会话的历史

代码流程

# 1. 创建内存存储字典 memory_store ={}# 2. 定义获取会话历史的函数(按 session_id 区分会话)defget_session_history(session_id:str):if session_id notin memory_store: memory_store[session_id]= InMemoryChatMessageHistory()return memory_store[session_id]# 3. 用 RunnableWithMessageHistory 包装模型 message_model = RunnableWithMessageHistory(model, get_session_history)# 4. 通过 config 指定会话 ID config ={"configurable":{"session_id":"123"}}# 5. 多轮对话,模型自动记住上下文 response1 = message_model.invoke({"input":"你好,我是小明"}, config=config) response2 = message_model.invoke({"input":"我叫什么名字?"}, config=config)# → 模型能回答出"小明",因为历史被缓存了

运行效果

  • 第一轮:用户说"我是小明",AI 正常打招呼
  • 第二轮:用户问"我叫什么名字",AI 能从历史中回忆出"小明"

从LangChain的v0.3版本开始,官⽅建议LangChain⽤⼾不要使⽤
RunnableWithMessageHistory ,⽽是利⽤ LangGraph 持久性 来完成


二、消息过滤

核心概念

使用 filter_messages 函数对消息列表进行筛选,按类型ID过滤消息。

关键函数

from langchain_core.messages import filter_messages 

过滤参数

参数作用示例
include_types只保留指定类型的消息["ai"] → 只保留 AI 消息
exclude_ids排除指定 ID 的消息["4"] → 排除 id 为 “4” 的消息

代码示例

messages =[ HumanMessage(content="你好,我是小明",id="1"), AIMessage(content="你好,小明!很高兴认识你!",id="2"), HumanMessage(content="我想知道我之前的名字",id="3"), AIMessage(content="你之前的名字是小绿!",id="4"),]# 过滤:只保留 AI 消息,且排除 的消息 filtered_messages = filter_messages( messages, include_types=["ai"], exclude_ids=["4"],)# → 结果只剩 的 AIMessage: "你好,小明!很高兴认识你!"

过滤逻辑

原始消息 → include_types=["ai"] 筛掉 Human 消息 → exclude_ids=["4"] 再排除 id=4 → 最终结果

原始: [Human#1, AI#2, Human#3, AI#4] ↓ include_types=["ai"] 中间: [AI#2, AI#4] ↓ exclude_ids=["4"] 结果: [AI#2] 

三、消息合并

核心概念

使用 merge_message_runs连续的同类型消息合并为一条,避免多条连续 Human 或 AI 消息导致模型报错或行为异常。

关键函数

from langchain_core.messages import merge_message_runs 

代码示例

messages =[ HumanMessage(content="你好",id="1"), HumanMessage(content="我是小明",id="2"),# 连续两条 Human AIMessage(content="你好,小明!",id="3"), AIMessage(content="很高兴认识你!",id="4"),# 连续两条 AI] merged_messages = merge_message_runs(messages)

合并效果

合并前(4条): human: 你好 human: 我是小明 ai: 你好,小明! ai: 很高兴认识你! 合并后(2条): human: 你好\n我是小明 ai: 你好,小明!\n很高兴认识你! 

两种使用方式

# 方式一:直接调用函数合并后传给模型 merged_messages = merge_message_runs(messages) model.invoke(merged_messages)# 方式二:通过管道(pipe)操作,合并与模型调用串联 chain = merge_message_runs | model response = chain.invoke(messages)

管道方式更简洁,适合在 LangChain 链式调用中使用。


四、流式输出

什么是流式输出

流式输出(Streaming) 是指 AI 模型逐字返回内容,而不是等待全部生成完毕后一次性返回。就像 ChatGPT 那样,文字一个个"打"出来,而不是突然全部出现。

为什么需要?

AI 生成长文本可能需要几秒甚至更长时间。传统方式用户需要等待整个响应完成才能看到内容,体验很差。流式输出实时展示生成过程,让用户感觉响应更快,交互更自然。

特性非流式流式
用户体验需要等待实时看到
适用场景短文本聊天对话、长文本
内存占用一次性加载逐块处理
可控性无法中断可随时停止

典型应用

  1. 聊天机器人:像 ChatGPT 一样逐字显示
  2. 文章生成:实时展示生成过程
  3. 代码生成:逐行显示代码
  4. 实时翻译:边翻译边显示

五、同步 vs 异步流式

LangChain 提供两种流式方式:同步(stream)和异步(astream)。

核心区别

特性同步 stream异步 astream
调用chain.stream()chain.astream()
循环for chunk inasync for chunk in
阻塞阻塞线程不阻塞,可并发
场景单个请求多个并发请求
性能一般更高

工作原理

同步流式: 阻塞当前线程,处理一个请求时无法处理其他请求。就像排队买咖啡,必须等前一个人买完。

异步流式: 使用协程机制,等待 AI 响应时可以切换到其他任务。就像服务员可以同时为多桌客人点单。

何时使用异步?

推荐:

  • 多用户 Web 应用
  • 高并发聊天机器人
  • 与其他异步操作结合

不需要:

  • 简单的单次调用
  • 学习测试阶段

六、流式输出基础用法

同步流式

from langchain_deepseek import ChatDeepSeek from langchain_core.output_parsers import StrOutputParser model = ChatDeepSeek(model="deepseek-chat", streaming=True) parser = StrOutputParser() chain = model | parser for chunk in chain.stream("写一个关于程序员的笑话"):print(chunk, end="|", flush=True)

关键点:

  • streaming=True:必须设置
  • flush=True:立即刷新输出

异步流式

import asyncio asyncdefmain(): chain = model | parser asyncfor chunk in chain.astream("写一个关于程序员的笑话"):print(chunk, end="|", flush=True)if __name__ =="__main__": asyncio.run(main())

关键点:

  • async def:定义异步函数
  • async for:异步迭代
  • asyncio.run():运行入口

七、输出解析器

StrOutputParser 是最常用的解析器,将模型输出转换为纯文本。

作用:

  • 提取文本内容
  • 去除多余格式
  • 统一输出格式

自定义解析器:

defcustom_parser(output:str)->str:return output.strip().replace("。","!") chain = model | parser | custom_parser 

应用场景:

  • 格式转换(Markdown → HTML)
  • 内容过滤审核
  • 特殊字符处理

八、流式输出实际应用

1. 聊天机器人

用户发送消息后,AI 回复逐字显示,像真人打字。使用异步流式提高响应速度。

2. 多用户并发

Web 应用中多个用户同时请求,异步流式可以并发处理。

性能对比:

  • 同步:3个请求需要 15 秒(串行)
  • 异步:3个请求只需 5 秒(并发)

3. FastAPI 集成

from fastapi import FastAPI from fastapi.responses import StreamingResponse @app.get("/chat")asyncdefchat_stream(question:str):asyncdefgenerate():asyncfor chunk in chain.astream(question):yield chunk return StreamingResponse(generate(), media_type="text/plain")

九、常见问题

1. 没有流式效果?

原因: 忘记 streaming=Trueflush=True

2. async for 报错?

原因: 使用了 ainvoke() 而不是 astream()

ainvoke() 返回完整结果,astream() 返回流式迭代器。

3. 性能对比

  • 单个请求:同步和异步相近
  • 多个并发:异步快 3 倍

十、总结对比

功能函数/类用途
内存缓存InMemoryChatMessageHistory + RunnableWithMessageHistory让模型记住多轮对话上下文
消息过滤filter_messages按类型/ID 筛选消息
消息合并merge_message_runs合并连续同类型消息
流式输出stream / astream实时逐字返回,提升体验
输出解析StrOutputParser将模型输出转为纯文本

典型应用场景

  • 内存缓存:多轮对话场景,用户问"我之前说了什么"时模型能回答
  • 消息过滤:只提取 AI 回复做摘要、排除某些敏感消息
  • 消息合并:用户连续发了多条消息时,合并后再发给模型,避免格式错误
  • 流式输出:聊天机器人逐字显示、长文本生成、FastAPI 接口集成

流式输出要点

  1. 流式输出 = 实时返回,提升体验
  2. 同步 = 简单,适合学习
  3. 异步 = 高性能,适合生产
  4. 必须设置 streaming=Trueflush=True

Read more

从美团全栈化看 AI 冲击:前端转全栈,是自救还是必然 [特殊字符][特殊字符][特殊字符]

从美团的全栈化看 AI 冲击:前端转全栈,是自救还是必然? 美团近年来在AI工具上的大力投入(如2025年推出的NoCode平台),确实让很多人联想到“AI对前端开发的冲击”,尤其是NoCode被描述为“全栈的AI工程师”:它能通过自然语言生成前端页面、后端逻辑、数据库,甚至一键部署小程序或网页。这让非程序员都能快速构建应用,内部已产生大量生产力工具。 但美团本身并没有明确推动“前端工程师强制转全栈”的组织变革。2024-2025年,美团进行了多次组织架构调整(如到家/到店事业群合并为核心本地商业、研发平台整合),主要是为了提升业务协同、应对抖音等竞争,并强化“零售+科技”战略。这些调整更多聚焦业务整合和技术平台升级,而不是针对前端岗位的全栈化转型。 AI 对前端开发的真实冲击(2025年现状) AI确实在重塑前端生态,但远没到“取代前端”的地步: * AI的优势:工具如Vercel V0、GitHub Copilot、Cursor、Devin等,能快速生成UI组件、布局、交互逻辑,

SpringBoot+Vue 家政服务平台平台完整项目源码+SQL脚本+接口文档【Java Web毕设】

SpringBoot+Vue 家政服务平台平台完整项目源码+SQL脚本+接口文档【Java Web毕设】

摘要 随着社会经济的快速发展和人们生活水平的不断提高,家政服务需求日益增长,传统的家政服务模式已无法满足现代家庭的高效、便捷和个性化需求。互联网技术的普及为家政服务行业提供了新的发展机遇,通过线上平台整合服务资源,优化服务流程,提升用户体验成为行业趋势。家政服务平台通过数字化手段连接服务提供者和消费者,实现供需精准匹配,解决传统家政服务中信息不对称、服务质量参差不齐等问题。关键词:家政服务、互联网平台、供需匹配、数字化管理。 本项目基于SpringBoot和Vue技术栈开发了一款高效、易用的家政服务平台,采用前后端分离架构,后端使用SpringBoot框架实现RESTful API,前端通过Vue.js构建动态交互界面。系统主要功能包括用户注册登录、服务分类展示、在线预约、订单管理、支付集成、评价反馈等。数据库采用MySQL存储数据,通过MyBatis-Plus实现高效数据操作。平台注重用户体验和服务质量,支持多角色管理(用户、家政人员、管理员),并引入智能推荐算法优化服务匹配。关键词:SpringBoot、Vue.js、RESTful API、智能推荐、多角色管理。 数据表设计

Rust与WebAssembly深度实战——将高性能Rust代码运行在浏览器与Node.js

Rust与WebAssembly深度实战——将高性能Rust代码运行在浏览器与Node.js

Rust与WebAssembly深度实战——将高性能Rust代码运行在浏览器与Node.js 一、学习目标与重点 1.1 学习目标 1. 理解WebAssembly基础:深入掌握WebAssembly(Wasm/Wasmtime)的核心定义、运行机制、与JavaScript的性能对比 2. 掌握Rust到Wasm的编译:熟练使用wasm-pack、cargo-web等工具链,完成Rust代码到Wasm模块的编译、打包、优化 3. 精通Rust与JavaScript交互:实现双向交互(Rust调用JS函数、JS调用Rust函数),处理复杂数据类型(数组、对象、字符串),管理内存(Wasm线性内存的分配与释放) 4. 开发真实Wasm应用:编写浏览器端高性能任务(Canvas图像滤镜、WebGL计算辅助)、Node.js端计算密集型任务(图像处理、加密解密、数据压缩) 5. 优化Wasm模块:使用wasm-opt工具优化Wasm体积,学习代码分割、懒加载、模块缓存

前端文本测量成了卡死一切创新的最后瓶颈,pretext实现突破了

前端文本测量成了卡死一切创新的最后瓶颈,pretext实现突破了

亲爱的前端开发者(以及所有关心界面未来的人),我最近把大量精力砸进了一个听起来小众、实则能重塑整个网页布局范式的项目。过去几年,我们一直在抱怨 CSS 强大却难以捉摸,DOM 测量方便却代价高昂。尤其在 AI 时代,界面需要动态、响应式、甚至上万元素同时运行时,文本测量成了卡死一切创新的最后瓶颈——它既是基础,又是地狱。 现在,这个瓶颈被彻底攻破了。我发现了一个开源纯 TypeScript 的用户态文本测量引擎,名叫 Pretext。它不需要 CSS、不依赖 DOM 测量,就能精准计算任意文本在任意宽度下的排版结果,支持整个网页的完整布局。体积只有几 KB,却能处理浏览器所有怪癖,支持全球语言(包括韩文混排 RTL 阿拉伯文和平台表情),还能轻松跑出 120fps 的复杂交互。 看效果 TypeScript 的用户态文本测量引擎,名叫 Prete 很多人以为 CSS