Hermes Decision Trace

2026-05-23-genericagent-vs-hermes-lcm-prompt-compaction

title: GenericAgent agent_loop / compress_history_tags vs Hermes LCM / Prompt Compaction

HTML完整论证
Wiki可检索归档
Feishu短入口交付
🎯
核心结论

GenericAgent 的 agent_loop.pyllmcore.compress_history_tags 不是 Hermes LCM 的替代物。它们更像一套轻量运行时压缩纪律:靠最小循环、单轮消息重建、标签级截断、周期性压旧历史,减少上下文噪音。

🧭
推荐路径

不要把 compress_history_tags 原样搬进 Hermes messages 上原地改

🛡️
关键边界

不调用真实 executor;生产动作另走审批。

关键判断

判断项摘要
推荐方案不要把 compress_history_tags 原样搬进 Hermes messages 上原地改
关键依据见完整记录中的评分依据、状态摘要和证据链。
落地方式按行动清单推进,保持可回退。
风险边界不跨执行边界;真实执行需另走审批。

证据摘要

  • 由 Hermes 会话生成。证据点 1
  • 如涉及外部事实,应在正文中保留来源或验证路径。证据点 2

行动清单

不要把 compress_history_tags 原样搬进 Hermes messages 上原地改
不要删除 <key_info> 原文;这类经常是用户约束和中间状态
不要用字符数 context_win * 3 替代 Hermes token counter
不要让 provider session history 接管 Hermes 本地会话历史
不要把 prompt compaction、LCM pre-summary trimming、UI echo cleanup 混成一个开关

边界 / 风险

风险点

未记录额外风险。

完整记录

---

title: GenericAgent agent_loop / compress_history_tags vs Hermes LCM / Prompt Compaction

created: 2026-05-23

updated: 2026-05-23

type: technical-comparison

status: active

tags: [GenericAgent, Hermes, LCM, prompt-compaction, context-density]

source:

  • https://github.com/lsdefine/GenericAgent/blob/main/agent_loop.py
  • https://github.com/lsdefine/GenericAgent/blob/main/llmcore.py
  • /home/ht/.hermes/plugins/hermes-lcm/engine.py
  • /home/ht/.hermes/hermes-agent/agent/context_prompt_compactor.py
  • /home/ht/.hermes/hermes-agent/agent/tool_description_compactor.py

public_html: https://taoge-morning-brief.pages.dev/decision-traces/2026-05-23-genericagent-vs-hermes-lcm-prompt-compaction.html

confidence: high

---

GenericAgent agent_loop / compress_history_tags vs Hermes LCM / Prompt Compaction

结论

GenericAgent 的 agent_loop.pyllmcore.compress_history_tags 不是 Hermes LCM 的替代物。它们更像一套轻量运行时压缩纪律:靠最小循环、单轮消息重建、标签级截断、周期性压旧历史,减少上下文噪音。

Hermes 当前 LCM 是更重、更可审计的lossless context management:原文落库、DAG summary、可 grep/describe/expand 回捞;Prompt Compaction 是入场前静态 prompt/schema/prose 压缩

最合理的吸收方式:不改 LCM 主链路;在 prompt compaction 与非 LCM / overflow fallback 场景吸收 GenericAgent 的“标签级保头尾截断”和“工具结果轻量清洗”思路。

GenericAgent 机制读后摘要

1. agent_loop.py 主循环

文件只有 133 行,核心在 agent_runner_loop

  • 初始 messages:system + user
  • 每 turn 调 client.chat(messages=messages, tools=tools_schema)
  • tool call 由 handler.dispatch 分发到 do_<tool_name>
  • 每个 tool 返回 StepOutcome(data, next_prompt, should_exit)
  • next_prompt 聚合后作为下一轮唯一 user message
  • 第 104 行关键:messages = [{"role": "user", "content": next_prompt, "tool_results": tool_results}]
  • 注释说明:just new message, history is kept in *Session

这意味着 GA 自己的 loop 不在本地 message list 里保完整长历史,而依赖 LLM session backend 维护历史;每轮只把下一步 prompt 和 tool_results 送进去。它的上下文控制重点不在 DAG,而在:

  1. loop 很短,减少框架层噪音
  2. tool schema 少,减少固定输入
  3. verbose=false 时清洗 assistant content 和工具参数展示
  4. 每 10 turn 重置 client.last_tools,重新发工具描述,避免长期工具描述状态异常

2. _clean_content

用于非 verbose 输出清洗:

  • 缩长代码块:超过 6 行只留前 5 行和行数提示
  • 删除 <file_content>...</file_content>
  • 删除 <tool_use> / <tool_call>
  • 合并多余空行

这不是严格上下文压缩,而是展示/回灌内容降噪。可借鉴点是:对 code block、tool call、file content 这类高噪声块按类型处理,不做泛化摘要。

