From b4e6f7e07eda3e8074264fb733e277cf3960c6fe Mon Sep 17 00:00:00 2001 From: Suzumiya Date: Wed, 16 Sep 2026 00:23:13 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E6=97=B6=E4=BA=A4=E6=9B=BF=E6=B8=B2?= =?UTF-8?q?=E6=9F=93=E7=9A=84=E5=88=87=E5=88=86=E7=82=B9=E5=90=B8=E9=99=84?= =?UTF-8?q?=E5=88=B0=E5=8F=A5=E5=AD=90=E8=BE=B9=E7=95=8C=EF=BC=8C=E9=81=BF?= =?UTF-8?q?=E5=85=8D=E6=8A=8A=E6=96=87=E6=9C=AC=E5=88=87=E7=A2=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - renderLiveStream:工具切分点向后吸附到最近的句末标点或换行(最多前探 200 字符) - 同一条句子里的多个工具因此落到同一切点,自动合并成一个块(不再出现 轻|量|的 这种碎片) - 找不到边界或已到文本末尾时保留原位置,不丢内容 --- .../ProjectSupervisorView.tsx | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx b/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx index bdbcc92df..e50ee4028 100644 --- a/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx +++ b/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx @@ -381,10 +381,38 @@ export function ProjectSupervisorView({ */ const renderLiveStream = (liveText: string, calls: typeof liveToolCalls) => { const offsets = directToolTextOffsets[liveToolCallTurnId] ?? {}; + /** + * 把切分点吸附到句子边界:工具事件与文本增量是交替到达的,直接按"当时的长度"切 + * 会把句子切碎(历史上出现过"我来做一次轻|量|的"这种)。这里向后找最近的 + * 句末标点或换行,同一句里的多个工具因此落到同一个切点、自动合并成一块。 + */ + const snapToBoundary = (source: string, index: number) => { + const start = Math.max(0, Math.min(index, source.length)); + const limit = Math.min(source.length, start + 200); + for (let cursor = start; cursor < limit; cursor += 1) { + const char = source[cursor]; + if (char === '\n') { + return cursor + 1; + } + if ('。!?;!?;'.includes(char)) { + return cursor + 1; + } + if ( + char === '.' && + (source[cursor + 1] === ' ' || source[cursor + 1] === undefined) + ) { + return cursor + 1; + } + } + return start; + }; const groups = new Map(); for (const call of calls) { const offset = offsets[call.id]; - const key = Number.isFinite(offset) ? Number(offset) : liveText.length; + const key = snapToBoundary( + liveText, + Number.isFinite(offset) ? Number(offset) : liveText.length, + ); const bucket = groups.get(key) ?? []; bucket.push(call); groups.set(key, bucket);