1. 核心问题:消息存哪?
答案:ChatHistoryProvider
所有对话消息由它统一管理
实际数据存在:
AgentSession.StateBag
2. 整体流程(很重要)
用户输入 → Agent.RunAsync()
↓ 调用前
ChatHistoryProvider.InvokingAsync()
👉 取历史消息
↓ 模型生成回复
↓ 调用后
ChatHistoryProvider.InvokedAsync()
👉 存新消息3. ChatHistoryProvider(抽象层)
本质:对话历史的存储策略接口
核心方法
public abstract class ChatHistoryProvider
{
public virtual string StateKey => this.GetType().Name;
// 取历史
ValueTask<IEnumerable<ChatMessage>> InvokingAsync(...);
// 存消息
ValueTask InvokedAsync(...);
// 取内部服务
TService? GetService<TService>();
}为什么设计成抽象类?
为了支持不同存储:
内存(默认)
数据库
Redis
文件
👉 本质就是:可插拔的“聊天记录后端”
4. 默认实现:InMemoryChatHistoryProvider
一句话理解
👉 把聊天记录存在 Session 内存里
内部结构
class State
{
public List<ChatMessage> Messages { get; set; }
}存储位置:
AgentSession.StateBag["InMemoryChatHistoryProvider"]核心方法
// 直接拿消息(仅内存实现有)
List<ChatMessage> GetMessages(session)
// 手动设置
void SetMessages(session, messages)5. 两种获取消息方式
✅ 通用方式(推荐)
var messages = await historyProvider
.InvokingAsync(new(agent, session, []));特点:
所有 Provider 通用
会走过滤 / reducer
⚡ 直接方式(仅内存)
var msgs = inMemoryProvider.GetMessages(session);特点:
快
原始数据(无加工)
6. Provider 是怎么来的?
chatClient.AsAIAgent(...)内部自动做了:
new InMemoryChatHistoryProvider()👉 默认你什么都不用管
7. 常用配置(InMemoryChatHistoryProviderOptions)
1️⃣ 初始消息(很常用)
StateInitializer = session => new State
{
Messages = new List<ChatMessage>
{
new(ChatRole.System, "你是一个助手")
}
}2️⃣ 限制上下文长度(关键)
ChatReducer = new TokenCountingChatReducer(4000)👉 防止上下文爆掉
3️⃣ 自定义 Key(多 Provider 时用)
StateKey = "CustomHistory"8. 优缺点(实话版)
👍 优点
快(无 IO)
简单
默认可用
易调试
👎 缺点
❌ 重启就没了
❌ 不支持多实例共享
❌ 聊久了吃内存
9. 适用场景
✔️ 用它就够
Demo
本地开发
短对话
单用户工具
❌ 不该用
生产环境
多用户系统
长期记忆
分布式
10. 想做持久化怎么办?
自己实现:
class DbChatHistoryProvider : ChatHistoryProvider
{
// 查数据库
InvokingAsync()
// 写数据库
InvokedAsync()
}👉 本质就两件事:
读历史
写新消息
11. 一句话总结
ChatHistoryProvider = 聊天记录管理器(可插拔)
InMemoryChatHistoryProvider = 默认内存实现(简单但不持久)