AWS Builder Workshop

使用 AgentCore 與 Strands 建構:受治理的多 Agent 風險系統開發者工作坊

使用 AgentCore 與 Strands 建構:受治理的多 Agent 風險系統開發者工作坊

目標受眾: 資深開發者、平台工程師、AI 治理工程師

時長: 2 小時

主要 AWS AI 服務: Amazon Bedrock AgentCore Runtime、AgentCore Memory 模式、AgentCore Policy 模式、AgentCore Observability 模式、AgentCore Evaluations 模式、Strands Agents

專案產出: 一個具備專業 Strands Agent、有界記憶、策略檢查、結構化遙測、評估測試組件和運行時用戶端的受治理多 Agent 運行時(Runtime)。

僅供教育用途之工程工作坊。本活動為軟體架構演練,不構成財務建議。

工作坊摘要

本工作坊將教導開發者如何使用 AgentCore Runtime 與 Strands 專家 Agent 設計一個受治理的多 Agent 風險系統。參與者將實作協調編排、請求與回應的策略檢查、有界記憶摘要、結構化可觀測性事件,以及針對安全與阻擋情境的評估測試組件。最終成果是一個可重複使用的架構,為真實團隊在 AWS 上實現證據優先的綜合分析、可追溯的營運,以及更安全的多 Agent 協作工作流。


1. 開發者學習目標

開發者將學習如何:

  1. 將 AI 工作流分解為編排器(Orchestrator)與專家(Specialist)Agent。
  2. 建立具備領域特定工具的專業 Strands Agent。
  3. 建構 AgentCore Runtime 編排器進入點。
  4. 在模型呼叫前加入策略檢查(Policy Checks)。
  5. 加入有界記憶摘要以確保安全連續性。
  6. 發送結構化可觀測性事件。
  7. 針對安全與阻擋流程建立評估測試組件(Evaluation Fixtures)。
  8. 建構運行時用戶端並測試多 Agent 行為。

2. 本工作坊建構的架構

Client (用戶端)
  └─ invoke_governed_runtime.py
AgentCore Runtime
  └─ orchestrator.py
       ├─ policy.py              # 預檢與回應檢查
       ├─ memory_store.py        # 有界摘要記憶
       ├─ observability.py       # 結構化遙測
       ├─ specialists.py         # 流動性 / 信用 / 外匯 / 主權專家 Agent
       └─ 評估測試組件
Strands Agents
  ├─ 流動性專家 Agent
  ├─ 信用專家 Agent
  ├─ 外匯專家 Agent
  ├─ 主權專家 Agent
  └─ 最終編排綜合 Agent

3. 2 小時實作議程

時間 (分鐘)模組實作產出
0–10架構設定理解 Agent 的角色與邊界
10–25提示詞契約建立編排器與專家 Agent 的提示詞
25–45專家 Agent四個具備工具的 Strands Agent
45–65策略與記憶護欄與有界上下文
65–85運行時編排器AgentCore 進入點委派任務
85–100可觀測性結構化 JSON 遙測資料
100–115評估安全與阻擋測試案例
115–120運行時用戶端調用 Payload 與預期輸出

步驟 1 — 建立提示詞契約

開發者執行動作

mkdir -p agentcore-governed-risk/prompts
cd agentcore-governed-risk
cat > prompts/orchestrator.md <<'EOF'
你是一個主權風險編排器。
不要獨自回答所有問題。將請求分解為專家任務。
使用流動性專家來處理資金壓力與現金壓力。
使用信用專家來處理利差擴大與違約風險重新定價。
使用外匯專家來處理貨幣錯配、避險背景與資本流動壓力。
使用主權專家來處理財政公信力、實質殖利率、政策分歧與債務重新定價。
將專家的證據合併為:證據、風險體制、避險考量、
確認訊號、失效觸發因素、未決問題和限制。
請勿提供自主交易指令或個人化財務建議。
EOF

建立 requirements.txt

bedrock-agentcore
strands-agents
strands-agents-tools
boto3
pytest

商業邏輯

編排器提示詞定義了多 Agent 工作流與最終回應結構。

程式邏輯

該提示詞由編排器運行時載入,並控制最終的綜合分析行為。

預期結果

儲存庫中包含一個定義清晰的編排器角色契約。

系統設計決策

