⛩️ Torii — AIエージェントのための門番

ADK製エージェントのevalを門番に、CIが赤くなったらエージェント自身が原因を診断して自己修復。緑を確認してmainへmergeするとCloud Runへ自動デプロイする、AIエージェントのための門番エージェント。

Target AgentはToriiが守るサンプルです。入力例: ORD-1001 の配送状況を教えてください

1. 契約違反を検知ADK evalがtool trajectoryと応答品質の回帰をredにします。
2. 原因を推論して検証ReasoningTrace、最小修正、実再evalの結果を修復履歴へ残します。
3. greenだけを届けるgreen確認済みの修復だけをpushし、merge後のデプロイを記録します。
green
最新の門
57
eval runs
44
green
13
red
7
自己修復 (green)

eval スコア時系列(赤→緑への回復)

自己修復履歴

時刻PRattempt結果診断(結論)変更詳細
2026-07-07T03:48:55 #1 2/3 green モデルがnext_actionフィールドへの文字列出力でツール呼び出しを代替してしまう問題を解決するため、プロンプト内で実際のツール呼び出しを強調する修正を行います。 prompts.py
trace / diff
observe: cancel_confirmed_orderのケースにおいて、モデルがcancel_orderツールを呼び出さず、レスポンスのnext_actionフィールドに"cancel_order"と出力して終了している。
hypothesize: プロンプトの指示で「then call the cancel_order tool.」とあるが、モデルはJSONのnext_actionに文字列を設定することでツールを呼び出した(あるいは次のステップとして指定した)と勘違いし、実際のツール呼び出しを省略してしまったと考えられる。
verify: 他の成功ケースでは正しくツールを呼び出しているが、キャンセルの場合は直前の「Do not call cancel_order...」という強い制約があり、モデルが実際の呼び出しに消極的になっている可能性がある。そのため、ツールを呼ぶ条件を満たした時の命令を強化する必要がある。
act: prompts.pyの指示を「then actually call the cancel_order tool.」に変更し、ツール自体の呼び出しを強制することで、文字列出力による代替を防ぐ。
--- a/src/target_agent/prompts.py
+++ b/src/target_agent/prompts.py
@@ -11,7 +11,7 @@
   call cancel_order for a first-time cancellation request unless the user also
   explicitly confirms the ID.
 - Treat phrases like "注文IDは ORD-1001 で確定です", "ORD-1001 で確定しています",
-  or "キャンセル実行してください" as explicit confirmation; then call the cancel_order tool.
+  or "キャンセル実行してください" as explicit confirmation; then actually call the cancel_order tool.
 - Never invent orders, dates, carriers, or policy details.
 
 Response format:
2026-07-07T03:47:50 #1 1/3 still_red LLMがツール実行を意図通りに行うよう、プロンプト内の「cancel_order」を「the cancel_order tool」に変更し、ツール呼び出しであることを強調します。 prompts.py
trace / diff
observe: cancel_confirmed_orderのケースにおいて、キャンセル確定の指示を受けたエージェントがcancel_orderツールを呼び出さず、JSONのnext_actionに文字列「cancel_order」を設定するのみで終了しています。
hypothesize: プロンプトの「then call cancel_order.」という指示が、ツール実行の指示ではなく「next_actionフィールドへの文字列設定」としてLLMに誤解されていると考えられます。
verify: 「then call the cancel_order tool.」と明確にツールを呼び出すよう記述を変更すれば、単なる文字列出力ではなく実際の関数実行(Tool Call)として正しく解釈されるはずです。
act: prompts.py内の「then call cancel_order.」を「then call the cancel_order tool.」へ修正するサージカルパッチを作成します。
--- a/src/target_agent/prompts.py
+++ b/src/target_agent/prompts.py
@@ -11,7 +11,7 @@
   call cancel_order for a first-time cancellation request unless the user also
   explicitly confirms the ID.
 - Treat phrases like "注文IDは ORD-1001 で確定です", "ORD-1001 で確定しています",
-  or "キャンセル実行してください" as explicit confirmation; then call cancel_order.
+  or "キャンセル実行してください" as explicit confirmation; then call the cancel_order tool.
 - Never invent orders, dates, carriers, or policy details.
 
 Response format:
2026-07-07T03:46:28 #1 1/3 green プロンプトのフォーマット指示を自然言語からJSON形式に変更することで、エージェントの出力をテストの期待値と一致させます。 prompts.py
trace / diff
observe: すべての失敗テストケースで、期待される出力がJSON形式(summary, order_id, next_actionのキーを持つ)であるのに対し、実際の出力は自然言語になっている。
hypothesize: prompts.pyの「Response format」セクションで、JSONを使用せず自然言語のみで回答するよう明示的に指示されていることが原因である。
verify: テストケースの期待値(expected)フォーマットに合わせて、プロンプトの指示をJSONオブジェクト(summary, order_id, next_actionの3つのキーを含む)を出力するように変更すればスコアが改善する。
act: prompts.pyのレスポンスフォーマットの指示を書き換え、厳密なJSON形式での出力を要求するようSurgical editを実施する。
--- a/src/target_agent/prompts.py
+++ b/src/target_agent/prompts.py
@@ -15,6 +15,6 @@
 - Never invent orders, dates, carriers, or policy details.
 
 Response format:
-- Reply in Japanese as a short friendly paragraph.
-- Write natural prose only. Do not use JSON or any structured/key-value format.
+- Output your response only in JSON format.
+- The JSON object must contain exactly three keys: "summary" (a short Japanese string), "order_id" (the order ID string, or null), and "next_action" (a short string describing the next step).
 """
2026-07-05T10:44:37 #5 1/3 green プロンプトのフォーマット指示を自然言語のみからJSON形式を強制する内容に修正し、評価テストの期待値と一致させます。 prompts.py
trace / diff
observe: エージェントが自然言語で回答しているため、全ケースのレスポンス評価で低いスコアが出て失敗しています。期待される回答は「summary」「order_id」「next_action」のキーを持つJSONオブジェクトです。
hypothesize: prompts.pyのResponse formatセクションで、JSON形式の出力を禁止し、自然言語で書くように明示的に指示されていることが原因です。この指示を、期待されるJSONキーを出力するように書き換えれば解決します。
verify: prompts.pyの「Response format:」以下の2行を、summary、order_id、next_actionをキーとして含むJSONでの返答を求める指示に置換することで、他の動作に影響を与えずにフォーマットを修正できるか確認しました。
act: prompts.py内の該当指示部分を、JSON形式での回答を強制する指示に置き換えます。
--- a/src/target_agent/prompts.py
+++ b/src/target_agent/prompts.py
@@ -12,6 +12,5 @@
 - Never invent orders, dates, carriers, or policy details.
 
 Response format:
-- Reply in Japanese as a short friendly paragraph.
-- Write natural prose only. Do not use JSON or any structured/key-value format.
+- Reply strictly with a JSON object containing the following keys: "summary" (a short Japanese summary), "order_id" (the order ID or null), and "next_action" (a short Japanese prompt for the user's next action).
 """
2026-07-05T10:37:56 #4 1/3 green prompts.py の運用ルールに、search_orders呼び出し後のサマリーへ商品名を含める指示を追加し、期待されるレスポンス内容と一致させます。 prompts.py
trace / diff
observe: order_search_by_emailケースにおいて、期待されるサマリーは各注文の「商品名(items)」を含んでいますが、実際のサマリーは「配送状況」を報告しており、response_match_scoreが0.756まで低下しています。
hypothesize: システムプロンプトに検索結果から何をサマリーに含めるべきかの具体的な指示がないため、LLMが任意に配送状況を抽出してしまっています。
verify: prompts.py の search_orders を使用する際のルールに対して「サマリーに各注文のアイテムを含める」という指示を追記することで、期待通りのサマリーが生成されるようになります。
act: prompts.py の該当箇所を最小限の置換で編集し、アイテム名の包含を指示します。
--- a/src/target_agent/prompts.py
+++ b/src/target_agent/prompts.py
@@ -5,7 +5,7 @@
 
 Operational rules:
 - Always use search_orders when the user provides an email address and asks
-  about their orders.
+  about their orders. Include the items for each order in your summary.
 - Always use get_shipping_status when the user asks where an order is.
 - Before canceling an order, confirm the exact order ID with the user. Only call
   cancel_order after the user has explicitly provided or confirmed that order ID.
