feat: manual context compression - #9795
Draft
C10H14N2O5 wants to merge 4 commits into
Draft
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TL;DR
/compactcommand for the local Agent Runner so users can explicitly request LLM-based context compression before the automatic threshold is reached.local + llm_compressconfigurations.Background
Fixes #9281
Related to #8348 and #9252, but intentionally does not implement configurable automatic compression thresholds.
AstrBot already supports automatic context management through maximum-turn enforcement and a token-threshold safeguard. When a request approaches the model context-window limit,
ContextManagercan summarize the history with an LLM or truncate it by conversation turns. However, token-triggered compression does not run until its threshold is reached. With models offering 512K, 1M, or larger context windows, users may encounter practical degradation far below the current 82% threshold:This PR adds the manual
/compactworkflow available in other agent harnesses. It lets users request compression at an appropriate point without changing AstrBot's automatic threshold or existing automatic behavior.The first version uses a command instead of a dedicated ChatUI button. AstrBot's command system already provides registration, autocomplete, enable/disable controls, renaming, and permission management. Reusing it keeps the patch reviewable while supporting WebChat and other messaging platforms without a new API or UI component.
Because an LLM-generated summary may omit role state, narrative facts, or task details, this capability is marked experimental and remains disabled by default. Users must explicitly opt in before the command can run.
Modifications / 改动点
1.
/compactuser commandRegister
/compactin the built-in command plugin and reuse the existing command-management behavior:llm_compresscontext strategy.The live user-visible flow remains intentionally concise:
The generated summary is never printed into the chat. WebChat additionally receives an
agent_statsevent so the context ring immediately reflects the estimated size of the compressed history.WebChat marks the progress status as ephemeral. The live client replaces it with the terminal result, while persisted history after a successful run contains only:
2. Opt-in experimental setting
Add the following configuration value:
{ "provider_settings": { "enable_manual_context_compression": false } }Configuration behavior:
false, so upgrading does not silently enable the feature for existing users.agent_runner_type=localandcontext_limit_reached_strategy=llm_compress.3. Reuse the existing compression flow
Add an optional
force_compressargument toContextManager.process(). Its default remainsfalse, preserving all existing automatic callers.With
force_compress=True, the manual command:_run_compression(), the existingTokenCounter, and the LLM summary compressor; andAutomatic compression retains its existing half-truncation protection. Only the manual path disables that fallback so a failed
/compactoperation cannot destructively truncate the original history.4. Preserve the latest complete turn
Add an optional
preserve_latest_roundbehavior to the LLM summary compressor. It defaults to disabled and therefore does not alter automatic compression output.When enabled by manual compression:
llm_compress_keep_recent_ratio, without splitting logical turns.This reduces the risk of changing the user's most recent requirements, tool state, or editable response.
5. Checkpoint-aware history persistence
The manual command reads the persisted database history and uses the existing checkpoint utilities during conversion:
bind_checkpoint_messages()associates persisted checkpoints with their messages.dump_messages_with_checkpoints()serializes the compressed result.6. Token-benefit validation and non-destructive failure
Estimate tokens both before and after compression:
On success, the compressed history and
token_usage=0are saved in the same database update. The next normal model response refreshes token state using the provider's actual usage information.7. Session locking, concurrency revalidation, and stop handling
/compactacquires the same unified-message-origin (UMO) session lock used by normal local Agent requests:/new, or modifies the same history through the Dashboard/API, the result is discarded./stoprequest prevents the compressed result from being written and explicitly reports that the original history was preserved.Dashboard and API history updates do not acquire this session lock. The additional conversation-ID and history comparisons therefore protect against external changes that occur while the LLM summary is being generated.
8. Safe verification after database exceptions
If
update_conversation()raises an exception, the command does not immediately assume that persistence failed. It safely reloads the database state once:This covers cases where a database update commits successfully but the client still receives an exception.
9. WebChat statistics, transient progress, and log privacy
webchat_ephemeralchain type and remains visible to connected clients.agent_statsevent is sent on a best-effort basis after persistence and after leaving the session lock.10. Reuse provider resolution
Refactor the existing context-compression provider resolver so both normal Agent construction and
/compactcan call it without changing its selection semantics:Existing automatic compression provider selection remains unchanged.
Scope and Compatibility
This PR intentionally keeps the following boundaries:
Only AstrBot's built-in local Agent Runner is supported.
Dify, Coze, Alibaba Cloud Bailian, DeerFlow, and other remote runners own their context remotely; this PR does not attempt to rewrite remote history.
No dedicated Compress now ChatUI button is added.
No backend API or OpenAPI schema is added.
The automatic 82% compression threshold is unchanged.
The configurable automatic token thresholds proposed in [Feature]Token-Threshold Context Compression #8348 and [Feature] 上下文压缩阈值可配置化 #9252 are out of scope.
The generated summary is not exposed in chat.
No dependency or lockfile is changed.
No database schema is changed.
Existing configurations are neither migrated nor enabled automatically.
Default behavior for all existing automatic compression callers remains unchanged.
This is NOT a breaking change. / 这不是一个破坏性变更。
Changed Files and Size
The working tree changes 18 tracked files relative to the baseline:
astrbot/builtin_stars/builtin_commands/commands/conversation.py/compactgates, locking, compression, persistence, and feedbackastrbot/builtin_stars/builtin_commands/main.py/compactastrbot/core/agent/context/compressor.pyastrbot/core/agent/context/config.pyastrbot/core/agent/context/manager.pyastrbot/core/astr_main_agent.pyastrbot/core/config/default.pyconfig-metadata.jsonfilesPatch size:
Most added lines are regression tests. The implementation adds no compatibility layer, UI component, or dependency.
Screenshots or Test Results / 运行截图或测试结果
Test Environment
25H226200.91683.1224.16.011.19.07.6.4c6a14e0600485293bd88cf78c04ecec967b21b50Verification Steps
llm_compressstrategy, and enable Manual Context Compression (Experimental)./compactwhile the context is below the automatic 82% threshold.Baseline Before Implementation
Before modifying source code, the following command was run from a clean
master...origin/masterworktree:Result:
The only warning was the pre-existing Python deprecation warning for
audioop, which is scheduled for removal in Python 3.13.Focused Implementation Tests
Compression core, command, checkpoint, and main-Agent provider tests:
Configuration loading and configuration-metadata conversion tests:
Final Related Regression
The final combined related regression used:
Result:
The warning is identical to the baseline warning.
This regression set covers:
force_compress=Truebypassing automatic gates;agent_statsdelivery;Local Tool Loop Agent Runner Regression
To verify that the optional
ContextManager.process()argument does not affect the normal local Agent request path:Result:
The warning is the same pre-existing
audioopdeprecation warning.Final Combined Regression
The final combined run covers the command, compression, provider, runner, session-lock, configuration, Dashboard SSE, Dashboard WebSocket, and OpenAPI WebSocket suites:
Result:
There were no failures, errors, or skips. The warning was the same pre-existing
audioopdeprecation warning.The WebChat suites cover transient progress forwarding, active-run snapshots, terminal-message replacement, and single-row persistence across Dashboard SSE, Dashboard WebSocket, and OpenAPI WebSocket.
Formatting and Static Checks
git diff --checkemitted only Windows working-tree LF/CRLF conversion notices and no whitespace errors.All three Chinese, English, and Russian
config-metadata.jsonfiles were also validated as parseable JSON.Dashboard Production Build
The following command was run from the
dashboarddirectory:Result:
Recommended Neo Profile on Windows
The
CONTRIBUTING.mdNeo profile was also run with dependency synchronization skipped because the environment was already synchronized:Ruff passed. The Neo test selection completed with
11 passed, 2 failed, 2 warnings. Both failures are existing Windows path-separator assertions intests/test_computer_skill_sync.py: the tests expectskills/skills.zip, while the Windows implementation returnsskills\skills.zip. Neither that test file norastrbot/core/computer/computer_client.pyis modified by this patch.An AstrBot instance launched from this checkout with
main.py --webui-dir dashboard/distresponded successfully athttp://127.0.0.1:6185with HTTP200.Manual WebUI and Runtime Verification
Real WebChat, the local Agent Runner, and an LLM provider were used for the following manual verification.
Successful manual compression
/compactwhile the context was well below the automatic 82% threshold.-1or above the current turn count.user: /compactplus one terminal bot message without a persisted⏳ Compressing context...entry.One successful run recorded:
Observed behavior:
WebChat compression and Context Ring change
QQ Official Bot
Failure preserves the original history
The compression prompt was temporarily changed to request expansion, verifying that output without a token reduction is never persisted:
The following failure paths were also verified:
None of these tested paths reduced the context ring, half-truncated history, or damaged subsequent recall.
Original history and Context Ring remain unchanged after failure
Session isolation and concurrency
/compacttwice in the same session did not restore stale history or drop the latest turn./compactwaited for it and then compressed history containing the completed response./newor modifying history through another entry point during compression prevented persistence:Stop behavior
A WebChat stop request cannot immediately cancel every in-flight provider request, but it prevents the result from being persisted. After the provider returned, the command reported:
History and the context ring remained unchanged. This protects persisted state, although a slow provider request may still run to completion and incur cost, as noted under Known Limitations.
Configuration and i18n
enable_manual_context_compression.dashboard/distwas used to verify the Chinese, English, and Russian labels and risk guidance, with no raw i18n keys shown.手动上下文压缩(实验性),Manual Context Compression (Experimental), andРучное сжатие контекста (экспериментальная функция), with complete localized risk guidance in each language.Observed configuration:
Simplified Chinese (
zh-CN)English (
en-US)Russian (
ru-RU)Known Limitations
ConversationManager.update_conversation(), the final update is not a database-level compare-and-swap. This path mitigates concurrent changes by re-reading and comparing the active conversation and persisted history immediately before the update.Checklist / 检查清单
😊 If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
/ 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。
👀 My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
/ 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”。
🤓 I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in
requirements.txtandpyproject.toml./ 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到
requirements.txt和pyproject.toml文件相应位置。😮 My changes do not introduce malicious code.
/ 我的更改没有引入恶意代码。
Summary by Sourcery
Enable safe, experimental manual context compression through the local Agent Runner while preserving existing automatic behavior and conversation integrity.
New Features:
/compactcommand for manually compressing context in local LLM-compression sessions.Bug Fixes:
Enhancements:
Tests: