What the Agent Does While Idle: Asynchronous Background Review After a Conversation

The article explains Hermes' Background Review mechanism that triggers asynchronous self‑improvement after a dialogue ends, detailing trigger conditions, a forked sub‑agent architecture, prompt selection, cost‑saving cache inheritance, a four‑step skill‑update priority, result reporting, and common pitfalls.

James' Growth Diary
James' Growth Diary
James' Growth Diary
What the Agent Does While Idle: Asynchronous Background Review After a Conversation

Trigger Conditions: When a Background Review Starts

Hermes maintains two independent counters to decide whether to run a memory review or a skill review after a conversation ends.

# agent/agent_init.py – defaults, overridable via config.yaml
agent._memory_nudge_interval = 10    # trigger memory review every N user turns
agent._turns_since_memory = 0        # cumulative counter

agent._iters_since_skill = 0          # cumulative tool‑call counter within the current turn
agent._skill_nudge_interval = 10     # trigger skill review every N tool calls

# load overrides from config.yaml
skills_config = _agent_cfg.get("skills", {})
agent._skill_nudge_interval = int(skills_config.get("creation_nudge_interval", 10))
mem_config = _agent_cfg.get("memory", {})
agent._memory_nudge_interval = int(mem_config.get("nudge_interval", 10))

At the end of a dialogue the two conditions are evaluated independently; if either is satisfied a background review thread is spawned.

# agent/conversation_loop.py – trigger check before conversation ends
_should_review_skills = False
if (agent._skill_nudge_interval > 0
        and agent._iters_since_skill >= agent._skill_nudge_interval
        and "skill_manage" in agent.valid_tool_names):
    _should_review_skills = True
    agent._iters_since_skill = 0

# only start review if the dialogue completed successfully and at least one review is needed
if final_response and not interrupted and (_should_review_memory or _should_review_skills):
    agent._spawn_background_review(
        messages_snapshot=list(messages),
        review_memory=_should_review_memory,
        review_skills=_should_review_skills,
    )

Every N user turns → memory review (✅)

Every N tool calls → skill review (requires skill_manage permission) (✅)

Interrupted conversations → no review

Sub‑Agent / restricted session → skill review disabled

Key design points: counters accumulate across turns, interrupted conversations are ignored, and the skill_manage permission gates skill reviews.

Fork Mechanism: Isolating the Review Agent

Instead of appending another dialogue round to the main Agent, Hermes forks an independent sub‑Agent that inherits the parent’s runtime state but runs in isolation.

# run_agent.py – spawn background review thread
def _spawn_background_review(self, messages_snapshot, review_memory=False, review_skills=False):
    from agent.background_review import spawn_background_review_thread
    target, _prompt = spawn_background_review_thread(
        self, messages_snapshot,
        review_memory=review_memory, review_skills=review_skills,
    )
    t = threading.Thread(target=target, daemon=True, name="bg-review")
    t.start()

The sub‑Agent is created with a limited tool whitelist (only memory and skills) and skips external memory plugins to avoid contaminating the user’s real memory.

# agent/background_review.py – sub‑Agent creation details
review_agent = AIAgent(
    model=agent.model,
    max_iterations=16,               # prevent runaway loops
    quiet_mode=True,                 # silent mode
    api_mode=_parent_api_mode,
    api_key=_parent_runtime.get("api_key") or None,
    credential_pool=getattr(agent, "_credential_pool", None),
    parent_session_id=agent.session_id,
    skip_memory=True,                # skip external memory plugins
)
# whitelist only memory and skill tools
set_thread_tool_whitelist(
    {t["function"]["name"] for t in get_tool_definitions(enabled_toolsets=["memory", "skills"], quiet_mode=True)},
    deny_msg_fmt="Background review denied: {tool_name}. Only memory/skill tools allowed."
)

The “three inheritance + two isolation” pattern means the sub‑Agent inherits authentication credentials, model base URL, and cached system prompts, while isolation is enforced via the tool whitelist and skip_memory=True.

Prefix‑Cache Inheritance: Reducing Token Costs

A naïve review would re‑initialize an Agent, rebuild the system prompt (often thousands of tokens), and resend the full conversation history, incurring high token costs. Hermes instead inherits the parent’s cached system prompt:

# agent/background_review.py – cache inheritance
review_agent._cached_system_prompt = agent._cached_system_prompt
review_agent.session_start = agent.session_start
review_agent.session_id = agent.session_id

