iT邦幫忙

2026 iThome 鐵人賽

DAY 3
0

同一把 API Key 同時能查資料、開工單、重設帳號、寄通知時,Agent 的工具清單就不再只是功能清單。

我沒有公司的 API Key。這個專案用的是 mock 工單和 mock SOP;「萬用 API Key」是我替這個練習場景放進去的危險前提。

Day 2 的 Agent 只有 create_ticket。它能根據問題開一張 mock 工單,能力小到幾乎不會讓人緊張。接著我想加查 SOP、重設密碼、寄通知。功能一列出來,我才發現一個很直接的問題:如果它們背後都共用同一個高權限憑證,模型每次選工具,實際上都在動用同一份過大的權限。

就算 prompt 寫著「只有必要時才能重設密碼」,API Key 也不會讀 prompt。後端看見的是一個帶著有效憑證的請求;如果 API 本身沒有再做權限判斷,誰送出請求都一樣。

這篇我沒有真的做出帳號重設 API。相反地,我先把它排除在 Agent 看得到的能力之外,並讓目前兩個工具各自待在清楚的邊界內。

我先把那把萬用 Key 拆成權限問題

假設我把 Agent 寫成這樣:

tools = [
    search_it_sop,
    create_ticket,
    reset_password,
    send_notification,
]

表面上只是多四個工具。實際上,模型會知道每個工具的名稱、描述與參數。任何一段使用者輸入、被檢索到的文件,甚至工具回傳的錯誤訊息,都可能讓它把任務帶到 reset_passwordsend_notification

如果這些工具又共享一把可以做所有事的 Key,風險不在模型有沒有「故意作惡」。模型只要判斷錯一次,後端就可能接受一個本來不該被發出的請求。

我把這件事拆成兩層來看:

模型能看見什麼工具?
    ↓
該工具背後的身分能做什麼?

第一層是 Agent 的能力邊界。第二層是後端與 API 的授權邊界。少任何一層都不夠:不把 reset_password 放進工具清單,模型就選不到它;即使未來加回來,帳號系統仍必須拒絕不符合角色、對象與核准條件的請求。

Day 3 只留下兩個工具

目前 Agent 只能看到這兩個工具:

self.agent = create_agent(
    model=model,
    tools=[search_it_sop, create_ticket],
    context_schema=HelpdeskContext,
    system_prompt=SYSTEM_PROMPT,
)

search_it_sop 是唯讀工具,查的是程式內固定的 mock SOP。create_ticket 會建立一張只存在記憶體中的 mock 工單。LangChain 只把 tools 清單中的工具交給模型選擇。LangChain Tools 文件

我沒有把「以後可能會用到」的能力先掛上去。現在不存在的工具,模型不能選;目前不存在的 API Key,也沒有機會被它帶著跑。

只讀,不等於沒風險

search_it_sop 看起來最安全,因為它只讀資料:

@tool
def search_it_sop(
    query: str,
    runtime: ToolRuntime[HelpdeskContext],
) -> list[dict[str, str]]:
    """Search read-only IT SOP articles before answering a procedural IT question."""
    return runtime.context.workflow.search_it_sop(query)

但只讀工具仍會把資料送回模型。今天它查的是我自己寫的三篇 mock SOP,內容可控;未來接外部文件或向量資料庫,檢索結果就得被視為不可信輸入。那會是 retrieval rails 的工作,不該因為它叫做「搜尋」就被當成安全的例外。

在目前版本,這個工具還有另一個用途:先查 SOP,才有資格建立工單。

if not self.sop_checked:
    self.trace.add(
        kind="guardrail",
        name="sop_first",
        status="blocked",
        detail="尚未查詢 SOP,拒絕建立工單。",
    )
    return {
        "status": "blocked",
        "reason": "Search the read-only SOP source before creating a ticket.",
    }

我沒有只在 prompt 裡寫「先查 SOP」。模型可能跳過那句話,所以開單工具本身會檢查 sop_checked。沒有搜尋紀錄,工具回傳拒絕結果,不會建立工單。

這條規則不是 RBAC 或 ACL,它只是這個 mock Helpdesk 的流程限制。不過它讓我確認了一件事:模型的建議和後端是否執行,可以是兩件不同的事。

我把身分留在 context,不交給模型填

兩個工具都透過 runtime.context 取用後端建立的流程物件:

@dataclass
class HelpdeskContext:
    workflow: HelpdeskWorkflow

HelpdeskWorkflow 由後端建立,裡面才有 requested_by、mock ticket store、mock knowledge base 與 trace。模型只能決定查詢字串,或整理工單的標題、描述與優先級;它不能自行把自己變成管理員,也不能把工單改掛到別人名下。

目前 requested_by 仍是寫死的 demo.user,不是驗證後的登入身分。這不是完整的身分設計,我不會假裝它已經能處理多使用者權限;我只是先避免把這個欄位交給模型生成。

我做了一個頁面,看工具是否真的被擋在邊界內

這次我沒有只留一個終端機指令。專案有本機頁面,可以輸入問題後查看 Agent 回覆、工具軌跡和 mock 工單。

uvicorn app.web:app --reload

開啟 http://127.0.0.1:8000 後,Day 2 的「SOP 優先」會跑成功開單的流程。Day 3 改用「先開單會被擋」,刻意在沒有查 SOP 時就要求寫入:

01  tool: create_ticket   requested
02  guardrail: sop_first  blocked
03  model: final_response 說明沒有建立工單

這個按鈕不需要 API Key,因為它不是在呼叫模型,而是可重現的 mock 流程。create_ticket 的呼叫確實進到後端,但在寫入前被 sop_first 擋下,所以不會出現 mock 工單。真正執行 LangChain Agent 的功能才需要模型 API Key;兩條路徑都會走同一個 SOP-first 檢查。

image

我用測試確認,這不是 prompt 在演戲

目前的測試直接呼叫後端流程,而不是期待模型每次都聽懂一句規則:

  • 沒查 SOP 時,建立工單會被擋下來。
  • 查過 SOP 後,才會建立一張 mock 工單。
  • 本機頁面的固定情境會嘗試在 SOP 查詢前開單,並確認它沒有建立工單。
python -m unittest discover -s tests -v

這些測試還不能證明「萬用 API Key」已經被解決。Day 3 根本沒有接真實帳號重設、通知或外部 API。它們證明的是目前的 Agent 沒被預先塞進高風險能力,而且流程規則確實在後端執行。

之後真的要接 API,我不會用同一把 Key 讓所有工具共用。至少要分成唯讀與可寫的身分,縮小每個憑證的範圍;高風險動作還需要針對使用者、資源與核准狀態做後端授權。下一篇我會讓工具故意失敗,再看 Agent 的重試會不會把一個錯誤擴大。

本日程式碼

day-03-sop-first-guardrail


上一篇
Day 2|30 分鐘做出第一個會開工單的 AI Agent:它已經能替你動手了
系列文
從 LLM、Agent 到 Guardrails:30 天打造可控、安全、可驗證的 AI Agent3
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

0
noalowo
iT邦新手 4 級 ‧ 2026-09-17 15:48:46

期待明天的發文

0
eating11111
iT邦新手 5 級 ‧ 2026-09-17 17:09:42

學長可以給我你的萬用 API Key 嗎 ?

過來韓老師這邊

過來樓梯口一下

noalowo iT邦新手 4 級 ‧ 2026-09-17 17:14:57 檢舉

已檢舉

我要留言

立即登入留言