0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CodexのSkill適用を推測しない――1回の依頼単位で証跡を記録する

0
Posted at

AIへ共通ルールを渡すためにSkillを用意しても、そのファイルが存在するだけでは、今回の処理で実際に使われたとは限りません。

読み込まれたのか。判断へ使われたのか。対象外として明示的に使わなかったのか。それとも記録がなく、何も判断できないのか。

ロコリス研究所では、複数のCodex作業について、実行中・エラー・人間の承認待ちなどを一覧で確認する「Locolisu AI Task Monitor」を開発しています。

Task Monitorを作った背景は、Note「複数AIを並行して使うと、どれを確認したか分からなくなる――Task Monitorを作った理由」にまとめています。

この画面へ、先ほどの違いを曖昧にしたまま「Skill適用済み」と表示すると、人間は実際より強い根拠があるように受け取ってしまいます。

そこでTask Monitorでは、Skill適用を推測せず、1回の依頼と処理を表すoperation単位で明示的なEvidence(証跡)を残す構成にしました。

この記事では、その設計に至った問題と、途中で起きた識別子の分裂、3状態へ分けた理由をまとめます。

この記事でいうSkillとoperation

この記事でいうSkillは、AIへ毎回同じ判断基準や作業手順を渡すための再利用可能な指示一式です。

たとえば、ロコリス研究所では次のような内容をSkillとして分けています。

  • 開発時に守る共通ルール
  • 文書を更新・同期するときの手順
  • Task Monitor固有の実装・検証方針
  • 既存製品を調べてから作るか判断する基準

Sessionは、一つのCodexとの会話・作業枠です。ただし、同じSessionの中でも依頼は複数回行われます。

そのため、Skill適用の記録単位をSession全体にはせず、Session内の1回の依頼と処理をoperationとして区別しました。

何を使ってCodexの情報を取得しているか

今回の構成は、Codexの公開APIや外部の監視ライブラリを利用していません。Codexがローカルへ保存したデータを、Task Monitor独自のCodexAdapterがread-onlyで直接読み、Codex固有のeventをObservation(観測結果)という共通形式へ変換しています。

読み取りには、Python標準ライブラリのsqlite3、json、pathlib.Pathを使用しています。

主な情報源と役割は次のとおりです。

情報源 取得する内容
state_5.sqlite Session ID、workspace、rollout JSONLのパスなど
thread_history_1.sqlite turn ID、状態、開始・完了時刻、errorの有無
session_index.jsonl Codexが保存したthread_name
Codex rollout JSONL Codexが実行ログとして保存するJSONL。message、reasoning、tool開始・終了、tool名、call IDなどのevent

SQLiteはmode=roとPRAGMA query_only=ONで開き、必要な列がない場合は安全側で取得しません。rollout JSONLもTask Monitor側が直接読み、Codex固有のeventをTURN_STARTED、TURN_FINISHED、TOOL_STARTED、TOOL_FINISHEDなどへ分類します。

ただし、これらはCodexの公開APIではありません。ローカルの内部データと保存形式へ依存しているため、Codex側のschemaやevent名が変わった場合は再検証が必要です。

Skillがあることと、使われたことは別

RepositoryにSkillが置かれていれば、AIはその内容を参照できます。しかし、次の事実までは保証できません。

  • 今回のoperationで読み込んだ
  • 今回の判断へ実際に使った
  • 複数あるSkillのうち、どれを使った
  • 対象外と判断して使わなかった

監視側がRepository内のファイルだけを見て「適用済み」と判定すれば、確認できるのはSkillの存在だけです。

また、Codexの実行記録からSkill名らしい文字列を探しても、それが説明のために登場したのか、実際に適用されたのかを安全に区別できません。

そこで、AIの動作から監視側が推測する方式は採用しませんでした。

Skill Evidenceは、Codex情報の取得とは別の経路で記録します。CodexがSkillの指示に従い、対象Skillを適用または非適用と判断した時点で、その判断を共通のEvidence Writerへ渡します。Writerは、その明示的な判断をSKILL_DECISION eventとしてJSONLへ記録します。SkillEvidenceAdapterは、schema、skill_id、状態、session_id、operation_key、記録時刻などを検証し、該当するoperationへ結び付けます。

