1. 本章目标
目标是解决多轮 LLM 对话中的核心问题:
Chat History 会不断增长,而模型的 Context Window 是有限的。
因此后端需要完成:
多轮聊天 ↓ Token 统计 ↓ 判断是否超限 ↓ 历史裁剪 ↓ 旧对话总结 ↓ Summary 压缩 ↓ 重新构造 Context ↓ 保证最终输入处于 Token Budget 内2. Token / Token ID / Embedding
三者不能混淆。
Text ↓ Tokenizer ↓ Token ↓ Token ID ↓ Embedding Layer ↓ Vector ↓ TransformerToken
Token 是 tokenizer 对文本进行切分后得到的离散单位。
它不是向量。
例如:
"I love AI" ↓ ["I", " love", " AI"]Token ID
每个 token 会对应一个整数编号:
["I", " love", " AI"] ↓ [40, 3021, 15592]Embedding
Token ID 再经过 Embedding Layer:
40 ↓ [0.12, -0.34, 0.88, ...]这里才真正变成向量。
3. Context Window
Context Window 可以理解成:
模型单次调用能够处理的 Token 总预算。
通常需要考虑:
System Prompt + Conversation History + RAG Documents + Current User Query + Model Output因此:
Input Tokens + Output Tokens <= Context Window不能把全部 Context Window 都分给输入。
例如:
Context Window = 8000 Input Budget = 7000 Output Reserve = 10004. Token Estimate
学习阶段使用简单近似:
def estimate_tokens(text: str) -> int: return max(1, len(text) // 4)注意:
这只是粗略估算。
真实项目应该使用具体模型对应的 tokenizer。
5. 统计整个 Context
def count_message_tokens( messages: list[dict] ) -> int: total_tokens = 0 for message in messages: total_tokens += estimate_tokens( message["content"] ) return total_tokens逻辑:
message 1 → token message 2 → token message 3 → token ... ↓ total_tokens6. Message-based Trimming
最简单的 Context Management:
始终保留 System Prompt + 只保留最近 N 条消息例如:
def trim_messages( messages: list[dict], max_messages: int ) -> list[dict]: if not messages: return [] if max_messages <= 0: return [] system_message = messages[0] if max_messages == 1: return [system_message] recent_messages = messages[1:][-(max_messages - 1):] return [ system_message, *recent_messages ]缺陷:
消息数量 ≠ Token 数量一条消息可能只有:
OK也可能是一篇 5000 字文章。
所以进一步进入 Token-based Trimming。
7. Token-based Trimming
目标:
System Prompt 永远保留 + 从最近的消息开始向前选择 + 直到达到 Token Budget核心:
for message in reversed(messages[1:]):因为最近的聊天通常比很早以前的聊天更加重要。
选完以后:
selected.reverse()恢复正常时间顺序。
8. Conversation Summary
直接删除旧历史会造成信息永久丢失。
因此可以:
Old History ↓ LLM Summarization ↓ Conversation Summary最终 Context:
System Prompt + Conversation Summary + Recent Messages这样可以用更少 Token 保留更早的重要信息。
9. messages_to_text()
LLM 做总结之前,需要把:
list[dict]转换成文本:
def messages_to_text( old_messages: list[dict] ) -> str: return "\n".join( f'{message["role"]}: {message["content"]}' for message in old_messages )这里组合了:
Generator Expression + join()例如:
[ {"role": "user", "content": "What is RAG?"}, {"role": "assistant", "content": "RAG combines retrieval and generation."} ]变成:
user: What is RAG? assistant: RAG combines retrieval and generation.10. summarize_messages()
旧聊天通过 LLM 转换成 Summary:
old_messages ↓ messages_to_text() ↓ summary prompt ↓ LLMClient.chat() ↓ result["answer"] ↓ summary总结时应保留:
Important facts
User goals
Decisions
Constraints
Unresolved questions
Summary 的重点是:
信息保真,而不是创造性。
因此通常使用相对低的 temperature。
11. build_context()
将:
System + Summary + Recent Messages重新组合:
def build_context( system_message: dict, summary: str, recent_messages: list[dict] ) -> list[dict]: summary_message = { "role": "system", "content": f"Conversation summary: {summary}" } return [ system_message, summary_message, *recent_messages ]12. Context Priority
不是所有 Context 信息优先级都一样。
可以建立:
Priority 1 System Prompt Priority 2 Current User Query Priority 3 Conversation Summary Priority 4 Recent History Priority 5 Very Old History未来加入 RAG:
System Prompt + Conversation Summary + Recent History + Retrieved Documents + Current Query这就是:
Context Budget Allocation
13. Summary Compression
如果:
System + Summary本身已经超过预算,那么删除 Recent History 也没有意义。
这时应该:
Summary ↓ 再压缩 ↓ 更短 Summary但不能无限循环。
应该使用:
Bounded Retry例如:
MAX_SUMMARY_RETRIES = 2最多尝试 2 次。
如果仍然超限:
raise ContextOverflowError(...)14. 为什么不能无限总结
无限循环可能产生:
无限 API 调用
无限费用
延迟不断增加
Summary 信息持续丢失
Summary 不一定越来越短
程序可能无法终止
因此真实工程要求:
压缩 ↓ 有限次数 ↓ 重新检查 ↓ 仍然失败 ↓ Fallback / Error15. Responsibility Separation
这一章很重要的软件设计思想:
trim_context()
只负责:
在固定 Context 合法的情况下 尽量保留 Recent Historysummarize_messages()
只负责:
Messages → Summarycompress_summary()
只负责:
Long Summary → Short Summaryprepare_context()
负责整个流程:
检查 ↓ 拆分 ↓ 总结 ↓ 压缩 ↓ 裁剪 ↓ 异常处理也就是:
prepare_context() 是 Orchestrator / Controller。
16. 本章最终架构
messages │ ▼ prepare_context() │ Token Count │ ┌──────────┴──────────┐ │ │ 未超预算 超预算 │ │ return messages ▼ Split History │ ┌─────────────┴─────────────┐ ▼ ▼ old_messages recent_messages │ │ ▼ │ summarize_messages() │ │ │ ▼ │ summary │ │ │ ▼ │ fixed context │ system + summary │ │ │ ▼ │ 是否超预算? │ / \ │ No Yes │ │ │ │ │ compress_summary() │ │ │ │ │ 最多 N 次 │ │ │ │ │ 仍然超预算? │ │ / \ │ │ No Yes │ │ │ │ │ │ │ Error │ │ │ │ └───────┴───────────────┐ ▼ trim_context() │ ▼ Final Context17. 本章涉及的 Python 知识
本章实际上复习并组合了:
list dict slice reversed() reverse() for loop generator expression join() f-string function typing class exception async / await OOP LLMClient nested dict early return最大的提升不是又学了几个 Python 语法,而是开始:
把多个函数组合成一个完整 AI Backend 子系统。
18. 当前学习进度
Python Fundamentals ✅ FastAPI / Pydantic ✅ Async HTTP / LLMClient ✅ LLM Messages ✅ Multi-turn Chat ✅ Context Management ✅ 本章完成 Structured Output ← 下一章 Tool Calling ⏳ Embedding ⏳ Vector Database ⏳ RAG ⏳ Agent ⏳ Production / Deployment ⏳下一阶段:
Structured Output → Tool Calling → RAG → Agent