Because the system prompt is cached, the sub‑Agent only pays for the conversation‑history tokens ( messages). PR #17276 measured an end‑to‑end cost reduction of roughly 26%.

Review Prompt Selection: Memory, Skill, or Combined

# agent/background_review.py
if review_memory and review_skills:
    prompt = _COMBINED_REVIEW_PROMPT   # both memory and skill
elif review_memory:
    prompt = _MEMORY_REVIEW_PROMPT    # memory only
else:
    prompt = _SKILL_REVIEW_PROMPT     # skill only

The memory prompt focuses on “who the user is” and records personal info only if it merits inclusion. The skill prompt is more aggressive, urging the Agent to surface any learnable signal. Signal priority order:

User corrected style/format or expressed frustration.

New technical tricks or workarounds appeared.

Missing or erroneous skills were detected.

"A pass that does nothing is a missed learning opportunity, not a neutral outcome."

Four‑Step Skill‑Update Priority

When updating the skill store, Hermes follows a strict priority; creating a brand‑new skill is the last resort.

Priority (first satisfied step wins):
1. Update a skill already loaded in the current dialogue → patch it directly
2. Update an existing umbrella (class‑level) skill → locate via skills_list/skill_view and patch
3. Add supporting files under an existing skill (references/, templates/, scripts/)
4. Create a brand‑new class‑level umbrella skill (only if steps 1‑3 are inapplicable)

Example of adding a supporting file:

# tools/skill_manager_tool.py – write a supporting file
skill_manage(
    action="write_file",
    name="python-debugging",
    file_path="references/asyncio-pitfalls.md",
    file_content="# asyncio 常见坑

...",
)

The rationale is to avoid skill‑library fragmentation; deepening existing skills with richer documentation and assets is preferred over proliferating many tiny overlapping skills.

Result Reporting

After the background review finishes, Hermes scans the sub‑Agent’s tool results, deduplicates them, and prints a concise summary.

# agent/background_review.py – summarize actions
def summarize_background_review_actions(review_messages, prior_snapshot):
    existing_tool_call_ids = {msg.get("tool_call_id") for msg in prior_snapshot if msg.get("role") == "tool"}
    actions = []
    for msg in review_messages:
        if msg.get("role") != "tool":
            continue
        if msg.get("tool_call_id") in existing_tool_call_ids:
            continue
        data = json.loads(msg.get("content", "{}"))
        if data.get("success") and "created" in data.get("message", "").lower():
            actions.append(data["message"])
        elif data.get("success") and "updated" in data.get("message", "").lower():
            actions.append(data["message"])
    return actions
# main thread prints the deduped summary
if actions:
    summary = " · ".join(dict.fromkeys(actions))
    agent._safe_print(f"  💾 Self-improvement review: {summary}")

Example output:

💾 Self-improvement review: Skill 'python-debugging' updated · Memory updated

Common Pitfalls

Negative constraints in the skill store – Users may complain about a tool; the review Agent could record a prohibition that persists even after the tool is fixed. Hermes’ prompt explicitly forbids capturing negative claims.

Storing user preferences only in memory – Preferences like “no markdown tables” end up in MEMORY.md but not in the corresponding skill, causing the Agent to revert to markdown later. The correct approach is to record the preference in both memory and skill.

Missing downgrade in codex_app_server mode – Without downgrade, all tool calls are rejected, and the review writes nothing. The source handles this, but custom api_mode settings must respect it.

Dangerous command approval blocks the main thread – If the sub‑Agent invokes a command that requires user confirmation (e.g., input()), the daemon thread blocks forever. Hermes installs an automatic‑deny callback for such commands.

Exhausting the iteration budget – The sub‑Agent is limited to 16 tool‑call iterations. If it spends all iterations without producing output, the review ends empty. The prompt encourages completion within 3‑4 iterations; otherwise, check the iteration‑budget logs.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

AIprompt engineeringAgentHermesBackground ReviewFork Mechanism
James' Growth Diary
Written by

James' Growth Diary

I am James, focusing on AI Agent learning and growth. I continuously update two series: “AI Agent Mastery Path,” which systematically outlines core theories and practices of agents, and “Claude Code Design Philosophy,” which deeply analyzes the design thinking behind top AI tools. Helping you build a solid foundation in the AI era.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.