修复策划 Agent 推理展示生命周期
按项目与回合绑定 reasoning 事件 保留回合结束后的思考过程并在新回合清理 补充默认折叠的展开收起交互与定向测试 更新推理与正文分离里程碑进度
This commit is contained in:
@@ -833,6 +833,11 @@ export function App({
|
||||
view: DesignView;
|
||||
} | null>(null);
|
||||
const [planningV2Reasoning, setPlanningV2Reasoning] = useState('');
|
||||
const designAgentReasoningTurnRef = useRef<{
|
||||
projectPath: string;
|
||||
clientTurnId: string;
|
||||
text: string;
|
||||
} | null>(null);
|
||||
const planningV2TurnRef = useRef<{
|
||||
projectPath: string;
|
||||
clientTurnId: string;
|
||||
@@ -1125,6 +1130,11 @@ export function App({
|
||||
projectPath: nextProjectPath,
|
||||
clientTurnId,
|
||||
};
|
||||
designAgentReasoningTurnRef.current = {
|
||||
projectPath: nextProjectPath,
|
||||
clientTurnId,
|
||||
text: '',
|
||||
};
|
||||
designAgentPendingViewRef.current = null;
|
||||
await designAgentEventSubscriptionReady();
|
||||
setChatAgentBusy(true);
|
||||
@@ -1648,6 +1658,7 @@ export function App({
|
||||
setPlanningV2Session(null);
|
||||
setPlanningV2TransientReplyTarget('');
|
||||
designAgentPendingViewRef.current = null;
|
||||
designAgentReasoningTurnRef.current = null;
|
||||
setPlanningV2Reasoning('');
|
||||
setPlanningV2Active(planningStartMode);
|
||||
planningV2ActiveRef.current = planningStartMode;
|
||||
@@ -2108,7 +2119,15 @@ export function App({
|
||||
setPlanningV2TransientReplyTarget(payload.text);
|
||||
}
|
||||
if (payload.reasoningText != null) {
|
||||
setPlanningV2Reasoning(payload.reasoningText);
|
||||
const reasoningTurn = designAgentReasoningTurnRef.current;
|
||||
if (
|
||||
reasoningTurn &&
|
||||
reasoningTurn.projectPath === payload.projectPath &&
|
||||
reasoningTurn.clientTurnId === payload.clientTurnId
|
||||
) {
|
||||
reasoningTurn.text = payload.reasoningText;
|
||||
setPlanningV2Reasoning(payload.reasoningText);
|
||||
}
|
||||
}
|
||||
if (payload.kind === 'tool' && payload.text) {
|
||||
setPlanningV2TransientReplyTarget(payload.text);
|
||||
@@ -11936,6 +11955,11 @@ export function App({
|
||||
projectPath: nextProjectPath,
|
||||
clientTurnId,
|
||||
};
|
||||
designAgentReasoningTurnRef.current = {
|
||||
projectPath: nextProjectPath,
|
||||
clientTurnId,
|
||||
text: '',
|
||||
};
|
||||
designAgentPendingViewRef.current = null;
|
||||
setPlanningV2TransientReplyTarget('');
|
||||
setPlanningV2Reasoning('');
|
||||
|
||||
@@ -263,8 +263,11 @@ export function ProjectSupervisorView({
|
||||
</div>
|
||||
))}
|
||||
{designReasoning ? (
|
||||
<details className="design-agent-reasoning">
|
||||
<summary>显示思考过程</summary>
|
||||
<details
|
||||
className="design-agent-reasoning"
|
||||
aria-label="策划 Agent 思考过程"
|
||||
>
|
||||
<summary>思考过程(点击展开)</summary>
|
||||
<pre>{designReasoning}</pre>
|
||||
</details>
|
||||
) : null}
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
React,
|
||||
render,
|
||||
screen,
|
||||
setComposerText,
|
||||
waitFor,
|
||||
} from './harness';
|
||||
|
||||
@@ -55,6 +56,24 @@ function designClarificationView() {
|
||||
};
|
||||
}
|
||||
|
||||
function designConversationView() {
|
||||
return {
|
||||
session: {
|
||||
sessionId: 'design-session-reasoning',
|
||||
projectId: 'local-project-draft',
|
||||
currentPhase: 'concept',
|
||||
approvedPhases: [],
|
||||
pendingApproval: null,
|
||||
pendingClarification: null,
|
||||
turnIndex: 1,
|
||||
lastError: null,
|
||||
},
|
||||
messages: [],
|
||||
running: false,
|
||||
canRetry: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function registerDesignAgentSurfaceTests() {
|
||||
it('hydrates an existing design session and decides approval through design commands', async () => {
|
||||
const harness = createProjectSupervisorRuntimeHarness({
|
||||
@@ -141,4 +160,54 @@ export function registerDesignAgentSurfaceTests() {
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps the current turn reasoning after completion and supports collapse/expand', async () => {
|
||||
const harness = createProjectSupervisorRuntimeHarness({
|
||||
designAgentView: designConversationView(),
|
||||
designAgentContinueView: designConversationView(),
|
||||
});
|
||||
window.__TAURI__ = {
|
||||
core: { invoke: harness.invoke },
|
||||
event: { listen: harness.listen },
|
||||
};
|
||||
window.history.pushState({}, '', '/');
|
||||
render(
|
||||
React.createElement(App, {
|
||||
initialProjectPath: harness.projectPath,
|
||||
orchestrationMode: 'single-supervisor',
|
||||
planningStartMode: true,
|
||||
projectSupervisorOnly: true,
|
||||
}),
|
||||
);
|
||||
|
||||
const input = await screen.findByLabelText('项目需求');
|
||||
await setComposerText(input, '请给出核心玩法方案');
|
||||
fireEvent.submit(input.closest('form') as HTMLFormElement);
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
harness.invoke.mock.calls.some(
|
||||
([command]) => command === 'continue_design_agent_session',
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
const continueCall = [...harness.invoke.mock.calls]
|
||||
.reverse()
|
||||
.find(([command]) => command === 'continue_design_agent_session');
|
||||
const clientTurnId = String(
|
||||
(continueCall?.[1] as { clientTurnId?: string }).clientTurnId,
|
||||
);
|
||||
harness.emitDesignAgentEvent({
|
||||
projectPath: harness.projectPath,
|
||||
clientTurnId,
|
||||
kind: 'reasoning',
|
||||
reasoningText: '先分析需求,再组织方案。',
|
||||
});
|
||||
|
||||
const summary = await screen.findByText('思考过程(点击展开)');
|
||||
const details = summary.closest('details') as HTMLDetailsElement;
|
||||
expect(details.open).toBe(false);
|
||||
fireEvent.click(summary);
|
||||
expect(details.open).toBe(true);
|
||||
expect(screen.getByText('先分析需求,再组织方案。')).not.toBeNull();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -741,6 +741,9 @@ function createProjectSupervisorRuntimeHarness({
|
||||
};
|
||||
}) => void)
|
||||
| null = null;
|
||||
let designAgentUpdateHandler:
|
||||
| ((event: { payload: Record<string, unknown> }) => void)
|
||||
| null = null;
|
||||
|
||||
const conversationRecord = (
|
||||
role: 'user' | 'assistant',
|
||||
@@ -1113,6 +1116,10 @@ function createProjectSupervisorRuntimeHarness({
|
||||
if (eventName === 'game-creator-agent-progress') {
|
||||
progressHandler = handler as unknown as typeof progressHandler;
|
||||
}
|
||||
if (eventName === 'design-agent-update') {
|
||||
designAgentUpdateHandler =
|
||||
handler as unknown as typeof designAgentUpdateHandler;
|
||||
}
|
||||
return () => {
|
||||
if (runtimeUpdateHandler === handler) {
|
||||
runtimeUpdateHandler = null;
|
||||
@@ -1123,6 +1130,9 @@ function createProjectSupervisorRuntimeHarness({
|
||||
if (progressHandler === handler) {
|
||||
progressHandler = null;
|
||||
}
|
||||
if (designAgentUpdateHandler === handler) {
|
||||
designAgentUpdateHandler = null;
|
||||
}
|
||||
};
|
||||
},
|
||||
);
|
||||
@@ -1149,6 +1159,9 @@ function createProjectSupervisorRuntimeHarness({
|
||||
setPlanningV2Result(state: Record<string, unknown> | null) {
|
||||
currentPlanningV2Result = state;
|
||||
},
|
||||
emitDesignAgentEvent(payload: Record<string, unknown>) {
|
||||
designAgentUpdateHandler?.({ payload });
|
||||
},
|
||||
setPlanningV2StartResult(state: Record<string, unknown> | null) {
|
||||
currentPlanningV2StartResult = state;
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
| 字段 | 值 |
|
||||
| --- | --- |
|
||||
| Version | 1.0 |
|
||||
| Status | awaiting-review |
|
||||
| Status | in-progress |
|
||||
| Date | 2026-09-14 |
|
||||
| Parent Spec | `docs/technical/【技术方案】策划Agent生产迁移与工作区浏览-2026-09-10.md` |
|
||||
| Related Issue | `GenarrativeAI/Genarrative#331` |
|
||||
@@ -12,6 +12,12 @@
|
||||
|
||||
让策划 Agent 能在流式回合中单独收到 Provider reasoning,并在 UI 中以默认折叠的思考过程展示;用户可见正文、工具调用和 GameAgent 现有行为保持不变。
|
||||
|
||||
## 当前实现进度(2026-09-14)
|
||||
|
||||
- 已完成共享 reasoning 字段、Provider 解析、策划事件映射以及正文流式收尾的前两轮提交。
|
||||
- 当前第三轮聚焦策划 Agent 前端 reasoning 生命周期:按 `projectPath + clientTurnId` 绑定事件,回合结束后保留本轮 reasoning,下一轮或项目切换时清理。
|
||||
- 现有 `<details>` 展示保持默认收起,并提供明确的展开/收起入口;不新增持久化字段,也不改动 GameAgent、Direct/Codex、supervisor 或已退役链路。
|
||||
|
||||
## 背景与现状
|
||||
|
||||
- `platform-llm` 当前只向上层提供正文累计值、正文增量和结束状态。
|
||||
|
||||
Reference in New Issue
Block a user