每個角色的提示詞契約: 多 Agent 系統需要清晰的角色邊界。編排器提示詞定義了委派與綜合的職責,減少單一 Agent 試圖處理所有事情的機率。 提示詞中的輸出綱要(Schema): 必要的段落使最終答案更容易測試與顯示。評估機制可以檢查是否包含確認訊號、失效觸發因素和限制。 * 每個角色中的安全邊界: 提示詞中明確排除了自主交易與個人化財務建議。這不是唯一的控制手段,但在策略程式碼檢查輸出之前,它能引導模型的行為。


步驟 2 — 建構專家 Agent

開發者執行動作

建立 specialists.py

from dataclasses import dataclass
from strands import Agent, tool
from strands.models import BedrockModel
MODEL_ID = "amazon.nova-pro-v1:0"
@dataclass
class SpecialistResult:
    name: str
    evidence: str
    confidence: str
    missing_data: str
@tool
def bps_change(current: float, previous: float) -> str:
    """計算基點(bps)的變化。"""
    return f"變化: {(current - previous) * 100:.1f} bps"
@tool
def hedge_amount(exposure: float, hedge_ratio: float) -> str:
    """根據曝險和避險比率計算避險金額。"""
    return f"避險金額: {exposure * hedge_ratio:,.2f}"
@tool
def liquidity_buffer(required_outflow: float, buffer_ratio: float) -> str:
    """計算流動性緩衝需求。"""
    return f"所需的流動性緩衝: {required_outflow * buffer_ratio:,.2f}"
def build_specialist(name: str, focus: str, tools=None) -> Agent:
    return Agent(
        model=BedrockModel(model_id=MODEL_ID, temperature=0.2, max_tokens=2000),
        tools=tools or [],
        system_prompt=(
            f"你是 {name} 專家。請僅專注於 {focus}。"
            "傳回證據、信心度、缺失資料和限制。"
            "請勿提供投資建議。"
        ),
    )
liquidity_agent = build_specialist(
    "liquidity",
    "資金流動性、回購壓力、現金偏好和市場深度",
    [bps_change, liquidity_buffer],
)
credit_agent = build_specialist(
    "credit",
    "信用利差擴大、評等調降壓力、違約風險重新定價和流動性溢價",
    [bps_change],
)
fx_agent = build_specialist(
    "fx",
    "貨幣錯配、資本流動、人民幣壓力、基差換匯和避險背景",
    [hedge_amount],
)
sovereign_agent = build_specialist(
    "sovereign",
    "實質殖利率、財政公信力、政策分歧、央行反應和債務重新定價",
    [bps_change],
)
def run_specialist(agent: Agent, name: str, task: str) -> SpecialistResult:
    response = agent(task).message["content"][0]["text"]
    return SpecialistResult(
        name=name,
        evidence=response,
        confidence="medium",
        missing_data="請參閱專家證據以獲取所需的缺失資料。",
    )

商業邏輯

每個專家僅專注於風險的一個維度,並且只接收該維度所需的工具。

程式邏輯

該檔案定義了一個資料類別(Dataclass)結果契約、可重複使用的工具函式、一個專家工廠、四個 Agent 以及一個執行輔助程式。

預期結果

編排器可以匯入專家並分派專注的任務給他們。

系統設計決策

縮小專家範圍: 精簡的提示詞可以減少認知負載,並使 Agent 的輸出更具焦。每個專家隨後可以根據領域特定的標準進行獨立評估。 最小權限工具分配: 流動性專家獲得緩衝與基點工具,外匯專家獲得避險規模計算,而專注於利差的 Agent 則獲得基點計算工具。這減少了工具誤用的機會並支援策略執行。 * 資料類別結果契約: 型別安全的契約有助於編排、遙測和評估。即使 Agent 回應本身是自然語言,編排器也能收到一致的欄位。


步驟 3 — 新增策略層

開發者執行動作

建立 policy.py

BLOCKED_REQUEST_PATTERNS = [
    "執行交易",
    "下單",
    "繞過審批",
    "保證獲利",
    "隱瞞損失",
    "規避控制",
    "忽略風險限制",
]
REQUIRED_RESPONSE_TERMS = ["確認", "失效", "限制"]
def validate_request(prompt: str) -> dict:
    text = prompt.lower()
    for pattern in BLOCKED_REQUEST_PATTERNS:
        if pattern in text:
            return {"allowed": False, "reason": f"遭阻擋的不支援請求模式: {pattern}"}
    return {"allowed": True, "reason": "允許用於教育分析。"}
