From a7d0e82f612214a4c198f0929fa9911dcd67e21b Mon Sep 17 00:00:00 2001 From: ingkele <598792849@qq.com> Date: Mon, 19 Oct 2026 19:31:08 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20AI=20=E5=AE=A2?= =?UTF-8?q?=E6=9C=8D=E5=9B=9E=E5=A4=8D=E5=86=85=E5=AE=B9=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=EF=BC=88=E5=8C=BA=E5=88=86=20SSE=20delta/completed=20=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=EF=BC=8Ccompleted=20=E4=BB=85=E4=BD=9C=E5=85=9C?= =?UTF-8?q?=E5=BA=95=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../router/service/client/ai/pub/chat.js | 36 +++++++++++++++++-- .../service/client/ai/pub/jjxDoReview.js | 7 +++- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/chat.js b/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/chat.js index c43baba..23c0e5e 100644 --- a/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/chat.js +++ b/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/chat.js @@ -25,22 +25,52 @@ module.exports = { } }); - let answer = ''; + // 扣子 v3 流式事件说明: + // conversation.message.delta —— 增量片段(应当拼接) + // conversation.message.completed —— 该条消息的「完整内容」(不能再次拼接,否则重复) + // 因此必须按事件类型区分,只累加 delta;completed 仅用于兜底。 let conversationId = ''; + const msgMap = new Map(); // messageId -> { text } + let curEvent = ''; const text = res.data || ''; const lines = text.split('\n'); for (const line of lines) { + if (line.startsWith('event:')) { + curEvent = line.slice(6).trim(); + continue; + } if (!line.startsWith('data:')) continue; const d = line.slice(5).trim(); if (d === '[DONE]' || d === '"[DONE]"') continue; try { const p = JSON.parse(d); if (p.conversation_id) conversationId = p.conversation_id; - if (p.type === 'answer' && p.content) { - answer += p.content; // 拼接,不是覆盖 + // 只取正文,忽略 verbose(工具调用日志)等其他类型 + if (p.type !== 'answer' || typeof p.content !== 'string') continue; + const key = p.id || '__default__'; + const rec = msgMap.get(key) || { text: '' }; + + if (curEvent === 'conversation.message.delta') { + // 防御:万一上游发的是累计内容而非增量,用长度+前缀判断避免重复 + if (p.content.length > rec.text.length && p.content.indexOf(rec.text) === 0) { + rec.text = p.content; + } else { + rec.text += p.content; + } + msgMap.set(key, rec); + } else if (curEvent === 'conversation.message.completed') { + // 已有 delta 拼出的内容就丢掉 completed,避免重复;没有则用它兜底 + if (!rec.text && p.content) { + rec.text = p.content; + msgMap.set(key, rec); + } } } catch (e) { /* 跳过 */ } } + + // 按消息首次出现顺序拼接(多段消息之间自带换行,直接相连) + let answer = ''; + for (const rec of msgMap.values()) answer += rec.text; if (!answer) answer = '暂时无法回复,请拨打 400-686-5559 咨询'; return { diff --git a/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/jjxDoReview.js b/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/jjxDoReview.js index 86b3b98..c1c27f7 100644 --- a/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/jjxDoReview.js +++ b/uniCloud-tcb/cloudfunctions/router/service/client/ai/pub/jjxDoReview.js @@ -73,8 +73,13 @@ module.exports = { if (p.conversation_id) conversationId = p.conversation_id; // 处理回答内容 + // 注意:conversation.message.completed 携带的是该条消息的完整内容, + // 若前面已经通过 delta 拼过,再拼一次就会重复,这里做去重 if (p.type === 'answer' && p.content) { - answer += p.content; + const isDup = eventName === 'conversation.message.completed' && answer.indexOf(p.content) >= 0; + if (!isDup) { + answer += p.content; + } // 每积累 200 字更新一次数据库(前端轮询能看到进度) if (answer.length - lastUpdateLen >= 200) {