feat(web): 工作台 AI 模式与差旅/风险建议交互优化

- 新增 PersonalWorkbenchAiMode 组件、AI 侧边栏与 orb 机器人视觉资源
- 新增 aiApplicationDraftModel / aiExpenseDraftModel / aiWorkbenchConversationStore
  及业务准入 aiSidebarBusinessAccess,支撑 AI 模式下的申请与报销草稿
- 顶栏、侧边栏、工作台样式重构,适配 AI 模式切换与响应式布局
- 同步 steward plan/off_topic、差旅报销引导流、风险建议卡片等测试
This commit is contained in:
caoxiaozhu
2026-06-18 22:12:24 +08:00
parent a6674a1e76
commit 0cde1f8990
65 changed files with 8011 additions and 1608 deletions

View File

@@ -22,7 +22,7 @@ const OFF_TOPIC_PLAN = {
attachment_groups: [],
confirmation_groups: [],
candidate_flows: [],
summary: '这看起来跟财务任务没什么关系...',
summary: '很抱歉主人,目前小财管家只能帮您整理**费用申请**和**费用报销**这两类事项。',
suggested_prompts: [
'我想要申请明天去北京出差3天支撑客户现场实施',
'我要报销上周去上海的高铁票',
@@ -64,24 +64,60 @@ test('isOffTopicStewardPlan returns true only for off_topic plan status', () =>
assert.equal(isOffTopicStewardPlan({}), false)
})
test('buildStewardPlanMessageText renders friendly off_topic guidance', () => {
test('buildStewardPlanMessageText renders backend-provided off_topic summary verbatim', () => {
// off_topic 文案由后端(含 LLM生成前端只负责透传不再拼接标题/引导
const text = buildStewardPlanMessageText(OFF_TOPIC_PLAN)
assert.match(text, /小财管家没看懂这件事/)
assert.equal(text, OFF_TOPIC_PLAN.summary)
// 推荐话术本身不在正文里展示,而是作为按钮单独渲染,避免重复。
for (const prompt of OFF_TOPIC_PLAN.suggested_prompts) {
assert.equal(text.includes(prompt), false, `正文不应包含推荐话术:${prompt}`)
}
})
test('buildStewardPlanMessageText keeps off_topic branch ahead of pending flow branch', () => {
// 即使 summary 缺省,也走 off_topic 分支而非默认任务文案
const text = buildStewardPlanMessageText({
plan_id: 'p-off-topic-default',
test('buildStewardPlanMessageText adapts greeting vs meaningless vs off_business summaries', () => {
// 问候场景:礼貌回应主人
const greetingText = buildStewardPlanMessageText({
plan_id: 'p-greeting',
plan_status: 'off_topic',
next_action: 'none',
suggested_prompts: ['申请出差']
summary: '### 您好主人,很高兴为您服务\n\n请问您今天要办理什么业务',
suggested_prompts: ['我想要申请明天去北京出差3天']
})
assert.match(text, /小财管家没看懂这件事/)
assert.match(greetingText, /您好主人/)
assert.match(greetingText, /请问您今天要办理什么业务/)
// 无意义场景:温和解释 + 引导换种说法
const meaninglessText = buildStewardPlanMessageText({
plan_id: 'p-meaningless',
plan_status: 'off_topic',
next_action: 'none',
summary: '### 这句话我暂时没识别到财务事项\n\n很抱歉主人目前小财管家只能帮您整理**费用申请**和**费用报销**。',
suggested_prompts: ['我想要申请明天去北京出差3天']
})
assert.match(meaninglessText, /这句话我暂时没识别到财务事项/)
assert.match(meaninglessText, /很抱歉主人/)
// 有意义但非业务场景LLM 生成的文案(这里 mock 模拟)
const llmText = '### 抱歉主人,这句话我暂时帮不上忙\n\n主人聊的是天气小财管家目前只能帮您整理**费用申请**和**费用报销**。'
const offBusinessText = buildStewardPlanMessageText({
plan_id: 'p-off-business',
plan_status: 'off_topic',
next_action: 'none',
summary: llmText,
suggested_prompts: ['我想要申请明天去北京出差3天']
})
assert.equal(offBusinessText, llmText)
})
test('buildStewardPlanMessageText falls back to client template when summary is missing', () => {
// 后端 summary 缺失时,前端有兜底文案保证体验不空白
const text = buildStewardPlanMessageText({
plan_id: 'p-empty',
plan_status: 'off_topic',
next_action: 'none',
suggested_prompts: []
})
assert.match(text, /这句话我暂时没识别到财务事项/)
assert.match(text, /费用申请.*费用报销|费用报销.*费用申请/)
})