这道题问的是多轮对话里的上下文管理,其实本质上就是在问:多个用户同时跟你聊天的时候,你怎么保证每个人的对话状态不乱,而且响应还要快。说白了,这是一个一致性和延迟之间的权衡问题。
我的设计思路是分两层。短期状态,就是最近几轮对话和[[Agent]]执行的中间变量,我放Redis集群里,TTL设个30分钟,没活动就自动清掉。结构上,用Hash存session的元信息,用List或者ZSet存消息序列。长期状态呢,比如用户画像、历史摘要、关键事实,这些得持久化,我用MongoDB或者ES,再配合一个[[向量库]]做检索。两个层各管各的,短期追求快,长期追求全。
具体到更新机制,写路径是这样的:用户请求进来,路由层按session_id哈希,保证同一个session的请求都打到同一个处理线程上,这样单线程处理就天然有序了。然后更新Redis,同时异步写DB,再发一个变更事件出去。并发控制我主要用乐观锁,Redis的WATCH加MULTI/EXEC,带上版本号校验,冲突了就重试。另外上下文不能无限长,我会用[[滑动窗口]]保留最近K轮,超长就触发摘要,调用LLM生成一个历史摘要替换掉原始对话。
高并发场景下,我做了几件事。先说读写分离,读走本地Caffeine缓存,写穿透Redis。接着说会话亲和性,同一个session固定路由到同一个Pod,减少锁竞争。再补充批量合并,高频的心跳或者状态变更先攒一波再写,避免频繁IO。最后补充异步持久化,Redis确认就算成功,DB落盘走MQ削峰。
这里有个坑需要注意。一致性我选择最终一致性,因为大部分场景下用户能容忍短暂的不一致,比如客服聊天,中间几秒的延迟用户感觉不到。但如果是关键操作,比如支付类的工具调用,那就必须强制同步落盘,确保不丢。所以我会把操作分两类,普通操作走异步,关键操作走同步。
另外我还想提一个点,就是上下文截断时生成的摘要,如果摘要质量不好,反而会丢失关键信息。所以我会在摘要生成后做一个质量校验,比如跟原始对话对比一下关键实体有没有丢,如果召回率低于阈值就保留原始对话。这个校验怎么做,其实也值得深入讨论。
所以整体上,我更倾向于把上下文管理看作一个分层缓存加异步持久化的系统,核心是分场景选择一致性级别。不搞一刀切,才能在高并发下既快又稳。