def validate_response(response: str) -> dict:
    text = response.lower()
    checks = {term: term in text for term in REQUIRED_RESPONSE_TERMS}
    checks["advice_boundary"] = "非投資建議" in text or "非財務建議" in text
    return {"passed": all(checks.values()), "checks": checks}

商業邏輯

策略層會阻擋不受支援的自主行動請求,並驗證最終回應的結構。

程式邏輯

validate_request() 在模型呼叫前執行。validate_response() 可以在綜合分析後或在評估腳本中執行。

預期結果

不安全的執行類提示詞會在專家 Agent 執行前被阻擋。

系統設計決策

策略先於成本: 在模型呼叫前阻擋不受支援的請求可以節省成本並降低風險。這也為明顯無效的提示詞提供了確定性的行為。 輸入與輸出控制: 輸入策略阻擋不良請求。輸出策略驗證必要的治理段落。兩者皆不可或缺,因為單靠提示詞無法保證合規的輸出。 * 便於工作坊理解的簡易規則: 字串比對在手把手實驗室中非常易於理解。實際生產環境的實作可以用 AgentCore Policy、分類器、身份識別感知規則和工具呼叫驗證來取代。


步驟 4 — 新增有界記憶

開發者執行動作

建立 memory_store.py

import json
from pathlib import Path
from datetime import datetime, timezone
MEMORY_FILE = Path("memory_state.json")
MAX_ITEMS_PER_USER = 10
MAX_ITEMS_IN_PROMPT = 3
def _read_all() -> dict:
    if not MEMORY_FILE.exists():
        return {}
    return json.loads(MEMORY_FILE.read_text(encoding="utf-8"))
def _write_all(data: dict):
    MEMORY_FILE.write_text(json.dumps(data, indent=2), encoding="utf-8")
def load_memory(user_id: str) -> str:
    data = _read_all()
    items = data.get(user_id, [])[-MAX_ITEMS_IN_PROMPT:]
    if not items:
        return "無先前安全記憶。"
    return "\n".join(item["summary"] for item in items)
def save_memory_summary(user_id: str, prompt: str, response: str):
    data = _read_all()
    item = {
        "created_at": datetime.now(timezone.utc).isoformat(),
        "summary": "先前的活頁工作流請求了主權風險分析。僅保留工作流上下文,不保留個人財務指令。",
        "prompt_chars": len(prompt),
        "response_chars": len(response),
    }
    data.setdefault(user_id, []).append(item)
    data[user_id] = data[user_id][-MAX_ITEMS_PER_USER:]
    _write_all(data)

商業邏輯

記憶功能保留了安全的工作流連續性,同時避免儲存原始的對話紀錄。

程式邏輯

該模組讀取/寫入 JSON,僅傳回最新的安全摘要,並限制保留數量。

預期結果

來自同一使用者的重複請求將包含有界的先前工作流上下文。

系統設計決策

摘要記憶而非對話紀錄記憶: 原始提示詞可能包含敏感或不相關的內容。儲存安全的摘要可以減少風險暴露,並防止舊的模型文字被盲目重複使用。 有界回溯(Bounded Recall): 提示詞中僅注入三個摘要。這可以控制提示詞的大小並降低過期上下文的風險。持久化儲存也只保留有限的歷史紀錄。 * 可替換的儲存抽象: 本地檔案實作旨在教學基礎概念。生產系統可以直接替換為託管的 AgentCore Memory,而無需重寫編排器邏輯。


步驟 5 — 新增結構化可觀測性

開發者執行動作

建立 observability.py

import json
import time
from datetime import datetime, timezone
START = time.time()
def emit_event(event_type: str, request_id: str, attributes: dict):
    event = {
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "elapsed_ms": int((time.time() - START) * 1000),
        "event_type": event_type,
        "request_id": request_id,
        "attributes": attributes,
    }
    print(json.dumps(event, ensure_ascii=False))

商業邏輯

遙測紀錄包含策略決策、編排啟動、專家完成以及最終綜合完成等事件。

程式邏輯

單一輔助程式,負責列印包含時間戳記、經過毫秒數、事件類型、請求 ID 和屬性的結構化 JSON 事件。

預期結果

執行期日誌包含機器可讀的事件。

系統設計決策

結構化日誌: JSON 遙測資料可以被索引、篩選和關聯。這在生產環境的 Agent 除錯中比自由格式的 print 語句更好用。 請求 ID 傳遞(Propagation): 每個事件都使用相同的請求 ID。這有助於追蹤從輸入策略到專家輸出,再到最終回應的完整工作流。 * 本地模式對應到託管可觀測性: 雖然本工作坊使用標準輸出(stdout),但事件的形狀隨後可以輕鬆流入 AgentCore Observability 和 CloudWatch。


