Files
X-Financial/web/tests/workbench-ai-intent-planner-model.test.mjs
caoxiaozhu 43779f8f2c fix(web): 多 task 串行推进 task2 不再被预览生成时提前触发
onPreviewReadyForNextTask 在 task1 申请核对表刚生成、用户还没操作时就
提前拉起 task2,与用户后续在 task1 上的保存草稿/提交操作互相打架,导致
task2 完全无反应。移除该提前推进回调,统一由 onApplicationActionCompleted
在 task1 真正完成后再推进 task2。

- useWorkbenchAiApplicationPreviewFlow: 删除预览生成时的提前推进分支
- usePersonalWorkbenchAiMode: startModelPlannedApplicationPreview 不再传 onPreviewReadyForNextTask
- useWorkbenchAiActionRouter: 低置信确认按钮分支同步删除该回调
- 新增时序回归测试:预览生成不提前推进、保存草稿后才推进
- 更新两处源码正则断言为 doesNotMatch
2026-06-30 11:40:31 +08:00

482 lines
22 KiB
JavaScript
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.
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import test from 'node:test'
import { fileURLToPath } from 'node:url'
import {
WORKBENCH_AI_INTENT_SOURCE_MODEL,
WORKBENCH_AI_INTENT_SOURCE_RULE_FALLBACK,
WORKBENCH_AI_STEP_BUILD_APPLICATION_PREVIEW,
WORKBENCH_AI_STEP_SAVE_APPLICATION_DRAFT,
WORKBENCH_AI_STEP_RUN_DUPLICATE_PRECHECK,
WORKBENCH_AI_STEP_SUBMIT_APPLICATION,
WORKBENCH_AI_STEP_VALIDATE_REQUIRED_FIELDS,
buildRuleFallbackWorkbenchAiIntentPlan,
isLowConfidenceTravelApplicationPlan,
normalizeWorkbenchAiIntentPlan,
resolveExecutableTravelApplicationPlan,
shouldRequestWorkbenchAiIntentPlan
} from '../src/composables/workbenchAiMode/workbenchAiIntentPlannerModel.js'
import { buildInlineApplicationPreview } from '../src/composables/workbenchAiMode/workbenchAiApplicationPreviewModel.js'
import { createWorkbenchAiMessageRuntime } from '../src/composables/workbenchAiMode/workbenchAiMessageModel.js'
const personalWorkbenchAiModeScript = readFileSync(
fileURLToPath(new URL('../src/composables/workbenchAiMode/usePersonalWorkbenchAiMode.js', import.meta.url)),
'utf8'
)
const stewardFlowScript = readFileSync(
fileURLToPath(new URL('../src/composables/workbenchAiMode/useWorkbenchAiStewardFlow.js', import.meta.url)),
'utf8'
)
const applicationPreviewFlowScript = readFileSync(
fileURLToPath(new URL('../src/composables/workbenchAiMode/useWorkbenchAiApplicationPreviewFlow.js', import.meta.url)),
'utf8'
)
const planningThinkingModelScript = readFileSync(
fileURLToPath(new URL('../src/composables/workbenchAiMode/workbenchAiPlanningThinkingModel.js', import.meta.url)),
'utf8'
)
test('workbench AI intent planner normalizes model travel application submit plan into executable steps', () => {
const plan = normalizeWorkbenchAiIntentPlan({
planning_source: 'llm_function_call',
tasks: [{
task_type: 'expense_application',
assigned_agent: 'application_assistant',
requested_action: 'submit',
confidence: 0.91,
ontology_fields: {
time_range: '2026-02-20 至 2026-02-23',
location: '上海',
reason: '辅助国网仿生产服务器部署',
transport_mode: '火车'
}
}]
}, {
prompt: '去上海出差,辅助国网仿生产服务器部署,交通火车,直接提交'
})
assert.equal(plan.source, WORKBENCH_AI_INTENT_SOURCE_MODEL)
assert.equal(plan.intent, 'create_travel_application')
assert.equal(plan.requestedAction, 'submit')
assert.deepEqual(plan.steps, [
WORKBENCH_AI_STEP_BUILD_APPLICATION_PREVIEW,
WORKBENCH_AI_STEP_VALIDATE_REQUIRED_FIELDS,
WORKBENCH_AI_STEP_RUN_DUPLICATE_PRECHECK,
WORKBENCH_AI_STEP_SUBMIT_APPLICATION
])
assert.deepEqual(plan.ontologyFields, {
time_range: '2026-02-20 至 2026-02-23',
location: '上海',
reason: '辅助国网仿生产服务器部署',
transport_mode: '火车'
})
assert.deepEqual(plan.slots, {
timeRange: '2026-02-20 至 2026-02-23',
location: '上海',
reason: '辅助国网仿生产服务器部署',
transportMode: '火车'
})
})
test('workbench AI intent planner keeps reimbursement task after first application task', () => {
const reimbursementTask = {
task_id: 'task-reimbursement-2',
task_type: 'reimbursement',
assigned_agent: 'reimbursement_assistant',
title: '业务招待费报销',
summary: '报销昨天的业务招待费 2000 元',
requested_action: 'preview',
confidence: 0.9,
ontology_fields: {
expense_type: 'entertainment',
expense_type_label: '业务招待费',
time_range: '2026-06-25',
amount: '2000元',
reason: '业务招待'
},
missing_fields: []
}
const plan = normalizeWorkbenchAiIntentPlan({
planning_source: 'llm_function_call',
tasks: [{
task_id: 'task-application-1',
task_type: 'expense_application',
assigned_agent: 'application_assistant',
requested_action: 'preview',
confidence: 0.93,
ontology_fields: {
expense_type: 'travel',
time_range: '2026-02-20 至 2026-02-23',
location: '上海',
reason: '服务国网服务器部署'
},
missing_fields: ['transport_mode']
}, reimbursementTask]
}, {
prompt: '2月20-23日去上海出差3天服务国网服务器部署并且报销昨天的业务招待费2000元'
})
assert.deepEqual(plan.stewardRemainingTasks, [reimbursementTask])
assert.deepEqual(resolveExecutableTravelApplicationPlan(plan).stewardRemainingTasks, [reimbursementTask])
})
test('workbench AI intent planner prefers server action steps when present', () => {
const plan = normalizeWorkbenchAiIntentPlan({
planning_source: 'llm_function_call',
tasks: [{
task_type: 'expense_application',
assigned_agent: 'application_assistant',
requested_action: 'submit',
confidence: 0.91,
ontology_fields: {
time_range: '2026-02-20 至 2026-02-23',
location: '上海',
reason: '辅助国网仿生产服务器部署',
transport_mode: '火车'
},
action_steps: [
{ action_type: 'fill_application_fields' },
{ action_type: 'build_application_preview' },
{ action_type: 'validate_required_fields' },
{ action_type: 'save_application_draft' }
]
}]
}, {
prompt: '2026-02-20 至 2026-02-23去上海出差辅助国网仿生产服务器部署交通火车直接提交'
})
assert.deepEqual(plan.steps, [
WORKBENCH_AI_STEP_BUILD_APPLICATION_PREVIEW,
WORKBENCH_AI_STEP_VALIDATE_REQUIRED_FIELDS,
WORKBENCH_AI_STEP_SAVE_APPLICATION_DRAFT
])
})
test('workbench AI intent planner falls back to rule plan for compact travel direct submit', () => {
const plan = buildRuleFallbackWorkbenchAiIntentPlan('去上海出差,辅助国网仿生产服务器部署,交通火车,直接提交')
assert.equal(plan.source, WORKBENCH_AI_INTENT_SOURCE_RULE_FALLBACK)
assert.equal(plan.intent, 'create_travel_application')
assert.equal(plan.requestedAction, 'submit')
assert.deepEqual(plan.steps, [
WORKBENCH_AI_STEP_BUILD_APPLICATION_PREVIEW,
WORKBENCH_AI_STEP_VALIDATE_REQUIRED_FIELDS,
WORKBENCH_AI_STEP_RUN_DUPLICATE_PRECHECK,
WORKBENCH_AI_STEP_SUBMIT_APPLICATION
])
})
test('workbench AI intent planner detects compact travel save-draft variant before rules are enough', () => {
const prompt = '2026-02-20 至 2026-02-23上海出差国网仿生产服务器部署火车保存草稿。'
const plan = buildRuleFallbackWorkbenchAiIntentPlan(prompt)
assert.equal(shouldRequestWorkbenchAiIntentPlan(prompt), true)
assert.equal(shouldRequestWorkbenchAiIntentPlan('帮我查询上海差旅标准'), true)
assert.equal(shouldRequestWorkbenchAiIntentPlan('1'), false)
assert.equal(plan.source, WORKBENCH_AI_INTENT_SOURCE_RULE_FALLBACK)
assert.equal(plan.intent, 'create_travel_application')
assert.equal(plan.requestedAction, 'save_draft')
assert.deepEqual(plan.steps, [
WORKBENCH_AI_STEP_BUILD_APPLICATION_PREVIEW,
WORKBENCH_AI_STEP_VALIDATE_REQUIRED_FIELDS,
WORKBENCH_AI_STEP_SAVE_APPLICATION_DRAFT
])
assert.deepEqual(resolveExecutableTravelApplicationPlan(plan), {
expenseType: 'travel',
expenseTypeLabel: '差旅费',
sourceText: prompt,
ontologyFields: {},
autoSubmit: false,
autoSaveDraft: true,
requestedSubmit: false,
submitRequiresConfirmation: false
})
})
test('workbench AI intent planner turns model fields and action into executable application preview payload', () => {
const prompt = '2026-02-20 至 2026-02-23上海出差国网仿生产服务器部署火车保存草稿。'
const plan = normalizeWorkbenchAiIntentPlan({
planning_source: 'llm_function_call',
tasks: [{
task_type: 'expense_application',
assigned_agent: 'application_assistant',
requested_action: 'save_draft',
confidence: 0.95,
ontology_fields: {
time_range: '2026-02-20 至 2026-02-23',
location: '上海',
reason: '国网仿生产服务器部署',
transport_mode: '火车'
},
missing_fields: []
}]
}, { prompt })
assert.deepEqual(resolveExecutableTravelApplicationPlan(plan), {
expenseType: 'travel',
expenseTypeLabel: '差旅费',
sourceText: prompt,
ontologyFields: {
time_range: '2026-02-20 至 2026-02-23',
location: '上海',
reason: '国网仿生产服务器部署',
transport_mode: '火车'
},
autoSubmit: false,
autoSaveDraft: true,
requestedSubmit: false,
submitRequiresConfirmation: false
})
})
test('workbench AI intent planner turns single application candidate flow into executable preview payload', () => {
const prompt = '2026-02-20 至 2026-02-23去上海出差辅助国网仿生产服务器部署交通火车'
const plan = normalizeWorkbenchAiIntentPlan({
planning_source: 'rule_fallback',
plan_status: 'needs_flow_confirmation',
pending_flow_confirmation: {
status: 'pending',
candidate_flows: [{
flow_id: 'travel_application',
label: '先发起出差申请',
confidence: 0.86,
ontology_fields: {
expense_type: 'travel',
time_range: '2026-02-20 至 2026-02-23',
location: '上海',
reason: '辅助国网仿生产服务器部署',
transport_mode: '火车'
},
missing_fields: []
}]
}
}, { prompt })
assert.equal(plan.source, WORKBENCH_AI_INTENT_SOURCE_RULE_FALLBACK)
assert.equal(plan.intent, 'create_travel_application')
assert.equal(plan.requestedAction, 'preview')
assert.deepEqual(resolveExecutableTravelApplicationPlan(plan), {
expenseType: 'travel',
expenseTypeLabel: '差旅费',
sourceText: prompt,
ontologyFields: {
expense_type: 'travel',
time_range: '2026-02-20 至 2026-02-23',
location: '上海',
reason: '辅助国网仿生产服务器部署',
transport_mode: '火车'
},
autoSubmit: false,
autoSaveDraft: false,
requestedSubmit: false,
submitRequiresConfirmation: false
})
})
test('workbench AI application preview prefers model ontology fields over local text guesses', () => {
const preview = buildInlineApplicationPreview(
'差旅费',
'2026-02-20 至 2026-02-23上海出差国网仿生产服务器部署火车保存草稿。',
{ name: '李文静', grade: 'P5', location: '武汉' },
{
ontologyFields: {
time_range: '2026-02-20 至 2026-02-23',
location: '上海',
reason: '国网仿生产服务器部署',
transport_mode: '火车'
}
}
)
assert.equal(preview.fields.time, '2026-02-20 至 2026-02-23')
assert.equal(preview.fields.location, '上海')
assert.equal(preview.fields.reason, '国网仿生产服务器部署')
assert.equal(preview.fields.transportMode, '火车')
})
test('workbench AI intent planner rejects policy question and requires confirmation for direct-submit request', () => {
assert.equal(buildRuleFallbackWorkbenchAiIntentPlan('帮我查询上海差旅标准'), null)
const request = resolveExecutableTravelApplicationPlan(
buildRuleFallbackWorkbenchAiIntentPlan('去上海出差,辅助国网仿生产服务器部署,交通火车,直接提交')
)
assert.deepEqual(request, {
expenseType: 'travel',
expenseTypeLabel: '差旅费',
sourceText: '去上海出差,辅助国网仿生产服务器部署,交通火车,直接提交',
ontologyFields: {},
autoSubmit: false,
autoSaveDraft: false,
requestedSubmit: true,
submitRequiresConfirmation: true
})
})
test('workbench AI message runtime persists direct-submit confirmation metadata', () => {
const { createInlineMessage, normalizeRuntimeMessage, serializeRuntimeMessage } = createWorkbenchAiMessageRuntime()
const message = createInlineMessage('assistant', '申请核对表', {
applicationPreview: { fields: { location: '上海' } },
requestedSubmit: true,
submitRequiresConfirmation: true
})
const serialized = serializeRuntimeMessage(message)
assert.equal(serialized.requestedSubmit, true)
assert.equal(serialized.submitRequiresConfirmation, true)
const normalized = normalizeRuntimeMessage(serialized)
assert.equal(normalized.requestedSubmit, true)
assert.equal(normalized.submitRequiresConfirmation, true)
})
test('workbench AI mode asks steward model plan before fallback execution', () => {
assert.match(stewardFlowScript, /async function resolveInlineExecutionPlan\(prompt, entry = \{\}, files = \[\], options = \{\}\)/)
assert.match(stewardFlowScript, /fetchStewardPlan\(planRequest/)
assert.match(stewardFlowScript, /timeoutMs:\s*35000/)
assert.match(personalWorkbenchAiModeScript, /async function executeModelPlannedWorkbenchIntent\(cleanPrompt, entry = \{\}, files = \[\]\)/)
assert.match(personalWorkbenchAiModeScript, /await stewardFlow\.resolveInlineExecutionPlan\(cleanPrompt, entry, files,\s*\{/)
assert.match(personalWorkbenchAiModeScript, /normalizeWorkbenchAiIntentPlan\(modelPlan,\s*\{\s*prompt:\s*cleanPrompt/)
assert.match(personalWorkbenchAiModeScript, /buildRuleFallbackWorkbenchAiIntentPlan\(cleanPrompt\)/)
assert.match(personalWorkbenchAiModeScript, /shouldRequestWorkbenchAiIntentPlan\(cleanPrompt\)/)
assert.match(personalWorkbenchAiModeScript, /resolveExecutableTravelApplicationPlan\(intentPlan\)/)
assert.doesNotMatch(personalWorkbenchAiModeScript, /fallbackIntentPlan/)
assert.match(personalWorkbenchAiModeScript, /autoSaveDraft:\s*travelApplicationRequest\.autoSaveDraft/)
assert.match(personalWorkbenchAiModeScript, /requestedSubmit:\s*travelApplicationRequest\.requestedSubmit/)
assert.match(personalWorkbenchAiModeScript, /submitRequiresConfirmation:\s*travelApplicationRequest\.submitRequiresConfirmation/)
assert.match(personalWorkbenchAiModeScript, /ontologyFields:\s*travelApplicationRequest\.ontologyFields/)
assert.match(personalWorkbenchAiModeScript, /stewardRemainingTasks:\s*travelApplicationRequest\.stewardRemainingTasks/)
assert.match(personalWorkbenchAiModeScript, /onApplicationActionCompleted:\s*startModelPlannedNextTask/)
// 多 task 串行推进:预览生成时不再提前拉起下一个 task(会与用户在 task1 上的操作互相打架),
// 改为只在 task1 完成(保存草稿/提交)后通过 onApplicationActionCompleted 推进 task2。
assert.doesNotMatch(personalWorkbenchAiModeScript, /onPreviewReadyForNextTask/)
assert.match(applicationPreviewFlowScript, /options\.autoSaveDraft/)
assert.doesNotMatch(applicationPreviewFlowScript, /onPreviewReadyForNextTask/)
assert.match(applicationPreviewFlowScript, /const actionCompletedHandler = typeof options\.onApplicationActionCompleted === 'function'/)
assert.match(applicationPreviewFlowScript, /actionCompletedHandler\(targetMessage\.stewardRemainingTasks/)
assert.match(applicationPreviewFlowScript, /onApplicationActionCompleted:\s*options\.onApplicationActionCompleted/)
assert.doesNotMatch(applicationPreviewFlowScript, /options\.autoSubmit && normalizeApplicationPreview\(preview\)\.readyToSubmit/)
assert.match(applicationPreviewFlowScript, /ontologyFields:\s*options\.ontologyFields/)
assert.match(applicationPreviewFlowScript, /executeInlineApplicationPreviewAction\(AI_APPLICATION_ACTION_SAVE_DRAFT/)
assert.match(applicationPreviewFlowScript, /buildInlineApplicationPreview\([\s\S]*ontologyFields:\s*options\.ontologyFields/)
assert.doesNotMatch(personalWorkbenchAiModeScript, /const travelApplicationRequest = resolveInlineTravelApplicationRequest\(cleanPrompt\)/)
})
test('workbench AI mode shows a visible planning response before waiting for steward model plan', () => {
assert.match(personalWorkbenchAiModeScript, /function startModelPlanningConversation\(cleanPrompt, entry = \{\}\)/)
assert.match(personalWorkbenchAiModeScript, /conversationMessages\.value\.push\(createInlineMessage\('user', cleanPrompt\)\)/)
assert.match(personalWorkbenchAiModeScript, /正在识别意图,准备拆解申请、报销和附件任务/)
assert.match(
personalWorkbenchAiModeScript,
/const plannerPendingMessage = startModelPlanningConversation\(cleanPrompt, entry\)[\s\S]*await stewardFlow\.resolveInlineExecutionPlan\(cleanPrompt, entry, files,\s*\{/
)
assert.match(
personalWorkbenchAiModeScript,
/pendingMessageId:\s*plannerPendingMessage\?\.id/
)
assert.match(applicationPreviewFlowScript, /options\.pendingMessageId/)
})
test('workbench AI mode streams planning thinking into the pending message', () => {
assert.match(planningThinkingModelScript, /buildModelPlanningProgressSchedule/)
assert.match(planningThinkingModelScript, /判断办理意图/)
assert.match(planningThinkingModelScript, /抽取关键信息/)
assert.match(planningThinkingModelScript, /规划执行步骤/)
assert.match(planningThinkingModelScript, /准备兜底策略/)
assert.match(personalWorkbenchAiModeScript, /function startModelPlanningProgressUpdates\(messageId\)/)
assert.match(personalWorkbenchAiModeScript, /globalThis\.setTimeout\(\(\) => \{\s*updateModelPlanningThinkingEvent\(messageId, event\)/)
assert.match(personalWorkbenchAiModeScript, /const stopPlanningProgressUpdates = startModelPlanningProgressUpdates\(plannerPendingMessage\.id\)/)
assert.match(personalWorkbenchAiModeScript, /stopPlanningProgressUpdates\(\)/)
assert.match(
personalWorkbenchAiModeScript,
/await stewardFlow\.resolveInlineExecutionPlan\(cleanPrompt, entry, files,\s*\{\s*pendingMessageId:\s*plannerPendingMessage\.id\s*\}\)/
)
assert.match(stewardFlowScript, /async function resolveInlineExecutionPlan\(prompt, entry = \{\}, files = \[\], options = \{\}\)/)
assert.match(stewardFlowScript, /fetchInlineStewardPlan\(planningMessageId, planRequest,\s*\{[\s\S]*includeAnswerDelta:\s*false/)
assert.match(applicationPreviewFlowScript, /mergeWorkbenchAiThinkingEvents\(previousThinkingEvents,\s*\[/)
})
test('workbench AI mode reuses planning pending message for regular steward replies', () => {
assert.match(
personalWorkbenchAiModeScript,
/stewardFlow\.requestInlineAssistantReply\(cleanPrompt, entry, files,\s*\{\s*pendingMessageId:\s*plannerPendingMessage\.id\s*\}\)/
)
assert.doesNotMatch(
personalWorkbenchAiModeScript,
/replaceInlineMessage\(plannerPendingMessage\.id,\s*createInlineMessage\('assistant', '已完成意图识别,继续为您整理回复。'/
)
assert.match(stewardFlowScript, /async function requestInlineAssistantReply\(prompt, entry = \{\}, files = \[\], options = \{\}\)/)
assert.match(stewardFlowScript, /const reusablePendingMessageId = String\(options\.pendingMessageId \|\| ''\)\.trim\(\)/)
assert.match(stewardFlowScript, /reusablePendingMessageId \? replaceInlineMessage\(reusablePendingMessageId, pendingMessage\) : conversationMessages\.value\.push\(pendingMessage\)/)
})
test('isLowConfidenceTravelApplicationPlan gates preview behind confirmation when model confidence is low', () => {
const lowConfidenceModelPlan = normalizeWorkbenchAiIntentPlan({
planning_source: 'llm_function_call',
tasks: [{
task_type: 'expense_application',
assigned_agent: 'application_assistant',
requested_action: 'preview',
confidence: 0.4,
ontology_fields: { location: '上海', reason: '部署' }
}]
}, { prompt: '上海那边好像要过去一趟搞部署' })
assert.equal(lowConfidenceModelPlan.source, WORKBENCH_AI_INTENT_SOURCE_MODEL)
assert.equal(isLowConfidenceTravelApplicationPlan(lowConfidenceModelPlan), true)
const highConfidenceModelPlan = normalizeWorkbenchAiIntentPlan({
planning_source: 'llm_function_call',
tasks: [{
task_type: 'expense_application',
assigned_agent: 'application_assistant',
requested_action: 'preview',
confidence: 0.9,
ontology_fields: { location: '上海' }
}]
}, { prompt: '去上海出差' })
assert.equal(isLowConfidenceTravelApplicationPlan(highConfidenceModelPlan), false)
const ruleFallbackPlan = buildRuleFallbackWorkbenchAiIntentPlan('去上海出差,辅助部署,交通火车')
assert.equal(ruleFallbackPlan.source, WORKBENCH_AI_INTENT_SOURCE_RULE_FALLBACK)
assert.equal(isLowConfidenceTravelApplicationPlan(ruleFallbackPlan), false)
const explicitSubmitLowConfidence = normalizeWorkbenchAiIntentPlan({
planning_source: 'llm_function_call',
tasks: [{
task_type: 'expense_application',
assigned_agent: 'application_assistant',
requested_action: 'submit',
confidence: 0.3,
ontology_fields: { location: '上海' }
}]
}, { prompt: '直接提交' })
assert.equal(explicitSubmitLowConfidence.requestedAction, 'submit')
assert.equal(isLowConfidenceTravelApplicationPlan(explicitSubmitLowConfidence), false)
assert.equal(isLowConfidenceTravelApplicationPlan(null), false)
})
test('workbench AI mode routes low confidence travel application plan to confirmation prompt', () => {
assert.match(personalWorkbenchAiModeScript, /isLowConfidenceTravelApplicationPlan\(intentPlan\)/)
assert.match(personalWorkbenchAiModeScript, /function startModelPlannedTravelApplicationConfirmation\(/)
assert.match(personalWorkbenchAiModeScript, /action_type:\s*'ai_application_confirm_intent'/)
assert.match(personalWorkbenchAiModeScript, /需要确认:您是要发起出差申请吗/)
})
test('shouldRequestWorkbenchAiIntentPlan skips chitchat and only triggers on business keywords', () => {
assert.equal(shouldRequestWorkbenchAiIntentPlan('你好'), false)
assert.equal(shouldRequestWorkbenchAiIntentPlan('谢谢'), false)
assert.equal(shouldRequestWorkbenchAiIntentPlan('嗯'), false)
assert.equal(shouldRequestWorkbenchAiIntentPlan('ok'), false)
assert.equal(shouldRequestWorkbenchAiIntentPlan('1'), false)
assert.equal(shouldRequestWorkbenchAiIntentPlan('帮我查报销'), true)
assert.equal(shouldRequestWorkbenchAiIntentPlan('我要出差'), true)
assert.equal(shouldRequestWorkbenchAiIntentPlan('帮我查询上海差旅标准'), true)
assert.equal(shouldRequestWorkbenchAiIntentPlan('删除3天前的草稿'), true)
})