fischer-agentkit/docs/plans/2026-07-02-002-fix-transien...

352 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
date: 2026-07-02
type: fix
title: 修复私董会 transient state 残留 + ReAct 工具调用引导不足
status: in-progress
---
## Summary
收尾两个独立 bug(1) 前端 store-level transient state`boardState` / `debateState` / `collaborationState`)在 `createConversation` / `selectConversation` / `deleteConversation` 三个动作下的重置口径不一致,导致新建对话后私董会顶部标题残留、跨会话状态泄漏;(2) ReAct 引擎 `_build_tool_use_prompt` 规则 3 "如果不需要工具就能回答,直接回答即可" 给 LLM 留出偷懒窗口,且工具调用提示被后置的 tool section 覆盖,导致复杂需求(涉及外部数据 / 多步分析)下 LLM 倾向于直接回答而非调用 `web_search` / `baidu_search`。Bug 1 覆盖前端 3 个 action 路径对称重置Bug 2 仅做 L0提示规则调整L1工具描述扩展与 L2PLAN_EXEC 启用)按用户决策拆为独立 plan。
## Progress
| Unit | 状态 | 验证 | 提交 |
|------|------|------|------|
| U1 createConversation 重置 | done | 5 前端单测 pass | 7376005 |
| U2 selectConversation 条件重置 | done | 5 前端单测 pass | 7376005 |
| U3 deleteConversation 补全 | done | 5 前端单测 pass | 7376005 |
| U4 ReAct prompt 规则重排 | done | 6 后端单测 pass | 7376005 |
| U5 端到端验证测试 | done | 11 单测全 pass | 7376005 |
| U6 Bug 2 L4 真实 LLM smoke test | pending | — | — |
| U7 工作树未提交变更清理 | pending | — | — |
PR: http://8.153.107.96/gitea/fischer/fischer-agentkit/pulls/17
ce-code-review: mode:agent, 无 actionable findings
ce-test-browser: agent-browser 已安装,待 U6 阶段执行
## Problem Frame
**Bug 1私董会顶部标题在新对话后残留**
根因([chatStore.ts:333-345](src/agentkit/server/frontend/src/stores/chatStore.ts#L333-L345)`createConversation()` 仅清空 `streamingSteps`**未重置** `stream.boardState.value` / `debateState.value` / `collaborationState.value`。`StickyModeHeader.vue:113-117` 的 `mode` computed 依赖 `chatStore.boardState` 渲染"私董会"模式(带旧专家头像行),新对话切换时状态未清空 → 旧私董会标题持续显示。
同源问题散落三处:
- `createConversation` (chatStore.ts:333-345) — 三个 state 全漏
- `selectConversation` (chatStore.ts:219-330) — 仅在 404 分支line 266-267重置 board/debate主流程 line 222 仅重置 collaboration正常切换不重置 board/debate
- `deleteConversation` (chatStore.ts:348-372) — line 364-365 重置 board/debate但**漏**了 `collaborationState`
**Bug 2Agent 面对复杂需求时倾向直接回答**
根因([react.py:1605-1616](src/agentkit/core/react.py#L1605-L1616)`_build_tool_use_prompt` 拼接的规则 3
```
3. 如果不需要工具就能回答,直接回答即可
```
此规则是 LLM 偷懒的合法依据。`base_prompt`[server/app.py:200-206](src/agentkit/server/app.py#L200-L206))已有正向引导"当你不确定事实信息、时事新闻或任何你不确信的话题时,你必须先使用搜索工具",但位置在 system prompt **前部**。**假设**:后注入的 tool section 可能在注意力分配上弱化了 base_prompt 的正向引导(此假设未经 ablation 验证,但规则重排本身的风险可控 — 见 KTD。`web_search` / `baidu_search` 工具描述也无"何时使用"的触发条件。
**预测Bug 1**:若在 `createConversation` 末尾加 `stream.boardState.value = null; stream.debateState.value = null; stream.collaborationState.value = null;``StickyModeHeader` 的 `mode` 返回 `null``v-if="mode"` 不渲染 → 标题消失;同源问题在 `selectConversation` / `deleteConversation` 也应统一口径。
**预测Bug 2**:若规则 3 措辞改为"涉及外部信息、实时数据、多步骤分析或你不确定的事实时必须使用工具;仅在确实无需工具时可直接回答",且**重排为规则 1**(先说"何时必须",再列"何时不必"LLM 面对"GitHub Trending + 商业价值分析"类需求时调用 `web_search` 的概率显著提升(实证需 L4 真实 LLM smoke test 验证)。
## Requirements
**R1**Bug 1`createConversation` 末尾重置 `stream.boardState.value = null; stream.debateState.value = null; stream.collaborationState.value = null;`(与现有 `stream.clearConvSteps` 顺序一致,先 stream-owned transient 后 streaming 步骤清理,避免响应式 watcher 误触发)
**R2**Bug 1`selectConversation` 在切换到不同 conversation 时(即非 404 分支、亦非 `selectConversation(sameId)`),替换 line 222 的无条件 `collaborationState` 重置为顶部条件性三 state 重置(仅当 `prevConvId !== id` 时触发 `boardState` / `debateState` / `collaborationState` 置 null避免 force-reload 同一会话时误清空状态
**R3**Bug 1`deleteConversation` 删除后切换到下一个会话时,若当前会话非 `currentConversationId`,三个 state 不动;若当前会话被删,三个 state 全部置 null包括 `collaborationState`),与 `selectConversation` 口径一致
**R4**Bug 2 L0`_build_tool_use_prompt` 规则重排为:
```
1. 涉及外部信息、实时数据、多步骤分析或你不确定的事实时必须使用工具
2. 每次只调用一个工具
3. 等待工具返回结果后再决定下一步
4. 仅在确实无需工具时可直接回答
5. 不要在回答中重复工具的输出,而是基于结果给出有用的总结
```
**R5**Bug 2 L0保持 base_prompt 不动(按用户决策 L1/L2 拆为独立 plan
**R6**Bug 2 L0不在本次改 web_search / baidu_search 工具 description
## Key Technical Decisions
- **三个 transient state 的重置时机选择**:选在 `createConversation` / `selectConversation` / `deleteConversation` action 内统一重置(而非在 `useChatStream` 初始化时按 conversationId 拆分 ref理由是当前 `useChatStream` 是 store-level 单例stream-owned state 全部是单一 ref。重构为"按 conversationId 拆分的 reactive map"是更大的架构变更(影响所有读取 `stream.boardState` 的组件),不在本 plan 范围内
- **selectConversation 切换检测**:以 `currentConversationId` 是否变化为重置条件(即 `id !== currentConversationId.value` 才重置),避免 `selectConversation(sameId, true)` 这种 force-reload 误清空
- **createConversation 中重置顺序**:先 transient stateboard/debate/collaboration`clearConvSteps`。理由transient state 派生渲染StickyModeHeader、CollaborationGraph清空它会触发对应组件卸载`clearConvSteps` 删除 streaming 步骤,触发 streaming UI 收尾。先 transient 后 streaming 保证 UI 状态先稳定再清理流
- **R4 规则重排而非删除**:保留原规则 3 的语义("无需工具时可直接回答")作为规则 4仅位置后移 + 措辞收敛。**理论选择**:本 plan 的修复理论是"位置优先" — 在同一 prompt block 内,靠前的规则获得更多注意力权重。此理论未经 ablation 验证但规则重排的回归风险可控trivial 输入走 DIRECT_CHAT 不进 ReAct。保留 rule 4 的真实理由:非 trivial 但无需外部工具的输入(如"总结这段文字"、"改写这段话")会进入 ReAct 循环,此类输入不应被强制工具化,需要一个 escape hatch。"你好/介绍下自己"类 L1 trivial 输入已走 DIRECT_CHAT不构成保留理由
- **不动 web_search / baidu_search description**:按用户决策推迟到独立 plan。L0 调整后若真实 LLM 行为改善有限再决定 L1
- **不在 L0 引入 PhasePolicy / PLAN_EXEC**L2启用 PLAN_EXEC 让复杂需求先输出计划再执行)按用户决策拆为独立 plan
- **测试策略**Bug 1 用 vitest 单元测试覆盖三个 action 的状态重置矩阵Bug 2 L0 用 pytest 单元测试断言 `_build_tool_use_prompt` 输出文本包含新规则 + 不包含旧规则 3 措辞;不强制跑真实 LLM依赖 API key 与网络,且不稳定),但写一个 mock-based test 验证 web_search 描述出现在 prompt 中
- **Bug 2 验收门槛**L0 文本断言通过后Bug 2 状态标记为"hypothesis applied, pending L4 verification"(非"fixed")。在 L1/L2 独立 plan 中包含真实 LLM smoke test用 5 个 probe query如"GitHub Trending 分析"、"最新 AI 新闻"等),对比 fix 前后 web_search 调用率,目标 ≥4/5 触发工具调用。L0 plan 不包含此 smoke test 但在 Verification 中显式记录此降级状态
## Implementation Units
### U1. createConversation 补全 transient state 重置
**Goal:** 修复 Bug 1 的第一处泄漏点 — `createConversation` 创建新会话时同步重置 `boardState` / `debateState` / `collaborationState` 三个 stream-owned ref。
**Requirements:** R1
**Dependencies:**
**Files:**
- src/agentkit/server/frontend/src/stores/chatStore.ts修改 line 333-345 `createConversation`
- src/agentkit/server/frontend/tests/unit/stores/chatStore.test.ts追加测试
**Approach:**
-`createConversation``stream.clearConvSteps(newConversation.id)` 之前插入三行:
```ts
stream.boardState.value = null;
stream.debateState.value = null;
stream.collaborationState.value = null;
```
- 不动 `stream.collaborationState` 已有逻辑line 222 在 `selectConversation` 顶部重置)
- 不动 `pendingConversations` / `pendingLastUsedAt`(与本 bug 无关)
**Patterns to follow:**
- `chatStore.ts:264-281` 的 404 fallback 中 `stream.boardState.value = null; stream.debateState.value = null;` 的写法line 266-267— 同样模式
- `chatStore.ts:222``stream.collaborationState.value = null;` — 单行重置
**Test scenarios:**
- Happy path`createConversation()` 后,`chatStore.boardState === null` 且 `chatStore.debateState === null``chatStore.collaborationState === null`
- Edge case`selectConversation` 一个含 board_started 的旧会话boardState 非 null`createConversation()` → 三个 state 全为 null
- Edge case`createConversation()` 后 `currentConversationId` 指向新会话 ID且新会话的 streamingStepsByConv entry 被清空(已有 `stream.clearConvSteps` 行为,回归测试)
**Verification:** `cd src/agentkit/server/frontend && npm run test:unit -- --reporter=verbose 2>&1 | grep -E "chatStore|transient"` 通过;手动验证:先开 @board再点新建对话StickyModeHeader 不再显示私董会专家头像行
### U2. selectConversation 统一 transient state 重置口径
**Goal:** 修复 Bug 1 的第二处泄漏点 — `selectConversation` 从带 boardState 的会话切到其他会话时残留旧 boardState同时与 `createConversation` / `deleteConversation` 口径对齐。
**Requirements:** R2
**Dependencies:** 无(与 U1 并行可合入)
**Files:**
- src/agentkit/server/frontend/src/stores/chatStore.ts修改 line 219-330 `selectConversation`
- src/agentkit/server/frontend/tests/unit/stores/chatStore.test.ts追加测试
**Approach:**
-`selectConversation` 顶部 line 220 `currentConversationId.value = id` 之后,判断是否切换到不同会话:
```ts
const isSwitching = currentConversationId.value !== id; // 注意line 220 已写入 id此处判断为切换条件需对比旧值
```
line 220 已先写入新 id所以需要先把旧 id 缓存到临时变量,再 line 220 写入新 id再判断
- 若 isSwitching 为 true 且新会话不含 board_started`restoreBoardStateFromMessages` 返回 null三个 state 置 null
- 由于后续 line 307 `stream.boardState.value = restoreBoardStateFromMessages(...)` 会覆盖,所以"先置 null 再被覆盖"是安全的
- 但若旧会话的 boardState 包含 stream-derived 数据(如 liveColorByName可能丢失 — 实际上 boardState.value 被整体覆盖为新对象,旧 stream-derived map 在 `allExperts` computed 中会基于新 boardState 重新构建([StickyModeHeader.vue:160-184](src/agentkit/server/frontend/src/components/chat/StickyModeHeader.vue#L160-L184)),所以无副作用
- 简化方案:直接删除 line 222 的 `stream.collaborationState.value = null;` 无条件重置,替换为 line 219 顶部统一三行:
```ts
const prevConvId = currentConversationId.value;
currentConversationId.value = id;
if (prevConvId !== id) {
stream.boardState.value = null;
stream.debateState.value = null;
stream.collaborationState.value = null;
}
```
- 保留 404 分支line 258-281的现有逻辑404 时 `boardState` / `debateState` 也置 nullline 266-267与主流程口径一致
**Patterns to follow:**
- `chatStore.ts:222` 已有无条件 `collaborationState` 重置 — 升级为条件性三 state 重置
- 404 分支 line 264-281 的多 state 重置 + 切换到下一个会话模式
**Test scenarios:**
- Happy path从有 boardState 的会话 A 切到会话 B无 boardState`boardState === null` `debateState === null` `collaborationState === null`
- Edge case从会话 A 切回 Aforce-reload 同一 id→ 三个 state 保持原值(不被无脑清空)
- Edge case从会话 A 切到会话 B也无 boardState→ 三个 state 保持 null无变化也无副作用
- Edge case404 后 `createConversation()` 流程(已有 fallback 测试)— 三个 state 全 null
**Verification:** `cd src/agentkit/server/frontend && npm run test:unit` 通过;手动验证:开 @board再点另一普通会话StickyModeHeader 切到普通模式(不显示私董会头像)
### U3. deleteConversation 补全 collaborationState 重置
**Goal:** 修复 Bug 1 的第三处泄漏点 — `deleteConversation` 删除当前会话时漏了 `collaborationState` 重置,与其他两个 action 口径对齐。
**Requirements:** R3
**Dependencies:** 无(与 U1/U2 并行可合入)
**Files:**
- src/agentkit/server/frontend/src/stores/chatStore.ts修改 line 362-371 `deleteConversation` 分支)
- src/agentkit/server/frontend/tests/unit/stores/chatStore.test.ts追加测试
**Approach:**
-`deleteConversation``if (currentConversationId.value === id)` 分支 line 364 之后追加一行:
```ts
stream.collaborationState.value = null;
```
- 与现有 line 364-365 的 board/debate 重置并列
- 不影响"删除非当前会话"分支line 357 仅从列表移除,三个 state 不变 — 这是正确行为,因为当前会话不切换)
**Patterns to follow:**
- `chatStore.ts:364-365``stream.boardState.value = null; stream.debateState.value = null;` 已有模式
**Test scenarios:**
- Happy path当前会话有 collaborationState来自 collaboration_graph 消息)→ `deleteConversation(currentId)``collaborationState === null`
- Edge case删除非当前会话 → 当前会话的三个 state 不变(无副作用)
- Edge case删除当前会话后自动 `createConversation()` → 三个 state 全 null与 U1 联动)
**Verification:** `cd src/agentkit/server/frontend && npm run test:unit` 通过
### U4. ReAct _build_tool_use_prompt 规则重排 + 措辞调整
**Goal:** 修复 Bug 2 L0 — 重排 `_build_tool_use_prompt` 规则列表,让"何时必须使用工具"排在"何时可以不用工具"之前,并收窄规则 3 的措辞,去除"偷懒窗口"。
**Requirements:** R4, R5, R6
**Dependencies:**
**Files:**
- src/agentkit/core/react.py修改 line 1605-1616 `_build_tool_use_prompt` 返回的 rules 字符串)
- tests/unit/test_react_engine.py追加测试 `TestReActToolUsePromptRules`
**Approach:**
- 修改 `return (` 起的多行字符串中规则部分:
- 现有规则 3 改为规则 4措辞从"如果不需要工具就能回答,直接回答即可"改为"仅在确实无需工具时可直接回答"
- 现有规则 1 改为规则 2语义不变"每次只调用一个工具"
- 现有规则 2 改为规则 3语义不变"等待工具返回结果后再决定下一步"
- 现有规则 4 改为规则 5语义不变"不要在回答中重复工具的输出"
- **新增规则 1**(在最前):"涉及外部信息、实时数据、多步骤分析或你不确定的事实时必须使用工具"
- 不动 `core_tools` / `extended_tools` 渲染逻辑
- 不动 `_render_core_tools` / `_render_extended_tools` / `_maybe_add_tool_search`
- 不动 `system_prompt` 拼接line 608-611`_build_tool_use_prompt` 仍以同样方式被追加
**Patterns to follow:**
- `react.py:1605-1616` 现有规则结构 — 替换为 5 条而非删除
**Test scenarios:**
- Happy path调用 `_build_tool_use_prompt([web_search_tool, read_file_tool])` → 输出包含"必须使用工具"且规则序号正确1 在 2 前)
- Edge casetools 列表为空 → 走 fast-path不调用 `_build_tool_use_prompt`),无变化
- 文本断言:输出不包含"如果不需要工具就能回答,直接回答即可"(旧规则 3
- 文本断言:输出包含"涉及外部信息、实时数据、多步骤分析或你不确定的事实时必须使用工具"(新规则 1
- 文本断言:输出包含 `<tool_use>` XML 格式示例(保持向后兼容)
**Verification:** `pytest tests/unit/test_react_engine.py -k ToolUsePromptRules` 通过;`pytest tests/unit/test_react_engine.py` 全套通过(不破坏现有 200+ 测试)
### U5. 端到端验证测试Bug 1 + Bug 2 联动)
**Goal:** 写一个端到端测试覆盖 Bug 1 的前端 store 行为链 + Bug 2 的后端 prompt 文本,验证两个 fix 在测试套件中都被回归保护。
**Requirements:** 全部 R1-R6
**Dependencies:** U1, U2, U3, U4
**Files:**
- src/agentkit/server/frontend/tests/unit/stores/chatStore.test.ts追加 `describe('transient state reset matrix')` 块)
- tests/unit/test_react_engine.py追加 `describe('Bug 2 L0 prompt rules')` 块)
**Approach:**
- **Bug 1 联动测试**:在 chatStore.test.ts 写一个"建私董会 → 切新对话 → 切回旧私董会"三步流程,断言中间步骤的三个 state 全为 null最终回到旧私董会时通过 `restoreBoardStateFromMessages` 重建(注意:此 case 测的是 store-level 状态切换,不依赖后端响应)
- **Bug 2 联动测试**:在 test_react_engine.py 写一个"注册 web_search 工具 → 调 `_build_tool_use_prompt` → 断言 prompt 文本"测试,验证新规则 1 出现在 prompt 头部、web_search 工具描述完整(包含 description + parameters
- 不跑真实 LLM依赖 API key仅文本层断言
**Patterns to follow:**
- `chatStore.test.ts:18-81``boardStartedMsg` / `speechMsg` / `conclusionMsg` fixture 模式
- `test_react_engine.py:402-420``TestReActSystemPrompt` 模式mock gateway + 调 execute + 断言 messages
**Test scenarios:**
- Bug 1 三步流程:建私董会(注入 board_started fixture`createConversation()` → 断言三 state null → `selectConversation(originalId)` → 断言 boardState 重建
- Bug 1 跨 session建会话 A 含 boardState → `selectConversation(B)`B 无 board→ 断言三 state null → `selectConversation(A)` 重新触发 restore → 断言 boardState 重建
- Bug 2 规则顺序:调 `_build_tool_use_prompt` → 用 regex `r'1\.[^2]*2\.'` 断言规则 1 出现在规则 2 之前
- Bug 2 web_search 描述:调 `_build_tool_use_prompt([web_search_tool])` → 断言输出包含 "搜索互联网信息"description 内容)
**Verification:** `cd src/agentkit/server/frontend && npm run test:unit` + `pytest tests/unit/test_react_engine.py` 全部通过
### U6. Bug 2 L4 真实 LLM smoke test 验证
**Goal:** 验证 U4 的 L0 规则重排在真实 LLM 调用中是否生效 — Agent 面对复杂需求(外部信息 / 实时数据 / 多步分析)时是否调用 `web_search` 而非直接回答。将 Bug 2 状态从 "hypothesis applied, pending L4 verification" 升级为 "verified" 或回退并触发 L1/L2。
**Requirements:** R4 验证闭环
**Dependencies:** U4已 donePR #17 合入后执行
**Files:**
- tests/manual/test_react_l4_smoke.py新建手动 smoke test 脚本,不进 CI
**Approach:**
- 准备 5 个 probe query覆盖外部信息 / 实时数据 / 多步分析 / 不确定事实 / 混合类型:
1. "收集 GitHub Trending 前 10 个项目信息并分析商业价值"(原 bug 复现 query
2. "最新 AI 领域有什么重要新闻?"(实时数据)
3. "对比 React 和 Vue 3 在大型项目中的性能差异"(多步分析 + 外部信息)
4. "今天上海天气怎么样?"(实时数据,简单)
5. "请帮我总结这段文字:..."(无需工具,验证 escape hatch 规则 4 仍有效)
- 对每个 query 跑 `ReActEngine.execute()`,记录是否触发 `web_search` tool call
- query 1-4 期望触发工具调用≥3/4 pass 算通过query 5 期望不触发
- 如通过率 < 3/4触发 L1工具描述扩展并创建独立 plan
- 使用 `agent-browser` 打开 http://localhost:15173 进行前端层验证可选 chat 中输入 probe query观察是否出现 tool_call step
**Test scenarios:**
- probe query 1-4ReAct 循环中至少出现一次 `web_search` tool call
- probe query 5ReAct 循环中无 tool call直接回答
- 回归断言U4 单测仍 passL0 文本未变
**Verification:** `python3 tests/manual/test_react_l4_smoke.py` 输出报告5 query tool call 统计 + pass/fail 判定通过后更新本 plan Progress U6 状态为 doneBug 2 状态升级为 "L4 verified"
### U7. 工作树未提交变更清理
**Goal:** 清理工作树中 32 个来自前序 session 的未提交变更 concern 分组提交使工作树恢复干净状态避免后续开发时 diff 噪声干扰 review
**Requirements:** 工程治理
**Dependencies:** 可与 U6 并行
**Files (按 concern 分组):**
| 分组 | 文件 | 来源 session | 建议提交方式 |
|------|------|-------------|-------------|
| A. expert avatar emoji 移除 | configs/experts/*.yaml (15 ) | emoji 移除 plan (2026-07-02-001) | 单独 commit `refactor: expert avatar 改为首字符` |
| B. dev 环境配置 | docker-compose.yaml, scripts/dev-start.sh, docker-compose.dev.yml, .env.dev, src/agentkit/server/config.py | dev 环境修复 session | 单独 commit `fix: dev 环境配置 + 端口隔离` |
| C. board 元数据持久化 | src/agentkit/experts/board_orchestrator.py, src/agentkit/server/routes/chat.py | 私董会持久化修复 session | 单独 commit `fix: board_speech/round_summary 持久化 avatar/color 元数据` |
| D. 前端方案B + board UI | StickyModeHeader.vue, expertIdentity.ts, useMessageRenderer.ts, BoardRoundCard.vue, MessageShell.vue, Scene4BoardDiscussion.vue, chatStream.ts, types.ts, LoginView.vue | 方案B + 私董会限制 session | review commit部分可能已通过 PR #15 合入 |
| E. .understand-anything | .understand-anything/* (3 tracked + untracked) | knowledge graph 工具 | 加入 .gitignore 或单独 commit |
| F. 未跟踪 plan/brainstorm 文档 | docs/brainstorms/2026-07-02-*.md, docs/plans/2026-07-02-001-*.md | ce-plan/ce-brainstorm 产物 | commit 为决策记录 |
**Approach:**
- 对每组 `git diff HEAD -- <files>` 检查变更内容确认无冲突
- 分组 commit每组 commit message 遵循 conventional commits 格式
- D 组需特别注意检查是否与已合入的 PR #15 内容重叠避免重复提交
- E .understand-anything建议加入 .gitignore 而非 commit是工具生成的本地索引
- 如某组变更属于未完成的 feature如方案B stash 而非 commit待对应 plan 完成后再提交
**Test scenarios:**
- 每组 commit `git status --short` 该组文件不再出现
- 全部完成后 `git status --short` 仅显示 untracked.understand-anything gitignored
- `npm run typecheck` + `ruff check src/` 仍通过确认无回归
**Verification:** `git status --short` 输出为空或仅 gitignored `npm run test:unit` + `pytest tests/unit/ -x -q` 全套通过
## Out of Scope
- **L1工具描述扩展**web_search / baidu_search / web_crawl 工具 description 添加"何时使用"触发关键词"需要最新互联网信息新闻Trending股价时使用 web_search")。按用户决策推迟为独立 plan
- **L2PLAN_EXEC 启用** default agent 上注入 `PhasePolicy` 让复杂需求先输出 Plan 再执行涉及 phase 配置auto-advance 阈值违规处理phase event WS 协议影响面较大按用户决策拆为独立 plan
- **重构 stream-owned state 为按 conversationId 拆分**当前是 store-level ref导致每次"切换会话"必须显式重置改为 `Map<conversationId, BoardState>` 可从根上消除泄漏但影响所有 `chatStore.boardState` 读取点StickyModeHeader / useMessageRenderer 属于架构重构
- **base_prompt 调整**保持原样按用户决策
- **私董会生命周期 / Skill 路由策略 / tool registry 架构 / LLM gateway**均不动
## Risks & Dependencies
- **R-U4-1**规则重排可能影响现有 LLM 行为 风险点在某些 LLM 训练分布下"正向规则 1"可能让 LLM **过度工具化**trivial 输入也调工具)。缓解测试覆盖 trivial 输入走 DIRECT_CHAT 不进 ReAct 循环request_preprocessor 已保证pytest 单元测试断言新规则在 prompt 中但不验证 LLM 行为
- **R-U1/2/3-1**state 重置顺序在**同步代码路径**createConversationdeleteConversation 重置块中不会触发中间态渲染 Vue 响应式批量更新在 microtask 中合并 `selectConversation` async 路径`await apiClient.getConversation(id)` 位于顶部 resetboardState null post-fetch restoreline 307 `restoreBoardStateFromMessages`之间 fetch 期间 Vue 会渲染一帧 boardState=null导致 StickyModeHeader 卸载再重载这是期望行为"无旧数据残留" race condition切换两个私董会会话时 header 会短暂消失再出现 若需平滑过渡可在 follow-up 中加 skeleton placeholder
- **D-frontend-build**前端改动需要重新 build static`npm run build:frontend`才能被 backend 静态服务拾取AGENTS.md 已记录此风险
## Deferred to Follow-Up Work
- L1web_search / baidu_search 工具 description 扩展独立 plan)— 仅在 U6 L4 smoke test 未通过时触发
- L2启用 PLAN_EXEC phase policy 处理复杂需求独立 plan)— 仅在 U6 通过但工具调用率仍不理想时触发
- 重构 stream-owned state 为按 conversationId 拆分架构性独立 plan
## Verification (per unit, summary)
- U1/U2/U3`cd src/agentkit/server/frontend && npm run test:unit` 全套通过新增 3 `describe` 块共 8+ test cases
- U4`pytest tests/unit/test_react_engine.py -k ToolUsePromptRules` 通过新增 1 test class 4-5 test cases done)。**Bug 2 状态声明**L0 文本断言通过后 Bug 2 标记为 "hypothesis applied, pending L4 verification" "fixed"真实 LLM smoke test U6 中执行
- U5完整套件通过端到端 4-5 个联动测试 done11 单测全 pass
- U6`python3 tests/manual/test_react_l4_smoke.py` 输出 5 probe query tool call 统计;≥3/4 外部信息 query 触发 web_search + query 5 不触发 Bug 2 状态升级为 "L4 verified"
- U7`git status --short` 为空或仅 gitignored `npm run test:unit` + `pytest tests/unit/ -x -q` 全套通过
- 集成`python3 -m pytest tests/unit/ -x -q`AGENTS.md 硬约束 + `cd src/agentkit/server/frontend && npm run test:unit` 通过
- Lint`ruff check src/ && ruff format src/`AGENTS.md 硬约束通过
- TypeScript`cd src/agentkit/server/frontend && npm run typecheck` 通过