Files
memind/scripts/simulate-tang-wechat-itl-flow.mjs
T
john d58dc2a251 feat(wechat): add Intent Transaction Layer with unified task schema
Introduce Draft → Confirm → Commit flow for WeChat schedule intents behind
feature flags, plus h5_tasks dual-write/read aggregation and rollout scripts
so reminders and automations get explicit user confirmation before persisting.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-24 15:59:17 +08:00

189 lines
5.7 KiB
JavaScript

#!/usr/bin/env node
/**
* 唐用户典型微信话术 ITL 全链路离线模拟(无需 DB)
*/
import { classifyUserIntent } from '../intent-classifier.mjs';
import { handleWechatIntentTransaction } from '../wechat/handlers/intent-transaction.mjs';
import { handleWechatScheduledTaskIntent } from '../wechat/handlers/scheduled-task.mjs';
import { formatUnifiedTaskListReply } from '../task-unified-service.mjs';
import { formatQueryGuardReply } from '../intent-query-guard.mjs';
const TANG = { userId: 'a70ff537-8908-486e-9b6c-042e07cc25db' };
const env = {
H5_INTENT_TRANSACTION_ENABLED: '1',
H5_UNIFIED_TASKS_ENABLED: '1',
H5_DEFAULT_TIMEZONE: 'Asia/Shanghai',
};
const MOCK_TASKS = [
{
type: 'automation',
title: '每日新闻页',
trigger: { repeat: 'daily', hour: 5, minute: 30 },
nextRunAt: Date.now() + 3600000,
},
{
type: 'automation',
title: '每日天气预报',
trigger: { repeat: 'daily', hour: 8, minute: 0 },
nextRunAt: Date.now() + 7200000,
},
];
function createDraftStore() {
let pending = null;
return {
async getPendingDraft(userId) {
return pending?.userId === userId ? pending : null;
},
async createDraft(payload) {
pending = { id: 'draft-sim', status: 'draft', ...payload };
return pending;
},
async cancelDraft() { pending = null; return { status: 'cancelled' }; },
async markDraftCommitted(_id, _userId, committedRef) {
pending = { ...pending, status: 'committed', committedRef };
return pending;
},
};
}
function createServices() {
const syncCalls = [];
const scheduleCalls = [];
return {
syncCalls,
scheduleCalls,
taskUnifiedService: {
async listUserTasks() { return MOCK_TASKS; },
async syncFromCommit(payload) { syncCalls.push(payload.kind); return { id: 'u-1' }; },
},
scheduleService: {
async createItem(payload) {
scheduleCalls.push(['createItem', payload.title]);
return { id: 'item-1', ...payload };
},
async createReminder(payload) {
scheduleCalls.push(['createReminder']);
return { id: 'rem-1', itemId: payload.itemId, remindAt: payload.remindAt, channel: 'wechat' };
},
buildTodoDigestText: async () => '今天有 1 条待办:跟进合同。',
},
scheduledTaskService: {
async listTasks() {
return MOCK_TASKS.map((task, index) => ({
id: `task-${index}`,
title: task.title,
recurrence: 'daily',
hour: task.trigger.hour,
minute: task.trigger.minute,
nextRunAt: task.nextRunAt,
timezone: 'Asia/Shanghai',
}));
},
async cancelTask() {
return { id: 'task-news', title: '每日新闻页', status: 'cancelled' };
},
},
};
}
async function simulate(text) {
const classification = classifyUserIntent(text);
const services = createServices();
const drafts = createDraftStore();
let handler = 'none';
let reply = null;
const itl = await handleWechatIntentTransaction({
intent: { agentText: text, msgId: `sim-${text.slice(0, 8)}`, msgType: 'text' },
user: TANG,
intentDraftService: drafts,
taskUnifiedService: services.taskUnifiedService,
scheduleService: services.scheduleService,
scheduledTaskService: services.scheduledTaskService,
env,
});
if (itl) {
handler = 'itl';
reply = itl;
} else {
const schedTask = await handleWechatScheduledTaskIntent({
intent: { agentText: text, msgId: 'sim-st' },
user: TANG,
scheduledTaskService: services.scheduledTaskService,
});
if (schedTask) {
handler = 'scheduled_task';
reply = schedTask;
}
}
if (classification.layer === 'L0' && !reply) {
reply = await formatQueryGuardReply(text, {
scheduleService: services.scheduleService,
scheduledTaskService: services.scheduledTaskService,
taskUnifiedService: services.taskUnifiedService,
userId: TANG.userId,
timezone: 'Asia/Shanghai',
});
handler = 'query_guard';
}
return { text, classification, handler, replyPreview: String(reply ?? '').split('\n').slice(0, 4).join(' / ') };
}
const cases = [
'有没有我的新闻定时任务',
'下午2点半提醒我开项目计划例会',
'确认',
'取消每日新闻任务',
'每天5点30帮我做今日新闻页面',
'设置提醒',
];
async function main() {
console.log('=== 唐用户 ITL 话术模拟 ===\n');
const drafts = createDraftStore();
const services = createServices();
for (const text of cases.slice(0, 2)) {
const row = await simulate(text);
console.log(JSON.stringify(row, null, 2));
}
await handleWechatIntentTransaction({
intent: { agentText: '下午2点半提醒我开项目计划例会', msgId: 'sim-card', msgType: 'text' },
user: TANG,
intentDraftService: drafts,
taskUnifiedService: services.taskUnifiedService,
scheduleService: services.scheduleService,
scheduledTaskService: services.scheduledTaskService,
env,
});
const confirm = await handleWechatIntentTransaction({
intent: { agentText: '确认', msgId: 'sim-confirm', msgType: 'text' },
user: TANG,
intentDraftService: drafts,
taskUnifiedService: services.taskUnifiedService,
scheduleService: services.scheduleService,
scheduledTaskService: services.scheduledTaskService,
env,
});
console.log(JSON.stringify({
text: '确认',
handler: 'itl_confirm',
replyPreview: String(confirm ?? '').split('\n').slice(0, 3).join(' / '),
syncKinds: services.syncCalls,
scheduleCalls: services.scheduleCalls,
}, null, 2));
console.log('\nunified list:', formatUnifiedTaskListReply(MOCK_TASKS).split('\n').join(' | '));
}
main().catch((err) => {
console.error(err);
process.exit(1);
});