diff --git a/src/main/config.ts b/src/main/config.ts index 933aafa..7e0eeff 100644 --- a/src/main/config.ts +++ b/src/main/config.ts @@ -122,7 +122,9 @@ export const DEFAULT_CONFIG: AppConfig = { 你的任务是: - 只能依据用户提供的上下文内容进行回答; - 每个答案中如果有信息来源于上下文,必须标注对应的引用标号(例如:[1],也可以是多个标号,例如:[1][3]); -- 如果问题的信息在上下文中找不到,请直接回复:"我不知道。" +- 如果上下文中没有足够的信息回答问题,不要编造内容,也不要生硬地只回一句"我不知道"。 + 请用一两句自然、平和的话说明手册中暂未查到相关内容,并给出一条简短、可操作的建议, + 例如请对方补充车型/年款、具体部件名称,或把故障现象描述得更具体,以便更准确地定位; - 不得使用自己的知识库或常识来回答问题; - 回答应简洁明了,不添加额外解释或发挥。 diff --git a/src/main/ipc/queryHandler.ts b/src/main/ipc/queryHandler.ts index d8ee487..fbc6906 100644 --- a/src/main/ipc/queryHandler.ts +++ b/src/main/ipc/queryHandler.ts @@ -40,7 +40,12 @@ export function registerQueryHandlers(getWin: () => BrowserWindow | null): void send('query:stage', { stage: '正在生成...' }) if (chunks.length === 0) { - send('query:delta', { delta: '我不知道。' }) + // 走到这里说明知识库里一条记录都没有(search 是全表余弦排序后截断, + // 只要有文档就一定会返回结果),是"还没入库"而不是"没找到相关内容", + // 提示要引导去导入文档,不能只回一句"我不知道"让人以为是模型答不上来 + send('query:delta', { + delta: '知识库中还没有任何文档,暂时无法回答。请先在「设置 → 文档入库」中导入手册,再来提问。' + }) send('query:done', {}) return } diff --git a/src/main/ipc/visionHandler.ts b/src/main/ipc/visionHandler.ts index 7aa31e3..916e684 100644 --- a/src/main/ipc/visionHandler.ts +++ b/src/main/ipc/visionHandler.ts @@ -40,7 +40,10 @@ export function registerVisionHandlers(getWin: () => BrowserWindow | null): void send('vision-query:context', { chunks }) if (chunks.length === 0) { - send('vision-query:delta', { delta: '我不知道。' }) + // 同 queryHandler:这里只可能是知识库为空,不是"没检索到相关内容" + send('vision-query:delta', { + delta: '知识库中还没有任何文档,暂时无法结合手册回答。请先在「设置 → 文档入库」中导入手册,再来提问。' + }) send('vision-query:done', {}) return } diff --git a/src/main/rag/generator.ts b/src/main/rag/generator.ts index 9aec557..3c4931a 100644 --- a/src/main/rag/generator.ts +++ b/src/main/rag/generator.ts @@ -25,6 +25,26 @@ interface ChatMessage { content: string | ContentPart[] } +// 每次请求对模型来说都是无状态的,它并不知道"上一次说了什么",所以只在系统提示词里 +// 写"每次换一种措辞"是无效的——实测同样的输入连发三次,答复一字不差。这里改为每次请求 +// 随机挑一条措辞引导追加到系统提示词末尾,让输入本身产生差异,答复自然就不会千篇一律。 +// +// 之所以不用调高 temperature 来制造随机性:那会同时影响命中文档的正常回答,而维修手册 +// 问答更看重稳定准确。这些引导只在"查不到内容"时才起作用,正常回答不受影响。 +const NO_MATCH_TONE_HINTS = [ + '若本次需要说明手册中未查到相关内容,请以"很抱歉"起头组织这句话。', + '若本次需要说明手册中未查到相关内容,请以"手册里"起头组织这句话。', + '若本次需要说明手册中未查到相关内容,请以"目前检索到的资料"起头组织这句话。', + '若本次需要说明手册中未查到相关内容,请以"这部分内容"起头组织这句话。', + '若本次需要说明手册中未查到相关内容,请用两句话表达:先说明情况,再给一条补充信息的建议。', + '若本次需要说明手册中未查到相关内容,请先简短致歉,再说明可以如何把问题描述得更具体。' +] + +function withToneHint(systemPrompt: string): string { + const hint = NO_MATCH_TONE_HINTS[Math.floor(Math.random() * NO_MATCH_TONE_HINTS.length)] + return `${systemPrompt}\n${hint}` +} + // 生成服务经 er_backend 代理访问其管理的 llama-server(OpenAI 兼容接口,SSE 流式) export async function streamGenerate( question: string, @@ -42,15 +62,17 @@ ${context} 请回答以下问题:${question}` + const finalSystemPrompt = withToneHint(systemPrompt) + console.log('\n[generator] ---- 发送给 LLM 的完整消息 ----') console.log(`[generator] 服务地址:${ER_BACKEND_URL}(er_backend 代理)`) - console.log('[generator] system prompt:\n', systemPrompt) + console.log('[generator] system prompt:\n', finalSystemPrompt) console.log('[generator] user message:\n', userMessage) console.log('[generator] ---- 消息结束 ----\n') await streamChatCompletion( [ - { role: 'system', content: systemPrompt }, + { role: 'system', content: finalSystemPrompt }, { role: 'user', content: userMessage } ], callbacks