Date: 2026-07-15 Status: approved for implementation planning
In dual_agent mode, ChatAgent selects EventAgent capabilities by appending
event names inside an <agent_events> block after its visible reply. The
current generated prompt explains the catalog and output format, but treats
event selection as optional and does not define when an event is required.
Recent local audit evidence shows the practical failure mode: ChatAgent says it
will adjust volume or check weather, but emits no event. Examples include
声音调到90, 调大一点音量, and later weather requests. The model therefore
produces a plausible conversational promise without causing the real action.
The generated prompt currently exists in two equivalent construction paths:
ToolRegistry.chat_event_system_message() supplies the runtime fallback for
API clients that did not submit an EventPrompt.buildAvailableEventsPrompt() generates the read-only EventPrompt shown and
submitted by the browser console.Both paths must retain the same behavioral contract. The text-event parser and
the <agent_events> wire format are already stable and are not the source of
the observed missed triggers.
Replace the weak optional wording with explicit selection rules, consistency invariants, and compact positive and negative examples. Keep the current protocol and model-call count.
This is the selected approach because it directly addresses the observed failure, has the smallest runtime scope, and can be evaluated independently before introducing new architecture.
Expose trigger and confirmation metadata through the event catalog and render event-specific policy dynamically. This scales better for a large heterogeneous registry, but requires domain, catalog API, frontend, and compatibility changes. It remains a possible follow-up if prompt-only evaluation shows that generic rules are insufficient.
Run a dedicated classification step before ChatAgent response generation. This could improve isolation but adds latency, token cost, another failure mode, and coordination complexity. It is not justified before measuring the prompt-only approach.
The generated EventPrompt will identify itself as protocol version 2 and use imperative rules. Its dynamic catalog remains name plus description only.
The prompt must communicate these rules:
声音太小了 can select device.volume.adjust, and
明天天气怎么样 can select knowledge.web.search.The generated shape will remain:
Event selection protocol v2.
Available events:
- event.name: description
...
[decision rules and compact examples]
First write the user-facing reply normally. If one or more events are selected,
append exactly this block after the visible reply:
<agent_events>
event_name
</agent_events>
Use exact available event names only, one per line. Do not include parameters.
The prompt will include a small, generic set of examples that match the current production-shaped capabilities without teaching argument extraction:
| User intent | Expected selection behavior |
|---|---|
声音太小了 |
Reply naturally and emit device.volume.adjust. |
声音调到90 |
Reply naturally and emit device.volume.adjust. |
明天天气怎么样 |
Reply naturally and emit knowledge.web.search. |
你回复太慢了 |
No event; response latency is not device volume. |
你能调音量吗 |
No event; this asks about capability rather than requesting action. |
提醒我开会 |
Ask for missing scheduling details and emit no event. |
我明天下午三点要开会 |
No event; this is a statement unless the user requests scheduling. |
| A complete request to create a schedule | Emit calendar.schedule.create. |
| An explicit goodbye or session-ending request | Emit session.terminate. |
| Ordinary greeting or emotional conversation | No event. |
Examples are selection guidance only. EventAgent remains responsible for arguments and validation.
The browser continues to generate a read-only EventPrompt from selected tools.
In dual_agent, exactly one managed EventPrompt is serialized. In
chat_agent_tools, generated EventPrompt content remains excluded and provider
tool schemas remain the only capability-selection surface.
The runtime continues to inject its registry-generated prompt only when no event instructions are already present. The Python and JavaScript prompt builders will use the same version marker, rule ordering, examples, catalog format, and output suffix.
isGeneratedEventPromptContent() must recognize both:
You may request EventAgent work using this text protocol.;Event selection protocol v2. prefix.Recognition remains conservative: a known prefix alone is insufficient. The content must also contain the event block markers and the exact-name/no- parameters instruction. This preserves cleanup of historical generated prompts without deleting ordinary custom prompts that merely mention event syntax.
Implementation will use TDD.
Deterministic tests will first fail against the current prompt and then prove:
dual_agent;Focused verification commands will cover the prompt/runtime tests and frontend source/behavior tests, followed by JavaScript syntax, the full suite, app import, and diff checks.
If a local live provider target and API key are available, run a fresh A/B evaluation with the same model and parameters. At minimum, evaluate the ten decision examples above across old and new prompts and record both missed required events and false positives. Existing historical SQLite sessions are evidence for the problem but must not be reused as an A/B input source because their accumulated context and prompt order differ.