3. _compact_tool_args

用于工具参数短显:

  • path 只显示 basename
  • update_working_checkpoint 只留前 60 字
  • ask_user 保留问题和候选项
  • 其他 args JSON 截到 120 字

可借鉴点是 tool args 的“可读短显”和“保留关键字段”。但这更影响 UI/日志,不应直接影响模型真实 tool call 参数。

4. compress_history_tags

核心实现 36-67 行:

  • 每次调用有冷却计数 _cd,默认 interval=5,不是每轮都压
  • force=True 时立即压
  • 默认保护最近 keep_recent=10 条消息
  • 对旧消息:
  • <thinking> / <think> / <tool_use> / <tool_result> 标签内容保头尾截断到 max_len=800
  • <history> / <key_info> / <earlier_context> 整块替换成 <tag>[...]</tag>
  • Claude content-block 里:
  • text block 做同样标签处理
  • tool_result content 截断字符串或 text block
  • tool_use input dict 每个字段截断
  • 打印 [Cut] before -> after

它是原地 lossy trimming,没有外部原文存储,也没有检索回捞。

5. trim_messages_history

它把 compress_history_tags 纳入上下文守门:

  • cap = sess.context_win * 3:用字符数粗略估算 token
  • target = cap * trim_keep_rate,默认 0.6
  • 先按 interval 尝试压旧 tag
  • 如果仍超 cap:force 压,只保最近 4 条
  • 如果还超:从头丢消息,保留至少 9 条左右,并清理开头孤立 tool_result

这是典型“轻量、粗糙、可用”的运行时上下文削峰策略。

Hermes 当前机制

1. LCM

LCM 是插件 /home/ht/.hermes/plugins/hermes-lcm,主入口 engine.py。它的架构明确:

  1. 每条消息原文持久化到 immutable MessageStore
  2. context pressure 时,把 fresh tail 之外的旧消息压成 D0 summary node
  3. D0 累积后按 fan-in 压成 D1/D2...
  4. active context = system prompt + DAG summaries + fresh tail
  5. 提供 lcm_grep / lcm_describe / lcm_expand / lcm_expand_query 回捞

关键区别:LCM 是 lossless 管理,summary 只是 active context 的投影;原文仍可查。

2. LCM 的压缩细节

engine.py 看:

  • should_compress_preflight 会先 ingest messages,再判断是否需要压缩
  • compress 会保护 system prompt 和 fresh tail
  • leaf chunk 可以 dynamic 调整,overflow 时可强制压旧 raw backlog
  • summarization 失败会尝试缩小 chunk rescue
  • tool result 在序列化前可能 externalize,大输出用 placeholder
  • summary node 带 source_ids/source_type/earliest_at/latest_at/expand_hint
  • active context 组装时按高 depth 优先,summary budget 和 tail budget 分开控制
  • assembly cap 可由 max_assembly_tokensreserve_tokens_floor 约束

这比 GA 的 compress_history_tags 重很多,但可审计、可回捞、适合长会话。

3. Hermes Prompt Compaction

现有实现分三层:

  • agent/context_prompt_compactor.py
  • 保护 URL / inline code / path
  • 遇到 bullet / numbered list 默认不压
  • 只删 filler prose
  • agent/tool_description_compactor.py
  • 保护 URL / inline code / path / CLI flag
  • 对 tool description 做保守替换
  • 不够收益或过度压缩就回退原文
  • agent/system_prompt.py 里 compact guidance blocks
  • memory / session_search / skills / search_router 等 guidance 有 compact 版

根据本机已有 rollout 记录:

  • compact guidance blocks:约 45.06% 收益
  • compact context files:约 37.64% sample 收益
  • compact skills prompt:约 6.66%
  • compact tool descriptions:约 1.33%

Prompt Compaction 与 LCM 的层级不同:前者压固定 prompt/schema/prose,后者压动态会话历史。

对比表

维度GenericAgent compress_history_tagsHermes LCMHermes Prompt Compaction
目标运行时旧历史削峰长会话 lossless 管理固定 prompt/schema/prose 减重
是否保原文是,MessageStore不涉及会话原文
压缩方式标签级 regex 截断/删除LLM summary + DAG保守规则替换/compact 文案
回捞能力lcm_grep/describe/expand无需回捞
触发interval / force / 超 captoken threshold / preflight / deferred maintenance / overflowconfig flag,构造 prompt 时
粒度message content 内部 tag/blockmessage chunk / summary nodeguidance/context/tool description prose
风险丢细节且不可恢复summary 失真但可 expand 原文误伤规则,已有保护
工程复杂度中低
适合 Hermes 主线只适合 fallback/局部已是主线已是主线

关键判断

1. 不要用 GenericAgent 替换 LCM

