Learning Objectives
- 把檢索、工具、Agent 迴圈、拒答與引用查核接成一個完整的程式
- 對測試集跑一次,報告檢索 recall、引用正確率、拒答行為與總 token 數
- 依評估結果判斷下一步該改哪裡
- 列出把應用部署前要注意的事
要做什麼
這一課把前面學過的東西接起來,做一個回答校務問題的助手:
| 使用者問 | 助手要做的事 | 前面哪一課 |
|---|---|---|
| 學校規定(缺課、停修、獎學金) | 查手冊,依資料回答並引用節編號 | 第 13、15 課 |
| 課表(哪間教室、什麼時候) | 查課表 | 第 15、16 課 |
| 手冊沒有寫的事 | 回答「手冊沒有寫」,不亂答 | 第 13、14 課 |
| 回答引用了不存在的節 | 程式攔下來 | 第 14 課 |
| 這個助手好不好 | 用測試集算出數字 | 第 14、20 課 |
手冊和課表都是虛構的教材,不是任何學校的資料。程式分四個階段組起來,每個階段都可以單獨檢查。這也是實際開發時的順序:先確認每一段是對的,再接起來。
範例的回應是預錄的真實輸出(2026-11-03,gpt-5.6-luna)。
第一階段:工具
工具的程式放在 project_tools.py,可以在右邊點進去看。這個階段還沒有接模型,只確認工具回傳的東西對不對。
第二階段:Agent 迴圈
由模型決定要呼叫哪個工具。請看 retrieved 怎麼記下檢索到的節:下一階段查核引用要用它,評估時也要用它。
多一層 Agent 的代價
第 13 課固定先檢索再回答,同一類問題一題約 350 token;這裡一題約 1,000 token。多出來的是工具描述、系統提示詞,以及多呼叫一次模型。
換來的是模型可以決定要不要查手冊、查哪一個工具,也會自己改寫查詢。如果應用只會查手冊,第 13 課的做法比較省也比較簡單。要不要用 Agent 應該依需求決定,不是越複雜越好。
第三階段:拒答與引用查核
模型的回答要過一關才給使用者看。迴圈搬進 assistant_loop.py。最後兩筆是我們手寫的回答,用來測試這一關擋得住什麼、擋不住什麼。
這一關的限制
最後一筆說明了引用查核的界線:它能抓到編造的來源,抓不到講錯內容。 想抓內容錯誤要再加別的方法,例如把回答的數字和引用段落比對,或用第 20 課的模型評分;這些方法都不是完全可靠。所以畫面上要附原文連結,讓使用者可以自己對照,這比多一道檢查更重要。
這裡用的是固定的提醒文字,不是重新呼叫模型。出問題時交給程式固定處理,行為比較容易預期。
第四階段:對測試集跑一次
我們用真實模型跑了 19 題(手冊 16 題、課表 3 題),結果存成一行一題的紀錄。這個範例只讀紀錄、算指標,不再呼叫模型。
讀這份結果
| 指標 | 結果 |
|---|---|
| 檢索 recall(正確的節有被找到) | 13 / 14 |
| 引用到正確的節 | 13 / 14 |
| 有答案卻回答「手冊沒有寫」 | 0 / 14 |
| 手冊沒有寫的題目有拒答 | 2 / 2 |
| 課表題答對(依關鍵字) | 3 / 3 |
| 總 token | 19,009(輸入 17,582、輸出 1,427;不含 embedding) |
看起來不錯,但有幾件事要照實說:
- q10「繳不出學費」還是沒答好。 檢索到
fee-1、course-2、lib-1,沒有就學貸款那一節。回答是「應在註冊期限內繳清學雜費;若未辦理就學貸款或減免而逾期未繳,將視同未完成註冊。[fee-1]」,有引用,也沒有編造,卻沒有告訴使用者該怎麼辦。 - 指標全過的題目也有錯。 q07 的回答寫「115學年度起適用新制」,手冊寫的是 114 學年度起。結論對,理由有小錯。這題的兩個指標都通過:我們是讀了回答才發現的,所以指標之外還要抽樣閱讀。
- q14 這次答對了。 第 20 課固定用問題原文檢索時,這題沒找到諮商中心;這次檢索到
health-2,回答也正確。可能因為模型改寫了查詢,但這只有一題、只跑一次,不能說改寫查詢一定比較好。 - 題目太少,也只跑一次。 19 題裡有 2 題是拒答、3 題是課表,能看出明顯的問題,不足以代表上線後的品質。第 20 課看過,同一個請求每次回答可能不同。
- 關鍵字只是粗略的檢查。 課表 3 題我們有另外讀過回答,內容正確。
下一步怎麼改,依問題排序:
- q10:改善檢索,例如關鍵字與向量混合搜尋、在手冊標題加上同義詞(第 11、12 課)。
- q07:在段落加上適用年度的欄位,由程式判斷;或在回答旁附原文,讓使用者對照。
- 測試集:加更多換句話說與新舊規定的題目,每題多跑幾次,並留一部分不拿來調整。
實作:補完引用查核
完成 check_citations(answer, retrieved),回傳 (cited, bad) 兩個串列:
cited:回答裡所有[節編號]的內容,依出現順序。節編號的格式是英文小寫、連字號、數字,例如course-1。bad:cited裡不在retrieved的節編號。
finalize 已經寫好。下面的回答有真實錄到的,也有手寫的,這一題不用呼叫模型。
小測驗:整合與評估
查核引用時,retrieved 應該從哪裡來?
回答「逾期每冊每日罰款一百元。[lib-1]」,而 lib-1 在檢索結果裡,手冊寫的是五元。這個專題的引用查核會怎樣?
評估結果只有 q10 指標沒過:檢索沒有拿到就學貸款那一節。最該先改哪裡?
這次 19 題的輸入 token 遠多於輸出 token,主要原因是什麼?
下面哪一個說法最符合這次的結果?
部署之前要注意的事
這個專題的程式還不能直接放上網給人用。真的要部署,至少先處理這些:
| 項目 | 要做什麼 |
|---|---|
| 金鑰 | 放在伺服器的環境變數,不進版本控制,不放前端;定期更換,設定用量上限 |
| 限流與費用 | 每位使用者每分鐘、每天的次數上限;限制輸入長度;監看每日 token 用量,超過就告警 |
| 逾時與錯誤 | 第 8 課的重試與逾時;模型掛掉時給固定訊息,不要把錯誤細節顯示給使用者 |
| 紀錄 | 記下問題、檢索結果、回答與 token,才有辦法除錯與改善;同時要決定保留多久、誰看得到,密碼與金鑰不可以記 |
| 隱私 | 使用者輸入的內容會送到模型供應商。先確認供應商的資料政策、學校的規定,並告知使用者 |
| 研究與校內規範 | 如果要拿使用者的對話做研究,要先確認研究倫理審查(IRB)與知情同意;在校內上線也要依學校的規定 |
| 提示詞注入 | 手冊內容、使用者上傳的文件都可能夾帶指令;工具權限要最小(第 17、18 課) |
| 回答的責任 | 標明這是 AI 產生的回答、附原文連結;重要決定(退學、獎懲)請使用者向承辦單位確認 |
蒐集與傳送資料的判斷,可以再看 AI 素養軌第 4 課資料邊界:你的資料去了哪裡。
接下來可以做什麼:
- 換成你自己的資料:社團的常見問題、某門課的講義。先寫 10 題測試題,再開始調整。
- 把助手包成網頁:需要一個後端接收問題、呼叫這段程式,再把結果顯示出來;串流的做法在第 8 課。
- 試著把檢索換成混合搜尋,用第四階段的指標看 q10 有沒有改善,同時確認其他題目沒有變差。
重點整理
- 系統要分段組裝、分段確認:工具、迴圈、把關、評估。
- 程式要自己記下檢索到什麼、用了多少 token,查核與評估才有依據。
- 引用查核抓得到編造的來源,抓不到講錯的內容。
- 這次的真實結果:recall 13 / 14,拒答 2 / 2,總共 19,009 token;q10 檢索失敗,q07 指標通過但回答有小錯。結果不完美,也知道下一步該改哪裡。
- 部署前要處理金鑰、限流、紀錄、隱私與校內規範。
這是生成式 AI 應用軌的最後一課。你現在可以用程式呼叫模型、接上自己的資料與工具,也知道怎麼用數字檢查它。工具會一直變,這幾件事的做法變得比較慢:弄清楚模型實際收到什麼、把判斷放在程式裡、用測試確認有沒有變好。