在 用 Langfuse 建立 Agent 評估流程 與 用 Langfuse 從線上 Trace 評估 Agent 正確率 中,Langfuse 主要用來記錄模型輸入輸出與評估意圖分類的正確率。
當 Agent 開始接入退款、轉帳或修改資料等具有副作用的 Tool 時,觀測重點就要延伸到安全維運:
這兩項機制的層次不同:業務安全政策(Guardrail)在事前檢查並阻擋未授權請求,確保敏感 Tool 根本不會被觸發;Langfuse 則在事後保存完整的決策鏈路,並透過 Score 與 Dashboard 呈現安全態勢。
在安全架構中,模型判定要退款後,系統不能直接執行退款,而是要依序通過政策檢查與後端執行。
對應的範例程式位於 langgraph-pydantic-intent-routing。核心安全執行控制器位於 security_audit/runner.py,其中的 run_security_audit() 是主入口函式。無論是 Web API、自動化測試或命令列腳本,都呼叫這個函式來驅動一次完整的安全對話流程:

三層防禦結構由外而內展開。其中 langfuse.start_as_current_observation(...) 是 Langfuse SDK 語法,只要用 with 包住程式碼,就會自動在 Trace 樹狀圖中新增一個監控節點(如 agent、guardrail 或 tool):
def run_security_audit(message, context):
# ① 階段 1:最外層 Agent Trace(意圖辨識)
with langfuse.start_as_current_observation(as_type="agent", name="customer-service-agent"):
intent, order_id = classify_intent(message)
if intent != "refund":
return "一般回覆(非敏感操作,直接結束)"
# ② 階段 2:Guardrail Trace(事前安全政策檢查)
with langfuse.start_as_current_observation(as_type="guardrail", name="refund-policy"):
if not check_policy(context, order_id):
return "政策攔截:非本人訂單(事前中止,不呼叫 Tool)"
# ③ 階段 3:Tool Trace(後端交易執行)
with langfuse.start_as_current_observation(as_type="tool", name="process-refund"):
return backend.process_refund(order_id)
整個架構的執行邏輯:辨識為敏感意圖才進 Guardrail ➔ 政策放行才進 Tool 執行。任何一步未放行就提早返回(Early Return),杜絕越權操作。
細看這三個階段在資安防護上的具體設計:
在將請求送入 LangGraph 前,使用者 ID 不能直接以明文送往觀測平台。透過 _pseudonymous_user_id() 以 HMAC-SHA256 生成去識別化別名(usr_...),並注入 Trace 屬性中:
with langfuse.start_as_current_observation(as_type="agent", name="customer-service-agent"):
with propagate_attributes(
trace_name="customer-service-agent",
user_id=_pseudonymous_user_id(context.session_user_id),
session_id=context.session_id,
environment=context.environment,
metadata={"request_id": context.request_id},
):
routing = invoke(graph, message, callbacks=[langfuse_handler])
當前發起請求的已登入身分(context.session_user_id)來自系統驗證(例如 JWT Token),不交由模型猜測。模型提取出 order_id 後,refund_policy.evaluate() 會向訂單資料庫執行唯讀查詢,比對訂單擁有者是否為當前登入者,並確認訂單狀態是否符合退款資格。
這項檢查由 guardrail 步驟記錄,並在 Metadata 存入固定原因代碼 reason_code。若未放行,流程直接返回拒絕訊息,絕對不呼叫退款 Tool:
with langfuse.start_as_current_observation(as_type="guardrail", name="refund-policy"):
decision = refund_policy.evaluate(context, order_id)
policy_span.update(
output={"outcome": decision.outcome.value},
metadata={"reason_code": decision.reason_code.value},
)
if decision.outcome is not PolicyOutcome.ALLOW:
return ... # 政策未通過時直接事前結束,不進入 Tool
[!NOTE] 為什麼不讓後端直接擋就好,還要在 Agent 層多做 Guardrail?
後端 API 當然必須具備 403 權限防禦;但將檢查前置到 Agent 的 Guardrail 層有兩大關鍵原因:
- 在副作用發生前踩煞車:避免模型被 Prompt Injection 套話或產生幻覺後,直接去呼叫後端扣款 API。
- 獨立資安觀測:在 Langfuse 上獨立記錄
as_type="guardrail",能精確統計「被嘗試了哪些 Prompt 企圖越權」,而不是把資安攻擊混在一般的後端 Tool 錯誤中。
只有政策判定 allow 時,才建立 tool 步驟呼叫後端執行退款,並記錄執行狀態:
with langfuse.start_as_current_observation(as_type="tool", name="process-refund"):
refund = refund_backend.process_refund(refund_args, context, decision)
tool_span.update(output={"status": refund.status})
專案在 .env 中已設定好 Langfuse 連線金鑰、執行環境與使用者雜湊金鑰(LANGFUSE_USER_HASH_KEY)。
切換至專案目錄並安裝依賴:
cd ai-agent-sample/langgraph/langgraph-pydantic-intent-routing
uv sync
執行一筆本人訂單的退款請求(當前使用者擁有訂單 ORD-1001):
uv run agent-security-audit \
"我要退款訂單 ORD-1001" \
--request-id req-allow-1 \
--idempotency-key refund-req-allow-1
終端機輸出顯示政策放行並執行 Tool:
Langfuse tracing:已啟用
Trace ID:4f8c12a8...
意圖:refund
政策結果:allow
原因代碼:OWNER_CONFIRMED
Tool 執行:是
回覆:退款已建立:REF-A1B2C3D4E5F6
執行一筆非本人訂單的越權請求(使用者嘗試退款屬於他人的訂單 ORD-2001):
uv run agent-security-audit \
"我要退款訂單 ORD-2001" \
--request-id req-deny-1 \
--idempotency-key refund-req-deny-1
終端機輸出顯示政策在事前已阻擋該操作,Tool 未被執行:
Langfuse tracing:已啟用
Trace ID:7c3b4a1f...
意圖:refund
政策結果:deny
原因代碼:NOT_OWNER
Tool 執行:否
回覆:退款未執行:NOT_OWNER
執行完成後,兩筆請求的完整決策鏈路已記錄在 Langfuse。點入 Langfuse Web UI 的 Tracing 頁面,可以檢視完整的稽核軌跡。
在 Langfuse 的 Tracing 頁面點入這兩筆 Trace,可以直接透過左側樹狀圖對比放行與阻擋的結構差異:
點開本人退款請求,左側面板呈現完整的放行鏈路:
customer-service-agent(根節點)
intent-routing(意圖辨識確認為退款)refund-policy(事前政策檢查,顯示 policy_allowed: True)process-refund(政策放行後建立的 Tool 步驟,完成退款)
點開非本人退款請求,最關鍵的合規證據在於鏈路在此中斷:
refund-policy(顯示 policy_allowed: False)後結束。process-refund 節點。unauthorized_access: True 與 policy_outcome: deny 標記。這直接向審計人員證明:即使模型正確識別出退款意圖,因為事前政策檢查未通過,退款 Tool 根本未被建立或呼叫。

