Commit Graph
20 Commits
Author SHA1 Message Date
seon 684e70ba60 feat(ai): authenticate to the Claude API with Workload Identity Federation
정적 API 키 대신 Workload Identity Federation을 쓴다. 에이전트는 AI VM의 Google 서명
ID 토큰(메타데이터 서버, audience https://api.anthropic.com, format=full)을
POST /v1/oauth/token(jwt-bearer)으로 교환해 단기 access token을 받고 Authorization: Bearer로
호출한다. 토큰은 캐시하고 만료 120초 전에 재교환하며, 401이면 한 번 재교환한다.
규칙·조직·서비스 계정·워크스페이스 ID 4개는 비밀이 아니므로 ai.env 환경 변수로 받고,
비어 있으면 기존 키 파일 경로로 동작한다. /selfcheck가 auth 모드와 실제 교환 결과를
보고해 캠페인 없이 설정을 검증할 수 있다. 테스트: 교환 1회 재사용·401 재교환·만료 전
갱신·거부 응답·불완전 ID 무시.
2026-10-15 11:48:26 +09:00
seon 53ff243183 feat: per-run model selection, usage in diagnosis results and a Claude Messages API provider
캠페인마다 진단 모델을 고를 수 있게 한다. 하네스는 runs.model 열을 추가해 POST /runs의
model을 저장하고, 활성 런의 model을 /diagnose 요청에 실어 보낸다. 에이전트는 요청의
model을 우선 사용하고(없으면 OPENAI_MODEL 기본값), 모델 id가 claude-로 시작하면
internal/anthropic(Claude Messages API 어댑터)로 라우팅한다. 어댑터는 Responses API
항목(message/function_call/function_call_output)을 system·tools(input_schema)·tool_use/
tool_result로 변환하고 previous_response_id 체인을 클라이언트 쪽에서 재현하며, Claude 5.5가
강제 tool_choice를 거부하므로 강제 호출은 auto + 지시문으로 바꾼다. 키는
ANTHROPIC_KEY_FILE(/run/secrets/anthropic-api-key)에서 읽고 비어 있으면 명확한 오류를 낸다.
Result에 usage(입력·출력·합계 토큰)를 추가해 하네스 diagnosis 이벤트에 비용 근거를 남긴다.
2026-10-14 23:21:46 +09:00
seon fa9c08c778 fix(hitl): re-read the full HITL row before mirroring an API approval
SyncLoop は store.Pending() の行(diagnosis を持たない)で埋め込みを描き直して
いたため、API 承認を反映した直後のメッセージで推定原因・根拠・判断理由が
<nil> になっていた(第 5 回 C-G2、2026-10-09)。編集前に GetHitl で全列を
読み直す。
2026-10-09 10:12:03 +09:00
seon f8e0aa9122 feat: alert dedupe, proposal state API, HITL mirrors API approvals, Jev recovering reads the alert condition
第 4 回キャンペーン(2026-10-08)の 3 つの穴を塞ぐ。
- ハーネス: 同じアラート名が ALERT_DEDUPE_S(既定 300 秒)以内に再着したら
  /diagnose を送らず note(dedupe) に記録する。Grafana は Pod ごとに 1 件ずつ
  出し 30 秒後にグループを再送するため、1 障害が診断 4 件・提案 4 件になっていた。
  全件 resolved になれば窓をリセットする。
- ハーネス: GET /proposals/{token} で承認・実行状態を返す。
- hitl: 60 秒ごとに未決の提案をハーネスへ照会し、API で承認された件は
  メッセージを「ハーネス API で承認」に書き換えてタイマーを外す。期限切れ処理の
  直前にも同じ照会を行う。
- AI: Jev 選別の now に alert_condition_now / alert_condition_clear /
  recovered_now を追加し、recovering は recovered_now を読む。SLO に触れない
  障害(replay 停止、メモリ圧迫)で 0.98 を返していた。now の p95 と 5xx 比率に
  route フィルタを付け、判定と同じ式にそろえる。
2026-10-09 08:33:41 +09:00
seon 8836b37927 fix(harness): do not hold recovery on layer-1 signature alerts
alerts_ok は第 2 層(signature なし)のアラートだけを見る。第 1 層のシグネチャ
アラートはプラットフォームが復旧を担う情報であり、Grafana の解決が 8 分遅れると
MTTR に乗ってしまうため除外する。
2026-10-08 21:27:34 +09:00
seon cd8fbdddab feat(harness): hold recovery while a Grafana alert is firing, add legacy:busy host resolution
キャンペーン第 2 回のグレーゾーン 3 件を受けた変更。SLO 3 系列に現れない症状
(レプリケーション一時停止、メモリ圧迫)でも、受信中のアラートが解決するまで
recovered を出さないよう、AlertRouter が firing 中のアラートを保持し judge が
alerts_ok を条件に加える。legacy:busy は直近 2 分で最も多くリクエストを受けた
MIG インスタンス(Mimir topk)を指し、run+シナリオ単位でキャッシュして revert が
同じインスタンスに当たるようにする(ILB のハッシュで api の接続が 1 台に偏る)。
2026-10-08 19:20:35 +09:00
seon 13797e5741 feat(ai): redesign the Jev triage state and add JEV_MODE=enforce review rules
第 2 回キャンペーンの突き合わせ結果を反映した。triage の state にコードで計算した
最新値の要約(now: プローブ、1 分 p95、1 分エラー率、再起動数、slo_now_ok)と
直近 5 分のエラーログ抜粋を加え、recovering は slo_now_ok を読むだけにした。
layer の choice を irreversible_needed の noul に置き換え、target_valid は
カタログ action のときだけ尋ねる。JEV_MODE=enforce では review の 2 ルール
(best_action 不一致かつ確信度 0.9 以上、target_valid 0.5 未満)で提案を
escalate に格下げし、判断を res.jev.enforced に記録する。既定は shadow のまま。
2026-10-08 17:45:28 +09:00
seon 795be156d9 fix(harness): require the 1-minute p95 inside the SLO before declaring recovery
キャンペーン第 2 回の C-G1 で、300 ms 遅延が 3 分続いている最中に 5 分窓の p95 平均が
0.375 秒まで薄まり、復旧と判定された。SLO 状態に p95_1m_s / latency_1m_ok を追加し、
recovered の条件に 1 分窓の p95 も含める。
2026-10-08 17:45:28 +09:00
seon 91cdd9ada5 feat(ai): add Jev shadow gates (triage before, review after the LLM diagnosis)
TypeSafe System One(Jev)への最小クライアントと、診断ループに並走する
shadow ゲート 2 つを追加した。triage はアラートと事前取得サンプルからラベル・
回復中か・層を判定し、review は診断の根拠・ランブック・target を検証して
best_action を選ぶ。JEV_MODE=shadow のときだけ動き、答えは ai.jev_shadow と
診断結果の jev フィールドに記録するだけで、処理の分岐は一切変えない。
2026-10-08 15:10:07 +09:00
seon af1cb4f6dc feat(ai): localize hitl Discord messages via HITL_LANG (en, ja)
Discord に投稿する提案・エスカレーション・期限切れ・ボタンの文言を
言語セット(en / ja)に分離し、HITL_LANG で起動時に選択できるようにした。
識別子(action id、ラベル、run id、JSON)は言語に関係なくそのまま出す。
2026-10-08 14:33:48 +09:00
seon 8b403c54df fix(agent): record token usage summed over all turns
previous_response_id로 잇는 Responses API는 턴마다 전체 컨텍스트를 입력으로 과금하는데
세션에는 마지막 턴의 usage만 남아 비용이 과소 기록되었다(실측 2026-10-08: 12턴, 마지막 턴 입력
10,997). 턴별 input/output/total을 합산하고 턴 수도 함께 기록한다.
2026-10-08 10:53:08 +09:00
seon 9bc3a3b5fe fix(rag): add the diagnosis column to existing hitl_messages tables
첫 이미지(f30af2a)가 만든 ai.hitl_messages에는 diagnosis 컬럼이 없어 /post의 insert가
42703으로 실패했다(실측). CREATE TABLE IF NOT EXISTS는 기존 테이블을 바꾸지 않으므로
ADD COLUMN IF NOT EXISTS를 스키마 적용 단계에 추가한다.
2026-10-08 02:33:54 +09:00
seon 683a2de530 style(ai): gofmt
gofmt 정렬만 적용. 동작 변경 없음.
2026-10-08 01:47:19 +09:00
seon 43696d48c8 fix(hitl): keep the diagnosis in the HITL ledger so edited messages retain evidence and rationale
버튼 처리·만료 후 메시지를 편집할 때 빈 진단으로 다시 그려 증거·근거 필드가 "-"로 지워지던
결함을 고친다. ai.hitl_messages에 diagnosis jsonb를 추가해 게시 시점의 진단을 저장하고
편집 시 그대로 재사용한다.
2026-10-08 01:47:04 +09:00
seon c442601ab3 docs(ai): describe the WP-15 Loki and Mimir label sets in the tool definitions
WP-15 Alloy 구성(k8s job=namespace/app, legacy job=legacy-api, DB job=postgres, cadvisor 선별 지표)에 맞춰
query_logs·query_metrics 도구 설명을 갱신한다. 동작 변경 없음.
2026-10-08 01:41:44 +09:00
seon f30af2a4c3 feat(ai): add AI VM services (diagnosis agent, pgvector RAG, Discord HITL bot) and harness approval endpoints
실험 설계서 6장(WP-12)의 AI VM 2층 구현.
- cmd/agent: 하네스 /diagnose 수신 → OpenAI Responses API 함수 호출 루프(읽기 전용 도구 7종:
  query_metrics, query_logs, get_probe_status, get_k8s, get_gcp_instances, get_aws_state,
  search_runbooks) → strict 스키마 submit_diagnosis(6.3) → 하네스 /proposals 등록 + HITL 게시,
  또는 신뢰도 0.5 미만·escalate 시 3층 큐 에스컬레이션. 세션·프롬프트 버전·러너북 벡터는
  pgvector(ai 스키마)에 저장. 라벨·조치 enum은 하네스 /scenarios·/actions/catalog에서 갱신.
- cmd/hitl: Discord Gateway 봇(intents GUILDS만). 승인/거부 버튼(custom_id approve:/reject:<token>),
  3초 내 DEFERRED_UPDATE_MESSAGE, 승인 시 하네스 승인 → /actions/execute → 원 메시지 편집,
  15분 무응답 시 에스컬레이션 채널로 이동. 재기동 시 미결 제안 타이머 복원.
- harness: POST /proposals/{token}/approve, /reject 추가. 제안은 진단 시점에 등록하고 승인은
  버튼 시점에 기록해 MTTA(승인-제안)가 실제 대기 시간이 되도록 함.
OpenAI·Discord 토큰은 Secret Manager 파일로만 읽고 코드·저장소에 두지 않는다.
2026-10-08 01:36:10 +09:00
seon de076a79e4 fix(harness): JSON-safe SLO state, unscored AI errors, mark recorded-only proposals executed 2026-09-24 02:22:29 +09:00
seon 5daf13b669 feat: add harness runner (scenario executor, alert receiver, action executor, judge, revert) 2026-09-24 02:06:08 +09:00
seon 8f5378b6ea feat: add demo-app api service
실험·앱 계층 설계서 3.1의 demo-app api(컨테이너 계층). /healthz, /readyz(DB ping +
legacy /healthz), /api/orders GET/POST(app.orders), /api/price/{item}(legacy-api via ILB,
LEGACY_TIMEOUT 기본 2s), /metrics(demo_http_*). OTLP 트레이스는 OTEL_EXPORTER_OTLP_ENDPOINT
설정 시 전송(otelhttp). 이미지는 공개 Artifact Registry selfheal-public(노드 익명 pull).
2026-09-24 01:32:06 +09:00
seon 7186ef0c49 feat: add legacy-api service
실험·앱 계층 설계서 3.2의 legacy-api(VM 계층). /healthz(프로세스 생존),
/v1/price/{item}(db-primary app.price 조회, DB 불가 시 503), /metrics(Prometheus).
Go 1.27.1, pgx v5.11.0, client_golang v1.24.1. 이미지는 Cloud Build로 Artifact Registry에
올린다(MIG 인스턴스가 VM SA로 pull).
2026-09-23 23:35:10 +09:00