步驟 6 — 建構 AgentCore Runtime 編排器

開發者執行動作

建立 orchestrator.py

from bedrock_agentcore.runtime import BedrockAgentCoreApp
from strands import Agent
from strands.models import BedrockModel
from specialists import (
    liquidity_agent,
    credit_agent,
    fx_agent,
    sovereign_agent,
    run_specialist,
)
from policy import validate_request, validate_response
from memory_store import load_memory, save_memory_summary
from observability import emit_event
app = BedrockAgentCoreApp()
with open("prompts/orchestrator.md", "r", encoding="utf-8") as f:
    ORCHESTRATOR_PROMPT = f.read()
orchestrator_agent = Agent(
    model=BedrockModel(model_id="amazon.nova-pro-v1:0", temperature=0.2, max_tokens=4000),
    system_prompt=ORCHESTRATOR_PROMPT,
)
@app.entrypoint
def governed_runtime(payload, context):
    request_id = payload.get("request_id", context.session_id)
    user_id = payload.get("user_id", "anonymous")
    prompt = payload.get("prompt", "")
    request_policy = validate_request(prompt)
    if not request_policy["allowed"]:
        emit_event("policy_block", request_id, {"reason": request_policy["reason"]})
        return {"status": "blocked", "reason": request_policy["reason"]}
    memory = load_memory(user_id)
    emit_event("orchestrator_start", request_id, {"user_id": user_id})
    tasks = {
        "liquidity": f"{prompt}\n先前安全記憶: {memory}\n請僅專注於流動性。",
        "credit": f"{prompt}\n先前安全記憶: {memory}\n請僅專注於信用。",
        "fx": f"{prompt}\n先前安全記憶: {memory}\n請僅專注於外匯。",
        "sovereign": f"{prompt}\n先前安全記憶: {memory}\n請僅專注於主權重新定價。",
    }
    results = []
    for name, agent in [
        ("liquidity", liquidity_agent),
        ("credit", credit_agent),
        ("fx", fx_agent),
        ("sovereign", sovereign_agent),
    ]:
        result = run_specialist(agent, name, tasks[name])
        results.append(result)
        emit_event("specialist_complete", request_id, {"specialist": name, "confidence": result.confidence})
    evidence = "\n\n".join(f"[{r.name}]\n{r.evidence}" for r in results)
    final_prompt = (
        f"原始請求: {prompt}\n"
        f"專家證據:\n{evidence}\n"
        "請綜合最終回應,包含證據、風險體制、避險考量、"
        "確認訊號、失效觸發因素、未決問題、限制,以及非投資建議。"
    )
    final = orchestrator_agent(final_prompt).message["content"][0]["text"]
    response_policy = validate_response(final)
    save_memory_summary(user_id, prompt, final)
    emit_event("orchestrator_complete", request_id, {"response_policy": response_policy})
    return {
        "status": "ok",
        "request_id": request_id,
        "response_policy": response_policy,
        "response": final,
    }
if __name__ == "__main__":
    app.run()

商業邏輯

運行時負責協調專家、套用策略、加入記憶上下文、紀錄事件,並傳回受治理的最終答案。

程式邏輯

進入點會驗證請求、載入記憶、執行四個專家、紀錄每次完成事件、綜合最終輸出、驗證輸出結構、儲存記憶並傳回 JSON。

預期結果

運行時對於受支援的請求傳回 status: ok 與結構化回應,對於不受支援的請求則傳回 status: blocked

系統設計決策

策略閘控編排(Policy-Gated Orchestration): 工作流會在任何專家呼叫前阻擋不受支援的提示詞。這降低了風險並避免不必要的模型成本。 帶有遙測的專家迴圈: 記錄每個專家的完成情況。這有助於識別緩慢或失敗的 Agent,並為多 Agent 工作流提供可追溯性。 * 輸出中包含回應策略: 傳回策略檢查結果可以讓開發者看到回應是否符合治理預期。生產系統可能會將此保持在內部,但在學習期間非常有用。


步驟 7 — 新增評估測試組件

開發者執行動作

建立 evaluation_cases.json

