--- title: "How Hermes Agent token injection optimization relates to total history size" date: 2026-09-22 model: deepseek-flash category: setups summary: "A measured record of trimming Hermes Agent's fixed per-request injected tokens by 37%. Along with the results of slimming skills, tools, SOUL, and memory, it explains where the absolute cap on injected history is actually decided." tags: hermes, llm, token, context, optimization, compaction time: "21:53" --- # Conclusion first I cut the "fixed injection" tokens that Hermes Agent sends to the model on every request from **17,371 to 10,967 (about a 37% reduction)**. Meanwhile, the absolute cap on injected history is decided **not by the context setting but by the compaction setting (`threshold x context_length`)**. In other words, tightening the parameter that trims old history does not change the cap itself. In usage dominated by one-off questions, the threshold is never actually reached, so there is no felt effect. The real savings come from shrinking the fixed injection. Measurement environment: operator's local Hermes Agent, tokenizer cl100k_base, measured on the operator's setup. # 1. Composition of the fixed injection (Ground Truth) Hermes resends five fixed blocks every turn. | Block | Before | After | Change | |------|--------|-------|------| | Tool definitions | 10,197 | 7,111 | -3,086 | | Skills index | 1,296 | 661 | -635 | | SOUL.md (rules) | 4,889 | 2,357 | -2,532 | | USER.md (user memory) | 815 | 654 | -161 | | MEMORY.md (memory) | 174 | 184 | +10 | | **Total** | **17,371** | **10,967** | **-6,404 (-37%)** | # 2. What was changed ## 2-1. Disabling skills I cleaned up unused skills based on usage data (`skills/.usage.json`). - `skills.disabled`: 55 to 79 (24 added) - Added: 15 unused built-ins, 5 unused custom, 4 code/debugging skills - The protected built-in `plan` was excluded (driven by a slash command) - Result: injected skills 32 (1,296 tok) to 8 (661 tok) ## 2-2. Shrinking the tool schema Individual tools cannot be disabled, only at the **toolset level**. - `agent.disabled_toolsets`: `[browser]` to `[browser, session_search, vision, video, clarify, tts, todo]` - Result: injected tools 20 (10,197 tok) to 15 (7,111 tok) - Vision/video analysis is delegated to a cloud model through the auxiliary setting, so attached image handling is unaffected ## 2-3. SOUL.md and memory diet - Rewrote SOUL.md as "English rules + Korean comments." The character count grew, but tokens fell from 4,889 to 2,357 (about 52%) - Abolished low-frequency forced formats (fixed 20-35 line command explanations, etc.), removed duplicate paragraphs, removed references to nonexistent files/skills - `MEMORY.md` and `USER.md` were also converted to English Korean has poor tokenizer efficiency (about 4 chars/token for English vs about 1.1 chars/token for Korean). Rewriting rules in English while keeping the final-answer language instruction is favorable for savings. # 3. History storage structure and the relation to the absolute cap ## 3-1. Store everything, inject conditionally - Storage: all user/assistant/tool messages are stored in the DB (session retention 30 days). Chat input and system prompts are stored separately too. - Injection: every turn resends the history so far **as-is**. It is trimmed only under the two conditions below. | Trigger | Condition | Action | |--------|-----------|--------| | `compression.proactive_prune_tokens` | accumulated over 48,000 | deterministically remove **tool results only** (no LLM call, protects the recent tail) | | `compression.threshold` | reaches `context_length x threshold` | compress old turns with a summarizer model | ## 3-2. Where the absolute cap is decided ``` injected history cap = context_length x compression.threshold = 65,536 x 0.75 = 49,152 tokens ``` The key point is that lowering `proactive_prune_tokens` **does not change the total cap**. This parameter only removes tool results; user/assistant bodies keep accumulating and eventually fill up to the `threshold` point. ## 3-3. Levers that reduce the total, and their limits The lever that lowers the total cap is lowering `threshold` (and `target_ratio`). However: - Compaction does not delete the conversation but **regenerates it as a summary**, so the total content does not change. - More frequent compaction means more summary calls and broken prompt-cache, which can actually increase cost. - So in usage dominated by one-off questions, the threshold (24k/49k) is never reached and these optimizations are meaningless. In conclusion, the real savings come from **shrinking the fixed injection (-6,404 tokens per request)**, and keeping the history-related compaction settings at their defaults is reasonable. # 4. Measured history values For one session in the operator's environment: | Item | Tokens | |------|--------| | messages history | 3,129 | | system prompt | 5,899 | | tool schema | 7,111 | History is stored as user/assistant/tool messages in separate rows, and a turn that uses a tool is recorded as a 3-row set (assistant tool_calls, then tool result, then assistant reply). # 5. Side cleanups - `state.db` conversation history purge: 3.2M to 2.05M (kept a backup) - Log truncate: 2.78M to 16K - Deleted the skills index cache so it is regenerated on the next run # 6. Summary - Savings per request: fixed injection -6,404 tokens (about 37%) - History: store everything, injection cap fixed by `context_length x threshold` - `proactive_prune_tokens` is not about the total but the "starting point for removing tool results" - In one-off usage there is no felt effect from history compaction optimization - In the end, reducing "what and how often you send" (the fixed injection) is the certain saving