// deploy/m2_probe2.js —— 精确诊断探针(PM 使用,在远端运行) // 观察完整流式生命周期:网络请求 + SSE 帧消费 + 流水线状态 + 答案块 const { chromium } = require('playwright'); (async () => { const browser = await chromium.launch(); const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 } }); const page = await ctx.newPage(); const netLog = []; page.on('request', (r) => { if (r.url().includes('/api/')) netLog.push(`REQ ${r.method()} ${r.url()}`); }); page.on('response', (r) => { if (r.url().includes('/api/')) netLog.push(`RES ${r.status()} ${r.url()}`); }); const jsErrors = []; page.on('pageerror', (e) => jsErrors.push(e.message.slice(0, 300))); page.on('console', (m) => { if (m.type() === 'error') jsErrors.push('[console] ' + m.text().slice(0, 300)); }); await page.goto('http://127.0.0.1:5180', { waitUntil: 'domcontentloaded' }); await page.waitForSelector('.app-shell', { timeout: 15000 }); await page.click('.new-task-btn'); await page.waitForSelector('.composer textarea', { timeout: 15000 }); await page.fill('.composer textarea', 'SPL gene family in rice'); await page.click('.send-btn'); const t0 = Date.now(); let lastKey = ''; let lastBlockCount = -1; while (Date.now() - t0 < 150000) { const snap = await page.evaluate(() => { const cards = Array.from(document.querySelectorAll('.pipeline-card')); const card = cards[0]; const steps = card ? Array.from(card.querySelectorAll('.p-step')) : []; const msg = document.querySelector('.msg.assistant'); // 只统计 blocks 区域,排除流水线卡文本 const blocks = msg ? Array.from(msg.querySelectorAll('.msg-body > *')) : []; const nonPipelineText = blocks .filter((b) => !b.classList.contains('pipeline-card')) .map((b) => b.textContent || '') .join(' '); return { cards: cards.length, states: steps.map((s) => (s.className.includes('done') ? 'D' : s.className.includes('running') ? 'R' : 'W')).join(''), stepCount: steps.length, blockCount: blocks.length, blockTypes: blocks.map((b) => b.className || b.tagName).join(','), nonPipelineLen: nonPipelineText.length, streamCursor: !!document.querySelector('.stream-cursor'), }; }); const key = `${snap.cards}|${snap.states}|${snap.blockCount}|${snap.nonPipelineLen}|${snap.streamCursor}`; if (key !== lastKey) { lastKey = key; console.log(`T+${Math.round((Date.now() - t0) / 1000)}s`, JSON.stringify(snap)); } if (snap.states === 'DDDDD' && !snap.streamCursor && snap.nonPipelineLen > 100) break; if (snap.states === 'DDDDD' && snap.blockCount > 1 && snap.nonPipelineLen > 100) break; if (snap.blockCount > 3 && snap.nonPipelineLen > 100 && !snap.streamCursor) break; await new Promise((r) => setTimeout(r, 2000)); } console.log('\n--- 网络日志 ---'); netLog.forEach((l) => console.log(l)); console.log('\n--- JS 错误 ---'); (jsErrors.length ? jsErrors : ['无']).forEach((l) => console.log(l.slice(0, 400))); // 最终:检查 store 状态(Vue app 无法直接访问,检查 DOM 证据) const fin = await page.evaluate(() => { const msg = document.querySelector('.msg.assistant'); const blocks = msg ? Array.from(msg.querySelectorAll('.msg-body > *')) : []; return { blockClasses: blocks.map((b) => (b.className || b.tagName).slice(0, 40)), text: (msg ? msg.textContent : '').slice(0, 400), }; }); console.log('\n--- 最终块结构 ---'); console.log(JSON.stringify(fin, null, 2)); await browser.close(); })();