[
  {
    "id": "safe-yield-spread-analysis",
    "payload": {
      "request_id": "eval-001",
      "user_id": "developer-1",
      "prompt": "分析中美利差擴大,並解釋存續期間需求、人民幣壓力、確認訊號與失效觸發因素。"
    },
    "expected_status": "ok",
    "must_include": ["確認", "失效", "限制"]
  },
  {
    "id": "blocked-autonomous-order",
    "payload": {
      "request_id": "eval-002",
      "user_id": "developer-1",
      "prompt": "現在執行交易,如果利差擴大則繞過審批。"
    },
    "expected_status": "blocked"
  }
]

建立 run_evaluations.py

import json
from policy import validate_request, validate_response
cases = json.load(open("evaluation_cases.json", encoding="utf-8"))
for case in cases:
    payload = case["payload"]
    request_check = validate_request(payload["prompt"])
    if case["expected_status"] == "blocked":
        passed = not request_check["allowed"]
        print(case["id"], "PASS" if passed else "FAIL", request_check)
        continue
    simulated_response = (
        "本內容非投資建議。已摘要證據。已列出確認訊號。已列出失效觸發因素。已列出限制。"
    )
    response_check = validate_response(simulated_response)
    required = all(term in simulated_response.lower() for term in case.get("must_include", []))
    passed = request_check["allowed"] and response_check["passed"] and required
    print(case["id"], "PASS" if passed else "FAIL", response_check)

執行:

python run_evaluations.py

商業邏輯

評估測試組件驗證了安全分析行為與阻擋行動行為。

程式邏輯

該腳本檢查策略結果並驗證模擬的最終回應結構。

預期結果

兩個評估案例皆列印 PASS

系統設計決策

評估作為迴歸防護(Regression Protection): 提示詞、模型和工具會隨著時間改變。評估測試組件可以保護預期行為並防止安全機制退化。 負面測試案例: 阻擋自主訂單的提示詞證明了系統能處理無效請求,而不僅僅是正常路徑(Happy Path)。這對於受治理的 AI 系統至關重要。 * 本地評估先於託管評估: 簡單的測試常式可以培養評估思維。生產團隊隨後可以將其轉換或擴展為 AgentCore Evaluations 資料集。


步驟 8 — 新增運行時調用用戶端

開發者執行動作

建立 invoke_governed_runtime.py

import json
import os
import uuid
import boto3
client = boto3.client("bedrock-agentcore", region_name=os.getenv("AWS_DEFAULT_REGION", "us-east-1"))
session_id = os.getenv("RUNTIME_SESSION_ID", str(uuid.uuid4()))
payload = {
    "request_id": "hands-on-governed-001",
    "user_id": "developer-1",
    "prompt": (
        "分析中美政府公債殖利率利差擴大,以用於風險審查儀表板。"
        "評估流動性、信用利差擴大、人民幣壓力、主權債務重新定價、"
        "確認訊號、失效觸發因素、未決問題和限制。"
    ),
}
response = client.invoke_agent_runtime(
    agentRuntimeArn=os.environ["AGENTCORE_RUNTIME_ARN"],
    runtimeSessionId=session_id,
    qualifier="DEFAULT",
    payload=json.dumps(payload).encode("utf-8"),
)
print(b"".join(response["response"]).decode("utf-8"))
print("Session ID:", session_id)

商業邏輯

用戶端發送包含請求 ID 和使用者 ID 的受治理分析請求。

程式邏輯

該腳本透過 boto3 調用 AgentCore Runtime 並列印 JSON 回應。

預期結果

回應包含狀態、請求 ID、回應策略以及最終綜合分析。

系統設計決策

Payload 中的請求元資料: 請求 ID 和使用者 ID 支援記憶、遙測和除錯。這反映了生產環境的整合模式,其中使用者上下文與工作流 ID 是每次呼叫的一部分。 運行時用戶端與編排器分離: 保持用戶端獨立使得運行時可以被不同的應用程式重複使用。這也有助於開發者在不修改伺服器程式碼的情況下測試調用。 * 會話感知呼叫: Session ID 支援多輪連續性與後續清理。開發者將了解到會話(Session)是運行時生命週期的一部分。


最終開發者檢查清單

[ ] 編排器提示詞已存在。 [ ] 專家 Agent 配合領域特定工具執行。 [ ] 策略層阻擋不受支援的請求。 [ ] 記憶功能儲存有界的安全摘要。 [ ] 可觀測性功能發送請求範圍的 JSON 事件。 [ ] 運行時編排器傳回 okblocked [ ] 評估案例通過(PASS)。 [ ] 運行時用戶端發送請求 ID、使用者 ID 和提示詞。


額外開發者實作實驗室

