feat: v0.3.18 上下文压缩机制修复 + MCP 工具就绪竞态修复 + 记忆系统加固
【上下文压缩机制修复】 - engine.ts 新增 lastRealInputTokens 记录 LLM 返回的真实输入 token,压缩判断取 max(估算值, 真实值),避免估算偏低导致不压缩但 API 413 - 修复 effectiveContextWindow 缺少默认值导致 compressionThreshold 变 NaN、压缩永不触发的 bug(添加 ?? 128_000 兜底) - compressMessages 保留区从固定 10 条改为按 token 预算动态截断(50% 上下文窗口) - 二次截断 charsPerToken 从 2 调整为 1.0,与 CJK_TOKEN_RATIO 一致 - 压缩后重置 lastRealInputTokens,避免跨迭代污染 - 每个 run 开始时重置 lastRealInputTokens 【前端 Token 显示修复】 - 区分"累计消耗"和"上下文占用"语义——之前 totalTokens(累计) / contextWindow(单次窗口) 得出无意义百分比 - TokenUsage.tsx 上下文占用改用 lastInputTokens,新增压缩节省行(绿色,仅当 > 0 时显示) - agent-store.ts TokenUsage 接口新增 lastInputTokens 和 lastCompressedSaved 字段,4 处初始值统一更新 - useAgentStream.ts usage 事件 lastInputTokens 替换不累加,compressed 事件通过 streamEvent 接收 savedTokens - handlers.ts onCompressed 同时发 toast + streamEvent,解决"压缩触发但前端 token 显示不降"缺陷 - 旧数据兼容使用 ?? 0,保证历史会话加载不崩溃 【MCP 工具就绪竞态修复】 - 修复输入框永久显示"工具加载中"的竞态条件:MCP initialize 几乎立即 resolve(connectServer 不 await),tools:ready 事件在前端监听器注册前已发出 - main.ts 维护 toolsReady 标志 + 注册 tools:isReady IPC handler 查询当前状态 - preload.ts 暴露 tools.isReady() 方法 - App.tsx 注册 onReady 监听器后立即查询 isReady(),无论事件是否错过都能恢复正确状态 【记忆系统加固】 - consolidator.ts 新增 runningPromise + waitForCompletion(35s),before-quit 等待固化完成,防止退出时异步 consolidate 数据丢失 - 固化到 MEMORY.md 的同时写入 semantic_memories 表,解决双轨存储无交叉验证问题 - manager.ts tokenize 按中英文标点切分子句后再做 bigram,优化中文分词 - 清理正则冗余括号 【SOUL.md 降级处理】 - context-builder.ts SOUL.md 为空或不存在时降级到默认身份,向前端发 toast 提示用户 - 新增 fallbackRoleNotified 去重标志,仅首次降级通知,避免每次发消息都弹 toast - SOUL.md 恢复内容时重置标志 【Token 估算调整】 - token-estimator.ts CJK_TOKEN_RATIO 从 1.5 调整为 1.0 【MCP 异步初始化】 - main.ts MCP 完成后广播 tools:ready 事件,前端 UI 据以控制输入框可用性 - preload.ts + global.d.ts 暴露 tools.onReady() 监听器 - App.tsx + ChatInput.tsx toolsReady 状态控制输入框 【版本号】 - package.json + package-lock.json 从 0.3.16 升级到 0.3.18
This commit is contained in:
@@ -44,21 +44,37 @@ interface MemorySearchOptions {
|
||||
minImportance?: number;
|
||||
}
|
||||
|
||||
/** 分词:将文本拆分为词项(支持中英文) */
|
||||
/**
|
||||
* 分词:将文本拆分为词项(支持中英文)
|
||||
*
|
||||
* v0.3.18 修复: 改进中文分词策略,减少 bigram 噪声
|
||||
* 之前对整段文本连续取 bigram,会跨越标点边界产生无意义组合
|
||||
* (如"开发规范。下一句"会产生"范。"和"。下"这类噪声 bigram),
|
||||
* 降低 IDF 区分度。
|
||||
* 现在先按中英文标点切分子句,再在每个子句内做 bigram,
|
||||
* 避免跨句组合,提升检索准确度。
|
||||
*/
|
||||
function tokenize(text: string): string[] {
|
||||
// 转小写,按非字母数字字符分割
|
||||
// 转小写
|
||||
const lower = text.toLowerCase();
|
||||
// 英文词
|
||||
const words = lower.match(/[a-z][a-z0-9_-]{1,}/g) ?? [];
|
||||
// 中文 bigram(相邻两字组合),覆盖 CJK 统一表意、扩展 A、平假名/片假名、谚文
|
||||
const cjkChars = lower.match(/[\u4e00-\u9fff\u3400-\u4dbf\u3040-\u30ff\uac00-\ud7af]/g) ?? [];
|
||||
|
||||
// v0.3.18 修复: 按中英文标点切分子句,再在每个子句内做 bigram
|
||||
// 标点包括:中文句号/逗号/顿号/分号/感叹/问号 + 英文 .,;!?()
|
||||
const sentences = lower.split(/[。,、;!?.,;!?()\n\r\t]/);
|
||||
const bigrams: string[] = [];
|
||||
for (let i = 0; i < cjkChars.length - 1; i++) {
|
||||
bigrams.push(cjkChars[i] + cjkChars[i + 1]);
|
||||
}
|
||||
// 单字 CJK 补 unigram(避免单字文档无 token)
|
||||
if (cjkChars.length === 1) {
|
||||
bigrams.push(cjkChars[0]);
|
||||
for (const sentence of sentences) {
|
||||
// 提取子句内的 CJK 字符(覆盖 CJK 统一表意、扩展 A、平假名/片假名、谚文)
|
||||
const cjkChars = sentence.match(/[\u4e00-\u9fff\u3400-\u4dbf\u3040-\u30ff\uac00-\ud7af]/g);
|
||||
if (!cjkChars || cjkChars.length === 0) continue;
|
||||
for (let i = 0; i < cjkChars.length - 1; i++) {
|
||||
bigrams.push(cjkChars[i] + cjkChars[i + 1]);
|
||||
}
|
||||
// 单字 CJK 子句补 unigram(避免单字文档无 token)
|
||||
if (cjkChars.length === 1) {
|
||||
bigrams.push(cjkChars[0]);
|
||||
}
|
||||
}
|
||||
return [...words, ...bigrams];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user