在右側屬性面板中,稽核紀錄需要辨認使用者,但觀測平台不能存放明文個資(如身分證字號或明文 UUID):
usr_3a9f...)。透過 _pseudonymous_user_id() 以 HMAC-SHA256 計算,相同的真實使用者會對應到同一個別名,既能跨 Session 追蹤同一個帳號的行為,又符合資安規範。demo-session-1,關聯同一次會話的對話脈絡。request_id: "req-allow-1",方便與業務系統的日誌交叉比對。文字 Metadata 只能在點開單筆 Trace 時人工閱讀,無法直接用來計算次數或即時告警。
Langfuse 的 Score 除了數值(Numeric)之外,也支援布林值(Boolean)。在安全監控中,我們將政策檢查結果以 Boolean 型態的 Score(即安全狀態標記 Flag) 記錄下來,用來驅動即時告警、態勢看板與威脅篩選。
這些安全標記是由 Python 程式碼在 Guardrail 檢查完後確定性寫入,不需要耗費額外 Token 請 AI 評分。
在 Langfuse 介面進入 Settings → Score Configs 點選 Create Score Config 建立兩個核心安全指標:
unauthorized_access(Data Type 選擇 Boolean):True 表示亮紅燈,偵測到非本人訂單越權存取。policy_allowed(Data Type 選擇 Boolean):True 表示亮綠燈,政策檢查放行。接著在 security_audit/runner.py 的 _score_policy() 中透過 SDK 寫入評分:
def _score_policy(observation: Any, decision: RefundPolicyDecision) -> None:
# 記錄政策是否放行
observation.score(
name="policy_allowed",
value=1 if decision.outcome is PolicyOutcome.ALLOW else 0,
data_type="BOOLEAN",
comment=decision.reason_code.value,
)
# 記錄是否為越權存取(寫在整筆 Trace 上便於清單直接篩選)
observation.score_trace(
name="unauthorized_access",
value=1 if decision.reason_code is ReasonCode.NOT_OWNER else 0,
data_type="BOOLEAN",
)
點選被攔截 Trace 頂部的 Scores 分頁,即可看到 policy_allowed: False 與 unauthorized_access: True(亮起紅燈標記)。
在 Langfuse 的 Tracing 總覽清單中,點擊 Filter 加入篩選條件:
Score: unauthorized_access = True
清單會立即過濾掉所有正常的對話,只留下被事前攔截的越權請求。資安值班人員可以直接點擊檢視是哪位使用者、在什麼時間點、嘗試操作哪筆訂單,快速排查攻擊行為。
累積多筆請求後,可以在 Langfuse 上建立視覺化看板來監控整體異常趨勢:
Agent Security。Ⓑ unauthorized_access (api):Ⓑ 代表 Boolean 型態,(api) 代表由程式碼自動打上。Ⓑ policy_allowed (api):統計放行比例。Scores
unauthorized_access
value = True
Time Series 或 Bar chart
Scores
policy_allowed
Pie chart
除了在看板查看圖表,重大資安事件需要即時通報:
Unauthorized Order Access Attempt
Scores
unauthorized_access
Count > 0(在 5 分鐘內只要出現一次就觸發)Alert 的職責在於通知值班人員介入調查;程式內部的事前政策檢查(Guardrail)才是阻擋越權的第一道防線,不能等收到告警才在後續補救。
這個系列從發布門檻出發,依序把評估拆成幾層,每一層用不同方式判斷,結果都寫成 Langfuse 的 Score:
intent 與 handled_by 是否符合預期。unauthorized_access 等 Boolean Score 則支援 Filter、Dashboard 與 Alert。前四層判斷 Agent 答得對不對,最後一層判斷 Agent 有沒有做不該做的事。Offline 評估在發布前把關,線上評估與監控在發布後持續觀察,兩邊透過失敗案例回流 Dataset 串成同一個循環。