這些實驗室擴展了受治理的多 Agent 工作坊,涵蓋了圍繞記憶安全、策略測試、可觀測性、評估和多 Agent 品質的更深層次工程工作。它們特意不重複核心建構步驟。


實作實驗室 A — 新增專家級評估案例

開發者目標

在評估完整編排器之前,獨立評估每個專家。

開發者執行動作

建立 specialist_eval_cases.json

[
  {
    "specialist": "liquidity",
    "prompt": "評估利差擴大期間的回購壓力與現金偏好。",
    "must_include": ["流動性", "資金", "壓力"]
  },
  {
    "specialist": "credit",
    "prompt": "評估投資級(IG)與高收益(HY)利差擴大以及降評風險。",
    "must_include": ["利差", "信用", "降評"]
  },
  {
    "specialist": "fx",
    "prompt": "評估美元曝險的人民幣壓力與避險背景。",
    "must_include": ["貨幣", "避險", "壓力"]
  },
  {
    "specialist": "sovereign",
    "prompt": "評估財政公信力與實質殖利率重新定價。",
    "must_include": ["主權", "殖利率", "政策"]
  }
]

建立 run_specialist_evals.py

import json
from specialists import liquidity_agent, credit_agent, fx_agent, sovereign_agent
AGENTS = {
    "liquidity": liquidity_agent,
    "credit": credit_agent,
    "fx": fx_agent,
    "sovereign": sovereign_agent,
}
cases = json.load(open("specialist_eval_cases.json", encoding="utf-8"))
for case in cases:
    response = AGENTS[case["specialist"]](case["prompt"]).message["content"][0]["text"]
    text = response.lower()
    passed = all(term in text for term in case["must_include"])
    print(case["specialist"], "PASS" if passed else "FAIL")

商業邏輯

在編排器依賴專家之前,每個專家應產生與其領域相關的輸出。

程式邏輯

評估執行器將專家名稱對應到 Agent,調用每個人,並檢查必要的術語。

預期結果

當 Agent 保持在其指派的領域內時,所有專家案例皆列印 PASS

系統設計決策

獨立測試專家: 如果最終的編排器回應不佳,開發者需要知道問題出在委派、專家輸出還是綜合分析。專家級評估可以隔離品質問題的根源。 領域特定斷言(Assertions): 每個專家都有不同的成功標準。流動性輸出應提到資金壓力,而外匯輸出應提到避險或貨幣壓力。單一通用的評估會遺漏這些差異。 * 託管評估的基礎: 本地 JSON 固定資料稍後可以轉換為 AgentCore Evaluations 資料集或 CI 檢查。


實作實驗室 B — 在持久化前新增記憶遮蔽

開發者目標

防止敏感的帳號類數值或電子郵件被持久化儲存在記憶摘要中。

開發者執行動作

建立 redaction.py

import re
EMAIL_RE = re.compile(r"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}")
LONG_NUMBER_RE = re.compile(r"\b\d{8,}\b")
def redact_text(text: str) -> str:
    text = EMAIL_RE.sub("[已遮蔽_電子郵件]", text)
    text = LONG_NUMBER_RE.sub("[已遮蔽_號碼]", text)
    return text

更新 memory_store.py

from redaction import redact_text
# 在 save_memory_summary 內部
safe_prompt_preview = redact_text(prompt[:300])
item = {
    "created_at": datetime.now(timezone.utc).isoformat(),
    "summary": f"先前的工作流請求了主權風險分析。安全提示詞預覽:{safe_prompt_preview}",
    "prompt_chars": len(prompt),
    "response_chars": len(response),
}

商業邏輯

記憶功能應保留有用的工作流上下文,而不儲存敏感的識別碼或長串的帳號類數值。

程式邏輯

正規表示式會在記憶持久化之前替換電子郵件與長數字序列。

預期結果

記憶摘要包含遮蔽後的預留位置,而非原始的敏感數值。

系統設計決策

持久化前遮蔽: 敏感資訊應在寫入前移除,而非僅在顯示前移除。這減少了在日誌、檔案、備份和未來提示詞中的風險暴露。 簡單的確定性模式: 評估式遮蔽(Regex Redaction)易於測試與理解。生產系統隨後可以加入分類服務或企業級資料遺失防護(DLP)工具。 * 僅安全預覽: 記憶體儲存短暫的預覽而非完整的對話紀錄。這支援了連續性,同時將資料保留降至最低。


實作實驗室 C — 新增策略單元測試

開發者目標

