From 9f9ad337abbedf27083901ed72f8df714390fed6 Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 28 Apr 2026 13:30:17 +0800 Subject: [PATCH] fix(insight): trim prompt noise and smooth settings animation --- electron/services/insightService.ts | 56 ++++++----------------------- src/pages/SettingsPage.scss | 18 +++++----- src/pages/SettingsPage.tsx | 11 +++--- 3 files changed, 26 insertions(+), 59 deletions(-) diff --git a/electron/services/insightService.ts b/electron/services/insightService.ts index 4bfdeba..5554a29 100644 --- a/electron/services/insightService.ts +++ b/electron/services/insightService.ts @@ -10,7 +10,7 @@ * 设计原则: * - 不引入任何额外 npm 依赖,使用 Node 原生 https 模块调用 OpenAI 兼容 API * - 所有失败静默处理,不影响主流程 - * - 当日触发记录(sessionId + 时间列表)随 prompt 一起发送,让模型自行判断是否克制 + * - 触发频率、冷却与名单过滤均在本地完成,不把调度统计塞进模型 prompt */ import https from 'https' @@ -449,7 +449,7 @@ class InsightService { try { const endpoint = buildApiUrl(apiBaseUrl, '/chat/completions') - const requestMessages = [{ role: 'user', content: appendPromptCurrentTime('请回复"连接成功"四个字。') }] + const requestMessages = [{ role: 'user', content: '请回复"连接成功"四个字。' }] insightDebugSection( 'INFO', 'AI 测试连接请求', @@ -827,26 +827,13 @@ ${topMentionText} } /** - * 记录触发并返回该会话今日所有触发时间(用于组装 prompt)。 + * 记录成功推送的见解,用于设置页展示今日触发统计。 */ - private recordTrigger(sessionId: string): string[] { + private recordTrigger(sessionId: string): void { this.resetIfNewDay() const existing = this.todayTriggers.get(sessionId) ?? { timestamps: [] } existing.timestamps.push(Date.now()) this.todayTriggers.set(sessionId, existing) - return existing.timestamps.map(formatTimestamp) - } - - /** - * 获取今日全局已触发次数(所有会话合计),用于 prompt 中告知模型全局上下文。 - */ - private getTodayTotalTriggerCount(): number { - this.resetIfNewDay() - let total = 0 - for (const record of this.todayTriggers.values()) { - total += record.timestamps.length - } - return total } private formatWeiboTimestamp(raw: string): string { @@ -905,7 +892,7 @@ ${topMentionText} if (lines.length === 0) return '' insightLog('INFO', `已加载 ${lines.length} 条朋友圈内容 (sessionId=${sessionId})`) - return `以下是该联系人的朋友圈内容(仅人类可读原文,最近 ${lines.length} 条):\n${lines.join('\n')}` + return `近期朋友圈内容(最近 ${lines.length} 条):\n${lines.join('\n')}` } catch (error) { insightLog('WARN', `拉取朋友圈内容失败 (sessionId=${sessionId}): ${(error as Error).message}`) return '' @@ -917,7 +904,6 @@ ${topMentionText} if (!allowSocialContext) return '' const rawCookie = String(this.config.get('aiInsightWeiboCookie') || '').trim() - const hasCookie = rawCookie.length > 0 const bindings = (this.config.get('aiInsightWeiboBindings') as Record | undefined) || {} @@ -938,10 +924,7 @@ ${topMentionText} return `[微博 ${time}] ${text}` }) insightLog('INFO', `已加载 ${lines.length} 条微博公开内容 (uid=${uid})`) - const riskHint = hasCookie - ? '' - : '\n提示:未配置微博 Cookie,使用移动端公开接口抓取,可能因平台风控导致获取失败或内容较少。' - return `近期公开社交平台内容(来源:微博,最近 ${lines.length} 条):\n${lines.join('\n')}${riskHint}` + return `近期公开社交平台内容(来源:微博,最近 ${lines.length} 条):\n${lines.join('\n')}` } catch (error) { insightLog('WARN', `拉取微博公开内容失败 (uid=${uid}): ${(error as Error).message}`) return '' @@ -1177,10 +1160,6 @@ ${topMentionText} // ── 构建 prompt ──────────────────────────────────────────────────────────── - // 今日触发统计(让模型具备时间与克制感) - const sessionTriggerTimes = this.recordTrigger(sessionId) - const totalTodayTriggers = this.getTodayTotalTriggerCount() - let contextSection = '' if (allowContext) { try { @@ -1211,24 +1190,10 @@ ${topMentionText} const customPrompt = (this.config.get('aiInsightSystemPrompt') as string) || '' const systemPrompt = customPrompt.trim() || DEFAULT_SYSTEM_PROMPT - // 可变的上下文统计信息放在 user message 里,保持 system prompt 稳定不变 - // 这样 provider 端(Anthropic/OpenAI)能最大化命中 prompt cache,降低费用 - const triggerDesc = - triggerReason === 'silence' - ? `你已经 ${silentDays} 天没有和「${resolvedDisplayName}」聊天了。` - : `你最近和「${resolvedDisplayName}」有新的聊天动态。` - - const todayStatsDesc = - sessionTriggerTimes.length > 1 - ? `今天你已经针对「${resolvedDisplayName}」收到过 ${sessionTriggerTimes.length - 1} 条见解(时间:${sessionTriggerTimes.slice(0, -1).join('、')}),请适当克制。` - : `今天你还没有针对「${resolvedDisplayName}」发出过见解。` - - const globalStatsDesc = `今天全部联系人合计已触发 ${totalTodayTriggers} 条见解。` - const userPromptBase = [ - `触发原因:${triggerDesc}`, - `时间统计:${todayStatsDesc}`, - `全局统计:${globalStatsDesc}`, + triggerReason === 'silence' && silentDays + ? `已 ${silentDays} 天未联系「${resolvedDisplayName}」。` + : '', contextSection, momentsContextSection, socialContextSection, @@ -1250,7 +1215,7 @@ ${topMentionText} `接口地址:${endpoint}`, `模型:${model}`, `Max Tokens:${maxTokens}`, - `触发原因:${triggerReason}`, + `触发类型:${triggerReason}`, `上下文开关:${allowContext ? '开启' : '关闭'}`, `上下文条数:${contextCount}`, '', @@ -1314,6 +1279,7 @@ ${topMentionText} } insightLog('INFO', `已为 ${resolvedDisplayName} 推送见解`) + this.recordTrigger(sessionId) } catch (e) { insightDebugSection( 'ERROR', diff --git a/src/pages/SettingsPage.scss b/src/pages/SettingsPage.scss index eccde56..ecf1d8d 100644 --- a/src/pages/SettingsPage.scss +++ b/src/pages/SettingsPage.scss @@ -916,16 +916,18 @@ } .insight-collapsible-setting { - display: grid; - grid-template-rows: 0fr; + max-height: 0; opacity: 0; - transform: translateY(-4px); - transition: grid-template-rows 0.22s ease, opacity 0.18s ease, transform 0.2s ease; + overflow: hidden; + transform: translate3d(0, -4px, 0); + contain: layout paint; + will-change: max-height, opacity, transform; + transition: max-height 0.2s ease, opacity 0.18s ease, transform 0.2s ease; &.expanded { - grid-template-rows: 1fr; + max-height: 128px; opacity: 1; - transform: translateY(0); + transform: translate3d(0, 0, 0); } &.collapsed { @@ -934,8 +936,8 @@ } .insight-collapsible-setting-inner { - min-height: 0; - overflow: hidden; + padding-top: 2px; + backface-visibility: hidden; } /* Premium Switch Style */ diff --git a/src/pages/SettingsPage.tsx b/src/pages/SettingsPage.tsx index 0980328..0d508ed 100644 --- a/src/pages/SettingsPage.tsx +++ b/src/pages/SettingsPage.tsx @@ -3301,7 +3301,7 @@ function SettingsPage({ onClose }: SettingsPageProps = {}) { 开启后,触发见解时会将该联系人最近 N 条聊天记录发送给 AI,分析质量显著提升。
- 关闭时:AI 仅知道统计摘要(沉默天数等),输出质量较低。 + 关闭时:不会发送聊天原文,输出质量较低。
开启时:聊天文本内容(不含图片、语音)会通过你配置的 API 发送给模型提供商。请确认你信任该服务商。
@@ -3352,8 +3352,7 @@ function SettingsPage({ onClose }: SettingsPageProps = {}) {
- 仅对列表中勾选了「朋友圈」且会触发见解的私聊联系人生效。 - 程序只会在触发见解时按需读取最近朋友圈内容,不会做后台持续扫描。 + 开启后,可在下方列表为私聊联系人单独允许朋友圈补充分析。程序只会在触发见解时按需读取,不会做后台持续扫描。
{aiInsightAllowMomentsContext ? '已开启' : '已关闭'} @@ -3377,7 +3376,7 @@ function SettingsPage({ onClose }: SettingsPageProps = {}) {
- 仅提取人类可读文本原文,不会拼接朋友圈原始 XML 字段。 + 发送给 AI 的朋友圈最大条数。条数越多上下文越充分,token 消耗也越多。

- 触发方式一:活跃会话分析 — 每当微信数据库变化(即你收到新消息)时,经过 500ms 防抖后,对符合黑白名单规则的活跃会话进行分析。
+ 触发方式一:活跃会话分析 — 每当微信数据库变化(即你收到新消息)时,经过约 2 秒防抖后,对符合黑白名单规则的活跃会话进行分析。
触发方式二:沉默扫描 — 每 4 小时独立扫描一次,对超过阈值天数无消息的联系人发出提醒。
- 时间观念 — 每次调用时,AI 会收到今天已向该联系人和全局发出过多少次见解,由 AI 自行决定是否需要克制。
+ 频率控制 — 冷却期、沉默间隔、黑白名单均在本地判断,不额外发送给模型。
隐私 — 所有分析请求均直接从你的电脑发往你填写的 API 地址,不经过任何 WeFlow 服务器。