GA 的压缩是 lossy trimming,优点是便宜、简单、稳定;缺点是被截掉的信息无法恢复。Hermes 当前 LCM 的核心价值正是“active context 变短,但原文还在”。这条不能倒退。

2. GenericAgent 对 LCM 的启发是“预清洗”,不是“替代 summarization”

LCM 已经在 _serialize_messages 中做了类似事情:

  • sanitize content
  • tool output externalize
  • 长 assistant/tool/user content 头尾截断
  • tool call arguments sanitize + 500 字截断

GenericAgent 可补的点是:对 <thinking>/<tool_use>/<tool_result>/<history>/<key_info> 这类 tag 的显式标签级策略。如果 Hermes 会话里确实出现这类 tag,可在 LCM pre-compaction sanitize 阶段加入保守处理,但必须只用于送 summarizer 的序列化文本,不能改 MessageStore 原文。

3. GenericAgent 对 Prompt Compaction 的启发有限但有一条有价值

Prompt Compaction 现在主要处理固定 prose。GA 的 _compact_tool_args/_clean_content 提醒我们:除了 prompt,还可以做展示层 / 日志层 / summarizer 输入层压缩,避免工具参数和代码块污染下一轮 reasoning。

但这不应和正式 prompt compaction 混成一个开关。建议单独归类为:

  • lcm.pre_summary_tag_trimming
  • display.compact_tool_call_echo
  • gateway/cardify_output_cleanup

4. GA 的“单轮 messages 重建”不适合 Hermes

GA 第 104 行每轮只保留下一条 user message,完整历史在 backend session。Hermes 要支持多 provider、gateway、cron、LCM、tool calls、session DB 和 exact recall,不能依赖 provider 侧 session state。Hermes 当前本地完整 message 管理是必要架构。

建议吸收项

P0:吸收到 LCM pre-summary sanitize

做一个保守函数,只作用于“送 summarizer 的 serialized copy”,不改原始存储:

  • <thinking> / <think>:保头尾,默认 800-1200 chars
  • <tool_use> / <tool_call>:保 tool name / 参数摘要,长字段截断
  • <tool_result>:已有 externalize 优先;未 externalize 的 tag 内容保头尾
  • <history> / <earlier_context>:如果是嵌套历史块,替换为 placeholder
  • <key_info>:谨慎,不建议整块删;可以保留首尾或摘要,因为里面可能有用户约束

验证:构造包含这些 tag 的 assistant/tool messages,确认 MessageStore 原文不变,serialize 后变短,summary 仍含关键 tool name/path/url/error。

P1:为非 LCM compressor / overflow fallback 增加轻量 tag trimming

如果某些 session 禁用 LCM 或 LCM summarization 失败,可以用 GA 风格做最后防线:

  1. 先压旧消息里的 tool/thinking/history tag
  2. 再保 fresh tail
  3. 最后才丢旧消息

这比直接 pop old messages 更温和。

P1:工具回显压缩

Hermes gateway / CLI 显示 tool call arguments 时,可以参考 _compact_tool_args

  • path 显示 basename + hover/expand 原文
  • 长 JSON 参数折叠
  • ask_user 保留 candidates
  • memory/checkpoint 只展示摘要

这属于 UI/日志,不影响模型输入。

P2:agent loop 参考,不改主循环

保留参考意义:短 loop、handler dispatch、StepOutcome 都很干净。但 Hermes 主循环要处理 provider fallback、budget、toolsets、gateway、memory、LCM、interrupt、cron 等,不适合向 GA 的极简 loop 回退。

不建议做的事

  • 不要把 compress_history_tags 原样搬进 Hermes messages 上原地改
  • 不要删除 <key_info> 原文;这类经常是用户约束和中间状态
  • 不要用字符数 context_win * 3 替代 Hermes token counter
  • 不要让 provider session history 接管 Hermes 本地会话历史
  • 不要把 prompt compaction、LCM pre-summary trimming、UI echo cleanup 混成一个开关

最小落地路线

  1. 新增 agent/history_tag_sanitizer.py 或放入 LCM extraction/sanitize 附近,只处理 copy。
  2. 在 LCM _serialize_messagessanitize_pre_compaction_content 后接 tag trimming。
  3. 增加 focused tests:
  • raw MessageStore 内容不变
  • serialized text 变短
  • thinking/tool_result/history tag 被处理
  • URL/path/error/tool name 保留
  1. 跑 LCM tests 与一次真实长会话压缩 smoke。

最终结论

GenericAgent 这部分对 Hermes 的价值不是“更好的 LCM”,而是给 LCM 前处理和 fallback trimming 提供一个非常实用的小模式。建议做 P0 局部吸收:LCM pre-summary tag trimming,但必须坚持 lossless 边界:原文永远进 MessageStore,压缩只发生在 summarizer input / active projection / display echo。