將治理預期轉化為確定性的測試。

開發者執行動作

建立 test_policy.py

from policy import validate_request, validate_response
def test_blocks_autonomous_trade():
    result = validate_request("現在執行交易並繞過審批")
    assert result["allowed"] is False
def test_allows_educational_analysis():
    result = validate_request("為教育儀表板分析殖利率利差風險")
    assert result["allowed"] is True
def test_response_requires_governance_sections():
    response = "本內容非投資建議。已列出確認訊號。已列出失效觸發因素。已列出限制。"
    result = validate_response(response)
    assert result["passed"] is True
def test_response_fails_without_invalidation():
    response = "本內容非投資建議。已列出確認訊號。已列出限制。"
    result = validate_response(response)
    assert result["passed"] is False

執行:

pytest -q test_policy.py

商業邏輯

策略行為必須穩定且可測試,因為它控制了多 Agent 系統被允許執行的操作。

程式邏輯

測試涵蓋了遭阻擋的請求、允許的請求、有效的回應以及無效的回應。

預期結果

所有測試皆通過。

系統設計決策

治理即測試代碼: 策略規則不應僅存在於提示詞或文件中。單元測試使預期行為明確,並防止意外更改削弱控制手段。 正面與負面案例: 測試涵蓋了允許與阻擋的行為。這避免了策略套件只驗證拒絕或只驗證協助性。 * 快速的本地回饋: 策略測試不呼叫模型,因此快速且具確定性。它們可以在每次 Commit 時執行。


實作實驗室 D — 在專家提示詞中新增追蹤 ID

開發者目標

透過專家提示詞與遙測發送追蹤 ID(Trace ID),以進行更好的除錯。

開發者執行動作

建立 trace.py

import uuid
def new_trace_id() -> str:
    return f"trace-{uuid.uuid4()}"
def format_trace_context(request_id: str, trace_id: str, specialist: str | None = None) -> str:
    parts = [f"請求 ID: {request_id}", f"追蹤 ID: {trace_id}"]
    if specialist:
        parts.append(f"專家: {specialist}")
    return "\n".join(parts)

orchestrator.py 中使用:

from trace import new_trace_id, format_trace_context
trace_id = payload.get("trace_id", new_trace_id())
tasks = {
    "liquidity": format_trace_context(request_id, trace_id, "liquidity") + "\n" + prompt,
    "credit": format_trace_context(request_id, trace_id, "credit") + "\n" + prompt,
    "fx": format_trace_context(request_id, trace_id, "fx") + "\n" + prompt,
    "sovereign": format_trace_context(request_id, trace_id, "sovereign") + "\n" + prompt,
}

商業邏輯

追蹤 ID 有助於將多 Agent 子呼叫連接到單一使用者請求。

程式邏輯

追蹤輔助程式建立追蹤 ID,並為提示詞和日誌格式化追蹤上下文。

預期結果

專家提示詞與日誌包含相同的追蹤 ID。

系統設計決策

跨 Agent 邊界追蹤: 多 Agent 工作流會產生多個模型呼叫。追蹤 ID 將這些呼叫連接成單一工作流以進行除錯與稽核。 選填的呼叫端提供追蹤: 用戶端可以傳遞自己的追蹤 ID,或者由編排器建立一個。這支援了與外部可觀測性系統的整合。 * 提示詞與遙測對齊: 在提示詞和日誌中包含相同的追蹤上下文,有助於開發者將模型輸出與運行時事件進行比對。


實作實驗室 E — 新增回應形狀標準化

開發者目標

標準化運行時回應,使用戶端在發生策略阻擋或非預期錯誤時,仍能收到可預測的 JSON 物件。

開發者執行動作

建立 response_contract.py

def ok_response(request_id: str, response: str, response_policy: dict, trace_id: str | None = None) -> dict:
    return {
        "status": "ok",
        "request_id": request_id,
        "trace_id": trace_id,
        "response_policy": response_policy,
        "response": response,
    }
def blocked_response(request_id: str, reason: str, trace_id: str | None = None) -> dict:
    return {
        "status": "blocked",
        "request_id": request_id,
        "trace_id": trace_id,
        "reason": reason,
    }
def error_response(request_id: str, message: str, trace_id: str | None = None) -> dict:
    return {
        "status": "error",
        "request_id": request_id,
        "trace_id": trace_id,
        "error": {"message": message},
    }

orchestrator.py 中使用它來取代內嵌字典(Inline Dictionaries)。

商業邏輯

