Skip to content

[Bug]WebChat 长连接在上下文压缩时因超时断开,导致 WebUI 假死 (使用推理模型时尤其严重) #6938

@MostimaBridges

Description

@MostimaBridges

What happened / 发生了什么

在使用 AstrBot 的 WebUI 进行聊天时,如果在新聊天输入短文本(如“你好”),且未触发上下文压缩,WebUI 能正常回复。但当对话历史较长(Token数较高),触发了内部的“上下文压缩(Context Compress)”机制,且当前使用的是**深度思考/推理模型(如 deepseek-reasoner)**时,会导致 WebUI 假死(一直转圈)。
问题根本原因(根据日志分析):
压缩长文本时,deepseek-reasoner 耗费了大量时间生成 过程(日志中耗时约 46 秒才完成压缩)。
在后端进行压缩运算的过程中,WebChat 的前端长连接(WebSocket/SSE)因为 30 秒没有收到任何消息,触发了等待超时并主动断开了连接(日志打印:[WebChat] 用户 test 断开聊天长连接。)。
当后端最终完成压缩并生成回复(Agent state transition: AgentState.RUNNING -> AgentState.DONE),此时由于前端已经断开,无法将消息推送给前端,导致前端一直卡在转圈状态。

Reproduce / 如何复现?

启动 AstrBot 并打开 WebChat 界面。
在提供商配置中,选择一个思考耗时较长的推理模型(如 deepseek-reasoner)。
往对话中输入大量文本或进行多轮对话,使得总 Token 数达到触发上下文压缩的阈值。
发送一条新消息,后端日志显示触发压缩:Compress triggered, starting compression...
等待约 30 秒,后端日志出现:[routes.chat:45]: [WebChat] 用户 test 断开聊天长连接。
后端最终运行结束生成回复(pipeline 执行完毕),但此时 WebUI 前端依然在转圈,无法收到任何消息。

AstrBot version, deployment method (e.g., Windows Docker Desktop deployment), provider used, and messaging platform used. / AstrBot 版本、部署方式(如 Windows Docker Desktop 部署)、使用的提供商、使用的消息平台适配器

AstrBot 版本:4.22.0
部署方式:Windows launcher
使用的提供商:deepseek-reasoner
消息平台:WebChat (WebUI)

OS

Windows

Logs / 报错日志

