# Structured Output — Phase 3 ステータス判定 ## 概要 Phase 3(ステータス判定)において、エージェントの出力を structured output(JSON スキーマ)で取得し、ルールマッチングの精度と信頼性を向上させる。 ## プロバイダ別の挙動 | プロバイダ | メソッド | 仕組み | |-----------|---------|--------| | Claude | `structured_output` | SDK が `StructuredOutput` ツールを自動追加。エージェントがツール経由で `{ step, reason }` を返す | | Codex | `structured_output` | `TurnOptions.outputSchema` で API レベルの JSON 制約。テキストが JSON になる | | OpenCode | `structured_output` | プロンプト末尾に JSON スキーマ付き出力指示を注入。テキストレスポンスから `parseStructuredOutput()` で JSON を抽出 | ## フォールバックチェーン `judgeStatus()` は3段階の独立した LLM 呼び出しでルールをマッチする。 ``` Stage 1: structured_output — outputSchema 付き LLM 呼び出し → structuredOutput.step(1-based integer) Stage 2: phase3_tag — outputSchema なし LLM 呼び出し → content 内の [MOVEMENT:N] タグ検出 Stage 3: ai_judge — evaluateCondition() による AI 条件評価 ``` 各ステージは専用のインストラクションで LLM に問い合わせる。Stage 1 は「ルール番号を JSON で返せ」、Stage 2 は「タグを1行で出力せよ」と聞き方が異なる。 セッションログには `toJudgmentMatchMethod()` で変換された値が記録される。 | 内部メソッド | セッションログ | |-------------|--------------| | `structured_output` | `structured_output` | | `phase3_tag` / `phase1_tag` | `tag_fallback` | | `ai_judge` / `ai_judge_fallback` | `ai_judge` | ## インストラクション分岐 Phase 3 テンプレート(`perform_phase3_message`)は `structuredOutput` フラグで2つのモードを持つ。 ### Structured Output モード(`structuredOutput: true`) 主要指示: ルール番号(1-based)と理由を返せ。 フォールバック指示: structured output が使えない場合はタグを出力せよ。 ### タグモード(`structuredOutput: false`) 従来の指示: 対応するタグを1行で出力せよ。 現在、Phase 3 は常に `structuredOutput: true` で実行される。 ## アーキテクチャ ``` StatusJudgmentBuilder └─ structuredOutput: true ├─ criteriaTable: ルール条件テーブル(常に含む) ├─ outputList: タグ一覧(フォールバック用に含む) └─ テンプレート: "ルール番号と理由を返せ + タグはフォールバック" runStatusJudgmentPhase() └─ judgeStatus() → JudgeStatusResult { ruleIndex, method } └─ StatusJudgmentPhaseResult { tag, ruleIndex, method } MovementExecutor ├─ Phase 3 あり → judgeStatus の結果を直接使用(method 伝搬) └─ Phase 3 なし → detectMatchedRule() で Phase 1 コンテンツから検出 ``` ## JSON スキーマ ### judgment.json(judgeStatus 用) ```json { "type": "object", "properties": { "step": { "type": "integer", "description": "Matched rule number (1-based)" }, "reason": { "type": "string", "description": "Brief justification" } }, "required": ["step", "reason"], "additionalProperties": false } ``` ### evaluation.json(evaluateCondition 用) ```json { "type": "object", "properties": { "matched_index": { "type": "integer" }, "reason": { "type": "string" } }, "required": ["matched_index", "reason"], "additionalProperties": false } ``` ## parseStructuredOutput() — JSON 抽出 Codex と OpenCode はテキストレスポンスから JSON を抽出する。3段階のフォールバック戦略を持つ。 ``` 1. Direct parse — テキスト全体が `{` で始まる JSON オブジェクト 2. Code block — ```json ... ``` または ``` ... ``` 内の JSON 3. Brace extraction — テキスト内の最初の `{` から最後の `}` までを切り出し ``` ## OpenCode 固有の仕組み OpenCode SDK は `outputFormat` を型定義でサポートしていない。代わりにプロンプト末尾に JSON 出力指示を注入する。 ``` --- IMPORTANT: You MUST respond with ONLY a valid JSON object matching this schema. No other text, no markdown code blocks, no explanation. ```json { "type": "object", ... } ``` ``` エージェントが返すテキストを `parseStructuredOutput()` でパースし、`AgentResponse.structuredOutput` に格納する。 ## 注意事項 - OpenAI API(Codex)は `required` に全プロパティを含めないとエラーになる(`additionalProperties: false` 時) - Codex SDK の `TurnCompletedEvent` には `finalResponse` フィールドがない。structured output は `AgentMessageItem.text` の JSON テキストから `parseStructuredOutput()` でパースする - Claude SDK は `StructuredOutput` ツール方式のため、インストラクションでタグ出力を強調しすぎるとエージェントがツールを呼ばずタグを出力してしまう - OpenCode のプロンプト注入方式はモデルの指示従順性に依存する。JSON 以外のテキストが混在する場合は `parseStructuredOutput()` の code block / brace extraction で回収する