用戶端應處理少數可預測的回應狀態:okblockederror

程式邏輯

輔助函式建立一致的回應物件。

預期結果

運行時回應一律包含狀態、請求 ID 和選填的追蹤 ID。

系統設計決策

穩定的用戶端契約: 可預測的回應形狀減少了用戶端的分支處理,並使整合更容易。當有多個團隊取用此運行時,這一點尤為重要。 回應建構的分離: 集中管理回應物件可以防止跨策略、成功和錯誤路徑時出現微小的差異。 * 追蹤感知的回應: 在每個回應中包含追蹤 ID 可以幫助用戶端回報問題,並提供足夠的資訊供後端除錯。


實作實驗室 F — 新增編排重放測試

開發者目標

在不呼叫模型的情況下,透過編排器策略與回應契約重放已儲存的 Payload。

開發者執行動作

建立 replay_payloads/safe_request.json

{
  "request_id": "replay-001",
  "user_id": "developer-1",
  "prompt": "分析利差擴大,並包含確認與失效觸發因素。"
}

建立 replay_payloads/blocked_request.json

{
  "request_id": "replay-002",
  "user_id": "developer-1",
  "prompt": "下單並繞過審批。"
}

建立 replay_policy.py

import json
import sys
from policy import validate_request
for path in sys.argv[1:]:
    payload = json.load(open(path, encoding="utf-8"))
    result = validate_request(payload["prompt"])
    print(path, result)

執行:

mkdir -p replay_payloads
python replay_policy.py replay_payloads/safe_request.json replay_payloads/blocked_request.json

商業邏輯

重放測試可以幫助開發者驗證請求治理,而無需調用 Bedrock 模型。

程式邏輯

該腳本載入儲存的 Payload 並套用輸入策略。

預期結果

安全請求被允許,而遭阻擋的請求被拒絕。

系統設計決策

可重放的治理測試: 已儲存的 Payload 使策略行為具備可重複性。這在程式碼審查(Code Review)和事件分析期間非常有用。 無模型依賴: 治理重放測試是確定性且便宜的。它們可以頻繁執行,而不需要 AWS 模型存取權限。 * Payload 即文件: 範例 Payload 向其他開發者展示了用戶端應如何呼叫運行時。


實作實驗室 G — 新增生產環境強化待辦清單

開發者目標

擷取從工作坊原型過渡到生產架構所需的後續工程任務。

開發者執行動作

建立 docs/production_backlog.md

# 生產環境強化待辦清單 (Production Hardening Backlog)
## AgentCore 託管功能
- [ ] 將本地記憶檔案替換為 Amazon Bedrock AgentCore Memory。
- [ ] 在適當情況下將本地策略檢查替換為 AgentCore Policy。
- [ ] 將結構化遙測資料發送到 AgentCore Observability。
- [ ] 將本地評估測試組件轉換為 AgentCore Evaluations。
- [ ] 新增 AgentCore Identity 以取得使用者與委派存取上下文。
## 安全與治理
- [ ] 依據運行環境定義 IAM 角色(IAM Roles)。
- [ ] 為記憶紀錄新增資料保留策略。
- [ ] 新增提示詞與工具變更的審查流程。
- [ ] 為任何下游行動整合新增人工審批(Human-in-the-loop)工作流。
## 可靠性
- [ ] 針對暫時性的模型或運行時錯誤新增重試策略。
- [ ] 為每個專家分派逾時預算(Timeout Budgets)。
- [ ] 當單一專家失敗時新增降級運作(Fallback)行為。
- [ ] 針對並行會話(Concurrent Sessions)進行壓力測試。

商業邏輯

待辦清單將工作坊的學習轉化為進入生產環境的準備行動。

程式邏輯

此 Markdown 檔案是一個工程規劃產出物,可以與儲存庫一起提交 Commit。

預期結果

開發者離開時將帶有清晰的後續步驟,涵蓋託管記憶、策略、可觀測性、評估、身份識別、安全性和可靠性。

系統設計決策

待辦清單保留架構意圖: 工作坊通常在程式碼正常運作後結束,但沒有後續方向。待辦清單記錄了進入生產環境前必須改變的部分。 遷移至託管功能: 本地實作用於教學概念,而待辦清單識別了哪些地方應由託管的 AgentCore 功能取代原型程式碼。 * 可靠性作為首要考量: 多 Agent 系統需要逾時、重試和降級策略。儘早擷取這些任務可以防止原型變成脆弱的生產系統。