2026-07-05T10:36:38 #4 1/3 green prompts.pyの出力フォーマットの指示を、自然言語からテストが期待するJSON形式へ修正します。 prompts.py
trace / diff
observe: 全テストケースでツールの呼び出し軌跡は正しいですが、出力形式のスコアが低くなっています。期待値は特定のキーを持つJSONですが、実際は自然言語の段落になっています。
hypothesize: prompts.pyに「JSONを使用せず自然言語でのみ回答すること」という指示があるため、モデルがテストの期待するJSON形式の出力を拒否していると考えられます。
verify: prompts.pyのResponse formatを、テストの期待値に沿ったJSON(summary, order_id, next_actionキーを持つ)のみを出力するよう書き換えれば、エラーは解消するはずです。
act: prompts.pyの該当箇所を、JSON出力の指示に置換するパッチを作成します。
--- a/src/target_agent/prompts.py
+++ b/src/target_agent/prompts.py
@@ -12,6 +12,6 @@
 - Never invent orders, dates, carriers, or policy details.
 
 Response format:
-- Reply in Japanese as a short friendly paragraph.
-- Write natural prose only. Do not use JSON or any structured/key-value format.
+- Return ONLY a valid JSON object. Do not output any plain text.
+- Required keys: "summary" (Japanese summary), "order_id" (string or null), and "next_action" (Japanese next step).
 """
2026-07-05T09:23:19 #4 1/3 green プロンプトのResponse formatを修正し、自然言語ではなく指定キーを持つJSONを出力するように指示を改めた。 prompts.py
trace / diff
observe: 全テストケースでツールの実行は正しく行われているが、期待されるJSONフォーマットではなく自然言語で返答しているためレスポンスマッチスコアが低下している。
hypothesize: prompts.pyのResponse formatで「JSONを使わず自然な文章のみで返す」よう明確に指示されていることが原因である。
verify: 各テストケースの期待値は `summary`, `order_id`, `next_action` をキーとするJSON文字列である。プロンプトの指示をこのJSON構造を出力するように変更すればスコアは改善する。
act: prompts.py内のフォーマット指示を、期待される3つのキーを持つJSONのみを出力するように書き換える。
--- a/src/target_agent/prompts.py
+++ b/src/target_agent/prompts.py
@@ -12,6 +12,6 @@
 - Never invent orders, dates, carriers, or policy details.
 
 Response format:
-- Reply in Japanese as a short friendly paragraph.
-- Write natural prose only. Do not use JSON or any structured/key-value format.
+- Output ONLY valid JSON.
+- The JSON must contain exactly three keys: "summary" (short Japanese explanation), "order_id" (string or null), and "next_action" (short Japanese prompt).
 """
2026-07-05T09:18:39 #3 3/3 green プロンプト内の「Only call」を「Always call」へ修正し、注文IDが確認できた際には必ずキャンセルツールを実行するよう行動を修正します。 prompts.py
trace / diff
observe: cancel_confirmed_orderケースにおいて、`cancel_order` ツールが呼び出されず、エージェントがテキストの返答のみでキャンセル完了として振る舞っています。
hypothesize: プロンプトの指示が「Only call cancel_order...(〜の後にのみ呼べ)」という制限のみになっており、「必ず呼び出して実行する」という強制力が不足しているため、LLMが自律的にツールをスキップしたと考えられます。
verify: 他のツール呼び出し指示と同様に「Always call」に変更することで、ユーザーがIDを確定させた後に必ずツールを実行するようになり、かつ無関係な動作への影響は最小限に抑えられます。
act: prompts.pyの該当箇所を最小限の手順で書き換え、ツール呼び出しを確実に行わせるように修正します。
--- a/src/target_agent/prompts.py
+++ b/src/target_agent/prompts.py
@@ -7,7 +7,7 @@
 - Always use search_orders when the user provides an email address and asks
   about their orders.
 - Always use get_shipping_status when the user asks where an order is.
-- Before canceling an order, confirm the exact order ID with the user. Only call
+- Before canceling an order, confirm the exact order ID with the user. Always call
   cancel_order after the user has explicitly provided or confirmed that order ID.
 - Never invent orders, dates, carriers, or policy details.
 
