最近在折腾一个本地知识库Agent,用的Llama-3-8B-Instruct,量化到INT4,单卡3090跑的。RAG流程刚开始挺顺,但多轮对话超过5轮之后,显存占用一路飙升,最后直接OOM。我看了下显存分配,KV cache增长特别快,而且系统提示词+历史消息全塞进上下文了。目前是每轮把完整历史拼进prompt再给模型,是不是该用滑动窗口或者摘要压缩?但摘要又怕丢关键信息。另外,用vLLM托管会不会好一点?还是说这个量级的模型本来就不适合做长期对话?求有经验的大佬指点一下。
本地部署7B模型当Agent后端,多轮对话后显存爆掉怎么办?
全部回复
共 87 条KV cache 确实是元凶,INT4 权重省的那点显存全被它吃回去了。你可以试试 llama.cpp 或 vLLM,它们对 KV cache 有分页管理和更激进的回收策略,比手动拼 prompt 强不少。滑动窗口加摘要混着用比较稳,近期几轮保留原文,更早的压成摘要,关键实体单独存一份外挂记忆。3090 24G 跑 8B 长期对话本身没问题,瓶颈在框架和上下文管理,不是模型量级。
我之前也踩过这个坑,INT4的8B在3090上跑多轮确实容易炸。KV cache是随对话长度线性涨的,你每轮拼完整历史等于让它反复吃同一份上下文,显存不爆才怪。滑动窗口加摘要压缩组合着用比较稳,关键实体和最近几轮原样保留,更早的做摘要,实测能撑到二十轮以上。vLLM的PagedAttention对KV cache管理确实好不少,但根子还是上下文策略,换框架治标不治本。
KV cache 确实是多轮对话里最容易被忽视的显存杀手,你观察到的现象很正常。8B INT4 权重本身只占 4-5G 左右,但每轮把完整历史重新拼进去,KV cache 是随序列长度线性增长的,5 轮之后上下文可能已经好几千 token 了,3090 的 24G 扛不住也合理。滑动窗口是最省事的做法,但容易把早期关键信息截掉,我一般会留一个固定的 system prompt 加最近 N 轮,再对更早的历史做滚动摘要,摘要提示词里明确要求保留实体、数字和结论,丢信息的概率能压下来不少。vLLM 的 PagedAttention 对 KV cache 的碎片管理确实有帮助,吞吐和显存利用率都会好一些,但它不会凭空变出显存,上下文长度该占的还是占。另外可以看看是不是每轮都把 RAG 检索到的文档 chunk 全塞进去了,那部分往往才是大头,检索结果做一下重排和截断,比单纯压缩对话历史见效更快。这个量级的模型做长期对话不是不行,关键是别把它当无限记忆用,该外置的状态就外置到向量库或者结构化存储里。
KV cache线性增长这事基本无解,只要你还把完整历史往context里塞,显存就是会一轮比一轮吃得多。我3090上跑过类似的配置,7B INT4权重本身没多少,真正爆的是cache和中间激活。滑动窗口是最省事的做法,直接只保留最近N轮,老对话该扔就扔,别舍不得。摘要压缩我也试过,效果看场景,知识库问答里如果关键实体和数字被摘要吃掉,后面回答会开始胡编,还不如滑窗来得稳。vLLM确实能靠PagedAttention把KV cache的碎片浪费压下去,吞吐也好看,但它不会帮你减少总token数,上下文还是那么长的话照样会顶到上限。我后来是滑窗加定期把老对话落盘做检索,需要时再捞回来拼进prompt,算是折中。另外3090只有24G,长期多轮对话本身就挺吃紧的,可能得接受这个量级的模型更适合短会话加外部记忆,而不是硬扛长上下文。
vLLM管KV cache确实稳,但根上还是得做滑动窗口,全量历史迟早炸。
3090跑8B INT4其实挺稳的,问题应该出在你每轮都塞完整历史这个做法上。KV cache是随上下文长度线性涨的,多轮下来直接吃掉十几G很正常,OOM基本是迟早的事。滑动窗口是最省事的方案,但确实会丢上下文,我的经验是保留最近几轮完整对话,再额外挂一个长期记忆的向量检索,把关键信息以摘要形式重新注入,比单纯截断或纯摘要都靠谱。另外系统提示词别每轮都重发一遍,能缓存就缓存,vLLM的prefix caching对固定前缀这块帮助挺大的。换vLLM确实会好一些,PagedAttention对KV cache的管理比HuggingFace那套高效太多,同样显存能撑的轮数明显更多。不过说到底这模型做长期对话不是不能做,是得接受一定程度的记忆有损,指望它像GPT那样长上下文无损召回不太现实。你可以先试试限制max_model_len加上窗口截断,成本最低,不行再上vLLM。
KV cache 增长是主因,每轮把完整历史拼进去等于让缓存线性膨胀。可以试试滑动窗口加关键信息锚点,比如把前几轮摘要成固定 token 塞进系统提示,比全量历史省不少。vLLM 的 PagedAttention 对缓存碎片化确实有帮助,但 8B INT4 在 3090 上跑长对话本身就很吃紧,建议先限制历史轮数再考虑换托管。