feat(runtime): add planner reflection history with rationale and thinking
Tests / Test failed: 2, passed: 849

- ToolCallDecision captures thinking blocks and pre-tool text output
- AnthropicToolCallingClient supports optional extended thinking (budget_tokens + beta header)
- PlannedStep carries rationale and thinking from each LLM decision
- WorldEvent replaces scene_summary with rationale/thinking/page fields (backward-compatible)
- AI planner system prompt instructs reflection before each tool call
- _history_summary() emits compact {page, rationale, action, success} dicts
- Cloud DB migration 0011 adds nullable rationale/thinking columns to planner_decision_log
- OpenAI client extracts reasoning_content into thinking field
This commit is contained in:
2026-07-15 12:43:22 +08:00
parent 96e403ee47
commit a5aeb8889c
26 changed files with 903 additions and 25 deletions
@@ -0,0 +1,36 @@
## MODIFIED Requirements
### Requirement: Bounded history of recent scene/action pairs
The system SHALL maintain `WorldState.history` as a fixed-size, bounded collection of the most recent per-step records, automatically evicting the oldest entry when a new entry is added past the configured bound. Each history record SHALL store the action name, success flag, page context (from `WorldState.current_page` at the time of recording), and optional rationale and thinking fields sourced from the executed `PlannedStep`. The `scene_summary` field SHALL be retained as an optional field for backward compatibility but SHALL NOT be required for new entries.
#### Scenario: WorldState survives across steps within a task
- **WHEN** a task executes multiple steps in sequence
- **THEN** the `WorldState` object associated with the task is the same object (or reflects continuously accumulated updates) across those steps, not reset between steps
#### Scenario: WorldState is scoped to a single task
- **WHEN** two different tasks run (sequentially or concurrently) against the same or different devices
- **THEN** each task has its own independent `WorldState`, and neither task's `WorldState` reflects the other task's app/page/variables/history
#### Scenario: History record includes rationale when planner provides it
- **WHEN** the executed `PlannedStep` carries a non-None `rationale`
- **THEN** the resulting `WorldEvent` stores that rationale string
#### Scenario: History record stores None rationale when planner does not provide one
- **WHEN** the executed `PlannedStep` has `rationale=None`
- **THEN** the resulting `WorldEvent` stores `rationale=None` without error
#### Scenario: History record includes thinking when planner provides it
- **WHEN** the executed `PlannedStep` carries a non-None `thinking`
- **THEN** the resulting `WorldEvent` stores that thinking string
#### Scenario: History record captures current page at time of recording
- **WHEN** a step is appended to history and `WorldState.current_page` is non-None at that moment
- **THEN** `WorldEvent.page` is set to that page value
#### Scenario: History record stores None page when current_page is unavailable
- **WHEN** a step is appended to history and `WorldState.current_page` is None
- **THEN** `WorldEvent.page` is `None` without error
#### Scenario: History size remains bounded
- **WHEN** steps are appended beyond the configured history bound
- **THEN** the oldest entries are evicted so history size never exceeds the bound