2026-07-05T09:18:15 #3 2/3 still_red JSONフォーマットの強制指示をユーザーへの最終返答時のみに限定する外科的修正を行い、ツール呼び出しを正常に発火させます。 prompts.py
trace / diff
observe: `cancel_confirmed_order` と `shipping_status_by_order_id` のケースで、ツールが呼ばれずJSON形式のテキスト応答のみで終了してしまっている。
hypothesize: `prompts.py` の「Reply ONLY with a valid JSON object.」という強い制約が原因で、LLMがツール呼び出しのfunction callingではなく、JSONテキスト出力を最優先の処理として誤認している。
verify: 実際の出力を見ると `next_action` フィールドに「get_shipping_status」や「キャンセル処理を実行します。」と入力されており、エージェントはツール呼び出しの意図を持っているがテキスト出力に留まっている。JSONの出力条件を「ユーザーへの最終回答時」に限定すれば、ツール呼び出しを阻害しなくなるはずである。
act: `prompts.py` の出力制約を「For the final response to the user, reply ONLY ...」に変更し、ツール呼び出し時はこの制約に縛られないよう修正する。
--- a/src/target_agent/prompts.py
+++ b/src/target_agent/prompts.py
@@ -12,6 +12,6 @@
 - Never invent orders, dates, carriers, or policy details.
 
 Response format:
-- Reply ONLY with a valid JSON object. Do not use markdown code blocks.
+- For the final response to the user, reply ONLY with a valid JSON object. Do not use markdown code blocks.
 - The JSON must contain exactly three keys: "summary" (Japanese summary), "order_id" (the order ID string or null), and "next_action" (Japanese text for the next prompt).
 """
2026-07-05T09:17:44 #3 1/3 still_red prompts.pyのフォーマット指示を修正し、期待されるキーを持ったJSONを出力させることで問題を解決できる。 prompts.py
trace / diff
observe: すべての失敗ケースで、期待される出力がJSON形式であるのに対し、実際の出力が自然言語のテキストになっている。
hypothesize: prompts.py の Response format セクションで「Do not use JSON...」とJSON形式を禁止し、自然言語での出力を強制していることが原因である。
verify: 期待値(expected response)を確認すると、すべて `summary`, `order_id`, `next_action` というキーを持つJSONオブジェクトであるため、この形式での出力を指示すればよい。
act: prompts.py の Response format 指定を書き換え、自然言語のみの制約を削除して3つのキーを持つJSONオブジェクトを出力するように修正する。
--- a/src/target_agent/prompts.py
+++ b/src/target_agent/prompts.py
@@ -12,6 +12,6 @@
 - Never invent orders, dates, carriers, or policy details.
 
 Response format:
-- Reply in Japanese as a short friendly paragraph.
-- Write natural prose only. Do not use JSON or any structured/key-value format.
+- Reply ONLY with a valid JSON object. Do not use markdown code blocks.
+- The JSON must contain exactly three keys: "summary" (Japanese summary), "order_id" (the order ID string or null), and "next_action" (Japanese text for the next prompt).
 """

デプロイイベント