これは独立した検証ツールがSkillの適用内容まで再現して判定する仕組みではなく、Codexによる明示的な自己申告です。Task Monitorが確認できるのは、所定の形式で判断が記録され、対象operationへ対応していることまでです。実際にSkillの内容どおり作業したか、結果が正しいかは、このEvidenceだけでは保証できません。

それでも、実行記録やSkill名から監視側が適用を推測するのではなく、誰がどのoperationについて何を申告したかを追跡できます。

最初に必要だったのは2状態ではなく3状態だった

当初、表示したいのは「適用した」「適用していない」の2状態に見えました。

しかし、明示的なEvidenceが届いていない状態を「適用していない」と扱うと、次の二つが同じ表示になります。

  1. 対象を確認したうえで、今回のoperationには適用しなかった
  2. Evidenceが記録されておらず、適用したか判断できない

この二つは意味が異なります。

また、記録が届いていても、形式が不正な場合や、同じSkillについて内容が矛盾する場合は、適用・非適用のどちらにも確定できません。

そのため、状態を次の3種類に分けました。

状態 意味
APPLIED 対象のSkillを今回のoperationへ適用したという明示的なEvidenceがある
NOT_APPLIED 対象を確認し、今回のoperationには適用しなかったという明示的なEvidenceがある
UNKNOWN Evidenceがない、不正、または矛盾しており、適用・非適用のどちらとも判断できない

大切なのは、UNKNOWNを失敗として隠さないことです。

証拠がないことは、非適用の証拠ではありません。監視画面では、分からないものを分からないまま表示する必要があります。

Session単位では粗すぎた

同じSessionで、最初の依頼には開発用Skillを使い、次の依頼には文書管理用Skillを使うことがあります。

Sessionへ一度でもSkill名が記録されたことを理由に、その後のすべての処理を「適用済み」と表示すると、どの依頼で使われたのか分からなくなります。

そこで、Skill Evidenceは次の組み合わせへ結び付けました。

session_id + operation_key

ここでいうoperation_keyの実体が、次に説明するoperation identityです。

session_idはCodex Sessionを区別する識別子、operation_keyはその中の1回の依頼を区別する識別子です。

これにより、同じSession内でも、operationごとに異なるSkill適用状態を表示できます。

一時的な識別子と正式な識別子が分裂した

operationを区別する仕組みを作った直後は、正式なturn IDを取得する前に、依頼時刻を使った一時的な識別子を発行していました。

prompt:<timestamp>

その後、Codex側の正式なturn IDを取得すると、次の識別子が作られます。

turn:<turn_id>

ここで、一時的なoperationと正式なoperationを別物として残してしまう問題が起きました。

  • 開始時刻はprompt:側にある
  • 終了状態はturn:側にある
  • Skill Evidenceはどちらか片方にだけある
  • 人間の承認状態が別のoperationへ結び付く

画面上では、実際には一つの作業なのに、二つのoperationとして見えることがあります。

これは表示上の重複だけではありません。状態、時刻、Evidence、人間の承認を同じ作業へ結び付けられないため、監視の意味そのものが崩れます。

operation identityを正式なturnへ統一した

原則として、operation identity(作業を一意に区別する識別情報)は次へ統一しました。

turn:<turn_id>

ただし、処理開始時点ではturn IDがまだ分からない場合があります。そのため、一時的なprompt:を完全になくすのではなく、正式なturn:が確認できた時点で、一つのoperationとして移行します。

移行時には、途中の状態が見える瞬間を作らないように、次の情報をまとめて移します。

  • 開始・終了時刻
  • AIの状態
  • 人間の承認状態
  • 最終Observation(観測結果)
  • Skill Evidence

この移行をatomic migration(途中状態を外部へ見せない一括移行)として扱いました。

turn IDを最後まで取得できない場合

turn IDをまだ取得できない間は、prompt:<timestamp>を一時的なoperation identityとして使用します。

ここでのRUNNINGは、Skill適用状態とは別の、operationの実行状態です。

人間の承認待ち候補にできるのは、監視中に実際にRUNNINGとなり、同じturn IDのTURN_FINISHEDとCodex内部の完了状態を確認できた正式なturnだけです。そのため、prompt:のfallbackしかないoperationを、推測で承認待ちにはしません。

