#!/usr/bin/env node /** * Local smoke: verify "世界杯现在赛况如何" routes to agent_orchestration. * Usage: node scripts/verify-world-cup-routing.mjs */ import crypto from 'node:crypto'; import { loadH5Environment } from './load-env.mjs'; import { createDbPool } from '../db.mjs'; import { createUserAuth } from '../user-auth.mjs'; loadH5Environment(import.meta.dirname); const PORTAL = `http://127.0.0.1:${process.env.H5_PORT ?? 8081}`; const USERNAME = 'john'; const PASSWORD = process.env.JOHN_PASSWORD ?? process.env.H5_ACCESS_PASSWORD ?? '888888'; const QUERY = '世界杯现在赛况如何'; const MAX_WAIT_MS = 120_000; function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } async function login() { const pool = await createDbPool(); const auth = createUserAuth(pool); let result = await auth.login({ username: USERNAME, password: PASSWORD, ip: '127.0.0.1' }); if (!result.ok) { throw new Error(`john 登录失败(请设置 JOHN_PASSWORD 环境变量): ${result.message ?? 'unknown'}`); } await pool.end(); return result.token; } async function createRun(token) { const requestId = crypto.randomUUID(); const response = await fetch(`${PORTAL}/api/agent/runs`, { method: 'POST', headers: { 'Content-Type': 'application/json', Cookie: `tkmind_user_session=${token}`, }, body: JSON.stringify({ request_id: requestId, user_message: { id: crypto.randomUUID(), role: 'user', content: [{ type: 'text', text: QUERY }], metadata: { userVisible: true, displayText: QUERY }, }, }), }); const body = await response.json().catch(() => ({})); if (!response.ok) { throw new Error(`POST /agent/runs ${response.status}: ${JSON.stringify(body)}`); } const run = body.run ?? body; return { runId: run.id, requestId, status: run.status }; } async function waitForRun(token, runId) { const started = Date.now(); while (Date.now() - started < MAX_WAIT_MS) { const response = await fetch(`${PORTAL}/api/agent/runs/${runId}`, { headers: { Cookie: `tkmind_user_session=${token}` }, }); const payload = await response.json().catch(() => ({})); const body = payload.run ?? payload; if (!response.ok) { throw new Error(`GET /agent/runs/${runId} ${response.status}: ${JSON.stringify(payload)}`); } if (['succeeded', 'failed'].includes(body.status)) { return body; } await sleep(1000); } throw new Error(`run ${runId} 未在 ${MAX_WAIT_MS}ms 内完成`); } async function loadRunEvents(runId) { const pool = await createDbPool(); const [rows] = await pool.query( `SELECT event_type, data_json, created_at FROM h5_agent_run_events WHERE run_id = ? ORDER BY created_at ASC`, [runId], ); await pool.end(); return rows; } async function loadAssistantReply(sessionId) { const pool = await createDbPool(); const [rows] = await pool.query( `SELECT text FROM h5_conversation_messages WHERE agent_session_id = ? AND role = 'assistant' ORDER BY created_at DESC LIMIT 1`, [sessionId], ); await pool.end(); return rows[0]?.text ?? ''; } async function main() { console.log(`==> Portal: ${PORTAL}`); const token = await login(); console.log('==> john 登录成功'); const { runId } = await createRun(token); console.log(`==> run 已创建: ${runId}`); const run = await waitForRun(token, runId); console.log(`==> run 终态: ${run.status} session=${run.sessionId ?? run.agent_session_id ?? 'n/a'}`); const events = await loadRunEvents(runId); const routed = events.find((row) => row.event_type === 'intent_routed'); const route = routed?.data_json?.route ?? null; const source = routed?.data_json?.source ?? null; const reason = routed?.data_json?.reason ?? null; console.log('\n--- intent_routed ---'); console.log(JSON.stringify({ route, source, reason, suggestedSkill: routed?.data_json?.suggestedSkill ?? null }, null, 2)); const sessionId = run.sessionId ?? run.agent_session_id; if (sessionId) { const reply = await loadAssistantReply(sessionId); console.log('\n--- assistant 回复摘要 ---'); console.log(reply.slice(0, 500)); if (/无法实时获取|无法获取.*最新赛况/i.test(reply)) { console.error('\nFAIL: 仍走 direct_chat 拒答路径'); process.exit(1); } } if (route !== 'agent_orchestration') { console.error(`\nFAIL: 期望 route=agent_orchestration,实际 ${route}`); process.exit(1); } if (run.status !== 'succeeded') { console.error(`\nFAIL: run 终态 ${run.status}: ${run.error ?? ''}`); process.exit(1); } console.log('\nPASS: 世界杯查询已走 agent 路径'); } main().catch((err) => { console.error(err instanceof Error ? err.message : err); process.exit(1); });