本文摘要历史记录无限增长会超出模型上下文窗口闲置会话也持续占用内存。本篇通过引入tiktoken进行精确 token 计量并实现基于消息数量的历史截断与会话超时清理机制。一、环境与前提本篇建立在第八篇完成的会话隔离之上。项目目录为agent-demo/入口文件main.py请确保其中已包含通过session_id和asyncio.Lock实现的sessions会话字典及event_generator函数。前置依赖Python 3.11已安装openai,fastapi,uvicorn本篇新增依赖tiktoken安装命令如下pipinstalltiktoken预期输出执行pip list | grep tiktoken将显示已安装的tiktoken包。实际输出tiktoken 0.5.1以实际安装版本为准。二、关键步骤步骤 1添加 Token 用量统计到ChatResponse目的返回每次请求消耗的 token 数便于成本监控。操作修改ChatResponse模型添加usage字段。在event_generator中流式响应结束后尝试从最后一个 chunk 提取usage信息并发送。# main.py 的相关修改部分frompydanticimportBaseModelfromtypingimportList,Dict,AnyclassChatResponse(BaseModel):reply:strusage:Dict[str,int]|NoneNoneasyncdefevent_generator(session_id:str,user_message:str,):# ... 会话获取、消息追加等前置逻辑 ...response_chunks[]usage_dataNoneasyncforchunkinopenai_stream:# ... 处理流式块追加文本到 response_chunks ...pass# 注意OpenAI 流式接口的 usage 提取方式取决于具体库版本以下为简化示例。# 更可靠的方式是使用非流式调用或确认 openai 库版本支持从流中获取 usage。ifhasattr(chunk,usage)andchunk.usage:usage_datachunk.usage.model_dump()final_reply.join(response_chunks)yieldfdata:{ChatResponse(replyfinal_reply,usageusage_data).json()}\n\n预期输出成功请求后SSE 流末尾的 JSON 数据将包含usage字段。实际输出未实测。步骤 2实现历史长度管理基于消息数量截断目的防止history列表无限增长导致 API 上下文超限。操作在event_generator函数中获取会话历史后、调用模型前对history进行截断。# main.py 的相关修改部分在 event_generator 函数内asyncdefevent_generator(session_id:str,user_message:str):asyncwithsessions_lock:session_datasessions.get(session_id,{history:[],last_active:None})# 更新最后活跃时间session_data[last_active]time.time()# 使用标准库 time# 添加当前用户消息session_data[history].append({role:user,content:user_message})# 截断历史保留最近 20 条消息MAX_HISTORY_MESSAGES20iflen(session_data[history])MAX_HISTORY_MESSAGES:session_data[history]session_data[history][-MAX_HISTORY_MESSAGES:]current_historysession_data[history].copy()# 后续使用 current_history 调用 openai_stream预期输出会话历史长度始终不超过 20 条。实际输出未实测。步骤 3实现会话超时清理目的定期清理长时间不活跃的会话释放内存。操作定义超时时间如 30 分钟。编写异步清理函数。在 FastAPI 的lifespan事件中启动清理任务。# main.py 的相关修改部分importasynciofromcontextlibimportasynccontextmanagerimporttime# 定义会话存储和锁沿用前文sessions:Dict[str,Dict]{}sessions_lockasyncio.Lock()SESSION_TIMEOUT30*60# 30 分钟单位秒asyncdefcleanup_expired_sessions():定期清理过期会话whileTrue:awaitasyncio.sleep(5*60)# 每 5 分钟检查一次current_timetime.time()asyncwithsessions_lock:expired_sessions[sidforsid,datainsessions.items()ifcurrent_time-data.get(last_active,current_time)SESSION_TIMEOUT]forsidinexpired_sessions:delsessions[sid]print(f[Cleanup] Session{sid}expired and removed.)asynccontextmanagerasyncdeflifespan(app:FastAPI):cleanup_taskasyncio.create_task(cleanup_expired_sessions())yieldcleanup_task.cancel()try:awaitcleanup_taskexceptasyncio.CancelledError:passappFastAPI(lifespanlifespan)预期输出服务器运行期间超过 30 分钟无活动的会话将被自动删除控制台有清理日志。实际输出未实测。步骤 4整合并更新run_agent_stream调用可选增强目的通过max_tokens参数限制模型单次回复长度。操作在构建openai.chat.completions.create调用时添加max_tokens参数。# 在 event_generator 函数中openai_streamawaitopenai.chat.completions.create(modelgpt-3.5-turbo,messagescurrent_history,streamTrue,max_tokens1024# 限制单次回复最大 token 数)预期输出模型单次回复长度受max_tokens1024限制。实际输出未实测。三、失败处理常见失败使用tiktoken.encoding_for_model(gpt-3.5-turbo)时可能因模型映射缺失而报错。报错原文KeyError: Could not automatically map gpt-3.5-turbo to a tokeniser. Please use tiktoken.get_encoding to explicitly get the tokeniser you expect.原因tiktoken库版本可能未包含最新模型的分词器映射。修复方法直接指定编码名称。importtiktoken encodingtiktoken.get_encoding(cl100k_base)# 适用于 gpt-3.5-turbo 和 gpt-4token_countlen(encoding.encode(text))四、替代方案与取舍本文演示了基于消息数量的截断但还有其他方案。做法适用条件代价边界不适用场景基于消息数量的截断1. 原型开发快速验证。2. 对话轮数固定且较短。1. 实现简单。2. 无法精确控制总 token 消耗可能浪费额度或意外超限。适用于对话结构稳定、每轮交互 token 消耗差异不大的场景。当用户问题或模型回复长度波动极大时容易导致总 token 数超限。基于 token 总量的截断1. 生产环境需严格控制成本与 API 调用成功率。2. 使用多种模型需精确管理上下文窗口。1. 需引入tiktoken依赖。2. 每次截断前需计算 token 数增加延迟和 CPU 开销。适用于任何需要精确控制 token 的场景是更健壮的方案。对于极简单的演示项目可能略显过度设计。选择建议生产环境中尤其是处理长文档或长对话时建议结合tiktoken实现基于 token 总量的截断。参考资料Background Tasks - FastAPIasyncio.sleep — Python 官方文档OpenAI API 文档 - Chat Completionstiktoken GitHub 仓库下一篇将探讨如何将对话历史与 Agent 状态持久化到数据库实现跨服务器重启的会话恢复。
企业数字化 ERP 产品动态
相关推荐
中文用户全线中招,Claude网页限速,只因回复里有汉字 万万没想到,Claude竟然被自己的「AI味儿」给坑惨了!
万万没想到,Claude竟然被自己的「AI味儿」给坑惨了!
今天凌晨,Anthropic放出一篇硬核的工程复盘。
他们把一个内部研究版Claude扔进Slack频道,「连轴… · 2026/9/26 4:12:07
Meta不再谈元宇宙,关键词改为“个人超级智能” 2026年的Meta Connect大会,元宇宙“隐身”了,个人超级智能成为绝对主角。
2026年的Meta Connect大会,元宇宙“隐身”了,个人超级智能成为绝对主角。
美国当地时间9月23日,Meta在加州门洛帕克举行年度Connect开发者大… · 2026/9/26 4:12:07
WorkBuddy数据与隐私设置全指南:工作区授权、缓存迁移与记忆管理 1. 为什么数据与隐私设置值得单独拎出来讲很多人上手 WorkBuddy 的时候,注意力全在"怎么让它帮我干活"上——写代码、抓数据、生成网站、跑自动化工作流,恨不得第一天就把所有 Skill 都装一遍。结果用了两三周,突然发现工作台里堆了… · 2026/9/26 4:12:07
三鱼共头纹样解析:从剪纸几何到现代设计的旋转对称之美 第一次看到《三鱼》这个题,是在一篇整理窗花图样的资料里。当时配图是一张褪了色的红窗花:三条鱼头挨着头挤在圆心,尾巴像三片扇叶一样甩向圆周,整个图案好像下一秒就会转起来。后来才知道,这个母题在民间叫“三鱼争头… · 2026/9/26 6:04:54
Claude Code 提示词模板库:从架构设计到工作流整合 1. 项目从哪来:为什么我把零散的 Claude Code 提示词沉淀成模板库先说个场景。刚开始用 Claude Code 那阵子,我干过不少重复劳动:每次让它写一个新功能,都要现场组织一大段提示词,把技术栈、目录结构、编码习惯、输出要… · 2026/9/26 6:04:54
AI热点速览日更方法论:信息分层筛选与结构化写作实战 1. 为什么我要做这个"AI热点速览"日更项目每天早上七点半,我端着咖啡坐在电脑前,第一件事不是看邮件,而是打开十几个信息源,把过去24小时里跟AI相关的动态过一遍。这个习惯我坚持了快两年,从最开始自己拿备忘… · 2026/9/26 6:04:54
WorkBuddy桌面工作台:基于腾讯云AI的智能工作流操作系统 1. 项目概述:WorkBuddy不是“另一个AI工具”,而是你桌面工作台的神经中枢WorkBuddy这个词,最近半年在技术圈、产品岗、运营团队和高校实验室里出现的频率,已经快赶上“大模型”“RAG”“Agent”这些词了。但有意思的是,… · 2026/9/26 6:04:54
AQE自适应执行.md Spark 的执行计划不是铁板一块,跑起来之后 AQE 还会改它。
提交 Spark 作业的时候,控制台吐出来的那份执行计划,多数人当成最终判决看完就关了。
它其实只是草稿。
真正跑起来之后,有一套机制会拿着运行时的真实数据量,… · 2026/9/26 6:04:54
AI安全框架怎么读?从英文PDF到可执行检查清单 简介:该PDF是美国国家标准与技术研究院(NIST)发布的《人工智能网络安全框架概况》初始初步草案(NIST IR 8596 iprd),聚焦AI系统设计、开发、训练、部署、运行、更新到退役全过程的安全风险,面向… · 2026/9/26 6:04:48
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46