prompt本文をEvidenceへ複製しない

Evidenceを増やせば、後から多くのことを確認できます。しかし、何でも保存すればよいわけではありません。

依頼本文やAIの回答には、次の情報が含まれる可能性があります。

  • 未公開の仕様
  • ファイルパス
  • 顧客や業務に関する情報
  • 認証情報に近い文字列
  • 公開を想定していない会話

まず、認証情報や顧客の個人情報など、AIへ渡す必要がない情報はpromptへ入力しないことが前提です。未公開の仕様、顧客名、業務名、内部のファイルパスなども、作業上必要でなければ省き、必要な場合は匿名化や置換を検討します。

Task Monitor側で本文を保存しなければ、この問題がなくなるわけではありません。promptはCodexへ渡され、Codex側のrollout JSONLなどに記録される可能性があります。

今回必要なのは、会話内容の再現ではなく、「どのoperationで、どのSkillについて、どの状態が明示されたか」です。

そのため、Evidenceへ依頼本文、回答本文、コマンド全文を保存する設計にはしませんでした。監視に必要な最小限の識別情報と適用状態だけを扱います。

これは、promptへ機密情報を入力しても安全になるという対策ではありません。Codex側に存在し得る情報をTask MonitorやSkill Evidenceへさらに複製・表示せず、情報が広がる範囲を増やさないための境界です。

実際のoperationで確認した

実装後、実際のoperationを使って次を確認しました。

  • APPLIED:2件
  • NOT_APPLIED:1件
  • Task MonitorとEvidence Writerでturn:<turn_id>が一致
  • identity修正後の関連テスト:83件成功

ここで確認できたのは、Skillの有効性そのものではありません。

確認できたのは、Codexが明示的に記録したSkill適用状態を、該当するoperationへ結び付け、監視画面へ表示できることです。

Skillを適用したと記録されていても、その判断や成果が正しいとは限りません。Evidenceは正しさの保証ではなく、人間が確認するときの材料です。

監視画面でSkill名を表示する意味

Task Monitorには、operationごとのSkill適用証跡を表示しています。

これは「Skillが使われたから安全」という合格表示ではありません。

人間が確認するときに、少なくとも次を区別するための表示です。

  • 適用したという明示的な記録がある
  • 適用しなかったという明示的な記録がある
  • 記録がなく、判断できない

たとえば、文書同期を行ったoperationで文書管理SkillがUNKNOWNなら、人間は結果だけでなく、手順がどこまで守られたかも確認対象にできます。

逆にAPPLIEDであっても、結果の確認を省略する理由にはなりません。

作って分かったこと

この仕組みで重要だったのは、Skillの数を増やすことではありませんでした。

重要だったのは、次の境界を崩さないことです。

  • ファイルが存在すること
  • AIが読み込めること
  • 今回のoperationで適用したという記録があること
  • 適用した結果が正しいこと
  • 人間が成果を承認したこと

これらを一つの「確認済み」にまとめると、監視画面は分かりやすく見えても、実際には何を確認したのか説明できなくなります。

分からない状態へUNKNOWNという名前を与え、正式なoperation identityへEvidenceを結び付けたことで、少なくとも推測を事実として表示しない構成にはできました。

まとめ

  • Skillの存在だけでは、今回の処理での適用を確認できない
  • Evidenceは独立検証の結果ではなく、CodexがWriterを通して残す明示的な自己申告である
  • Skill EvidenceはSession全体ではなくoperation単位で残す
  • 明示的な非適用と、Evidenceがない・不正・矛盾している状態を分けるため、APPLIED / NOT_APPLIED / UNKNOWNの3状態にする
  • 一時的なprompt:と正式なturn:を別作業として残さない
  • AIへ不要な機密情報を渡さず、Evidenceには依頼・回答・コマンド全文を複製しない
  • Evidenceは正しさの保証ではなく、人間が確認するための材料である

AIの動作を監視する仕組みでは、推測で表示項目を埋めるほど画面は整って見えます。しかし、監視に必要なのは、空欄を減らすことではなく、確認できた事実と確認できない状態を区別することでした。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?