← 返回文章

THE NOTEBOOK / ARTICLES

Day 7:第一週交付前,先讓本地聊天程式跑完一次驗收

以兩輪真實本地 runtime checkpoint,驗收 JSON 格式回答與 session history。

AI ENGINEERING · 2026

Day 06 讓互動模式真的帶上前一輪對話。到了第一週最後一天,我們做出的本地聊天程式(Chat Runner)已經可以單次呼叫、持續互動、加入 system message、驗證 JSON 回答,也能保留 Session history。

這五項能力各自有測試,還缺一個重要的確認:把它們串在同一條路徑上,會不會互相打架?

今天沒有加入新的 AI 概念。我把第一週收束成一個 --checkpoint 命令,讓它對真實的本地 Qwen3.8 連續發出兩個 request,同時驗收 JSON 格式與對話歷史。

GitHub Repository

今天的驗收標準

這篇主文是 Level 1:啟動 runtime,再執行一條 checkpoint 命令。Level 2 可以替換 model 或 runtime URL;Level 3 則是修改驗收問題和判定條件。

Checkpoint 不是把 Day 02 到 Day 06 的命令再手動跑一遍。它固定了一組可機器判定的任務:

第一輪:請記住代號「港口 17」,並以固定 JSON 回答「收到」
    ↓
把第一輪的 user / assistant message 放入 history
    ↓
第二輪:只問「上一題的代號是什麼?」
    ↓
回答必須是固定 JSON,answer 必須恰好等於「港口 17」

這條路徑同時用到四個已完成的零件:

第二輪 prompt 裡沒有「港口 17」。如果程式沒有把第一輪送回模型,這個代號就沒有地方可以來。

為什麼不只看「能不能聊天」?

手動輸入幾句話,看到模型回答,只能證明畫面看起來可用。它沒有回答下列問題:

Checkpoint 把這些條件寫進 Python。回答只要少一個欄位、limitations 不是陣列、第一輪沒有回「收到」,或第二輪沒有回「港口 17」,這次驗收都會失敗。

格式正確不夠。這一天多加的,正是「答案有沒有做對這個小任務」的判定。

run_checkpoint() 做了什麼

第一輪固定使用 temperature=0 和 json_answer=True:

first_content = call_local_model(
    CHECKPOINT_FIRST_PROMPT,
    base_url=base_url,
    model=model,
    system_prompt=system_prompt,
    temperature=0,
    max_tokens=max_tokens,
    json_answer=True,
    history=history.copy(),
)
first_answer = parse_answer(first_content)

call_local_model() 已經會驗證模型回覆,回傳的是重新排版過的 JSON 字串。Checkpoint 再呼叫一次 parse_answer(),是因為接下來還要檢查 answer 和 limitations 的內容。

第一輪通過後,問題與完整的 JSON 回答才會加入 history:

history.extend(
    [
        {"role": "user", "content": CHECKPOINT_FIRST_PROMPT},
        {"role": "assistant", "content": first_content},
    ]
)

第二輪沿用相同的 system message、JSON 規則與生成參數,只多了這份 history。最後會直接比對完整物件,不能只檢查回答裡有沒有代號:

{"answer": "港口 17", "limitations": []}

這樣才能同時檢查欄位、型別與任務結果。

實際跑完第一週 checkpoint

先在終端機一啟動 MLX-LM server:

uv run mlx_lm.server \
  --model mlx-community/Qwen3.8-27B-4bit \
  --port 8081

等 server 開始監聽 127.0.0.1:8081 後,在終端機二執行:

uv run python frontier_knowledge.py \
  --checkpoint \
  --model mlx-community/Qwen3.8-27B-4bit \
  --max-tokens 1024

本次在 Python 3.13.15、mlx-lm 0.31.3 與 mlx-community/Qwen3.8-27B-4bit 的實際輸出是:

Checkpoint 1/2:單次 JSON 回答通過(answer=收到)
Checkpoint 2/2:Session history 回答通過(answer=港口 17)
Day 7 checkpoint:PASS

命令結束碼是 0。Server log 也記錄了兩個 POST /v1/chat/completions 都回傳 HTTP 200。

如果 runtime 連不到、回傳非法 JSON,或第二輪答錯代號,程式會在 stderr 輸出 Day 7 checkpoint:FAIL,並以結束碼 1 離開。因此這條命令不只能給人看,shell 或後續的自動化流程也能判斷成功與失敗。

18 項自動測試守住哪些邊界

真實模型測試可以確認整條路徑跑得通,卻不適合當成每次都完全相同的回歸測試。模型輸出有它的不確定性,載入 27B 模型也會增加測試時間。

Day 07 因此另外加了 5 項不需要啟動 runtime 的測試:

連同 Day 05 和 Day 06 的回歸測試,目前共 18 項:

uv run python -m unittest discover -s tests -v

本次結果為 18 項全數通過。

這個 checkpoint 還沒有證明什麼

PASS 的範圍很窄。它證明這支本地聊天程式在本次環境中能夠呼叫模型、驗證回答格式,並在第二輪找回前一輪的代號。

它還沒有檢查:

最後一點正好是第二週的主題。目前這支程式只會用模型已有的參數和當下 prompt 作答,還不會讀專案內的工程文件。

第一週留下什麼

今天增加的是一條可重複執行的本地聊天程式 checkpoint。它解決了「各功能分開看起來正常,合在一起卻沒有驗收」的缺口。

Day 08 會開始建立 knowledge/raw 與第一組工程文件。到那時,這支程式才有可以查找與引用的本地資料。

參考資料

繼續探索其他想法。

所有文章 ↗