MINARA

可觀測性

運行中 Agent 的日誌、審計與調試

當 Agent 出現異常時,必須弄清原因。Minara Agent 會產生三條獨立的信息流:結構化日誌審計日誌層級事件。每條流都有其用途。本頁說明各類信息的歸屬,以及如何排查常見故障。

為什麼是三條流,而不是一個大日誌? Agent 異常通常呈現三種形態:基礎設施故障(LLM 調用失敗、數據庫鎖死)、Agent 執行了錯誤操作(以錯誤參數調用工具),或權限邏輯意外攔截了某個操作。把三者混在一條流裡會導致難以閱讀。分開後,可以直接定位到對應的表:行為問題看 audit,權限問題看 tier_events,基礎設施問題看結構化日誌。三條流共享 trace_id,需要完整還原時可將它們串聯起來。

實際示例6 階段資金安全會為每次涉及資金的調用向 tier_events 寫入一行記錄,以便事後證明某筆交易通過了所有預檢。

三條信息流

信息流存儲位置用途
結構化日誌$dataDir/logs/*.ndjson 及標準輸出運行狀態、啟動信息、LLM 調用
審計日誌SQLite audit"Agent 調用了哪些工具,結果如何"
層級事件SQLite tier_events"該調用為何被允許或攔截"

經驗法則:調試行為問題,先看審計日誌;調試基礎設施問題,先看結構化日誌;調試權限問題,先看層級事件。

結構化日誌

apps/agent/src/core/logger.ts 提供了一個簡潔的 JSON 日誌記錄器:

logger.info("skills/registry", "skill_registered", { id });
logger.warn("agent/loop", "max_iterations_reached", { session_id });
logger.error("llm/anthropic-wire", "cache_miss", { reason });

每行是一個 JSON 對象,結構如下:

{
  "ts": "2026-04-14T12:34:56.789Z",
  "level": "info",
  "category": "skills/registry",
  "event": "skill_registered",
  "correlation_id": "turn_abc123",
  "trace_id": "t_xyz",
  "data": { "id": "minara.core" }
}

關聯 ID 由包裹輪次執行的 withCorrelation(id, fn) 生成。輪次內產生的所有日誌行通過 AsyncLocalStorage 繼承相同的 ID。執行 grep correlation_id=turn_abc123 $dataDir/logs/*.ndjson 即可獲取某輪次的完整記錄。

日誌輪轉

日誌按天輪轉,寫入 $dataDir/logs/。通過以下環境變量配置級別:

  • LOG_LEVELdebug / info / warn / error,默認 warn

設置 LOG_LEVEL=debug 是安全的:結構化格式可用 jq 過濾噪音,但日誌量約為原來的 4 倍。

審計日誌:事實來源

所有工具調用均經過 apps/agent/src/core/audit-log-hook.ts 中的 auditLogHook。表結構如下:

CREATE TABLE audit (
  id              TEXT PRIMARY KEY,
  session_id      TEXT,
  trace_id        TEXT,
  tool_name       TEXT,
  tool_set        TEXT,
  args_json       TEXT,       -- redacted
  result_json     TEXT,
  blocked         INTEGER,
  block_reason    TEXT,
  permission_tier INTEGER,
  source          TEXT,       -- user | cron | autopilot | delegation
  duration_ms     INTEGER,
  created_at      INTEGER
);
CREATE VIRTUAL TABLE audit_fts USING fts5(
  tool_name, block_reason, args_json, content='audit'
);

FTS5 支持對完整歷史進行全文檢索:

SELECT created_at, tool_name, blocked, block_reason
  FROM audit
 WHERE audit MATCH 'withdraw'
 ORDER BY created_at DESC
 LIMIT 50;

排查時從這裡開始。 當用戶反饋"Agent 行為異常"時,第一步是按順序拉取該會話的審計記錄。LLM 的推理文本、工具參數、原始工具輸出和時間戳都在其中。

脫敏處理

tools/_shared/result.ts 中的脫敏器會在數據寫入審計日誌前,將已知敏感字段(api_keysecretpasswordtokenprivate_keymnemonicseed)替換為 ***。加上密鑰從不作為工具參數傳入(而是在工廠函數階段從 process.env 讀取)的規則,審計日誌可以放心分享給支持團隊,或直接附在 bug 報告中,無需額外脫敏。

層級事件:攔截原因

core/permission-tier-hook.ts 對每次權限決策(無論允許還是攔截)都會向 tier_events 寫入一行:

CREATE TABLE tier_events (
  id            TEXT PRIMARY KEY,
  tool_name     TEXT,
  source        TEXT,
  tier          INTEGER,
  allow_ceiling INTEGER,
  decision      TEXT,          -- allow | block | pending_confirmation
  reason        TEXT,
  trace_id      TEXT,
  created_at    INTEGER
);

審計日誌告訴你發生了什麼;層級事件告訴你權限系統為何做出該決定。兩張表通過 trace_id 關聯,常一起查詢:

SELECT a.tool_name, a.blocked, te.decision, te.reason
  FROM audit a
  LEFT JOIN tier_events te ON te.trace_id = a.trace_id
                           AND te.tool_name = a.tool_name
 WHERE a.session_id = ?
 ORDER BY a.created_at;

關聯 ID 與追蹤 ID

系統中有兩種不同的 ID:

  • correlation_id 以輪次為粒度,在 Agent 循環每輪開始時生成,出現在結構化日誌和審計記錄中。
  • trace_id 以工作流運行或信號為粒度,從 SignalContextWorkflowInstance 傳遞到下游所有工具調用,包括子 Agent 委託。

用戶聊天輪次通常只有新的 correlation_id,沒有 trace_id。定時任務觸發時兩者都有。trace_id 可用於查詢"14:03 的 BTC 提醒在整個生命週期內做了什麼,包括它派生的子 Agent"。

常見排查場景

"Agent 為何拒絕調用 X?"

SELECT tool_name, decision, reason, created_at
  FROM tier_events
 WHERE tool_name = 'swap'
   AND decision = 'block'
 ORDER BY created_at DESC
 LIMIT 10;

查看 reason 字段,常見值如下:

  • tier_exceeds_ceiling:技能未激活,或當前輪次的 allowRiskTier 低於工具的風險等級。
  • analysis_to_trade_boundary:該輪次已進行了分析調用;交易調用必須在獨立的用戶消息中發起。
  • daily_cap_exceededdaily_spend 加上本次調用的名義金額將超過 MINARA_DAILY_CAP_USD
  • kill_switch_active:某人(或 Agent 本身)觸發了 kill
  • tool_set_not_allowed:當前輪次的 allowedToolSets 不包含該工具。

"為什麼這次輪次很慢?"

SELECT tool_name, AVG(duration_ms) AS avg_ms, COUNT(*) AS n
  FROM audit
 WHERE session_id = ?
 GROUP BY tool_name
 ORDER BY avg_ms DESC;

結合按 correlation_id 過濾的結構化日誌,可查看 LLM 調用耗時和緩存命中率。

"提示詞緩存是否生效?"

在日誌中檢索 llm.cache_read_input_tokens。如果某會話的該值全為 0,說明可緩存的提示詞塊不穩定(可能每輪都在重新生成,導致緩存失效)。參見 LLM 集成

"Autopilot 昨夜做了什麼?"

SELECT created_at, tool_name, blocked, substr(result_json, 1, 200)
  FROM audit
 WHERE source = 'autopilot'
   AND created_at > ?
 ORDER BY created_at;

健康檢查接口

HTTP 網關提供以下接口:

  • GET /healthz:存活探針,進程正常且數據庫可打開時返回 200
  • GET /status:就緒狀態詳情,包含數據庫統計、技能數量、活躍觸發器、最近一次 LLM 調用成功時間。

完整 schema 參見 API 參考

對接外部工具

如需將日誌接入 Loki / Datadog / Grafana Cloud,可通過管道傳輸標準輸出:

docker run minara 2>&1 | vector --config vector.toml

所有標準輸出均為合法的 NDJSON,沒有單獨的"結構化日誌"導出路徑,標準輸出即為規範來源。


可觀測性的設計有意保持簡單:三張表、一種日誌格式、一個關聯字段、一個追蹤字段。出現問題時,讀行記錄;一切正常時,忽略它。這就是全部的設計。

本頁目錄