Overview
DeerFlow (ByteDance's open-source SuperAgent harness, github.com/bytedance/deer-flow) implements automatic conversation title generation via a dedicated TitleMiddleware. After the first complete user+assistant exchange, a lightweight LLM call generates a concise title for the session. This is a small but impactful UX improvement that Hermes currently lacks entirely — sessions are identified only by timestamps and platform keys.
This becomes critical as Hermes builds toward a Web UI (#501), session search (existing session_search tool), and better session management. Users currently cannot quickly identify sessions in session_search results or when browsing stored transcripts.
Research Findings
How DeerFlow Does It
DeerFlow's TitleMiddleware (backend/src/agents/middlewares/title_middleware.py) runs as an after_agent hook with these specifics:
Trigger conditions — title is generated only when:
- No title exists yet (
state.get("title") is falsy)
- Exactly 1 user message AND at least 1 assistant message in the thread
- This ensures title generation happens after the first complete exchange, not on every turn
Implementation pattern:
def _should_generate_title(self, state):
if state.get("title"): return False # already has title
user_messages = [m for m in messages if m.type == "human"]
assistant_messages = [m for m in messages if m.type == "ai"]
return len(user_messages) == 1 and len(assistant_messages) >= 1
LLM call — uses a cheap/fast model (configurable via TitleConfig) with a focused prompt:
title_config = get_title_config()
model = create_chat_model(title_config.model or default_model)
# Short system prompt asking for a 3-8 word title
# Input: first user message + first assistant response
Graceful fallback — if the LLM call fails, falls back to truncating the user message:
except Exception:
title = user_message_text[:50] + ("..." if len(user_message_text) > 50 else "")
Configuration (title section in config.yaml):
title:
enabled: true
model: null # null = use default model; or specify a fast/cheap model
Key Design Decisions
- One-shot, not continuous — title is generated exactly once per thread, not updated as conversation evolves
- Cheap model — can use a different (faster/cheaper) model than the main agent model
- Non-blocking — failure doesn't affect the main agent response
- Post-response — runs after the agent finishes, so it doesn't add latency to the user-visible response
Current State in Hermes Agent
Hermes sessions are identified by deterministic keys like agent:main:telegram:dm or agent:main:cli. The SessionDB (hermes_state.py) stores sessions with fields including session_key, started_at, last_activity, and metadata — but no title field.
session_search results currently return session timestamps and matched content snippets, but no human-readable title. This makes it hard to quickly identify what a past session was about.
The SessionStore (gateway/session.py) manages session lifecycle but also has no title concept.
Relevant files:
hermes_state.py — SessionDB, SQLite schema with sessions + messages tables
gateway/session.py — SessionStore, SessionEntry, session key generation
agent/context_compressor.py — has the summarization LLM call pattern we could reuse
Implementation Plan
Classification
This is a core codebase change, not a skill or tool. It touches the session storage layer, the agent loop, and potentially the gateway. The LLM call pattern already exists in context_compressor.py and can be reused.
What We'd Need
- Add
title column to sessions table in hermes_state.py (ALTER TABLE migration)
- Title generation function (reuse the auxiliary LLM client pattern from
context_compressor.py)
- Hook into the agent loop to trigger title generation after first exchange
- Store title in SessionDB
- Surface titles in
session_search results and /sessions command output
Phased Rollout
Phase 1: Core title generation
- Add
title TEXT column to sessions table with migration
- Add
generate_title() function using the summarization/auxiliary model
- Call it after the first complete turn in
run_agent.py or gateway message handler
- Fallback to truncated first user message on failure
- Config:
title_generation_enabled (default true), title_generation_model (optional override)
Phase 2: Surface titles everywhere
- Include titles in
session_search results
- Show titles in
/sessions command output
- Include in session metadata for gateway platforms
- Show in CLI session listing
Phase 3: Web UI integration
Pros & Cons
Pros
Cons / Risks
- Small additional latency on first turn (mitigated by running post-response)
- Extra LLM call cost (mitigated by using cheap model + only once per session)
- Schema migration needed for existing sessions (simple ALTER TABLE)
Open Questions
- Should the title be updatable (e.g., if the conversation topic shifts significantly)?
- Should there be a
/title command to manually set/override the title?
- What model to default to for title generation? (Could reuse
CONTEXT_COMPRESSION_MODEL config)
References
Overview
DeerFlow (ByteDance's open-source SuperAgent harness, github.com/bytedance/deer-flow) implements automatic conversation title generation via a dedicated
TitleMiddleware. After the first complete user+assistant exchange, a lightweight LLM call generates a concise title for the session. This is a small but impactful UX improvement that Hermes currently lacks entirely — sessions are identified only by timestamps and platform keys.This becomes critical as Hermes builds toward a Web UI (#501), session search (existing
session_searchtool), and better session management. Users currently cannot quickly identify sessions insession_searchresults or when browsing stored transcripts.Research Findings
How DeerFlow Does It
DeerFlow's
TitleMiddleware(backend/src/agents/middlewares/title_middleware.py) runs as anafter_agenthook with these specifics:Trigger conditions — title is generated only when:
state.get("title")is falsy)Implementation pattern:
LLM call — uses a cheap/fast model (configurable via
TitleConfig) with a focused prompt:Graceful fallback — if the LLM call fails, falls back to truncating the user message:
Configuration (
titlesection in config.yaml):Key Design Decisions
Current State in Hermes Agent
Hermes sessions are identified by deterministic keys like
agent:main:telegram:dmoragent:main:cli. TheSessionDB(hermes_state.py) stores sessions with fields includingsession_key,started_at,last_activity, andmetadata— but no title field.session_searchresults currently return session timestamps and matched content snippets, but no human-readable title. This makes it hard to quickly identify what a past session was about.The
SessionStore(gateway/session.py) manages session lifecycle but also has no title concept.Relevant files:
hermes_state.py—SessionDB, SQLite schema withsessions+messagestablesgateway/session.py—SessionStore,SessionEntry, session key generationagent/context_compressor.py— has the summarization LLM call pattern we could reuseImplementation Plan
Classification
This is a core codebase change, not a skill or tool. It touches the session storage layer, the agent loop, and potentially the gateway. The LLM call pattern already exists in
context_compressor.pyand can be reused.What We'd Need
titlecolumn tosessionstable inhermes_state.py(ALTER TABLE migration)context_compressor.py)session_searchresults and/sessionscommand outputPhased Rollout
Phase 1: Core title generation
title TEXTcolumn tosessionstable with migrationgenerate_title()function using the summarization/auxiliary modelrun_agent.pyor gateway message handlertitle_generation_enabled(default true),title_generation_model(optional override)Phase 2: Surface titles everywhere
session_searchresults/sessionscommand outputPhase 3: Web UI integration
Pros & Cons
Pros
Cons / Risks
Open Questions
/titlecommand to manually set/override the title?CONTEXT_COMPRESSION_MODELconfig)References
hermes_state.py,gateway/session.py,agent/context_compressor.py