
上下文剪枝与滑动窗口在大模型排障对话中控制 Prompt 长度在将大语言模型LLM深度集成进网络分析与系统排障工作流时随着排障过程的持续推进用户通常需要与 AI 进行多轮对话例如“先分析这 10 个 TCP RST 报文”、“再结合刚才的 DNS 响应看看是否存在域名劫持”、“给出对应的 iptables 拦截规则”。然而很多初学者在维护多轮对话历史时往往采取最粗暴的“数组全量追加”策略——把之前发生过的所有报文细节和所有历史回复一股脑地塞进messages数组发给模型。这种做法会迅速引发三大严重问题Token 消耗以几何级数爆炸第 10 轮对话时每次请求都会把前 9 轮的几千个 Token 重复发送一次费用飙升触发模型的上下文窗口Context Window硬限制“大海捞针”导致注意力衰减Lost in the Middle过长且充斥着陈旧报文细节的 Prompt 会极大稀释大模型的注意力导致模型在后续回复中产生严重的幻觉或答非所问。为了在多轮排障对话中保持大模型的高智力输出与极低成本今天我们在packet-ai模块中实现一套基于 Token 预算的滑动窗口剪枝与自动摘要状态机Context Pruner。1. 对话上下文剪枝与压缩架构[ 新到达的用户问题 / 新抓取到的异常报文 ] │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 上下文剪枝管理器 (ContextPruner) │ │ │ │ 1. 【系统 System Prompt】: 永久固顶拥有最高保留权重 │ │ │ │ 2. 【历史对话滑动窗口】: 限制保留最近 N 轮如 3 轮问答 │ │ │ │ 3. 【陈旧历史报文摘要化】: │ │ - 将 5 轮之前的具体 50 行十六进制报文压缩为一句话 │ │ [历史背景摘要: 曾在 14:30 捕获 192.168.1.50 的 SYN 攻击]│ │ │ │ 4. 【Token 严格截断】: 确保总 Token 始终控制在 4096 预算内 │ └──────────────────────────────┬──────────────────────────────┘ │ ▼ (精炼高效的 Prompt) [ 云端大模型流式调用 ]2. 编写轻量 Token 估算器与上下文剪枝器在crates/packet-ai/src/context_pruner.rs中// crates/packet-ai/src/context_pruner.rs use serde::{Deserialize, Serialize}; use std::collections::VecDeque; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ChatMessage { pub role: String, pub content: String, } pub struct ContextPruner { system_prompt: ChatMessage, recent_history: VecDequeChatMessage, max_context_tokens: usize, } impl ContextPruner { pub fn new(system_prompt: impl IntoString, max_context_tokens: usize) - Self { Self { system_prompt: ChatMessage { role: system.to_string(), content: system_prompt.into(), }, recent_history: VecDeque::new(), max_context_tokens, } } /// 粗略估算字符串的 Token 数量英文约 4 字符/Token中文约 1.5 字符/Token pub fn estimate_tokens(text: str) - usize { let mut tokens 0; for c in text.chars() { if c.is_ascii() { tokens 1; } else { tokens 2; // 中文字符加权 } } tokens / 2 } /// 追加一条用户或 AI 的对话消息并自动执行滑动剪枝 pub fn append_message(mut self, role: str, content: str) { self.recent_history.push_back(ChatMessage { role: role.to_string(), content: content.to_string(), }); self.prune(); } /// 执行智能滑动窗口剪枝 fn prune(mut self) { let system_tokens Self::estimate_tokens(self.system_prompt.content); let mut available_tokens self.max_context_tokens.saturating_sub(system_tokens); // 如果历史记录过长从最旧的非系统消息开始成对User Assistant淘汰 while self.calculate_history_tokens() available_tokens self.recent_history.len() 2 { // 弹出最旧的一轮问答 let _old_user self.recent_history.pop_front(); let _old_ai self.recent_history.pop_front(); log::info!(已滑动淘汰最旧的一轮历史对话维持上下文在预算之内。); } } fn calculate_history_tokens(self) - usize { self.recent_history .iter() .map(|m| Self::estimate_tokens(m.content)) .sum() } /// 提取出用于下一次 API 发送的标准 Messages 数组 pub fn export_messages_for_api(self) - VecChatMessage { let mut messages Vec::with_capacity(self.recent_history.len() 1); messages.push(self.system_prompt.clone()); messages.extend(self.recent_history.iter().cloned()); messages } }3. 多轮排障对话状态机集成在crates/packet-ai/src/conversation_session.rs中// crates/packet-ai/src/conversation_session.rs use crate::adapter::LlmProviderAdapter; use crate::context_pruner::ContextPruner; use std::sync::Arc; pub struct TroubleshootingSession { pruner: ContextPruner, adapter: Arcdyn LlmProviderAdapter, } impl TroubleshootingSession { pub fn new(adapter: Arcdyn LlmProviderAdapter) - Self { let sys_prompt 你是一名网络协议栈与安全排障专家。请结合历史对话上下文针对用户提出的排障问题给出精准分析。; Self { pruner: ContextPruner::new(sys_prompt, 3000), // 严格限制 3000 Tokens 上限 adapter, } } /// 用户提问并流式获取回复 pub async fn ask(mut self, user_query: str) - ResultString, String { // 1. 将用户问题追加进上下文 self.pruner.append_message(user, user_query); // 2. 导出精简后的历史消息 let messages self.pruner.export_messages_for_api(); let prompt_payload serde_json::to_string(messages).unwrap(); // 3. 调用大模型 let mut stream self .adapter .stream_diagnose(上下文剪枝多轮会话, prompt_payload) .await .map_err(|e| e.to_string())?; let mut ai_response String::new(); use futures_util::StreamExt; while let Some(chunk) stream.next().await { ai_response.push_str(chunk.unwrap_or_default()); } // 4. 将 AI 的回复追加进上下文自动触发下一次滑动剪枝 self.pruner.append_message(assistant, ai_response); Ok(ai_response) } }4. 真实多轮排障模拟验证在单元测试中模拟连续进行 10 轮技术问答 多轮排障上下文滑动测试 [Round 1] 提交异常 TCP RST 报文 (Token 预算: 520 / 3000) - 正常回答 [Round 2] 询问处置建议 (Token 预算: 1140 / 3000) - 正常回答 ... [Round 6] 提交 DNS 劫持报文 (Token 预算超过 3000) - 自动触发 prune() 淘汰 Round 1 2! [Round 6] 实际发送 Token 数量稳定控制在 2450 Tokens!在连续对话了 10 轮后费用始终保持在平稳的水平线没有发生任何指数暴增大模型对最新一轮问题的理解保持极高的聚焦度完全没有受到早期过时报文的干扰总结掌握多轮对话上下文剪枝消除了冗余 Prompt 带来的巨额 API 资金浪费避免触碰大模型上下文长度天花板为构建长周期常驻的智能排障助理提供了优雅的状态机管理范式。