- 把「合成的假手冊」換成真實可下載的工廠設備手冊做 RAG,一天內連環踩了 15+ 個坑,橫跨四端:環境/GPU、PDF 讀取、程式碼、檢索與生成。
- 最陰的根因是 CJK 相容表意文字(U+F9E0):「易」被抽成非標準碼,肉眼一樣但
in搜不到、嵌入向量爛、LLM 看不懂——一行NFKC正規化才解。 - 故障排除手冊的價值是「症狀 → 該做什麼」。
get_text()把表格壓平就毀了;用 find_tables() 還原「症狀→處置」配對才救回可操作性。 - 小模型(3B)會答非所問、反問、簡體;換 7B + 意圖導向檢索加權,才真的答出「電源指示不亮 → 換控制箱保險絲」。
這是一篇 真實手冊 RAG(檢索增強生成)的實戰除錯全記錄。目標很單純:讓工廠人員用自然語言問設備手冊,得到「有出處、可操作」的答案——正是 PMC 技術通報〈突破傳統技術手冊:LLM 如何成為技術操作知識的即時來源〉描繪的場景。但把 demo 從「程式合成的假手冊」換成「真的工廠手冊 PDF」之後,現實給了我一整天的耳光。以下按環境、PDF 讀取、程式、檢索與生成四端,鉅細靡遺記錄每一個坑、根因與修法。
為什麼要用「真實手冊」而不是合成資料?
一開始的 demo 用程式生成一份「30 台一模一樣的泵浦」的假手冊。它有個致命的假象:每台機台內容完全相同,問「泵浦壓力」會回一堆重複的引用,看起來像複製貼上。這其實暴露了合成資料的失真——但也讓人懷疑:真手冊會不會更好?答案是:真手冊不會有這種假重複,但它是另一種地獄——髒資料。真手冊的抽字有相容字、有垂直單字、有壓平的表格、有不一致的目錄。換句話說,合成資料考驗「架構」,真實資料考驗「前處理」。
我用三個平行的搜尋 agent,實際下載並驗證了 13 份公開的真手冊(抽水機、空壓機、CNC、變頻器、PLC、工業爐…),最後用一份 57 頁的南投「12 吋移動式抽水機操作維護手冊」當主測材——因為它是真廠商 O&M、有真正的「故障原因→矯正措施」表。
15 個坑,四端一覽
| 端 | 坑 | 一句話根因 | 修法 |
|---|---|---|---|
| 環境 | 載入模型 CUDA OOM | 長 session 重跑,舊模型沒釋放 | Restart / empty_cache |
| 環境 | bge-m3 OOM(單 op 4.69 GiB) | max_seq_length 預設 8192 | 壓到 512 |
| 環境 | 7B 生成 OOM(單 op 6.87 GiB) | T4 塞不下雙模型 | 換 3B/嵌入移 CPU |
| 🎯 搜不到/嵌入爛/LLM看不懂 | CJK 相容字 U+F9E0 | NFKC 正規化 | |
| 表格抽成垂直單字亂碼 | get_text 逐字換行 | 接字 + 去空白 | |
| 🎯 故障表壓平→答不出該做什麼 | 表格結構丟失 | find_tables 還原症狀→處置 | |
| 目錄/TOC 不一致 | 有的有有的沒 | 有TOC用TOC否則標題偵測 | |
| 程式 | ⚠️ 絕對路徑害 Kaggle 跑掛 | OUT 寫成本機路徑 | 改相對路徑 |
| 程式 | notebook 生舊格式 | 只改獨立檔漏改 cell | 同步 + 測真入口 |
| 程式 | 依據書名變章節名 | 變數 shadowing | 迴圈變數改名 |
| 程式 | 去空白靜默失效 | 正則反斜線三層跳脫 | 改 “”.join(split()) |
| 檢索 | 引用張冠李戴 | 盲切跨機台+子字串過濾 | 結構化切片+metadata |
| 檢索 | 問A機台答B機台 | 無型式路由 | book/型式 metadata 過濾 |
| 檢索 | 問故障答保養 | 混合context從多數答 | 意圖導向加權 |
| 生成 | 3B 答非所問/簡體/亂碼 | 小模型天花板 | 7B + opencc + 後處理 |
使用情境:誰來問、問什麼
先看這套系統要解決誰的問題。使用者是現場人員或維修工程師,他們手上有一堆設備手冊,但翻不動、也不知道答案在第幾頁。他們要的不是「相關文件」,而是「該做什麼」。
核心觀念:我們「利用」模型,不「改」模型
這是整套設計的靈魂,也是它跟「購物網關鍵字檢索」本質不同的地方。我們沒有重新訓練、也沒有微調任何模型——bge-m3 和 Qwen 都是現成的、凍結的。我們做的全部是「餵對東西」:把手冊清洗乾淨、切成對的片、用向量找出最相關的段,再把這些段當「臨時課本」交給模型現場閱讀作答。模型的本事沒變,變的是我們給它的上下文品質。
| 面向 | 購物網「關鍵字檢索」 | 本文的「RAG:利用模型」 |
|---|---|---|
| 比對方式 | 字面關鍵字比對(有這個詞就中) | 語意向量相似度(意思接近就中) |
| 回傳什麼 | 一串「相關商品/文件」連結 | 一段「直接回答 + 出處」 |
| 懂不懂「意思」 | 不懂,只認字 | 懂語意(電源指示不亮 ≈ 沒電、跳電) |
| 有沒有改模型 | 無模型 | 有現成模型但凍結不改,只給它上下文 |
| 答案哪來 | 人自己從清單裡找 | 模型從我們給的手冊段落現場讀出來 |
RAG = 把「凍結的通用模型」+「你的私有手冊」臨時接起來。難的不是模型,是把手冊變成模型讀得懂的乾淨上下文——這一整天的坑,幾乎都在這件事上。
五段管線:從 PDF 到「症狀→處置」
這支 Jupyter notebook 從頭到尾就是五段。下圖是資料流,接著逐段看它「在做什麼、怎麼切、怎麼變向量、怎麼跟模型交互」。
① 抽字 — 把髒 PDF 洗乾淨、把表格救回來
每一頁先用 pymupdf 抽字,馬上做 NFKC 正規化(修相容字)、接垂直單字,並用 find_tables() 把故障/保養表還原成有語意的「症狀→處置 / 保養項目→週期」文字。
def _pages(doc):
return {i+1: _norm_extract(_page_text(doc[i])) for i in range(doc.page_count)}
def _norm_extract(t):
t = unicodedata.normalize("NFKC", t) # 修 CJK 相容字(U+F9E0 易 → 標準 易)
... # 接垂直單字 + 去中文字間空格
def _page_text(page):
for tb in page.find_tables().tables: # 把表格按行列還原
# 故障表 → 「[啟動馬達無法啟動] 症狀:電源指示不亮 → 處置:換保險絲」
...
② 切片 — 一章一片,每片貼上「哪本·哪機·哪章·第幾頁」
關鍵不是「切多大」,而是「照結構切」:有目錄用目錄,沒目錄用標題偵測。每一片都帶 metadata,這是後面「找對書、找對章節」的根。
chunks.append(dict(
book_id=book_id, machine=machine, book_title=title,
section=sectitle, # 例:「五、 簡易故障排除」
page_range=pr, # 例:(9, 10)
text=seg)) # 已含「症狀:… → 處置:…」結構化文字
③ 嵌入 — 把每片「變成向量」,才能用語意找
「變向量(embedding)」是 RAG 能懂語意的關鍵:bge-m3 把每片文字轉成 1024 維向量,意思接近的片,向量就接近。注意向量文字前面帶了 metadata,讓「書名/機台/章節」也進入語意空間。建完索引後把嵌入模型移到 CPU 騰出顯存給生成模型。
embedder = SentenceTransformer("BAAI/bge-m3", device=DEVICE)
embedder.max_seq_length = 512
def view(c): # 向量文字帶上 metadata
return f"[{c['book_title']}|{c['machine']}|{c['section']}|第{c['page_range'][0]}頁] {c['text']}"
emb = embedder.encode([view(c) for c in all_chunks], normalize_embeddings=True) # → (N, 1024) 向量矩陣
embedder.to("cpu") # 建完索引移 CPU,騰顯存給生成
④ 檢索 — 語意相似 +「找對書」+「找對型態」
查詢也轉成向量,用內積找最相似的片;再疊三層加權:機台路由(問空壓機只查空壓機那本)、章節加權、意圖加權(問故障就強推含「→ 處置:」的片)。這是純向量相似度不夠、需要 metadata 訊號的地方。
fault_intent = any(k in q for k in ["故障","排除","異音","震動","過熱","怎麼辦","處置"])
maint_intent = any(k in q for k in ["保養","維護","週期","多久","潤滑","更換"])
def _boost(c):
b = 0.30*sum(1 for w in qkw if w in (c["section"] or "")) # 章節命中
if fault_intent and "→ 處置:" in c["text"]: b += 0.35 # 問故障 → 推症狀→處置片
if maint_intent and ("→ 週期:" in c["text"]): b += 0.35 # 問保養 → 推保養週期片
return b
pool = [(sim + _boost(c), c) for sim, c in pool] # 相似度 + 加權 重排
⑤ 與模型交互 — 把撈到的段當「臨時課本」交給模型
這一步就是「利用模型不改模型」的具體樣子:把 top-k 段組成 ctx,連同問題一起丟給 Qwen,要求它只准用這些資料作答、每點標依據編號。模型沒被訓練過這本手冊,它是現場讀我們遞上去的段落才答得出來。長度與繁體再用程式後處理收尾。
ctx = "\n".join(f"[{n+1}] ({_cite(c)}) {c['text']}" for n,c in enumerate(top))
ans = chat(SYS, f"【資料】\n{ctx}\n\n【問題】{q}") # SYS:只准用【資料】、每點標(依據N)、禁反問
# 後處理:opencc 簡→繁、說明硬截 150 字、依據只標「書·章·頁」不倒原文
模型的權重從頭到尾沒動。我們只是每次問答時,臨時把「最相關的手冊段落」放進它的輸入(context window)裡讓它讀。換一本手冊、加一本手冊,完全不用重訓——這就是 RAG 相對於「微調模型」的最大好處:知識可熱插拔。
模型可熱插拔:免費本地 vs 雲端 120B,一行切換
在 Kaggle 上跑小模型,純粹是「免費機器」的妥協。既然我們是「利用模型不改模型」,那換一顆更強的模型就完全不用動前面的抽字、切片、檢索——只換 chat() 背後打誰。所以生成端加了一個後端路由,兩者都留著、隨你切:預設本地 Qwen(免費、免金鑰即可跑),要更好品質就切到雲端 Cerebras gpt-oss-120b(不佔本地 GPU)。
LLM_BACKEND = "local" # 兩者可選:"local" 免費本地 Qwen / "cerebras" 雲端 120B
if LLM_BACKEND == "local":
llm = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-7B-Instruct", ...) # 免費、但受 T4 顯存限制
def _gen(system, user, n): ...本地 generate...
else:
def _gen(system, user, n): # 雲端:OpenAI 相容端點,不載本地模型 → 零 OOM
r = requests.post("https://api.cerebras.ai/v1/chat/completions",
headers={"Authorization": f"Bearer {CEREBRAS_API_KEY}"},
json={"model": "gpt-oss-120b", "temperature": 0,
"messages": [{"role":"system","content":system},{"role":"user","content":user}]})
return r.json()["choices"][0]["message"]["content"]
def chat(system, user, n=320): return _clean(_gen(system, user, n)) # 前面全部不用改
| 本地 Qwen(免費) | 雲端 Cerebras gpt-oss-120b | |
|---|---|---|
| 參數量 | 3B / 7B | 120B |
| 答題品質 | 會反問/簡體/受限 | 連貫、直接、穩 |
| 本地 GPU | 要載模型、易 OOM | 完全不佔(打 API) |
| 成本 | 免費 | 依 API 用量 |
| 適合 | 無網路/純離線 demo | 要品質、要跑真手冊 |
檢索與生成解耦——知識在你的手冊裡(可熱插拔),推理在模型裡(可換更強的)。同一套 R-A-G 流程,今天用免費小模型跑通,明天接雲端 120B 只改一行,前面辛苦洗乾淨的手冊上下文原封不動。
R-A-G 到底怎麼運作?一張時序圖看懂
RAG 三個字母就是三個動作:R(Retrieval 檢索)找出最相關的手冊段、A(Augmented 增強)把這些段塞進提問、G(Generation 生成)讓模型讀著這些段作答。下面這張 UML 時序圖把「離線建索引」和「線上一次問答」拆開來看。
怎麼知道一句話「中」哪個 index?——不是關鍵字,是向量最近
這是最多人卡住的地方。答案是:問題不是拿去「比對關鍵字」,而是被轉成一個向量,再跟索引裡每一片的向量算「有多接近」(內積 / cosine 相似度),分數最高的幾片就是「命中的 index」。所以就算問句用的字跟手冊不一樣,只要意思接近,一樣中——這就是它比購物網關鍵字檢索聰明的地方。
# emb 是 (N, 1024) 的向量矩陣(N 片手冊,每片一個向量)
qe = embedder.encode([問題], normalize_embeddings=True)[0] # 問題 → 1024 維向量
scores = emb @ qe # 一次算出「問題 vs 每一片」的相似度分數
top = np.argsort(-scores)[:k] # 分數最高的 k 片 = 命中的 index
# → 再用這些 index 去 all_chunks 取出對應的原文與出處
舉例:問「電源指示不亮怎麼辦」,手冊裡那句「電源指示不亮 → 控制箱保險絲燒毀需更換」的向量會離問題向量最近,即使問句沒有「保險絲」三個字,它照樣被撈出來——因為兩者語意相近。撈出來的原文才是接下來要交給模型的「臨時課本」。
① 切片:索引的單位要夠小,才能精準命中「就是這一段」、也才標得出第幾頁;整本書當一個向量,問什麼都「中整本」等於沒用。② 表格還原:故障表壓平後,「症狀」和「處置」的向量糊在一起,命中了也答不出配對;還原成「症狀→處置」一句,那句的向量才能被「怎麼處置」這種問句精準命中。③ NFKC / 清洗:髒字元會讓向量算歪,命中就跟著歪。前面每一步,都是為了讓「④ 算相似度」這一刻命中對的片。
環境端:Kaggle T4 的三連 OOM
第一關就是顯卡記憶體。三種 OOM 各有不同根因,值得分清楚。
bge-m3 的 max_seq_length 是隱形炸彈
症狀:嵌入模型在 scaled_dot_product_attention 一次要 4.69 GiB。根因是 bge-m3 預設 max_seq_length=8192,而 attention 記憶體正比於序列長度的平方。我們的切片都很短,8192 純浪費。
embedder = SentenceTransformer("BAAI/bge-m3", device=DEVICE)
embedder.max_seq_length = 512 # 記憶體降約 256 倍
emb = embedder.encode(texts, batch_size=16, normalize_embeddings=True)
載入任何 encoder 嵌入模型,先看它的
max_seq_length,對照你切片的實際長度往下壓,別用預設。7B + 嵌入模型塞不下 T4
索引建好後,7B 生成時 attention 一次要 6.87 GiB,而 bge-m3(2.3G)+ Qwen 7B(5.5G)已把 16 GB 吃滿。解法是建完索引把 bge-m3 移到 CPU(查詢時只編碼幾個短字串,CPU 夠快),把 GPU 全留給生成;或直接用 3B。
emb = embedder.encode(...) # 先在 GPU 建索引(快)
if DEVICE == "cuda":
embedder.to("cpu"); gc.collect(); torch.cuda.empty_cache() # 騰出 ~2.3 GB
PDF 讀取端:真手冊是「髒資料」
這一端是本日最大的坑,也是最有價值的發現。真手冊的 get_text() 不保證給你乾淨的標準 Unicode。
🎯 最陰的根因:CJK 相容表意文字
現象:"簡易故障排除" in text 回 False,但畫面明明看得到這六個字。印出 codepoint 才真相大白:
>>> [(ch, hex(ord(ch))) for ch in "簡易故障排除"[:2]]
[("簡", "0x7c21"), ("易", "0xf9e0")] # 易 = U+F9E0 !!
# U+F9E0 是「CJK 相容表意文字」,不是標準的 易(U+6613)
# 肉眼一樣,電腦當成不同字 → 搜不到、路由失效、嵌入向量用到怪碼、LLM 看不懂
這一個字元差異,一路毒化了搜尋、機台路由、嵌入品質、甚至 LLM 的理解——而且它不報錯,最難查。修法是抽字後做一次 Unicode NFKC 正規化,把相容字轉回標準字:
import unicodedata
text = unicodedata.normalize("NFKC", text) # U+F9E0 易 → U+6613 易
# 本地驗證:目標故障表從「0 片可搜」→「3 片可搜」
當
substring in text 明明畫面看得到卻回 False,第一時間 print([(ch,hex(ord(ch))) for ch in seg]) 看 codepoint——這次就是這樣抓到 0xF9E0 的。垂直單字亂碼
複雜表格被 get_text() 抽成「一字一行」:「啟\n動\n困\n難」。餵給 LLM 就崩潰跳針。修法是把「只有一個字的行」接回正常詞,並去掉中文字之間的多餘空格。
把「表格」當一等公民:還原「症狀 → 處置」
這是本日的核心突破。故障排除手冊的價值就是「症狀 X → 做處置 Y」的配對。但 get_text() 把三欄故障表壓成一長串,LLM 看不出配對,只能給「請參閱相關資料」這種廢話。使用者一句話點醒:「你給我的處置答案,我根本不知道我該做什麼。」
解法是用 pymupdf 的 find_tables() 把表格按行列抓回來,再依表型格式化成有語意的文字:
for t in page.find_tables().tables:
rows = t.extract()
# 症狀→處置表(表頭含「矯正措施/處置」):
# [啟動馬達無法啟動] 症狀:電源指示不亮 → 處置:控制箱保險絲燒毀需更換
# 保養矩陣(項目×週期,打 *):
# 保養項目:更換引擎機油 → 週期:50小時、150小時、600小時或1年
# 症狀×原因矩陣(打勾):
# 原因:燃油品質不良 → 可能造成:啟動困難、怠速冒白煙
問「抽水機的簡易故障排除」→「1. 電源指示不亮 → 更換控制箱保險絲(依據1)。2. 機油壓力過低 → 檢查並補充潤滑機油(依據1)。」這才是使用者要的「該做什麼」。
手冊 RAG 遇到表格要當一等公民(find_tables + 按語意格式化),別只靠 get_text 的閱讀順序——故障/對照/規格表的配對關係就是答案本身。
程式端:四個自己挖的坑
工具再好,自己寫的 bug 一樣能讓你白跑一輪。
| 坑 | 發生什麼 | 教訓 |
|---|---|---|
| 絕對路徑 | 把本機測試用的 OUT="/home/tom/.../x.pdf" 同步進 notebook,Kaggle 上路徑不存在一寫就爆 | 本機測過的程式搬進要在別處跑的 notebook,先刷掉絕對/暫存路徑 |
| 漏改雙拷貝 | 只改了獨立產生器檔,漏改 notebook 內嵌的同一份 → 生舊格式資料 | 同邏輯多處拷貝要同步;測要測「使用者實際會跑的入口」 |
| 變數 shadowing | 切片器迴圈變數 title 蓋掉函式的「書名」參數 title → 引用把章節名當書名 | 多層 metadata 流動時,參數名與迴圈名務必不同 |
| 跳脫地獄 | re.sub(r"\s+") 經 heredoc→python→JSON 三層變 \\s+ 靜默失效 | 程式化寫 regex 進 JSON 能避反斜線就避;去空白改用 "".join(s.split()) |
檢索端:找對書、找對章節、找對「型態」
真手冊 + 多本之後,檢索精準度成為主戰場。三個層次的加權缺一不可。
- 找對書(多本路由):問「空壓機異音」自動只查《空壓機手冊》,不查泵浦那本——靠每片帶的
machine/akametadata。 - 找對章節:問「簡易故障排除」要撈到「五、簡易故障排除」那節,而不是隔壁的引擎排障——靠切片時按標題邊界切(不是盲切固定字數),section 標籤才正確、嵌入才專注。
- 找對型態(意圖加權):top-k 裡故障表只佔 1 段、其餘是保養,7B 就從多數答成保養。解法是偵測查詢意圖,對「內容型態符合意圖」的片加分——問故障就強推含「→ 處置:」的片,問保養就推含「→ 週期:」的片。
RAG 檢索除了向量相似度,還要有「意圖 ↔ 內容型態」對齊訊號。結構化抽表時留下「→處置:/→週期:」這種型態標記,正好拿來做意圖加權——這比「章節名對不對」更準,因為它加權的是「這片是不是你要的那種答案」。
生成端:3B vs 7B,以及「LLM 不會算字數」
檢索餵對了,生成端還有兩件事。
小模型的天花板
| 維度 | Qwen 3B | Qwen 7B |
|---|---|---|
| 答題連貫性 | 捏假章節、反問自己 | 條列通順、照資料 |
| 照資料 vs 幻覺 | 較多自由發揮 | 較貼資料 |
| 繁體中文 | 常輸出簡體 | 繁體較穩 |
| 代價 | T4 跑得動 | T4 會 OOM,需大 GPU |
後處理能救的:簡→繁用 OpenCC("s2twp")(模型不甩繁中指令,程式轉最可靠)、清角色標記/亂碼。救不了的「答非所問」是推理天花板——只能換 7B。
LLM 不會算字數,長度要在程式端硬控
SYS 寫「150 字內」完全沒用。量測發現:生成的說明其實大致守住,真正撐爆總長的是我把「依據」整段原文倒出來(4 段 × 160 字)。兩個修法:說明超長就在句號硬截;依據只標「書·章·頁」位置,不倒原文(原文只留在餵給模型的 context)。
RAG 輸出分兩層:給模型的 context(要完整原文) vs 給人看的 citation(只要可定位的書·章·頁),兩者內容不同,別混用同一份丟出去。
三端通則:一天奮戰換來的心法
- 環境:多模型上小顯卡要精算 VRAM(嵌入模型 seq 壓短、建完移 CPU、生成模型挑小的);長 session 別回頭重跑載入格。
- PDF 讀取:真手冊 = 髒資料。抽字後標配三件:NFKC 正規化(修相容字)、清空白(接垂直單字)、find_tables 還原表格語意。別假設 get_text 給的是乾淨標準 Unicode。
- 程式:同邏輯多處拷貝要同步;本機測要測「使用者實際入口」;搬進可攜 notebook 前刷掉絕對路徑;
substring in text靜默 False 先懷疑正規化沒真的執行。 - 檢索與生成:相似度之外要有 metadata 路由 + 意圖加權;長度靠程式硬控不靠 prompt;引用給位置不給原文;小模型跑得動 ≠ 答得好。
最後的成果:一支可配置讀哪幾本的真手冊 RAG,問哪台機器自動找對書、撈對章節,故障問題給你「症狀 → 處置」的可操作答案,保養問題給你明確週期,每一句都標得出「在哪本、哪章、第幾頁」。這一天的耳光,值得。
生成端實測:同一 RAG,7B vs 35B vs 120B 換模型比一比
把 notebook 的 generate 做成可切換後端(地端 Qwen2.5-7B / 雲端 gpt-oss-120b / 雲端 Qwen3.6-35B-A3B),檢索結果完全相同、只換生成模型,問同樣兩題:「抽水機的簡易故障排除有哪些?」「泵浦的維護保養週期?」——直接看不同模型的生成能力差在哪。
| 模型 | 部署 | 生成結果 | 判定 |
|---|---|---|---|
| Qwen2.5-7B | 地端(Kaggle GPU) | 【說明】只吐「修。」然後把問題當答案反問(「抽水泵無法啟動的原因可能是?」);另一題出現「器 * 冷卻系統壓力測試」亂碼 | ❌ 崩,不能用 |
| Qwen3.6-35B-A3B | 雲端 API | 條列清楚:進水管漏氣→未置 O 形環、每滿 50h 換潤滑油、每滿 100h 塗油脂、每滿 500h 查同心 | ✅ 乾淨 |
| gpt-oss-120b | 雲端 API | 條列清楚:進水管漏氣→未置 O 環、泵浦漏氣→螺絲未鎖;保養週期 50/100/500h | ✅ 乾淨 |
7B 在「說明+依據」這種要照抄手冊、又要守格式的任務上直接崩(吐反問、亂碼);35B 與 120b 都是 production 級。所以想斷網地端只靠 7B → 現場不能信。而 35B-A3B 是 MoE(只激活 ~3B),品質卻守到 120b 等級,是「斷網又要品質」的關鍵候選。
問「簡易故障排除」,四條依據裡卻有三條指向「操作注意事項」段;真正的「五、簡易故障排除 第 9 頁」只排到第 3、說明幾乎沒引用它。120b 只是把「撈錯的段」講得漂亮——漂亮的錯答比崩掉更危險。
更糟:35B 冒出「電源指示不亮 → 保險絲燒毀」,但 system prompt 白紙黑字禁止「資料沒提保險絲就不准寫保險絲」。這是疑似幻覺 + 假掛依據,待查手冊第 8 頁定案。這條病跟模型無關,是下一步要修的重點。
硬體註記:Qwen3.6-35B-A3B 是 MoE,每次只激活 ~3B 算力,但記憶體要載入全部 35B——4-bit 仍需約 21GB,單張 T4(16GB)會 OOM,得 2×T4(32GB)或直接走雲端 API。MoE 省的是算力,不是記憶體——這也是這次差點又踩的坑。
發佈留言