@fanchao8609/agent_brain_sync 1.16.3 → 1.16.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/relevant.js +41 -1
- package/src/todo.js +9 -10
package/package.json
CHANGED
package/src/relevant.js
CHANGED
|
@@ -139,13 +139,53 @@ export function matchPage(body, pageName, queryWords) {
|
|
|
139
139
|
// 查"zzzz不存在"时 qGrams={zz,不存,存在}, 页里恰好有"存在" → ratio=0.5 误命中。
|
|
140
140
|
// 两个修正: (1) 重复字符的 gram 不算(zz 去重); (2) 至少得命中 2 个不同 gram。
|
|
141
141
|
const qGrams = new Set(queryWords.flatMap((w) => ngrams(String(w).toLowerCase())));
|
|
142
|
-
const pGrams =
|
|
142
|
+
const pGrams = pageGrams(text, name);
|
|
143
143
|
let overlap = 0;
|
|
144
144
|
for (const g of qGrams) if (pGrams.has(g)) overlap++;
|
|
145
145
|
const ratio = qGrams.size ? overlap / qGrams.size : 0;
|
|
146
146
|
return { exact, overlap, ratio };
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
/** 页正文侧 2-gram 集合的缓存。
|
|
150
|
+
*
|
|
151
|
+
* 为何缓存(2026-10-07 实测):load 慢的根因就在这里。
|
|
152
|
+
* queryHint 走 topicStrength → 对【每个词 × 每一页】调 rankPage → matchPage,
|
|
153
|
+
* 而每次都对同一页重算 ngrams(text.slice(0,4000))。
|
|
154
|
+
* 实测规模:17 个活跃词 × 63 页 = 1071 次 matchPage,实测 156ms,
|
|
155
|
+
* 而不同页只有 63 个 —— 同一次展开算了 17 遍。
|
|
156
|
+
* CPU profile:matchPage + ngrams + 那条正则占 load 总耗时的 82%。
|
|
157
|
+
* 端到端:改看板前 191ms / 空看板 40ms(todo 越大词越多 → 越慢,用户实报的「卡」)。
|
|
158
|
+
*
|
|
159
|
+
* 键的构造(关键):用正文当 Map 的键、页名当二级 Map 的键 —— 两级分开存,
|
|
160
|
+
* 而不是拼成一个字符串键。好处:「同正文不同页名」天然是两个不同的表项,
|
|
161
|
+
* 永远不可能互相覆盖(曾用 name+NUL+text 拼串,那是在赌没人会改错键;
|
|
162
|
+
* 现有 20 个测试实际上抓不住那种错,改成两级后这个 bug 类不存在)。
|
|
163
|
+
* 缓存值是**只读的 Set**,matchPage 只查不改 —— 避免把页名 gram 写进缓存。
|
|
164
|
+
*
|
|
165
|
+
* 为何用普通 Map(非 WeakMap):字符串是原始值,不能当 WeakMap 键。
|
|
166
|
+
* 故给容量上限,满了清空(一轮遍历内复用已足够)——
|
|
167
|
+
* ponytail: 不做 LRU,清空式淘汰对本场景够用。
|
|
168
|
+
*/
|
|
169
|
+
const PAGE_GRAMS_CACHE = new Map();
|
|
170
|
+
const PAGE_GRAMS_MAX = 500;
|
|
171
|
+
|
|
172
|
+
/** 取(或算)一页的 2-gram 集合(含页名)。返回的 Set 只读,调用方不得修改。 */
|
|
173
|
+
function pageGrams(text, name) {
|
|
174
|
+
// 两级 Map:外层按正文、内层按页名。不用拼串键 —— 见上方注释。
|
|
175
|
+
let inner = PAGE_GRAMS_CACHE.get(text);
|
|
176
|
+
if (inner) {
|
|
177
|
+
const hit = inner.get(name);
|
|
178
|
+
if (hit) return hit;
|
|
179
|
+
} else {
|
|
180
|
+
if (PAGE_GRAMS_CACHE.size >= PAGE_GRAMS_MAX) PAGE_GRAMS_CACHE.clear();
|
|
181
|
+
inner = new Map();
|
|
182
|
+
PAGE_GRAMS_CACHE.set(text, inner);
|
|
183
|
+
}
|
|
184
|
+
const set = new Set(ngrams(text.slice(0, 4000)).concat(ngrams(name)));
|
|
185
|
+
inner.set(name, set);
|
|
186
|
+
return set;
|
|
187
|
+
}
|
|
188
|
+
|
|
149
189
|
/** 拆字符 2-gram(中英文都适用)—— 模糊匹配的最小单位。
|
|
150
190
|
* 先过 [\p{L}\p{N}] 再切,单一字符组成的 gram(如 zz) 没区分度,丢掉。 */
|
|
151
191
|
function ngrams(s) {
|
package/src/todo.js
CHANGED
|
@@ -392,18 +392,17 @@ export function rebuildStructure(text, spec) {
|
|
|
392
392
|
const out = [spec.h1];
|
|
393
393
|
const pre = trimBlank(preamble);
|
|
394
394
|
if (pre.length) out.push('', ...pre);
|
|
395
|
-
//
|
|
396
|
-
//
|
|
397
|
-
//
|
|
398
|
-
//
|
|
399
|
-
//
|
|
400
|
-
//
|
|
401
|
-
|
|
395
|
+
// 每个分区标题前恒有一个空行(含空分区)。
|
|
396
|
+
// 坑(2026-10-06 定位):旧写法对空分区走 `else out.push(name)` —— 不补空行,
|
|
397
|
+
// 于是「有内容分区 → 空分区」的接缝被挤成 `- [[页]] …` 紧贴 `## Sources`。
|
|
398
|
+
// 表现成「index 里那个空行反复丢」:人工补回 → 下次任意写操作(过 enforceBrainFormat)
|
|
399
|
+
// 又归一掉,来回拉锯(本机 corp_agent 实测,17961e5 补、f3f7e84 又丢)。
|
|
400
|
+
// 原注释担心「每次首跑都写盘一次」——实测不会:归一后自身就是稳定形态,
|
|
401
|
+
// 第二次跑 fixed 为空、零字节改动(幂等,见 test/todo 的自检)。
|
|
402
|
+
// H1 后必须恒有空行:`# H1` 与首个分区相贴是门面事故,不容忍。
|
|
402
403
|
for (const [idx, name] of spec.order.entries()) {
|
|
403
404
|
const body = trimBlank(bucket.get(name) || []);
|
|
404
|
-
|
|
405
|
-
else out.push(name);
|
|
406
|
-
first = false;
|
|
405
|
+
out.push('', name, ...body);
|
|
407
406
|
}
|
|
408
407
|
for (const e of extras) {
|
|
409
408
|
const body = trimBlank(e.lines);
|