Skip to content

fix: keep per-wakeup content out of proactive agent system prompts - #9398

Open
shentry wants to merge 1 commit into
AstrBotDevs:masterfrom
shentry:fix/8473-cron-prompt-cache-order
Open

fix: keep per-wakeup content out of proactive agent system prompts#9398
shentry wants to merge 1 commit into
AstrBotDevs:masterfrom
shentry:fix/8473-cron-prompt-cache-order

Conversation

@shentry

@shentry shentry commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Closes #8473

Problem

Both proactive wakeup paths assembled req.system_prompt like this before calling build_main_agent:

req.system_prompt += "...previous conversation history:\n---\n{dump}\n---\n"   # changes every wakeup
req.system_prompt += WOKE_SYSTEM_PROMPT.format(cron_job=cron_job_str)          # changes every wakeup

build_main_agent then appends the persona, skills and tool declarations to that same string. So the whole static tail sits behind content that differs on every single run, the prefix cache can never match, and the cron runs in the issue report cached_tokens=None.

The issue reports the cron path. The background-task-result path in astr_agent_tool_exec.py had the identical construction and the identical problem, so this PR fixes both — fixing only one would have left the same bug in place one file over.

Change

Split the two wake prompts into a static system half and a dynamic user half, and move the per-run content into the user turn:

before after
static wake rules system prompt system prompt (unchanged)
cron job / background task payload system prompt user prompt
conversation history dump system prompt user prompt

The static rules deliberately stay in the system prompt, so the instructions keep their system role and "Proceed according to your system instructions" in the user turn stays accurate. Only the parts that actually vary move.

Result: for a given session the system prompt is byte-identical across wakeups, so persona + tools + rules form a stable cacheable prefix, and everything that varies rides in the last message where it was never cacheable anyway.

Tests

Added TestWokeMainAgentPromptCaching to tests/unit/test_cron_manager.py, which drives _woke_main_agent and captures the ProviderRequest handed to build_main_agent:

  • conversation history reaches req.prompt, not req.system_prompt
  • cron job payload reaches req.prompt, not req.system_prompt
  • two wakeups differing in both history and payload produce an identical system_prompt (and different prompt)
  • the static wake rules are still in the system prompt
  • no history section is emitted for a fresh conversation

The first three fail against the previous implementation and pass with this change.

  • pytest tests/unit/ -> 734 passed
  • ruff format --check / ruff check (0.15.22) on the four touched files -> clean

Note on the full suite: pytest tests/ has 3 pre-existing failures in tests/test_dashboard.py (test_t2i_*), and ruff format --check flags 7 pre-existing test files. I verified both are present on a clean master checkout without my changes and are unrelated to this PR.

Follow-up not done here

The cron system prompt still starts with the wake rules, so its prefix differs from a normal chat turn's and the two paths cache separately. Sharing one prefix would mean letting build_main_agent append the wake rules after the persona/tool blocks, which is a bigger refactor than this fix needs. Happy to do it in a follow-up if you want it.

Summary by Sourcery

Ensure proactive agent wakeups keep dynamic per-run data out of the system prompt so provider prefix caching can be reused across runs.

Enhancements:

  • Refine cron wakeup and background task wakeup prompt construction to split static system instructions from dynamic user content, improving prefix cache effectiveness.
  • Centralize reusable user prompt templates for cron job context, background task context, and conversation history in astr_main_agent_resources for consistent behavior.

Tests:

  • Add unit tests around cron wakeup requests to assert history and job payload are carried in the user prompt, the system prompt remains static across wakeups, and no history section is emitted for empty conversations.

Both proactive wakeup paths built the system prompt by appending the
conversation history dump and the triggering payload before calling
build_main_agent. build_main_agent then appends the persona, skills and
tool declarations to that same string, so every static block ended up
behind content that changes on every single wakeup. The provider's prefix
cache could never match and cron runs reported cached_tokens=None.

Split the two wake prompts into a static system half and a dynamic user
half, and move the conversation history and the job payload into the user
turn:

- cron/manager.py, the scheduled wakeup reported in the issue.
- astr_agent_tool_exec.py, the background-task-result wakeup, which had the
  same construction and the same problem.

The static wake rules stay in the system prompt, so instructions keep their
system role and only the per-run parts move. The system prompt is now
byte-identical across wakeups of the same session.

Tests cover that the history and the payload reach req.prompt but not
req.system_prompt, that two wakeups differing in both still produce an
identical system prompt, and that the static rules are still present.

Closes AstrBotDevs#8473
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. area:core The bug / feature is about astrbot's core, backend labels Jul 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core The bug / feature is about astrbot's core, backend size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] 主动唤醒机制中prompt拼接顺序导致缓存命中率为0

1 participant