284 lines
9.8 KiB
Markdown
284 lines
9.8 KiB
Markdown
|
|
# Agent UI Schema and Event Wiring Implementation Plan
|
||
|
|
|
||
|
|
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||
|
|
|
||
|
|
**Goal:** 打通 agent 工具结果在实时事件与历史回放的一致渲染链路:SSE 实时带 UI,落库 content 存摘要,完整 UI schema 存 storage 并通过 metadata 回填。
|
||
|
|
|
||
|
|
**Architecture:** 后端在 `TOOL_CALL_RESULT` 持久化链路中引入“摘要 + 全量分离”策略:摘要写 `messages.content`,全量 payload 写对象存储,metadata 仅存索引路径;history 读取时按 metadata 反查 storage 回填 `ui`。前端复用现有 AG-UI 事件模型,实现 runs/events/history 三路统一映射到 `ChatListItem`,并补齐 step 事件渲染与 history 多模态渲染。
|
||
|
|
|
||
|
|
**Tech Stack:** FastAPI, SQLAlchemy, AgentScope runtime/events, Supabase Storage, Flutter (Bloc/Cubit), Dart models/tests, AG-UI events
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Task 1: Add Tool Summary Rule Engine (Backend)
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Create: `backend/src/core/agentscope/events/tool_result_summary.py`
|
||
|
|
- Test: `backend/tests/unit/core/agentscope/events/test_tool_result_summary.py`
|
||
|
|
|
||
|
|
**Step 1: Write the failing test**
|
||
|
|
|
||
|
|
```python
|
||
|
|
from core.agentscope.events.tool_result_summary import build_tool_content_summary
|
||
|
|
|
||
|
|
|
||
|
|
def test_calendar_write_summary() -> None:
|
||
|
|
text = build_tool_content_summary(
|
||
|
|
tool_name="calendar_write",
|
||
|
|
args={"title": "项目评审"},
|
||
|
|
result={"start_time": "明天 10:00"},
|
||
|
|
error=None,
|
||
|
|
)
|
||
|
|
assert text.startswith("已创建日程")
|
||
|
|
```
|
||
|
|
|
||
|
|
**Step 2: Run test to verify it fails**
|
||
|
|
|
||
|
|
Run: `uv run pytest backend/tests/unit/core/agentscope/events/test_tool_result_summary.py -q`
|
||
|
|
Expected: FAIL with import/module/function missing.
|
||
|
|
|
||
|
|
**Step 3: Write minimal implementation**
|
||
|
|
|
||
|
|
```python
|
||
|
|
def build_tool_content_summary(*, tool_name: str, args, result, error) -> str:
|
||
|
|
if error:
|
||
|
|
return f"{tool_name} 执行失败"
|
||
|
|
if tool_name == "calendar_write":
|
||
|
|
return "已创建日程"
|
||
|
|
return f"{tool_name} 执行完成"
|
||
|
|
```
|
||
|
|
|
||
|
|
**Step 4: Run test to verify it passes**
|
||
|
|
|
||
|
|
Run: `uv run pytest backend/tests/unit/core/agentscope/events/test_tool_result_summary.py -q`
|
||
|
|
Expected: PASS.
|
||
|
|
|
||
|
|
**Step 5: Extend tests for all rules and refactor**
|
||
|
|
|
||
|
|
Add cases for `calendar_read/calendar_delete/calendar_share/user_resolve/error/fallback/truncation` and implement full rule table.
|
||
|
|
|
||
|
|
**Step 6: Commit**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
git add backend/src/core/agentscope/events/tool_result_summary.py backend/tests/unit/core/agentscope/events/test_tool_result_summary.py
|
||
|
|
git commit -m "feat: add deterministic tool result summary engine"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Task 2: Persist Full Tool Payload to Storage and Keep Content Lightweight
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Modify: `backend/src/core/agentscope/events/store.py`
|
||
|
|
- Test: `backend/tests/unit/core/agentscope/events/test_store.py`
|
||
|
|
|
||
|
|
**Step 1: Write the failing tests**
|
||
|
|
|
||
|
|
Add tests asserting:
|
||
|
|
- `TOOL_CALL_RESULT` persists summary to `content`.
|
||
|
|
- metadata includes `storage_bucket/storage_path/tool_call_id`.
|
||
|
|
- uploaded payload includes full `ui/args/result/error`.
|
||
|
|
|
||
|
|
**Step 2: Run targeted tests (RED)**
|
||
|
|
|
||
|
|
Run: `uv run pytest backend/tests/unit/core/agentscope/events/test_store.py -q`
|
||
|
|
Expected: FAIL on new assertions.
|
||
|
|
|
||
|
|
**Step 3: Implement minimal storage write path**
|
||
|
|
|
||
|
|
In `_persist_tool_call_result`:
|
||
|
|
- build `full_payload` from event fields.
|
||
|
|
- call summary engine for `content`.
|
||
|
|
- upload payload via tool result storage (inject dependency if needed).
|
||
|
|
- store only path/index in metadata.
|
||
|
|
|
||
|
|
**Step 4: Run tests (GREEN)**
|
||
|
|
|
||
|
|
Run: `uv run pytest backend/tests/unit/core/agentscope/events/test_store.py -q`
|
||
|
|
Expected: PASS.
|
||
|
|
|
||
|
|
**Step 5: Add fallback test and implementation**
|
||
|
|
|
||
|
|
Add case where storage upload fails but tool message still persists with summary and no crash.
|
||
|
|
|
||
|
|
**Step 6: Commit**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
git add backend/src/core/agentscope/events/store.py backend/tests/unit/core/agentscope/events/test_store.py
|
||
|
|
git commit -m "feat: store tool payload in object storage with metadata index"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Task 3: Hydrate History Tool UI from Metadata Storage Path
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Modify: `backend/src/v1/agent/repository.py`
|
||
|
|
- Test: `backend/tests/unit/v1/agent/test_repository.py`
|
||
|
|
|
||
|
|
**Step 1: Write failing tests**
|
||
|
|
|
||
|
|
Add/adjust assertions:
|
||
|
|
- history tool payload resolves `ui` from storage payload.
|
||
|
|
- when storage missing, fallback to `messages.content` summary.
|
||
|
|
|
||
|
|
**Step 2: Run tests (RED)**
|
||
|
|
|
||
|
|
Run: `uv run pytest backend/tests/unit/v1/agent/test_repository.py -q`
|
||
|
|
Expected: FAIL on `ui` hydration and fallback assertions.
|
||
|
|
|
||
|
|
**Step 3: Implement minimal hydration logic**
|
||
|
|
|
||
|
|
In `_to_snapshot_message` for tool role:
|
||
|
|
- read storage via `metadata.storage_bucket/storage_path`.
|
||
|
|
- map hydrated payload fields to snapshot (`ui`, `content`, `toolCallId`).
|
||
|
|
- keep safe fallback when storage read fails.
|
||
|
|
|
||
|
|
**Step 4: Run tests (GREEN)**
|
||
|
|
|
||
|
|
Run: `uv run pytest backend/tests/unit/v1/agent/test_repository.py -q`
|
||
|
|
Expected: PASS.
|
||
|
|
|
||
|
|
**Step 5: Commit**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
git add backend/src/v1/agent/repository.py backend/tests/unit/v1/agent/test_repository.py
|
||
|
|
git commit -m "fix: hydrate tool ui from metadata storage in history snapshots"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Task 4: Keep SSE TOOL_CALL_RESULT Compatible with Existing Frontend Parsing
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Modify: `backend/src/core/agentscope/runtime/agent_route_runtime.py`
|
||
|
|
- Test: `backend/tests/unit/core/agentscope/runtime/test_agent_route_runtime.py`
|
||
|
|
|
||
|
|
**Step 1: Write failing test**
|
||
|
|
|
||
|
|
Add assertion that emitted `TOOL_CALL_RESULT` data contains expected renderable fields (`callId/toolName/result/error` and `ui` path from result payload).
|
||
|
|
|
||
|
|
**Step 2: Run tests (RED)**
|
||
|
|
|
||
|
|
Run: `uv run pytest backend/tests/unit/core/agentscope/runtime/test_agent_route_runtime.py -q`
|
||
|
|
Expected: FAIL on missing/incorrect payload fields.
|
||
|
|
|
||
|
|
**Step 3: Implement minimal payload normalization**
|
||
|
|
|
||
|
|
Normalize tool result event payload so frontend can keep current parsing without contract breaks.
|
||
|
|
|
||
|
|
**Step 4: Run tests (GREEN)**
|
||
|
|
|
||
|
|
Run: `uv run pytest backend/tests/unit/core/agentscope/runtime/test_agent_route_runtime.py -q`
|
||
|
|
Expected: PASS.
|
||
|
|
|
||
|
|
**Step 5: Commit**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
git add backend/src/core/agentscope/runtime/agent_route_runtime.py backend/tests/unit/core/agentscope/runtime/test_agent_route_runtime.py
|
||
|
|
git commit -m "fix: preserve frontend-compatible tool result event payload"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Task 5: Wire Frontend History + Events to Unified Rendering Path
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Modify: `apps/lib/features/chat/data/services/ag_ui_service.dart`
|
||
|
|
- Modify: `apps/lib/features/chat/presentation/bloc/chat_bloc.dart`
|
||
|
|
- Modify: `apps/lib/features/chat/data/models/tool_result.dart`
|
||
|
|
- Modify: `apps/lib/features/home/ui/screens/home_screen.dart`
|
||
|
|
- Test: `apps/test/features/chat/ag_ui_service_test.dart`
|
||
|
|
- Create/Modify: `apps/test/features/chat/chat_bloc_test.dart`
|
||
|
|
|
||
|
|
**Step 1: Write failing tests**
|
||
|
|
|
||
|
|
Add tests asserting:
|
||
|
|
- history tool message with `ui` becomes `ToolResultItem`.
|
||
|
|
- SSE `TOOL_CALL_RESULT` with `ui` renders same item shape.
|
||
|
|
- attachments in history user message are mapped for multimodal rendering.
|
||
|
|
|
||
|
|
**Step 2: Run tests (RED)**
|
||
|
|
|
||
|
|
Run: `cd apps && flutter test test/features/chat/ag_ui_service_test.dart`
|
||
|
|
Expected: FAIL on new mapping assertions.
|
||
|
|
|
||
|
|
**Step 3: Implement minimal mapping changes**
|
||
|
|
|
||
|
|
- In service/bloc, unify history and event mapping into same conversion path.
|
||
|
|
- Keep existing `UiSchemaRenderer` input format untouched.
|
||
|
|
- Ensure fallback to content text when `ui` missing.
|
||
|
|
|
||
|
|
**Step 4: Run tests (GREEN)**
|
||
|
|
|
||
|
|
Run: `cd apps && flutter test test/features/chat/ag_ui_service_test.dart`
|
||
|
|
Expected: PASS.
|
||
|
|
|
||
|
|
**Step 5: Commit**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
git add apps/lib/features/chat/data/services/ag_ui_service.dart apps/lib/features/chat/presentation/bloc/chat_bloc.dart apps/lib/features/chat/data/models/tool_result.dart apps/lib/features/home/ui/screens/home_screen.dart apps/test/features/chat/ag_ui_service_test.dart apps/test/features/chat/chat_bloc_test.dart
|
||
|
|
git commit -m "feat: unify realtime and history tool card rendering"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Task 6: Add Step Event Rendering for Intent/Execution/Report
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Modify: `apps/lib/features/chat/presentation/bloc/chat_bloc.dart`
|
||
|
|
- Modify: `apps/lib/features/home/ui/screens/home_screen.dart`
|
||
|
|
- Test: `apps/test/features/chat/chat_bloc_test.dart`
|
||
|
|
|
||
|
|
**Step 1: Write failing test**
|
||
|
|
|
||
|
|
Add test verifying `STEP_STARTED/STEP_FINISHED` transitions produce visible stage state.
|
||
|
|
|
||
|
|
**Step 2: Run tests (RED)**
|
||
|
|
|
||
|
|
Run: `cd apps && flutter test test/features/chat/chat_bloc_test.dart`
|
||
|
|
Expected: FAIL on missing stage state.
|
||
|
|
|
||
|
|
**Step 3: Implement minimal state and UI**
|
||
|
|
|
||
|
|
- Track current stage enum in `ChatState`.
|
||
|
|
- Render compact stage progress row in chat screen.
|
||
|
|
|
||
|
|
**Step 4: Run tests (GREEN)**
|
||
|
|
|
||
|
|
Run: `cd apps && flutter test test/features/chat/chat_bloc_test.dart`
|
||
|
|
Expected: PASS.
|
||
|
|
|
||
|
|
**Step 5: Commit**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
git add apps/lib/features/chat/presentation/bloc/chat_bloc.dart apps/lib/features/home/ui/screens/home_screen.dart apps/test/features/chat/chat_bloc_test.dart
|
||
|
|
git commit -m "feat: render agent step progress from AG-UI events"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Task 7: Verification Gate (Backend + Frontend)
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Modify (if needed): `docs/plans/2026-03-11-agent-multimodal-smoke-runbook.md`
|
||
|
|
|
||
|
|
**Step 1: Run backend targeted tests**
|
||
|
|
|
||
|
|
Run: `uv run pytest backend/tests/unit/core/agentscope/events/test_tool_result_summary.py backend/tests/unit/core/agentscope/events/test_store.py backend/tests/unit/v1/agent/test_repository.py backend/tests/unit/core/agentscope/runtime/test_agent_route_runtime.py -q`
|
||
|
|
Expected: PASS.
|
||
|
|
|
||
|
|
**Step 2: Run frontend targeted tests**
|
||
|
|
|
||
|
|
Run: `cd apps && flutter test test/features/chat/ag_ui_service_test.dart test/features/chat/chat_bloc_test.dart`
|
||
|
|
Expected: PASS.
|
||
|
|
|
||
|
|
**Step 3: Run backend quality checks**
|
||
|
|
|
||
|
|
Run: `uv run ruff check backend/src backend/tests`
|
||
|
|
Expected: PASS.
|
||
|
|
|
||
|
|
**Step 4: Run backend type checks**
|
||
|
|
|
||
|
|
Run: `uv run basedpyright`
|
||
|
|
Expected: 0 errors.
|
||
|
|
|
||
|
|
**Step 5: Update runbook evidence**
|
||
|
|
|
||
|
|
Record changed contract, test evidence, and known follow-ups.
|
||
|
|
|
||
|
|
**Step 6: Commit**
|
||
|
|
|
||
|
|
```bash
|
||
|
|
git add docs/plans/2026-03-11-agent-multimodal-smoke-runbook.md
|
||
|
|
git commit -m "docs: record tool ui schema storage and rendering verification"
|
||
|
|
```
|