From 4870e8a577c818f079770e1419426a51ddcbcad5 Mon Sep 17 00:00:00 2001 From: Huarch Date: Wed, 20 May 2026 17:51:59 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BC=9A=E8=AF=9D=E6=A0=87?= =?UTF-8?q?=E9=A2=98=E7=94=9F=E6=88=90=E9=80=BB=E8=BE=91=EF=BC=8C=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E6=B6=88=E6=81=AF=E9=99=90=E5=88=B6=E4=B8=8E=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/chatSession.ts | 43 +++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/src/routes/chatSession.ts b/src/routes/chatSession.ts index 03e90bf..7cb21dc 100644 --- a/src/routes/chatSession.ts +++ b/src/routes/chatSession.ts @@ -4,7 +4,10 @@ import { type OpencodeRuntimeAdapter } from "../runtime/opencode.js"; import { collectTextContent } from "./chatStream.js"; -const TITLE_PROMPT_TIMEOUT_MS = 2500; +const TITLE_PROMPT_TIMEOUT_MS = 5000; +const TITLE_CONTEXT_MESSAGE_LIMIT = 40; +const TITLE_CONTEXT_CHAR_LIMIT = 2400; +const TITLE_CONTEXT_MESSAGE_CHAR_LIMIT = 240; const buildSessionTitle = (message: string) => { const normalized = message.replace(/\s+/g, " ").trim(); @@ -18,7 +21,7 @@ const buildTitleConversationContext = async ( runtime: OpencodeRuntimeAdapter, sessionId: string, ) => { - const messages = await runtime.messages(sessionId, 12); + const messages = await runtime.messages(sessionId, TITLE_CONTEXT_MESSAGE_LIMIT); const recentMessages = messages .filter( (message) => @@ -26,19 +29,35 @@ const buildTitleConversationContext = async ( ) .map((message) => ({ role: message.info.role, - content: collectTextContent(message.parts).replace(/\s+/g, " ").trim(), + content: collectTextContent(message.parts) + .replace(/\s+/g, " ") + .trim() + .slice(0, TITLE_CONTEXT_MESSAGE_CHAR_LIMIT), })) - .filter((message) => message.content.length > 0) - .slice(-6); + .filter((message) => message.content.length > 0); if (recentMessages.length === 0) { return ""; } - return recentMessages - .map((message) => `${message.role === "user" ? "用户" : "助手"}:${message.content}`) - .join("\n") - .slice(0, 2400); + const formattedMessages = recentMessages.map( + (message) => `${message.role === "user" ? "用户" : "助手"}:${message.content}`, + ); + const fullConversation = formattedMessages.join("\n"); + if (fullConversation.length <= TITLE_CONTEXT_CHAR_LIMIT) { + return fullConversation; + } + + const headCount = Math.min(4, formattedMessages.length); + const tailCount = Math.min(8, Math.max(0, formattedMessages.length - headCount)); + const middleOmitted = formattedMessages.length > headCount + tailCount; + const summary = [ + ...formattedMessages.slice(0, headCount), + ...(middleOmitted ? ["……(中间省略若干轮对话)"] : []), + ...formattedMessages.slice(-tailCount), + ].join("\n"); + + return summary.slice(0, TITLE_CONTEXT_CHAR_LIMIT); }; const normalizeGeneratedTitle = (rawTitle: string, fallback: string) => { @@ -75,15 +94,17 @@ export const generateSessionTitle = async ( titleSession.id, [ "你是会话标题生成器。", - "请根据用户问题生成一个 8-16 字中文标题。", + "请根据下面整段多轮对话生成一个 8-16 字中文标题。", "要求:简洁、可读、避免标点、不要引号、不要解释。", - "请优先概括最近这轮对话的核心任务或结论。", + "先理解完整对话,再概括核心任务或结论。", + "不要直接照抄用户任一条消息原文。", "只输出标题本身。", "", conversation, ].join("\n"), ) .then(async () => { + await runtime.waitForSessionIdle(titleSession.id, TITLE_PROMPT_TIMEOUT_MS); const messages = await runtime.messages(titleSession.id, 20); const assistantMessage = [...messages] .reverse()