[2026-03-25 17:20:34.264] [Core] [INFO] [core.event_bus:61]: [default] [webchat(webchat)] test/test: 你好
[2026-03-25 17:20:34.267] [Core] [DBUG] [waking_check.stage:157]: enabled_plugins_name: ['']
[2026-03-25 17:20:34.274] [Core] [DBUG] [method.star_request:44]: plugin -> session_controller - handle_session_control_agent
[2026-03-25 17:20:34.277] [Core] [DBUG] [method.star_request:44]: plugin -> session_controller - handle_empty_mention
[2026-03-25 17:20:34.278] [Core] [DBUG] [method.star_request:44]: plugin -> astrbot_plugin_videos_analysis - auto_parse_bili
[2026-03-25 17:20:34.280] [Core] [DBUG] [method.star_request:44]: plugin -> astrbot_plugin_videos_analysis - auto_parse_xhs
[2026-03-25 17:20:34.281] [Core] [DBUG] [method.star_request:44]: plugin -> astrbot_plugin_videos_analysis - auto_parse_mcmod
[2026-03-25 17:20:34.283] [Core] [DBUG] [method.star_request:44]: plugin -> astrbot_plugin_videos_analysis - process_direct_video
[2026-03-25 17:20:34.284] [Core] [DBUG] [method.star_request:44]: plugin -> astrbot_plugin_videos_analysis - auto_parse_dy
[2026-03-25 17:20:34.286] [Core] [DBUG] [agent_sub_stages.internal:166]: ready to request llm provider
[2026-03-25 17:20:34.286] [Core] [DBUG] [agent_sub_stages.internal:185]: acquired session lock for llm request
[2026-03-25 17:20:34.303] [Core] [DBUG] [pipeline.context_utils:95]: hook(OnLLMRequestEvent) -> astrbot - decorate_llm_req
[2026-03-25 17:20:34.304] [Core] [DBUG] [pipeline.context_utils:95]: hook(OnLLMRequestEvent) -> astrbot-web-searcher - edit_web_search_tools
[2026-03-25 17:20:34.305] [Core] [INFO] [respond.stage:184]: Prepare to send - test/test:
[2026-03-25 17:20:34.307] [Core] [INFO] [respond.stage:200]: 应用流式输出(webchat)
[2026-03-25 17:20:34.308] [Core] [DBUG] [runners.base:64]: Agent state transition: AgentState.IDLE -> AgentState.RUNNING
[2026-03-25 17:20:34.309] [Core] [DBUG] [runners.tool_loop_agent_runner:312]: [BefCompact] RunCtx.messages -> [14] system,user,assistant,user,assistant,user,assistant,user,assistant,tool,assistant,user,assistant,user
[2026-03-25 17:20:34.310] [Core] [DBUG] [runners.tool_loop_agent_runner:312]: [AftCompact] RunCtx.messages -> [14] system,user,assistant,user,assistant,user,assistant,user,assistant,tool,assistant,user,assistant,user
[2026-03-25 17:20:37.414] [Core] [DBUG] [runners.base:64]: Agent state transition: AgentState.RUNNING -> AgentState.DONE
[2026-03-25 17:20:37.414] [Core] [DBUG] [pipeline.context_utils:95]: hook(OnLLMResponseEvent) -> astrbot - record_llm_resp_to_ltm
[2026-03-25 17:20:37.453] [Core] [INFO] [result_decorate.stage:189]: 流式输出已启用,跳过结果装饰阶段
[2026-03-25 17:20:37.454] [Core] [DBUG] [pipeline.scheduler:93]: pipeline 执行完毕。
[2026-03-25 17:20:42.213] [Core] [DBUG] [sources.openai_source:283]: completion: ChatCompletion(id='a9e932bf-b363-4b82-81eb-64549ff8f384', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=None, reasoning_content='First, the user query is "你好", which is Chinese for "hello". This is a greeting.\n\nMy task is to generate a conversation title based on the user's input. I need to generate a concise title in the same language as the input, which is Chinese. The title should be no more than 10 words and capture only the core topic.\n\nI have specific rules: if the input is a greeting, small talk, or has no clear topic, I should return "". "你好" is definitely a greeting, so it falls under this category.\n\nThe output should only be the title itself or "", with no explanations.\n\nSo, for "你好", since it's a greeting, I should output "".\n\nConfirming: Input language is Chinese, but since it's a greeting, I don't need to generate a title. Just return "".\n\nFinally, ensure the output is only "" without any additional text.'))], created=1774430434, model='deepseek-reasoner', object='chat.completion', service_tier=None, system_fingerprint='fp_eaab8d114b_prod0820_fp8_kvcache_new_kvcache', usage=CompletionUsage(completion_tokens=200, prompt_tokens=120, total_tokens=320, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=196, rejected_prediction_tokens=None), prompt_tokens_details=PromptTokensDetails(audio_tokens=None, cached_tokens=64), prompt_cache_hit_tokens=64, prompt_cache_miss_tokens=56))
[2026-03-25 17:20:57.170] [Core] [DBUG] [webchat.webchat_queue_mgr:126]: Started listener for conversation: 9f92ffbe-e494-4bea-a28d-64acfe3a1186
[2026-03-25 17:20:57.170] [Core] [DBUG] [webchat.webchat_adapter:220]: WebChatAdapter: [Plain(type=<ComponentType.Plain: 'Plain'>, text='你好', convert=True)]
[2026-03-25 17:20:57.170] [Core] [INFO] [core.event_bus:61]: [default] [webchat(webchat)] test/test: 你好
[2026-03-25 17:20:57.188] [Core] [DBUG] [waking_check.stage:157]: enabled_plugins_name: ['']
[2026-03-25 17:20:57.196] [Core] [DBUG] [method.star_request:44]: plugin -> session_controller - handle_session_control_agent
[2026-03-25 17:20:57.197] [Core] [DBUG] [method.star_request:44]: plugin -> session_controller - handle_empty_mention
[2026-03-25 17:20:57.198] [Core] [DBUG] [method.star_request:44]: plugin -> astrbot_plugin_videos_analysis - auto_parse_bili
[2026-03-25 17:20:57.199] [Core] [DBUG] [method.star_request:44]: plugin -> astrbot_plugin_videos_analysis - auto_parse_xhs
[2026-03-25 17:20:57.200] [Core] [DBUG] [method.star_request:44]: plugin -> astrbot_plugin_videos_analysis - auto_parse_mcmod
[2026-03-25 17:20:57.201] [Core] [DBUG] [method.star_request:44]: plugin -> astrbot_plugin_videos_analysis - process_direct_video
[2026-03-25 17:20:57.202] [Core] [DBUG] [method.star_request:44]: plugin -> astrbot_plugin_videos_analysis - auto_parse_dy
[2026-03-25 17:20:57.205] [Core] [DBUG] [agent_sub_stages.internal:166]: ready to request llm provider
[2026-03-25 17:20:57.206] [Core] [DBUG] [agent_sub_stages.internal:185]: acquired session lock for llm request
[2026-03-25 17:20:57.232] [Core] [DBUG] [pipeline.context_utils:95]: hook(OnLLMRequestEvent) -> astrbot - decorate_llm_req
[2026-03-25 17:20:57.232] [Core] [DBUG] [pipeline.context_utils:95]: hook(OnLLMRequestEvent) -> astrbot-web-searcher - edit_web_search_tools
[2026-03-25 17:20:57.232] [Core] [INFO] [respond.stage:184]: Prepare to send - test/test:
[2026-03-25 17:20:57.232] [Core] [INFO] [respond.stage:200]: 应用流式输出(webchat)
[2026-03-25 17:20:57.232] [Core] [DBUG] [runners.base:64]: Agent state transition: AgentState.IDLE -> AgentState.RUNNING
[2026-03-25 17:20:57.232] [Core] [DBUG] [runners.tool_loop_agent_runner:312]: [BefCompact] RunCtx.messages -> [24] system,user,assistant,user,assistant,user,assistant,tool,assistant,tool,assistant,tool,assistant,tool,assistant,user,assistant,user,assistant,user,assistant,user,assistant,user
[2026-03-25 17:20:57.232] [Core] [DBUG] [context.manager:95]: Compress triggered, starting compression...
[2026-03-25 17:21:27.170] [Core] [DBUG] [routes.chat:45]: [WebChat] 用户 test 断开聊天长连接。
[2026-03-25 17:21:43.423] [Core] [DBUG] [sources.openai_source:283]: completion: ChatCompletion(id='5e2bb65b-f99a-44e0-8be3-acc560aff07b', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='根据我们的完整对话历史,以下是核心要点和项目进展总结:\n\n## 1. 用户初始目标与当前状态\n初始目标:为《死神模拟器 Wiki》网站的攻略页面添加剧透遮罩功能,基于已有的角色介绍页面实现。\n\n当前状态:✅ 已完成\n- 已成功为攻略页面添加完整的剧透遮罩系统\n- 已从角色介绍页面提取出通用的剧透遮罩模块\n- 形成了可复用的标准化解决方案\n\n## 2. 核心讨论主题与成果\n\n### 主题一:攻略页面剧透遮罩实现\n成果:基于角色介绍页面的现有实现,为攻略页面的结局条目添加了:\n- 统一的CSS样式(毛玻璃效果遮罩层)\n- 本地存储持久化系统\n- 侧边栏重置按钮\n- 页面顶部剧透警告横幅\n- 完整的JavaScript交互逻辑\n\n### 主题二:通用模块提取(最新重点)\n成果:从现有代码中提取出三部分可重用模块:\n\n#### 2.1 HTML结构模块\n- 遮罩核心结构(必需)\n- 页面警告框(可选)\n- 重置按钮(可选)\n\n#### 2.2 CSS样式模块\n- 完整的遮罩样式系统\n- 响应式设计\n- 动画效果\n\n#### 2.3 JavaScript核心模块\n- 配置驱动的初始化系统\n- 本地存储管理\n- 事件绑定自动化\n- 可扩展的API接口\n\n### 主题三:工具使用与配置\n成果:提供灵活的配置选项:\n- 页面间存储隔离(不同键名)\n- 自定义选择器支持\n- 渐进增强设计\n\n## 3. 工具使用情况\n总调用次数:4次\n- astrbot_execute_python:2次(处理HTML代码和保存文件)\n- astrbot_execute_shell:2次(检查工作目录)\n\n最有价值的工具输出:\n1. 成功生成了包含完整剧透系统的攻略页面HTML代码\n2. 识别并解决了中文字符编码问题(Unicode处理)\n3. 确认了文件保存位置和大小\n\n## 4. 关键技术洞察\n1. 存储隔离设计:不同页面使用独立的localStorage键名,避免数据冲突\n2. 模块化架构:HTML、CSS、JS分离,便于维护和重用\n3. 配置驱动:通过配置对象实现不同页面的定制化需求\n4. 用户体验:提供"确认查看"和"先不看"两种选择,尊重用户意愿\n\n## 5. 最终交付成果\n1. 完整的攻略页面代码:包含17个结局的剧透保护\n2. 通用剧透遮罩模块:可应用于网站的任何页面\n3. 详细使用文档:包含配置示例和最佳实践\n4. 向后兼容:不影响现有功能的正常运行\n\n## 6. 项目价值\n现在用户拥有了一个完整的、可扩展的剧透管理系统,可以:\n- 快速为其他页面添加剧透保护\n- 统一全站的用户体验\n- 轻松维护和更新功能\n- 基于用户反馈进行调整和优化\n\n总结:从单一页面的功能实现,发展到可复用的通用模块,项目已经完成了从"解决问题"到"建立系统"的演进,为网站的长期发展奠定了良好的技术基础。', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=None, reasoning_content='Okay, let me look back at the conversation with the user to create a comprehensive summary. The user is working on a game wiki website for "死神模拟器" (Death Simulator), and the main goal is to add spoiler masking functionality to their guide page. They already had implemented this feature on the character introduction page and wanted it extended to the guide page.\n\nFirst, I need to outline the initial user goal and current status. The user wanted spoiler masks on their guide page, similar to what they had on the character page. I provided the modified guide page code with full spoiler masking, and later extracted a reusable module from the character page's implementation. So the progress is complete - both pages now have the spoiler system, and we've created a modular version for future use.\n\nLooking at the core topics discussed, there were three main parts: the initial request for the guide page modification, the implementation of the spoiler system on the guide page, and finally extracting a reusable module. Each topic had a clear outcome - the guide page was successfully enhanced, and a modular system was created for easier future implementation.\n\nFor tool usage, I made several function calls. The Python executor was used to process and output the modified HTML code for the guide page, and the shell commands helped check the working directory. From these outputs, the key insight was that the spoiler system could be standardized into a reusable module with configurable options for different pages.\n\nThe latest primary focus was definitely the creation of the universal spoiler masking module. This is the most valuable outcome as it provides a maintainable solution for the entire website. The module includes HTML templates, CSS styling, and JavaScript with configuration options, making it easy to implement on any page that needs spoiler protection.\n\nI should structure the summary to first state the user's goal, then cover each core topic with its outcome, highlight the tool usage insights, and emphasize the reusable module as the key achievement. The user's language is Chinese, so I'll write the summary accordingly, keeping it concise but covering all important aspects.'))], created=1774430457, model='deepseek-reasoner', object='chat.completion', service_tier=None, system_fingerprint='fp_eaab8d114b_prod0820_fp8_kvcache_new_kvcache', usage=CompletionUsage(completion_tokens=1149, prompt_tokens=37439, total_tokens=38588, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=None, audio_tokens=None, reasoning_tokens=418, rejected_prediction_tokens=None), prompt_tokens_details=PromptTokensDetails(audio_tokens=None, cached_tokens=37376), prompt_cache_hit_tokens=37376, prompt_cache_miss_tokens=63))
[2026-03-25 17:21:43.434] [Core] [INFO] [context.manager:104]: Compress completed. 64706 -> 34797 tokens, compression rate: 54.37%.
[2026-03-25 17:21:43.435] [Core] [DBUG] [runners.tool_loop_agent_runner:312]: [AftCompact] RunCtx.messages -> [10] system,user,assistant,user,assistant,user,assistant,user,assistant,user
[2026-03-25 17:21:51.442] [Core] [DBUG] [runners.base:64]: Agent state transition: AgentState.RUNNING -> AgentState.DONE
[2026-03-25 17:21:51.442] [Core] [DBUG] [pipeline.context_utils:95]: hook(OnLLMResponseEvent) -> astrbot - record_llm_resp_to_ltm
[2026-03-25 17:21:51.475] [Core] [INFO] [result_decorate.stage:189]: 流式输出已启用,跳过结果装饰阶段
[2026-03-25 17:21:51.476] [Core] [DBUG] [pipeline.scheduler:93]: pipeline 执行完毕。

Are you willing to submit a PR? / 你愿意提交 PR 吗?

  • Yes!

Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:coreThe bug / feature is about astrbot's core, backendbugSomething isn't workingfeature:chatuiThe bug / feature is about astrbot's chatui, webchat

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions