feat(ssa): SSA Agent mode MVP - prompt management + Phase 5A guardrails + UX enhancements

Backend:
- Agent core prompts (Planner + Coder) now loaded from PromptService with 3-tier fallback (DB -> cache -> hardcoded)
- Seed script (seed-ssa-agent-prompts.ts) for idempotent SSA_AGENT_PLANNER + SSA_AGENT_CODER setup
- SSA fallback prompts added to prompt.fallbacks.ts
- Phase 5A: XML tag extraction, defensive programming prompt, high-fidelity schema injection, AST pre-check
- Default agent mode migration + session CRUD (rename/delete) APIs
- R Docker: structured error handling (20+ patterns) + AST syntax pre-check

Frontend:
- Default agent mode (QPER toggle removed), view code fix, analysis result cards in chat
- Session history sidebar with inline rename/delete, robust plan parsing from reviewResult
- R code export wrapper for local reproducibility (package checks + data loader + polyfills)
- SSA workspace CSS updates for sidebar actions and plan display

Docs:
- SSA module doc v4.2: Prompt inventory (2 Agent active / 11 QPER archived), dev progress updated
- System overview doc v6.8: SSA Agent MVP milestone
- Deployment checklist: DB-5 (seed script) + BE-10 (prompt management)

Made-with: Cursor
This commit is contained in:
2026-03-08 15:23:09 +08:00
parent c681155de2
commit ac724266c1
24 changed files with 1598 additions and 140 deletions

View File

@@ -23,7 +23,7 @@ import type { WorkflowPlan } from '../types';
// Types
// ────────────────────────────────────────────
export type ChatIntentType = 'chat' | 'explore' | 'consult' | 'analyze' | 'discuss' | 'feedback';
export type ChatIntentType = 'chat' | 'explore' | 'consult' | 'analyze' | 'discuss' | 'feedback' | 'system';
export interface ChatMessage {
id: string;
@@ -33,6 +33,10 @@ export interface ChatMessage {
intent?: ChatIntentType;
status?: 'complete' | 'generating' | 'error';
createdAt: string;
/** Agent 执行 ID用于在对话中渲染可点击的结果卡片 */
executionId?: string;
/** Agent 执行的查询摘要 */
executionQuery?: string;
}
export interface IntentMeta {
@@ -134,20 +138,45 @@ export function useSSAChat(): UseSSAChatReturn {
if (!resp.ok) return;
const data = await resp.json();
const loaded: ChatMessage[] = [];
if (data.messages?.length > 0) {
const loaded: ChatMessage[] = data.messages
.filter((m: any) => m.status !== 'generating')
.map((m: any) => ({
id: m.id,
role: m.role,
content: m.content || '',
thinking: m.thinkingContent,
intent: m.intent,
status: m.status || 'complete',
createdAt: m.createdAt,
}));
setChatMessages(loaded);
loaded.push(
...data.messages
.filter((m: any) => m.status !== 'generating')
.map((m: any) => ({
id: m.id,
role: m.role as 'user' | 'assistant',
content: m.content || '',
thinking: m.thinkingContent,
intent: m.intent,
status: (m.status || 'complete') as 'complete' | 'generating' | 'error',
createdAt: m.createdAt,
executionId: m.executionId,
executionQuery: m.executionQuery,
})),
);
}
// 从 store 中的 agentExecutionHistory 注入结果卡片
const { agentExecutionHistory } = useSSAStore.getState();
for (const exec of agentExecutionHistory) {
if (exec.status === 'completed') {
loaded.push({
id: `exec-card-${exec.id}`,
role: 'assistant',
content: `✅ 分析完成:${exec.query}`,
status: 'complete',
intent: 'system',
executionId: exec.id,
executionQuery: exec.query,
createdAt: exec.createdAt || new Date().toISOString(),
});
}
}
loaded.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
setChatMessages(loaded);
} catch (err) {
console.warn('[useSSAChat] Failed to load history:', err);
}
@@ -329,13 +358,27 @@ export function useSSAChat(): UseSSAChatReturn {
}
if (parsed.type === 'code_result') {
const { updateAgentExecution } = useSSAStore.getState();
const { updateAgentExecution, agentExecution: curExec } = useSSAStore.getState();
updateAgentExecution({
reportBlocks: parsed.reportBlocks,
generatedCode: parsed.code || useSSAStore.getState().agentExecution?.generatedCode,
generatedCode: parsed.code || curExec?.generatedCode,
status: 'completed',
durationMs: parsed.durationMs,
});
// 在对话中插入可点击的结果卡片
const execId = curExec?.id;
const execQuery = curExec?.query || content;
setChatMessages(prev => [...prev, {
id: crypto.randomUUID(),
role: 'assistant' as const,
content: `✅ 分析完成:${execQuery}`,
status: 'complete' as const,
intent: 'system',
executionId: execId,
executionQuery: execQuery,
createdAt: new Date().toISOString(),
}]);
continue;
}
@@ -448,7 +491,7 @@ export function useSSAChat(): UseSSAChatReturn {
role: 'assistant' as const,
content: auditContent,
status: 'complete' as const,
intent: 'system' as any,
intent: 'system',
createdAt: new Date().toISOString(),
}]);