時刻サービスrevisionURLcommit
2026-07-10T13:09:41 dashboard torii-dashboard-00021-bx7 https://torii-dashboard-d3zvsimeqq-an.a.run.app b889fa0
2026-07-10T13:09:40 target_agent torii-target-agent-00001-qsl https://torii-target-agent-d3zvsimeqq-an.a.run.app b889fa0
2026-07-10T13:05:17 dashboard torii-dashboard-00020-kfj https://torii-dashboard-d3zvsimeqq-an.a.run.app 0de9a62
2026-07-10T13:05:15 target_agent torii-target-agent-00001-qsl https://torii-target-agent-d3zvsimeqq-an.a.run.app 0de9a62
2026-07-09T09:51:09 dashboard torii-dashboard-00019-tk9 https://torii-dashboard-d3zvsimeqq-an.a.run.app 7376a7f
2026-07-09T09:51:08 target_agent torii-target-agent-00001-qsl https://torii-target-agent-d3zvsimeqq-an.a.run.app 7376a7f
2026-07-07T11:52:00 dashboard torii-dashboard-00018-z4p https://torii-dashboard-d3zvsimeqq-an.a.run.app 7ef6bbd
2026-07-07T11:51:59 target_agent torii-target-agent-00001-qsl https://torii-target-agent-d3zvsimeqq-an.a.run.app 7ef6bbd
2026-07-07T10:08:00 dashboard torii-dashboard-00017-smc https://torii-dashboard-d3zvsimeqq-an.a.run.app 59f6c5a
2026-07-07T10:07:58 target_agent torii-target-agent-00001-qsl https://torii-target-agent-d3zvsimeqq-an.a.run.app 59f6c5a
2026-07-07T03:34:56 dashboard torii-dashboard-00016-ps7 https://torii-dashboard-d3zvsimeqq-an.a.run.app 8a15a13
2026-07-07T03:34:54 target_agent torii-target-agent-00001-qsl https://torii-target-agent-d3zvsimeqq-an.a.run.app 8a15a13
2026-07-07T03:22:25 dashboard torii-dashboard-00015-qmb https://torii-dashboard-d3zvsimeqq-an.a.run.app 7111590
2026-07-07T03:22:25 target_agent torii-target-agent-00001-qsl https://torii-target-agent-d3zvsimeqq-an.a.run.app 7111590
2026-07-07T03:00:48 dashboard torii-dashboard-00014-6g5 https://torii-dashboard-d3zvsimeqq-an.a.run.app 82055e0
2026-07-07T03:00:46 target_agent torii-target-agent-00001-qsl https://torii-target-agent-d3zvsimeqq-an.a.run.app 82055e0
2026-07-06T15:54:33 dashboard torii-dashboard-00013-t5s https://torii-dashboard-d3zvsimeqq-an.a.run.app 08603f3
2026-07-06T15:54:32 target_agent torii-target-agent-00001-qsl https://torii-target-agent-d3zvsimeqq-an.a.run.app 08603f3
2026-07-05T22:19:52 dashboard torii-dashboard-00012-f87 https://torii-dashboard-d3zvsimeqq-an.a.run.app 4c0e4ff
2026-07-05T22:19:50 target_agent torii-target-agent-00001-qsl https://torii-target-agent-d3zvsimeqq-an.a.run.app 4c0e4ff
2026-07-05T22:09:50 dashboard torii-dashboard-00011-qnn https://torii-dashboard-d3zvsimeqq-an.a.run.app d14e790
2026-07-05T22:09:50 target_agent torii-target-agent-00001-qsl https://torii-target-agent-d3zvsimeqq-an.a.run.app d14e790
2026-07-05T22:03:59 dashboard torii-dashboard-00010-lzc https://torii-dashboard-d3zvsimeqq-an.a.run.app 9db5c40
2026-07-05T22:03:59 target_agent torii-target-agent-00001-qsl https://torii-target-agent-d3zvsimeqq-an.a.run.app 9db5c40
2026-07-05T21:51:48 dashboard torii-dashboard-00009-xtz https://torii-dashboard-d3zvsimeqq-an.a.run.app b2b1438
2026-07-05T21:51:48 target_agent torii-target-agent-00001-qsl https://torii-target-agent-d3zvsimeqq-an.a.run.app b2b1438
2026-07-05T14:20:23 dashboard torii-dashboard-00008-qgn https://torii-dashboard-d3zvsimeqq-an.a.run.app 375de9c
2026-07-05T14:20:22 target_agent torii-target-agent-00001-qsl https://torii-target-agent-d3zvsimeqq-an.a.run.app 375de9c
2026-07-05T14:12:51 dashboard torii-dashboard-00007-qgz https://torii-dashboard-d3zvsimeqq-an.a.run.app 41f9483
2026-07-05T14:12:49 target_agent torii-target-agent-00001-qsl https://torii-target-agent-d3zvsimeqq-an.a.run.app 41f9483
2026-07-05T13:15:05 dashboard torii-dashboard-00006-zxv https://torii-dashboard-d3zvsimeqq-an.a.run.app e0f2886
2026-07-05T13:15:04 target_agent torii-target-agent-00001-qsl https://torii-target-agent-d3zvsimeqq-an.a.run.app e0f2886
2026-07-05T13:13:14 dashboard torii-dashboard-00005-5jg https://torii-dashboard-d3zvsimeqq-an.a.run.app d7fc136
2026-07-05T13:13:12 target_agent torii-target-agent-00001-qsl https://torii-target-agent-d3zvsimeqq-an.a.run.app d7fc136
2026-07-05T12:42:36 dashboard torii-dashboard-00002-nsg https://torii-dashboard-d3zvsimeqq-an.a.run.app 02a6347
2026-07-05T12:42:35 target_agent torii-target-agent-00001-qsl https://torii-target-agent-d3zvsimeqq-an.a.run.app 02a6347
2026-07-05T11:48:58 target_agent torii-target-agent-00001-qsl https://torii-target-agent-632478120410.asia-northeast1.run.app af9afea
2026-07-05T11:40:14 dashboard torii-dashboard-00001-85t https://torii-dashboard-632478120410.asia-northeast1.run.app 7c18077