iT邦幫忙

2026 iThome 鐵人賽

DAY 12
0

Day 10 我先把 context 的來源分開,Day 11 再把惡意 SOP 擋在模型外面。做到這裡,我原本很自然地想:既然不可信內容已經過濾,那多放一點歷史訊息,Agent 應該會知道得更多。

問題是,安全的資料也可能過期、重複或與眼前任務無關。對話拖得越長,VPN、Wi-Fi、印表機和先前的工具結果全部堆在一起,真正重要的一句話反而更難被找到。裡面如果還夾著帳號或備用碼,風險也跟著進了模型。

今天我會沿用 Day 11 接好的 Live LLM,但不會拿單次回答宣稱模型「變笨幾%」。真正要固定的是送進模型前的 context contract:哪些保留原文、哪些壓成摘要、哪些直接省略,以及敏感資料有沒有先被移除。

今天真正呼叫 LLM 的位置

Day 12 的 Live experiment 會先執行 prepare_history_context(),再呼叫 ChatOpenAI。目前輸入框提供最新一則 Prompt,另外五則本機測試歷史則用來穩定重現相關性、摘要與敏感資料移除。整份 chat log 不會直接塞進 messages。

raw history
  → 移除已知敏感資料
  → 保留最新要求
  → 補入較舊但相關的紀錄
  → 壓縮可安全摘要的穩定資料
  → 組成 model-visible blocks
  → Live LLM

現在這五個步驟已經接進 Day 12 Live 實驗。頁面會用六則本機測試歷史建立 context,其中目前 Prompt 由我親自輸入;敏感內容先被移除,剩下的三個 blocks 才會交給真實模型。

Context window 放得下,不代表模型每一段都用得一樣好

長 context 最直接的好處是能放更多資料,但容量不是注意力保證。2023 年的《Lost in the Middle》在多文件問答和 key-value retrieval 實驗中發現,相關資訊位於長 context 中間時,受測模型的表現常比資訊放在開頭或結尾差。Lost in the Middle

這份研究不能直接代表每個新模型,也不能拿來預測這個 Helpdesk Agent 的正確率。不過它提醒我一件很實際的事:不能把「沒有超過 context window」當成「模型一定找得到」。

Anthropic 將 context 變長後,模型回想與使用資訊的能力逐漸下降稱為 context rot,並建議保留能支撐目前任務的高訊號內容,而不是把所有歷史原封不動放進去。Effective context engineering for AI agents

我先做一份故意很亂的對話歷史

Day 12 的 mock history 有六則訊息:

訊息 內容 最後怎麼處理
message-01 測試裝置是 Windows 11 壓成穩定資料摘要。
message-02 先前的 Wi-Fi 問題已結束 與本次問題無關,省略。
message-03 VPN 曾出現 E401 雖然較舊,但和目前問題相關,保留原文。
message-04 一組假的測試備用碼 標成 secret,在排序與摘要前移除。
message-05 印表機缺紙,已自行解決 比 VPN 紀錄新,仍然省略。
message-06 VPN 又出現 E401,只要排障,不要開單 最新要求,保留原文。

如果我只取最後三則,message-03 會被排除,模型反而看不到前一次 E401 的處理結果。只看 recency 不夠,只看 relevance 也不行;最新一句「不要開單」同樣不能丟。

我同時保留 recency 和 relevance

這次的 prepare_history_context() 先找出最新的安全訊息,再從較早歷史中選出和 VPN、E401 最相關的一則。最後才用剩餘空間放摘要:

prepared = prepare_history_context(
    history,
    query_terms=("VPN", "E401"),
    max_blocks=3,
)

整理後只有三個 model-visible blocks:

history-summary  裝置是測試用 Windows 11 筆電
message-03       VPN 曾出現 E401,重新登入後暫時恢復
message-06       現在 VPN 又出現 E401,只要排障步驟,不要開工單

message-05 雖然比較新,卻是已經結束的印表機問題,因此不會擠掉較舊的 VPN 紀錄。這個 demo 用固定關鍵字計算相關性,不是完整的 semantic retrieval;換成真實系統後,仍要測試同義詞、否定句和多主題對話。

歷史壓縮不是直接砍掉最舊訊息

刪除最舊訊息很簡單,但舊資料可能仍然重要。這次我只把「測試裝置是 Windows 11」保留成摘要;已結束的 Wi-Fi 和印表機問題沒有摘要價值,直接省略。

ModelContextBlock(
    block_id="history-summary",
    reason="summary",
    content="較早紀錄:" + safe_summary,
)

這裡的 safe_summary 是預先寫好的 mock 欄位,不是讓另一個模型即時摘要,所以畫面每次都會相同。正式系統若用模型壓縮歷史,摘要本身也要測:它可能漏掉否定詞、把暫時狀態寫成永久偏好,甚至保留本來應該刪除的敏感資訊。原始歷史的存取與保留期限則是另一個資料治理問題,不能因為 prompt 變短就當作已刪除。

敏感資料要在排序和摘要以前移除

我不希望備用碼先被摘要模型讀過,再從最後的 prompt 遮掉。Day 12 的處理順序是先依程式端 metadata 找出 sensitivity="secret",整則排除後,才進行 relevance 排序與歷史壓縮。

Trace 只記錄「1 則 secret 已移除」,不記錄它的內容。OWASP 對 Sensitive Information Disclosure 的建議也包含資料清理、輸入驗證與限制外部資料來源;只在 system prompt 要求模型不要洩漏,仍可能被繞過。OWASP LLM02:2025 Sensitive Information Disclosure

這個 demo 的敏感標記是固定測試資料,不是通用的 PII detector。真實系統還需要資料分類、使用者隔離、存取控制與刪除政策。Day 12 先守一條較小的界線:已知不需要給模型的資料,就不要把它放進 context。

image
image

我這次讓畫面與測試留下什麼

固定資料區的「Context 精簡」仍保留,用來重現六則歷史如何變成三個 context blocks;文章主畫面則使用上面的 Live 實驗:

context    history_input                 6_messages
guardrail  sensitive_data_minimization   dropped
context    relevance_selection           kept
context    history_compaction            summarized
context    irrelevant_history            dropped
context    recency_selection             kept
context    model_context                  3_blocks

單元測試會確認最新要求仍在、較舊的 E401 紀錄勝過較新的印表機訊息、mock secret 沒有出現在 context 或摘要。Promptfoo 也新增 context_is_minimized,檢查輸出與 trace 不含那段敏感測試字串。

python -m unittest discover -s tests -v
npx --yes promptfoo@latest eval -c evals/promptfooconfig.yaml --no-progress-bar --no-cache

目前共有 79 個 unit tests 通過,Promptfoo 是 10 passed、0 failed、0 errors。這證明固定的 selection policy 沒有退化,沒有證明任何模型在長 context 下都會答對。

Day 13 會把單一惡意 SOP 擴成 Promptfoo 攻擊案例,測試不同措辭和 retrieval payload 是否會讓既有防線失守。

本日程式碼

day-12-live-context


上一篇
Day 11|RAG 最危險的不是幻覺,而是它把資料當成命令
系列文
從 LLM、Agent 到 Guardrails:30 天打造可控、安全、可驗證的 AI Agent 共 12 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言