Optimize the Hermes configuration to reduce token consumption and improve Chinese trigger efficiency.
Optimization Process
1. Back up the current configuration
cp -r ~/.hermes/routing ~/.hermes/routing-backup-$(date +%Y%m%d-%H%M%S)
2. Routing file consolidation
Check for duplicates:
unified-index.mdandskill-index.mdtypically overlap by 30-40%- Delete
unified-index.mdandunified-index.json
Streamline skill-router.md:
- Remove line number markers (such as
28|,29|) - Merge similar trigger words
- Use a more compact format
- Goal: compress from 13KB+ to 6-8KB
Verify:
ls -la ~/.hermes/routing/*.md
du -sh ~/.hermes/routing/*.md
3. Chinese localization of trigger words
Identify pure English trigger words:
# 找出只有英文触发词的 skill
import re
pattern = r'\|\s*`([^`]+)`\s*\|[^|]*\|[^|]*\|\s*([^|]+)\s*\|'
# 检查是否有中文字符
has_chinese = bool(re.search(r'[\u4e00-\u9fff]', triggers))
Add Chinese equivalent words:
findmy→查找设备, 定位, 找手机codex→代码生成, AI编程, 代码代理excalidraw→手绘图, 流程图, 白板github-auth→GitHub认证, gh登录, git凭据
Three-file synchronized update process(when adding a new skill or adding Chinese trigger words to an existing skill):
- skill-index.json — primary data source. Use a Python script to batch update the triggers array, deduplicate and sort, then write back.
- skill-router.md — routing table. Update the trigger word list in the corresponding category section.
- skill-index.md — table index. Update the trigger word column in the corresponding category table.
Complete process for batch adding Chinese trigger words:
import json
# 1. 读取 skill-index.json
with open('~/.hermes/routing/skill-index.json', 'r') as f:
skills = json.load(f)
# 2. 检查哪些 skills 没有中文触发词
no_chinese = []
for skill in skills:
triggers = skill.get('triggers', [])
has_cn = any(any('\u4e00' <= c <= '\u9fff' for c in t) for t in triggers)
if not has_cn:
no_chinese.append(skill['name'])
print(f"没有中文触发词的 skills ({len(no_chinese)}):")
for s in sorted(no_chinese):
print(f" - {s}")
# 3. 定义中文触发词映射
chinese_triggers = {
'apple-notes': ['苹果备忘录', '备忘录', 'Apple备忘录', '笔记'],
'apple-rmndrs': ['苹果提醒', '提醒事项', 'Apple提醒', '待办'],
# ... 为每个 skill 添加
}
# 4. 批量更新
for skill in skills:
if skill['name'] in chinese_triggers:
existing_triggers = skill.get('triggers', [])
new_triggers = chinese_triggers[skill['name']]
all_triggers = list(set(existing_triggers + new_triggers))
skill['triggers'] = sorted(all_triggers)
# 5. 写回
with open('~/.hermes/routing/skill-index.json', 'w') as f:
json.dump(skills, f, indent=2, ensure_ascii=False)
Key pitfalls:
- Skill names in skill-index.json must match exactly; first use a script to check which skills exist and which are missing
- Missing skills need a complete entry added first (name/category/grade/priority/security_review/supersedes/triggers/description)
- The skill-index.md table row format is inconsistent (some start with
|and some start with||); when patching, include enough context to avoid multiple matches - MCP-type skills such as open-computer-use are classified under
mcpcategory, notappleorred-teaming - After batch updating, skill-router.md and skill-index.md need to be updated in sync
- Finally, remember to commit to the git repo
4. Compression threshold and context window calibration
Core finding (2026-04-27): Hermes's auto-compress trigger point looks at 主模型 context_length × compression.threshold, not at auxiliary.compression.extra_body.context_window。
Determination formula:
- Trigger threshold =
max(int(model.context_length * compression.threshold), 64000) - Example:
model.context_length: 2000000andcompression.threshold: 0.5→ approximately1000000tokens before compress is triggered
Correct way to change it:
- First look at
compression.threshold(the common default is0.5) - Then look at the actual context source of the main model:
model.context_length- or
custom_providers[].models[<exact model name>].context_length
- If the current model name has an alias/suffix (such as
gpt-5.4(xhigh)),custom_providers.modelsmust write theexact same namekey; writing onlygpt-5.4is not stable enough - When the custom endpoint
/modelsis inaccurate or missing, directly write the integer inmodel.context_lengthto avoid Hermes falling back to an incorrect default value
Do not confuse:
auxiliary.compression.extra_body.context_window: only affects the summary model request body parametersauxiliary.compression.context_length: only used for auxiliary compression model feasibility check/override- What truly determines when auto-compress happens is still the main model's context_length
5. Memory compression (architecture chain format)
Core finding (2026-04-24): flat entries → architecture chain, one tree-shaped dependency replaces 10+ scattered entries.
Architecture chain template:
元:[核心行为规则]→[次级规则]→[三级规则]
├─红线:[不可做]
├─重试:[失败策略]
├─[领域A]链:[约束]→[执行]→[验证]
└─[领域B]链:[约束]→[执行]→[验证]
Example:
元:长忆从严→直做勿问→效驱>轮询→全量不挑。
├─红线:勿删cron/自动化,清前确认存废。
├─重试:始后败不重(断线除外),败即弃。
├─cron链:模型驱→禁脚本→deliver=feishu→冲突必解→sniper参数分离不与长驻争。
├─Hansa链:只收不挪(无FluxA权)→cron走MCP→状态机→答题agent推理。
└─Quest链:禁Twitter/X→禁视频→Medium/Dev.to/gist可。
USER.md optimization:
- 10+ flat entries → 1 architecture chain
- Classical Chinese compression, each entry ≤20 characters
- Store only hard rules; delete project details
MEMORY.md optimization:
- Keep only cross-project stable technical facts
- Delete all project details → belongs in repo docs / session_search
- Goal: < 500 bytes
5. Skills cleanup
# 1. 查大小排序
du -sh ~/.hermes/skills/* | sort -rh | head -20
# 2. 统计每个目录 skill 数
for d in ~/.hermes/skills/*/; do
count=$(find "$d" -name "SKILL.md" | wc -l)
size=$(du -sh "$d" | cut -f1)
echo "$count skills, $size: $(basename $d)"
done | sort -t, -k1 -rn
Deletion criteria:
- Auto-installed large packages (>1M and >10 skills, never manually loaded)
- Cloned repos (containing .git directory)
- Empty directories (0 skills)
- Domains the user will not use
Boundary confirmation before deletion (2026-04-25 retrospective):
- First list an inventory by "functional domain," then delete; do not misjudge "has duplicates" as "all can be deleted."
- Tools like OpenChronicle can only be benchmarked against the same functional domain: timeline memory / activity replay / session retrieval / personal behavior archiving.
- Do not casually delete cross-domain productivity skills (such as coding agents like codex / opencode / claude-code) unless the user explicitly includes that domain in the cleanup scope.
- When the user says "kill the feature library/conflicting items," interpret it by default as: only delete conflicting items in the same function as the target tool; it is not a global deduplication and massive cleanup.
- Before destructive deletion, first provide a "list to be deleted + reasons for keeping"; when the scope is unconfirmed, prefer only adding routing and do not delete.
6. Routing reference cleanup (must be done after deleting skills)
⚠️ skill-index.json top level is an array [{skill}, ...], not a dict {"skills": [...]}
Python cleanup (sed is unreliable for multi-line sections):
import json, re
# skill-index.json
data = json.load(open('skill-index.json'))
data = [s for s in data if s.get('category') not in deleted_cats
and s.get('name') not in deleted_skills]
json.dump(data, open('skill-index.json', 'w'), indent=2)
# skill-index.md — 删除 section + table rows
content = open('skill-index.md').read()
for cat in deleted_cats:
pattern = rf'## {re.escape(cat)}\n.*?(?=\n## |\Z)'
content = re.sub(pattern, '', content, flags=re.DOTALL)
for skill in deleted_skills:
lines = content.split('\n')
content = '\n'.join(l for l in lines if skill not in l)
open('skill-index.md', 'w').write(content)
# skill-router.md — 删除对应行
lines = open('skill-router.md').readlines()
lines = [l for l in lines if not any(s in l for s in deleted_skills)]
open('skill-router.md', 'w').writelines(lines)
Verification:
grep -c "DELETED_SKILL" skill-router.md skill-index.md
# 期望:0
Expected benefits
| Optimization item | Before optimization | After optimization | Savings |
|---|---|---|---|
| routing file | 90KB+ | 50-60KB | 30-40% |
| skill-router.md | 13KB | 6-8KB | 50%+ |
| English trigger words | 30+ | <15 | 50%+ |
| USER.md | 1900 bytes | 1200 bytes | 37% |
Token savings: about 2000-3000 tokens per conversation
Verification steps
- Check file size
- Verify trigger word matching
- Test routing functionality
- Monitor token usage
Risks and Pitfalls
- Deleting unified-index.md may cause some routes to fail
- Added Chinese trigger words may not be used by the user
- Backups are needed for rollback
- After deleting skills, routing references must be cleaned up in sync(skill-router.md, skill-index.md, skill-index.json)
- skill-index.json is an array, not a dict: top level
[{...}], not{"skills": [...]} - sed is unreliable for multi-line sections: use Python regex to clean skill-index.md
- Use write_file for MEMORY.md, use the memory() tool for the user profile: two independent systems
- First take the current state of repo/AGENTS as authoritative: the actual landing point of routing must be verified on site; do not treat
~/.hermes/hermes-agent/routing/、~/.hermes/routing/, project-internal.hermes/routing/as one of them as always true. If the current task already has a project-internal.hermes/routing/, prioritize updating its official file; do not first write the official routing to/tmp/*and then stop there. - memory replace requires an exact match of old_text: Multiple entries cannot be replaced at once; they must be removed one by one and then new entries added
- The architecture chain format is superior to a flat list: A tree-like dependency relationship saves more tokens than 10+ loose entries and is logically clearer
- When modifying
config.yaml, first read the exact context before patching: containingapi_key、base_url, inline map (such asproviders: {}) YAML is very prone to accidentally affecting adjacent keys due to fuzzy matching; do not directly replace based on redacted fragments or memory