0ff369f818
leo 2026-08-05 實撞:「新上傳的『loop-engine-north-star.md』語義 0 命中,
但舊的『人力媒合系統規劃書』語義 2 命中」。
## 根因不在向量——向量是好的
直打 Vectorize 實測(youlin 實例、bge-m3、1024 維 index):
搜「閉環機」→ loop-engine-north-star.md 穩坐第 1-4 名(0.638/0.603/0.588/0.552)
D1 與 Vectorize 也對得上:659 筆 embeddable 全 is_embedded=1、index vectorCount 659。
真兇是 **min_score 門檻綁在舊模型的分數尺度上**:
舊 bge-base-en-v1.5:中文分數全擠 0.65-0.90(沒區辨力)⇒ t183 取 0.75 砍雜訊,對
新 bge-m3 :尺度整體下移(相關 0.5-0.85、雜訊 0.4 上下)⇒ 0.75 砍掉的是正解
leo 看到的「舊檔中、新檔不中」由此而來——「人力媒合系統規劃書」拿 0.842 僥倖存活,
其餘全被門檻掃掉。08-05 換 bge-m3 的「四處同步」清單(embed.ts/deploy.ts/
deploy-all.mjs/worker.js)**漏了這第五處**,因為它不在 kbdb 而在 portal 呼叫端。
## 改動
· kbdb/src/embed.ts:新增 DEFAULT_MIN_SCORE=0.5,**緊鄰 DEFAULT_EMBED_MODEL**
——門檻是模型的性質,放模型旁邊,下次換模型的人一定會看到
· cypher-executor/src/routes/portal-data.ts:拿掉硬寫的 0.75,
只在使用者顯式指定時才傳 min_score(不再各自持有一份數字=不再漂移)
· console-ui/public/portal/os-split.test.mjs:修好被今天 fe0ee82 弄壞的自測
(結尾標記寫死文案 ⇒ 改文案就炸「抽不到函式區塊」;改成錨定結構)
+斷言同步改成 Mac 給 DMG
## 0.5 怎麼來的(實測分布,不是猜的)
「閉環機」 0.638/0.603/0.588/0.552 全是目標檔 ── 斷崖 ── 0.446 才是雜訊
「火星座標 奧林帕斯山」 0.750…0.500 全對,0.475 以下才是雜訊
「人力媒合系統規劃書」 0.842 對,0.550 起是雜訊
誠實 trade-off:0.5 非每個查詢都乾淨(「AI 上課名冊」0.658 的 ax-academy 會擠進來),
但「偶有雜訊」遠優於現況「什麼都搜不到」。
## 驗
· kbdb 87/87 綠(三筆斷言隨新契約更新:預設不再是「不過濾」)
· cypher-executor 9 failed/301 passed=**與改動前逐數相同**(git stash 前後各跑一次)⇒ 既有債
· portal os-split 自測 10/10 綠
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
57 lines
3.6 KiB
JavaScript
57 lines
3.6 KiB
JavaScript
import fs from 'node:fs';
|
||
const html = fs.readFileSync(new URL('./index.html', import.meta.url).pathname,'utf8');
|
||
// 抽出 daemonPick 相關函式(從 DAEMON_BASE_DEFAULT 到 daemonHint 結尾)
|
||
//
|
||
// 🔴 2026-08-05:結尾標記本來寫死 daemonHint 的**整句文案**,於是同日改 Mac 提示語
|
||
// (zip→DMG 的步驟不同)就讓這支自測直接炸「抽不到函式區塊」,而且沒人發現。
|
||
// ⇒ 改成錨定「函式結束」這個結構,不再綁文案——文案本來就會改,測試不該為此壞掉。
|
||
const start = html.indexOf('var DAEMON_BASE_DEFAULT');
|
||
const hintAt = html.indexOf('function daemonHint', start);
|
||
const endMark = '\n }';
|
||
const end = hintAt < 0 ? -1 : html.indexOf(endMark, hintAt) + endMark.length;
|
||
if (start < 0 || hintAt < 0 || end < start) throw new Error('抽不到函式區塊');
|
||
const src = html.slice(start, end);
|
||
|
||
const cases = [
|
||
['Windows', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120 Safari/537.36'],
|
||
['Mac', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 Safari/605.1.15'],
|
||
['iPhone', 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 Safari/604.1'],
|
||
['Linux', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/120 Safari/537.36'],
|
||
];
|
||
let pass=0, fail=0;
|
||
const chk=(l,c,extra='')=>{ if(c){console.log('PASS:',l);pass++;} else {console.log('FAIL:',l,extra);fail++;} };
|
||
|
||
for (const [name, ua] of cases) {
|
||
const fn = new Function('navigator','window', src + '; return {daemonPick:daemonPick, daemonHint:daemonHint, daemonBase:daemonBase};');
|
||
const api = fn({userAgent: ua}, {});
|
||
const d = api.daemonPick();
|
||
const label = d.sure ? d.pick.label : '(兩個都給)';
|
||
const url = d.sure ? d.pick.url : d.mac.url + ' + ' + d.win.url;
|
||
console.log(`\n[${name}] sure=${d.sure} → ${label}`);
|
||
console.log(` url: ${url}`);
|
||
if (name==='Windows') {
|
||
chk('Windows 給 win zip', d.sure && d.pick.url.endsWith('ArcrunRAG-win-unsigned.zip'), d.pick&&d.pick.url);
|
||
chk('Windows 另一版是 Mac', d.other && d.other.url.endsWith('ArcrunRAG-mac.dmg'));
|
||
chk('Windows 話術提 藍色視窗', api.daemonHint('win').includes('仍要執行'));
|
||
}
|
||
if (name==='Mac') {
|
||
// 2026-08-05:Mac 一律給 DMG(拖進 Applications 的標準安裝畫面),不再給 zip
|
||
// ——zip 解開就是一個裸 .app,使用者會直接在「下載」資料夾雙擊執行,自更新會蓋錯位置。
|
||
chk('Mac 給 dmg(不是 zip)', d.sure && d.pick.url.endsWith('ArcrunRAG-mac.dmg'));
|
||
chk('Mac 另一版是 Windows', d.other && d.other.url.endsWith('win-unsigned.zip'));
|
||
chk('Mac 話術提 右鍵打開', api.daemonHint('mac').includes('右鍵'));
|
||
}
|
||
if (name==='iPhone' || name==='Linux') {
|
||
// iPhone 含 "Mac OS X" 但不是桌機 Mac;Linux 兩者皆非 → 都該落在「不確定=兩個都給」
|
||
if (name==='Linux') chk('Linux 判不出來→兩個都給', d.sure===false);
|
||
if (name==='iPhone') chk('iPhone 不該被判成 Mac(手機→兩個都給)', d.sure===false, 'sure='+d.sure);
|
||
}
|
||
}
|
||
// 舊 key 相容
|
||
const fn2 = new Function('navigator','window', src + '; return daemonBase();');
|
||
console.log('\n[相容] daemonDownload 舊 key →', fn2({userAgent:''},{ARCRUN_CONFIG:{daemonDownload:'https://x.dev/d/ArcrunRAG-mac-unsigned.zip'}}));
|
||
chk('舊 key 推得出目錄', fn2({userAgent:''},{ARCRUN_CONFIG:{daemonDownload:'https://x.dev/d/ArcrunRAG-mac-unsigned.zip'}})==='https://x.dev/d/');
|
||
chk('daemonBase 新 key 優先', fn2({userAgent:''},{ARCRUN_CONFIG:{daemonBase:'https://y.dev/z'}})==='https://y.dev/z/');
|
||
console.log(`\n=== ${pass} passed, ${fail} failed ===`);
|
||
process.exit(fail?1:0);
|