mcpField
S
sn-infographic

sn-infographic

github.com/opensensenovaデザイン・UI6.0k174 回インストール2 日前に更新
リポジトリ

sn-infographic について

sn-infographicは、テキストの依頼からインフォグラフィックを生成するエージェント向けスキルです。プロンプトの評価・拡張に加え、複数回の生成結果を画像レビューで評価して順位付けします。

主な機能

  1. 01プロンプト品質の評価
  2. 02必要に応じたプロンプト拡張
  3. 03複数回の生成と画像レビュー
  4. 04品質順の結果提示
  5. 05簡潔または詳細な出力

活用シーン

  1. 01短い依頼を画像プロンプトに変換
  2. 02複数回生成した画像の比較
  3. 03指定した比率とサイズでのインフォグラフィック生成

MCP 対応

入力にはMCPとの関係が記載されていません。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/opensensenova/sensenova-skills --skill sn-infographic

インストール後、Claude Code で/sn-infographicを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
analysis-framework.md
base-prompt.md
evaluation-standard.md
layout-style-selection.md
references/layouts/
asymmetry.md
axial-expansion.md
bento-grid.md
big-typography.md
binary-comparison.md
breaking-the-grid.md
bridge.md
center-focus.md
chapter-layout.md
character-guide.md
circular-flow.md
collage-glitch.md
comic-strip.md
comparison-matrix.md
conflict-contrast.md
containerization.md
dashboard.md
data-landscape.md
deconstruction.md
diagonal-composition.md
disrupted-flow.md
edge-tension.md
editorial-vogue.md
emotional-gradient.md
f-pattern.md
flashback.md

詳細

sn-infographic

インフォグラフィック生成シーン向けスキル(ティア 1)。sn-image-base(ティア 0)が提供する sn-image-generate、sn-image-recognize、sn-text-optimize ツールに依存します。

特徴:

  • プロンプト品質の評価(自動モード)
  • プロンプトの拡張(強制/自動モード)
  • 画像生成と VLM によるレビューを複数回実施
  • 品質ランキングに基づいて最良の結果を出力

入力仕様

パラメーター型デフォルト値説明
user_promptstring必須ユーザーの元のリクエストです。UTF-8 テキストで、Markdown、URL、構造化データを含めることができます。長さは、基盤となる LLM のコンテキスト予算によってのみ制限されます。
max_roundsint1生成ラウンドの最大数です。有効範囲:1~8。max_rounds=1 の場合、ステップ 3 の VLM レビューと早期終了チェックの両方がスキップされます。
output_modestringfriendlyfriendly:1 行のコンテンツ説明 + rank=1 の画像 1 枚
verbose:品質ランキング全体 + 所要時間の統計 + すべての画像(順位順)
prompts_expand_modestringautoauto:まず user_prompt の品質を評価し、品質が不十分な場合にのみステップ 2 の拡張を実行します
force:評価をスキップし、常にステップ 2 の拡張を実行します
disable:ステップ 2 をスキップし、user_prompt をそのまま expanded_prompt として使用します
aspect_ratiostring推定(16:9)ユーザーが対応している比率を明示した場合(例:16:9/9:16、または 宽高比/画面比例/aspect ratio を使用した場合)は、Main Agent が設定します。それ以外の場合は未設定のままとなり、Worker が references/runtime-parameters.md に従い、ステップ 0 で user_prompt から推定します(向き/シーンの手がかりを使用)。
image_sizestring推定(2k)ユーザーがサイズ(2k/4k)を明示した場合は、Main Agent が設定します。それ以外の場合は、Worker がステップ 0 で推定します(現在、選択肢は 2k のみです)。SN_IMAGE_GEN_MODEL=sensenova-u1.5-lite の場合は 4k を使用できます。ほかのモデルでは拒否される場合があり、その場合はスキルがエラーを表示します。

抽出する項目の担当者: Main Agent によるパラメーター抽出では、ユーザーが明示した値がある場合に限り、max_rounds、output_mode、prompts_expand_mode、aspect_ratio/image_size を解決します。明示的な値がない場合、aspect_ratio と image_size はステップ 0 で Worker が推定します。

API の設定

このスキルのすべての API 呼び出しは、sn-image-base スキルの sn_agent_runner.py を通じて実行されます。認証パラメーターにはデフォルト値が使用されるため(CLI > 環境変数 > 組み込みのデフォルト値)、明示的に渡す必要はありません。

呼び出しの種類ツール認証パラメーター説明
LLMsn-text-optimize(評価/拡張)デフォルトでは SN_TEXT_API_KEY -> SN_CHAT_API_KEY -> SN_API_KEY の順に読み込みます組み込みのデフォルト値は Sensenova の社内ネットワークサービスを参照します
VLMsn-image-recognize(画像レビュー)デフォルトでは SN_VISION_API_KEY -> SN_CHAT_API_KEY -> SN_API_KEY の順に読み込みます組み込みのデフォルト値は Sensenova の社内ネットワークサービスを参照します
画像生成sn-image-generateデフォルトでは SN_IMAGE_GEN_API_KEY -> SN_API_KEY の順に読み込みます。SN_IMAGE_GEN_API_KEY が必要なのは、画像固有の上書きを行う場合のみですsn-image-base の画像生成設定を使用します

MissingApiKeyError が発生した場合、またはモデルを指定する必要がある場合: CLI パラメーターを使って明示的に渡してください。パラメーターの詳細は $SN_IMAGE_BASE/references/api_spec.md を参照してください。

$SN_IMAGE_BASE パスの説明: $SN_IMAGE_BASE は sn-image-base スキルのインストールディレクトリです(SKILL.md が存在する場所)。 エージェントは、インストール済みスキルの一覧からスキル名 sn-image-base を使ってこのパスを特定できます。

アーキテクチャ:メインエージェント + ワーカーエージェント

このスキルでは、2 層構造のエージェントアーキテクチャを使用します。

役割責任
メインエージェントユーザーのリクエストを受け取り、パラメーターを正規化し、事前チェックを送信し、ワーカーを起動し、結果を収集して、テキストと画像をユーザーに送信します
ワーカーエージェント生成パイプライン(拡張 → 複数回の生成とレビュー → 並べ替え)を実行し、構造化 JSON を返します

責任の境界:

  • ワーカーエージェントはユーザーにメッセージを直接送信せず、構造化 JSON のみを返します
  • ユーザーに表示されるすべてのメッセージの送信は、メインエージェントが担当します
  • ワーカーエージェントの最後のメッセージは、Return Contract で定義された JSON 文字列のみでなければなりません
  • ワーカーエージェント内部の VLM 呼び出しは、サブエージェントを起動せず、常に直接実行されます

ワークフロー

メインエージェントのワークフロー
  1. ユーザーリクエストからパラメーターを3段階で抽出します。

    1. インラインKVディレクティブ — key=value形式のトークンを解析します。ここで、key ∈ {max_rounds, output_mode, prompts_expand_mode, aspect_ratio, image_size}です。認識されたトークンをユーザーメッセージから取り除き、残りをuser_promptとします。例:"生成一张信息图 max_rounds=3 output_mode=verbose" → user_prompt="生成一张信息图"、max_rounds=3、output_mode=verbose。

    2. キーワード認識(大文字と小文字を区別せず、除去後のテキストに適用)— 以下の表に従って、インラインKVでまだ設定されていないパラメーターを設定します。

      パラメータートリガーキーワード解決値
      output_modeverbose, 详细, 详尽, 完整统计verbose
      friendly, 简洁, 精简friendly
      max_roundsN 轮, N rounds, 重试 N 次(Nを解析)N、[1, 8]の範囲に制限
      prompts_expand_mode强制扩写, force expand, force expansionforce
      不扩写, 跳过扩写, disable expansion, no expanddisable
      aspect_ratio明示されたサポート対象の比率(16:9 9:16 4:3 3:4 1:1 2:3 3:2 4:5 5:4 21:9 9:21)。宽高比/画面比例/比例/aspect ratioの前置きがあってもなくても可その比率(runtime-parameters.mdのサポート対象一覧に照らして検証します。サポートされていない値の場合は、Workerが推論できるよう未設定のままにします)
      image_size明示されたサポート対象サイズ(2k 4k)。image_size/分辨率/清晰度/image sizeの前置きがあってもなくても可そのサイズ(高清/超清のような曖昧な品質表現は対象になりません。サポートされていない値の場合は、Workerが推論できるよう未設定のままにします)
    3. デフォルト — まだ設定されていないパラメーターは、max_rounds=1、output_mode=friendly、prompts_expand_mode=autoにフォールバックします。aspect_ratioとimage_sizeには、メインエージェントのデフォルト値はありません。明示的な値が検出されない場合は、ステップ0でWorkerが推論できるよう未設定のままにします。

    優先順位:インラインKV > キーワード認識 > デフォルト。インラインKVの値はInput Specificationに照らして検証します(範囲外のmax_roundsは[1, 8]の範囲に制限し、認識されない列挙値はデフォルトにフォールバックして、メインエージェントが不一致をログに記録します)。

  2. 統一された事前メッセージを送信します:"Using sn-infographic skill to generate infographic, please wait..."

  3. 完全なパラメーターと作業ディレクトリを渡して、Worker Agent(サブエージェント)を起動します。

  4. Worker Agentがstatus=okかつneed_main_agent_send=trueを返した場合:

    • max_rounds = 1:返されたJSONのexpanded_promptからテキスト要約を生成し(長さと言語のルールについてはOutput Format → friendly modeを参照)、送信した後、rank=1の単一画像を送信します。
    • max_rounds > 1、friendly mode:rank=1のラウンドのresultとviolationsに基づいてテキスト要約を生成し、送信した後、rank=1の単一画像を送信します。
    • max_rounds > 1、verbose mode:verboseテンプレートをレンダリングし(置換ルールについてはOutput Format → verbose modeを参照)、送信した後、すべての画像をrank順に送信します。
  5. Worker Agentがstatus=errorを返した場合は、実際のerrorフィールドの内容をユーザーに報告します。

Worker Agentのワークフロー

Worker Agentは、user_prompt、max_rounds、prompts_expand_mode、任意のaspect_ratioとimage_size(それぞれ、ユーザーが明示的に指定した場合にのみ設定)、およびこのスキルの作業ディレクトリ(SKILL_DIR)を受け取ります。(output_modeはメインエージェント側に留まり、Workerにはこれに依存する分岐はありません。)

ワーカー環境

以下の bash スニペットで $NAME として参照される変数です。ワーカーは、それぞれを使用するステップの前にバインドする必要があります。

変数ソース使用箇所
USER_PROMPTメインエージェントの入力 — 元のユーザーリクエストステップ 1 の評価、ステップ 2.0 のコンテンツ分析
MAX_ROUNDSメインエージェントの入力(デフォルト 1)ステップ 3 のループ上限、早期終了ゲート
PROMPTS_EXPAND_MODEメインエージェントの入力(デフォルト auto)ステップ 1 の分岐
SKILL_DIRエージェントのランタイムが現在のスキルのインストールパス(例:~/.openclaw/skills/sn-infographic、~/.hermes/skills/sn-infographic)を解決しますreferences/* の読み込み
SN_IMAGE_BASEエージェントのランタイムが、インストール済みスキルのレジストリでスキル名 sn-image-base を基に解決しますscripts/sn_agent_runner.py の実行
TASK_IDステップ 0(date +%Y%m%d_%H%M%S)一意性トークン
TEMP_DIRステップ 0(/tmp/openclaw/sn-infographic/${TASK_ID})すべての中間生成物用の作業ディレクトリ
IMAGE_SIZEユーザーが明示的なサイズを指定した場合はメインエージェントの入力。それ以外の場合は、USER_PROMPT からステップ 0 で推定します(選択肢は 1 つ、2k)sn-image-generate --image-size
ASPECT_RATIOユーザーが明示的な比率を指定した場合はメインエージェントの入力。それ以外の場合は、USER_PROMPT からステップ 0 で推定します(デフォルト 16:9)sn-image-generate --aspect-ratio
EXPANDED_PROMPTステップ 1(ステップ 2 がスキップされた場合は USER_PROMPT のコピー)またはステップ 2.3(展開後の結果)sn-image-generate --prompt
LAYOUT、STYLEステップ 2.1 の選択結果(フォールバック先は hub-spoke / corporate-memphis)ステップ 2.3 のシステムプロンプトの組み立て
ROUNDステップ 3 のループカウンター(for ROUND in $(seq 1 "$MAX_ROUNDS"))ラウンドごとのファイル名(round_${ROUND}.png)

命名規則:自身のファイルには $SKILL_DIR、スキル間の参照には $SN_<SKILL_NAME>(例:$SN_IMAGE_BASE)を使用します。

JSON の解析:sn_agent_runner.py ... -o json の各呼び出しは、標準出力に JSON の エンベロープ({"status", "result", "model", ...}。診断情報は標準エラー出力に出力されます)を出力します。呼び出しに失敗すると、status は ok 以外の値(例:failed)になり、result は省略されます。そのため、.result を読む前に、エンベロープの .status == ok を必ず確認してください。確認しない場合、.result が欠落すると文字列の null として出力されます。これは有効な JSON であるため、jq -r と extract_json.py のどちらでもエラーにならずに処理されてしまいます。LLM 自身の JSON(評価/分析ステップ)は result 文字列の中にあり、余分な文章や ```json フェンスが含まれる場合があります。jq を使う前に、ランナーの出力を $SN_IMAGE_BASE/scripts/extract_json.py にパイプしてください(標準入力を読み取り、復元した JSON を出力します。JSON が見つからない場合はゼロ以外で終了します)。内部の LLM/VLM JSON を解析するステップでは、.result も同様にパイプしてください。ステータスが ok 以外の場合、または extract_json.py がゼロ以外で終了した場合、応答は使用できません → Error Flow JSON を返します。

ステップ 0 — 初期化
  1. task_id(タイムスタンプ、形式 YYYYMMDD_HHMMSS)を生成し、共通の一時ディレクトリ /tmp/openclaw/sn-infographic/<task_id>/ を TEMP_DIR として作成します。後続のステップで書き込みを行う前に、TEMP_DIR が存在している必要があります。

    TASK_ID=$(date +%Y%m%d_%H%M%S)
    TEMP_DIR="/tmp/openclaw/sn-infographic/${TASK_ID}"
    mkdir -p "$TEMP_DIR"
    
  2. 空の rounds リストを初期化します

  3. aspect_ratio と image_size を解決します(ASPECT_RATIO/IMAGE_SIZE にバインド)。明示的なメインエージェントの値があればそれを使い、なければ $SKILL_DIR/references/runtime-parameters.md に従って user_prompt から推定します。デフォルト:aspect_ratio → 16:9、image_size の推定候補は現在 2k の 1 つです。

Step 1 — 画像プロンプトを書き換えるかどうかを判断する(必ず実行)

このステップでは、Step 3 で画像を生成する前に、ユーザーの画像生成用promptテキストを書き換え/拡張するかどうかを判断し、ブール値のshould_expandを生成します。Step 2 をスキップする場合は、EXPANDED_PROMPTも設定し、prompts_expand_skipped = trueを記録します。

対象範囲(ステップ名を過度に解釈しないでください)。 ここでの「拡張」とは、画像生成用のテキストプロンプトを書き換えることだけを意味します。それ以上のことではありません。これはタスクの分解、計画の生成、計画レビューのゲートではなく、Step 1 自体がエージェントを起動することもありません。Worker 自身が行う単一のsn-text-optimize呼び出しです。これをsubagent-driven-development/delegate_task形式のワークフローに当てはめないでください。また、計画ファイルも作成しないでください。(このスキルで使用するサブエージェントは Worker だけです。Main Agent によって 1 回起動され、Worker 自身はサブエージェントを起動しません。「責任の境界」を参照してください。)

PROMPTS_EXPAND_MODEは、Main Agent から渡される解決済みの入力です。Step 1 はその値に基づいて動作するものであり、ユーザーリクエストを再解析するものではありません。モード値の解決(Main Agent によるパラメーター抽出)と、この判断の実行は別々の作業です。前者を完了しても、Step 1 が完了したことにはなりません。このステップをスキップしないでください。autoモードでは、以下の評価呼び出しが必須です。プロンプトの見かけ上の品質からshould_expandを推測してはいけません。

スキップされる可能性があるのはStep 2(プロンプトの拡張)だけであり、このステップでスキップすると判断された場合に限ります。分岐はPROMPTS_EXPAND_MODEによって決まります。

autoモード(デフォルト):

  1. 評価呼び出しを実行します(必須)。内側の評価 JSON は.result内にあります。スキーマは$SKILL_DIR/references/evaluation-standard.md(required_results、optional_results)です。

    EVAL_ENVELOPE=$(python "$SN_IMAGE_BASE/scripts/sn_agent_runner.py" sn-text-optimize \
      --system-prompt-path "$SKILL_DIR/references/evaluation-standard.md" \
      --user-prompt "$USER_PROMPT" \
      --output-format json | python "$SN_IMAGE_BASE/scripts/extract_json.py")
    
    # A failed runner envelope (status != ok) carries no .result.
    EVAL_STATUS=$(printf '%s' "$EVAL_ENVELOPE" | jq -r '.status')
    
    EVAL=$(printf '%s' "$EVAL_ENVELOPE" | jq -r '.result' \
      | python "$SN_IMAGE_BASE/scripts/extract_json.py")
    
  2. should_expandを判断します。

    • required_pass:required_results内のすべてのanswerが"yes"である
    • optional_pass:optional_results内でanswer="yes"の数/合計が0.6以上である
    • should_expand = not (required_pass and optional_pass)
  3. 保守的なフォールバック:EVAL_STATUSがokでない場合(評価呼び出し自体が失敗した場合)、またはextract_json.pyがゼロ以外で終了した場合は、デフォルトでshould_expand = trueにします。Step 2.0/2.3とは異なり、評価に失敗してもWorkerは中断しません。autoは拡張にフォールバックします。

  4. should_expand = trueの場合:Step 2を実行します。

  5. should_expand = falseの場合:Step 2をスキップし、EXPANDED_PROMPTに元のプロンプトを設定して、prompts_expand_skipped = trueを記録します。

    EXPANDED_PROMPT="$USER_PROMPT"
    echo "$EXPANDED_PROMPT" > "$TEMP_DIR/expanded-prompt.txt"
    

forceモード:

  • 評価をスキップし、必ずStep 2を実行します(拡張は必須です)。
  • prompts_expand_skippedは記録されません。このフィールドはStep 2がスキップされた場合にのみReturn JSONに含まれます(「Return Contract」のルールを参照してください)。

disableモード:

  • 評価とStep 2の両方をスキップし、user_promptをそのままexpanded_promptとして使用します。

    EXPANDED_PROMPT="$USER_PROMPT"
    echo "$EXPANDED_PROMPT" > "$TEMP_DIR/expanded-prompt.txt"
    
  • prompts_expand_skipped = trueを記録します。

ステップ 2 — コンテンツ分析+レイアウトとスタイルの選択+プロンプトの拡張

2.0 コンテンツ分析(sn-image-base の sn-text-optimize ツールを使用):

ANALYSIS_ENVELOPE=$(python "$SN_IMAGE_BASE/scripts/sn_agent_runner.py" sn-text-optimize \
  --system-prompt-path "$SKILL_DIR/references/analysis-framework.md" \
  --user-prompt "$USER_PROMPT" \
  --output-format json | python "$SN_IMAGE_BASE/scripts/extract_json.py")

.result から内部の分析 JSON を(エンベロープではなく、ステップ 2.1 で data_type / tone / audience を直接 jq で取得できるように)$TEMP_DIR/analysis.json に保存します。以下のガードは、まず失敗したエンベロープを拒否します(ステータスが ok 以外、または extract_json.py がゼロ以外を返す場合 → .error を含むエラーフロー):

# A failed runner envelope (status != ok) has no .result → return Error Flow.
if [ "$(printf '%s' "$ANALYSIS_ENVELOPE" | jq -r '.status')" != "ok" ]; then
  ANALYSIS_ERROR=$(printf '%s' "$ANALYSIS_ENVELOPE" | jq -r '.error')
  # return Error Flow JSON {"status":"error","error":"$ANALYSIS_ERROR"} to Main Agent and stop
fi

printf '%s' "$ANALYSIS_ENVELOPE" | jq -r '.result' \
  | python "$SN_IMAGE_BASE/scripts/extract_json.py" > "$TEMP_DIR/analysis.json"

スキーマ:$SKILL_DIR/references/analysis-framework.md(data_type、tone、audience およびステップ 2.1 / 2.2 で使用するその他のフィールドを定義しています)。

2.1 レイアウトとスタイルの選択

これは、$SKILL_DIR/references/layout-style-selection.md の候補テーブルから名前レベルで重み付きランダムに選択する処理です。レイアウト/スタイルの名前だけを対象とします。ここでは references/layouts/ または references/styles/ 内のファイルを開かないでください。また、最適なものを選ぶために「選択肢を比較」しないでください。選択はランダムであり、ファイルの内容に基づく判断は行いません。選択されたレイアウトファイル 1 つとスタイルファイル 1 つは、後のステップ 2.3 でそれぞれ 1 回だけ読み込みます。

  1. 一時ディレクトリ $TEMP_DIR/analysis.json から分析結果を読み込みます。
ANALYSIS=$(cat "$TEMP_DIR/analysis.json")
  1. data_type / tone / audience から、$SKILL_DIR/references/layout-style-selection.md で定義された候補の検索と重み付きランダムサンプリングを実行し、LAYOUT 名と STYLE 名をそれぞれ 1 つ取得します(名前のみ。ファイルは読み込みません)。
  2. 定義ファイルが存在することだけを [ -f ] で確認して選択結果を検証します(読み込みは引き続き行いません)。ファイルがない場合は hub-spoke + corporate-memphis にフォールバックします。
[ -f "$SKILL_DIR/references/layouts/${LAYOUT}.md" ] || LAYOUT=hub-spoke
[ -f "$SKILL_DIR/references/styles/${STYLE}.md" ] || STYLE=corporate-memphis
  1. 選択結果を一時ディレクトリに保存します:$TEMP_DIR/layout-style.json。

layout-style.json の形式:

{
  "layout": "<layout>",
  "style": "<style>"
}

2.2 構造化コンテンツの生成

分析結果と構造化コンテンツのテンプレートを読み込み、テンプレートのルールに基づいて user_prompt をデザイン用の構造化コンテンツに変換します。

ANALYSIS=$(cat "$TEMP_DIR/analysis.json")
LAYOUT_STYLE=$(cat "$TEMP_DIR/layout-style.json")
STRUCTURED_CONTENT_TEMPLATE=$(cat "$SKILL_DIR/references/structured-content-template.md")

テンプレートで定義された 3 つのフェーズ(概要の作成 → セクションの作成 → データ整合性の確認)に従い、analysis.json の学習目標、視覚化の機会、重要なデータを組み合わせて構造化コンテンツを生成し、一時ディレクトリに保存します。

cat > "$TEMP_DIR/structured-content.md" << 'EOF'
<Content generated based on structured-content-template.md format>
EOF

構造:$SKILL_DIR/references/structured-content-template.md。

ルール:すべてのデータを正確に保持してください。書き換えないでください。ソースにない情報を追加しないでください。

2.3 プロンプトの拡張(sn-image-base の sn-text-optimize ツールを使用):

レイアウト/スタイルの選択結果を読み込み、システムプロンプトをファイルを直接連結して組み立てます(レイアウト/スタイルファイルにはバッククォートと $(...) が含まれているため、引用符で囲まれていないヒアドキュメントの本文では実行されてしまいます。ヒアドキュメントは使用しないでください)。

LAYOUT=$(jq -r '.layout' "$TEMP_DIR/layout-style.json")
STYLE=$(jq -r '.style' "$TEMP_DIR/layout-style.json")

{
  cat "$SKILL_DIR/references/prompts-expand-system.md"
  printf '\n\n---\n\n## Selected Layout: %s\n\n' "$LAYOUT"
  cat "$SKILL_DIR/references/layouts/${LAYOUT}.md"
  printf '\n\n---\n\n## Selected Style: %s\n\n' "$STYLE"
  cat "$SKILL_DIR/references/styles/${STYLE}.md"
  printf '\n\n---\n\n## Output Template Reference\n\n'
  cat "$SKILL_DIR/references/base-prompt.md"
} > "$TEMP_DIR/expand-system-prompt.md"

structured-content.md の内容をユーザープロンプトとして使用し(引数の長さとクォートの問題を避けるため --user-prompt-path で渡します)、一時ファイルからシステムプロンプトを読み込み、sn-text-optimize を呼び出します。

EXPAND_ENVELOPE=$(python "$SN_IMAGE_BASE/scripts/sn_agent_runner.py" sn-text-optimize \
  --system-prompt-path "$TEMP_DIR/expand-system-prompt.md" \
  --user-prompt-path "$TEMP_DIR/structured-content.md" \
  --output-format json | python "$SN_IMAGE_BASE/scripts/extract_json.py")

result フィールドを expanded_prompt として抽出し、一時ディレクトリに書き込みます。ここでの .result は JSON ではなく拡張後のプロンプトテキストなので、extract_json.py で解析するのはエンベロープだけです。まず .status == ok を確認してください。失敗したエンベロープには .result がないため、jq -r '.result' はリテラル文字列 null を返し、画像プロンプトとして "null" が書き込まれてしまいます。

# A failed runner envelope (status != ok) has no .result → return Error Flow.
if [ "$(printf '%s' "$EXPAND_ENVELOPE" | jq -r '.status')" != "ok" ]; then
  EXPAND_ERROR=$(printf '%s' "$EXPAND_ENVELOPE" | jq -r '.error')
  # return Error Flow JSON {"status":"error","error":"$EXPAND_ERROR"} to Main Agent and stop
fi

EXPANDED_PROMPT=$(printf '%s' "$EXPAND_ENVELOPE" | jq -r '.result')
echo "$EXPANDED_PROMPT" > "$TEMP_DIR/expanded-prompt.txt"

expanded-prompt.txt:UTF-8 の単一文字列。ステップ 3 で sn-image-generate --prompt にそのまま渡します。

上記のステータスガードに加えて、解析に失敗した場合や、出力の切り詰めが疑われる場合(返された内容が不完全な場合)も、Worker は同様にエラーフローの JSON(status=error、実際のメッセージ)を返して終了する必要があります。ユーザーに直接メッセージを送ってはいけません(「責任の境界」を参照)。

ステップ 3 — 画像生成ループ

ROUND を 1 から max_rounds まで順番に実行します。各反復内で、シェル変数 ROUND に現在のラウンド番号を設定し、後続のラウンドで互いのファイルを上書きしないよう、すべてのパスで ${ROUND} を使用します。

for ROUND in $(seq 1 "$MAX_ROUNDS"); do
  # the Generate Image / Review Image / Save Round Result blocks below run inside this loop body
  :
done

画像を生成(sn-image-base の sn-image-generate ツールを使用):

python "$SN_IMAGE_BASE/scripts/sn_agent_runner.py" sn-image-generate \
  --prompt "$EXPANDED_PROMPT" \
  --image-size "$IMAGE_SIZE" \
  --aspect-ratio "$ASPECT_RATIO" \
  --save-path "$TEMP_DIR/round_${ROUND}.png" \
  -o json

画像をレビュー(max_rounds > 1 の場合にのみ実行):

  • VLM モデルが設定されていない場合:VLM 設定を追加するか、max_rounds=1 に設定するようユーザーに提案する Error Flow JSON を返します。
  • VLM 呼び出しが失敗するかタイムアウトした場合:フォールバックは行わず、実際のエラーを含む Error Flow JSON を返します。
python "$SN_IMAGE_BASE/scripts/sn_agent_runner.py" sn-image-recognize \
  --system-prompt-path "$SKILL_DIR/references/prompts-critic-system.md" \
  --user-prompt "Evaluate the diagram in the image against the rules. Output your assessment." \
  --images "$TEMP_DIR/round_${ROUND}.png" \
  --output-format json

VLM の応答をラウンドごとのレコードにマッピング(VLM の応答スキーマは $SKILL_DIR/references/prompts-critic-system.md で定義されており、各違反は 4 フィールドのオブジェクトです):

  • vlm.result → rounds[i].result(そのまま — "PASS" または "FAIL")
  • vlm.violations → rounds[i].violations(そのまま渡す — { rule_id, rule_name, detail, revised_description } オブジェクトの配列)
  • len(vlm.violations) → rounds[i].violations_count
  • vlm.reasoning → rounds[i].reasoning(文字列をそのまま渡す)

max_rounds=1(VLM 呼び出しなし)の場合、ラウンドレコードは result="PASS"、violations=[]、violations_count=0、reasoning="" をデフォルト値とします。

ラウンド結果を保存:

{
  "round": 1,
  "image": "$TEMP_DIR/round_1.png",
  "result": "PASS|FAIL",
  "violations_count": 1,
  "violations": [
    {
      "rule_id": "5",
      "rule_name": "Illegible Text",
      "detail": "<offending element description>",
      "revised_description": "<suggested fix per the prompts-critic-system.md standards>"
    }
  ],
  "reasoning": "<VLM reasoning, or \"\" when max_rounds=1>",
  "timing": {
    "image_generation": { "elapsed_seconds": 12.34, "model": "sn_image_model" },
    "vlm_review": { "elapsed_seconds": 5.67, "model": "sensenova-6.8-flash-lite" }
  }
}

早期終了の確認(max_rounds > 1 の場合にのみ実行):

  • result=PASS の場合、直ちにループを終了し、生成を続行しません
  • result=FAIL の場合、残りのラウンドがあれば次のラウンドに進みます
ステップ 4 — 画像品質のランキング

violations_count の昇順、続いて round の昇順で画像を並べ替え、構造化 JSON を Main Agent に返します。

返却契約

Worker Agent の完了後、最後のメッセージは次の JSON 文字列のみでなければなりません(コードフェンスなしの生の JSON。前後にテキストを付けないでください)。

以下の例で使用する表記:

  • <...> — ドキュメント内のプレースホルダーです。実行時に実際の値に置き換えてください。
  • A|B — 列挙されたリテラルのうち 1 つです。返却する JSON には、そのうち正確に 1 つを含めてください(例:"result": "PASS" または "result": "FAIL"。「PASS|FAIL」というリテラル文字列は不可)。
  • $VAR — シリアライズ前に解決済みの値へ展開する必要があります。たとえば、スキーマ内の "image": "$TEMP_DIR/round_1.png" は、Step 3 で実際に書き込まれた絶対パス(例:"/tmp/openclaw/sn-infographic/20260521_120000/round_1.png")として返し、"$TEMP_DIR/round_1.png" というリテラルは返さないでください。
  • 条件付きフィールド — Rules の項目に「…の場合は省略」とあるフィールドは、その場合、null を設定するのではなく、JSON から完全に削除してください。

通常フロー:

{
  "status": "ok",
  "need_main_agent_send": true,
  "expanded_prompt": "<original user_prompt if prompts_expand_skipped, else expanded result from Step 2.3>",
  "prompts_expand_skipped": true,
  "early_terminated": true,
  "timing": {
    "total_elapsed_seconds": 35.12,
    "prompt_evaluation": { "elapsed_seconds": 2.11, "model": "sensenova-6.8-flash-lite" },
    "content_analysis": { "elapsed_seconds": 3.22, "model": "sensenova-6.8-flash-lite" },
    "prompt_expand": { "elapsed_seconds": 8.45, "model": "sensenova-6.8-flash-lite" }
  },
  "rounds": [
    {
      "round": 1,
      "image": "$TEMP_DIR/round_1.png",
      "result": "PASS|FAIL",
      "violations_count": 1,
      "violations": [
        {
          "rule_id": "5",
          "rule_name": "Illegible Text",
          "detail": "<offending element description>",
          "revised_description": "<suggested fix>"
        }
      ],
      "reasoning": "<VLM reasoning, or \"\" when max_rounds=1>",
      "timing": {
        "image_generation": { "elapsed_seconds": 12.34, "model": "sn_image_model" },
        "vlm_review": { "elapsed_seconds": 5.67, "model": "sensenova-6.8-flash-lite" }
      }
    }
  ]
}

エラーフロー:

{
  "status": "error",
  "error": "<Actual error information>"
}

ルール:

  • status=ok には need_main_agent_send: true を含めてください。
  • expanded_prompt:status=ok では常に含めます。prompts_expand_skipped=true の場合は元の user_prompt、それ以外の場合は Step 2.3 の結果を値にします。
  • prompts_expand_skipped:Step 2 をスキップした場合(prompts_expand_mode=disable、または auto で評価に合格した場合)のみ true を含めます。それ以外の場合は省略します。
  • early_terminated:Step 3 が PASS によってループを早期終了した場合のみ true を含めます。それ以外の場合(max_rounds=1 の実行を含む)は省略します。
  • violations:VLM のレスポンスから取得したオブジェクトの配列。スキーマは $SKILL_DIR/references/prompts-critic-system.md(rule_id、rule_name、detail、revised_description)です。result=PASS または max_rounds=1 の場合は [] です。
  • violations_count:len(violations)。max_rounds=1 の場合は 0 です。
  • reasoning:VLM の reasoning フィールドをそのまま使用します。max_rounds=1 の場合は "" です。
  • max_rounds=1 の場合、VLM によるチェックなしで画像を納品するため、単一ラウンドの result はデフォルトで "PASS" です。
  • トップレベルの timing:
    • total_elapsed_seconds:Step 0 から JSON を返すまでの Worker の実時間。
    • prompt_evaluation:Step 1 の評価による {elapsed_seconds, model}。prompts_expand_mode=auto の場合のみ含めます。
    • content_analysis:Step 2.0 の {elapsed_seconds, model}。prompts_expand_skipped=true の場合は省略します。
    • prompt_expand:Step 2.3 の {elapsed_seconds, model}。prompts_expand_skipped=true の場合は省略します。
  • rounds[].timing.image_generation.model:"sn_image_model" を固定値として使用します(sn-image-generate は model フィールドを返しません)。
  • rounds[].timing.vlm_review:max_rounds=1 の場合は省略します。

出力形式

friendly モード(デフォルト)

テキスト要約 — Main Agent が生成する、1 文の説明です。長さは言語を問わず 50 文字以下です(漢字 1 文字 = 1 単位、ASCII 文字 1 文字 = 1 単位)。言語は user_prompt の主な言語に合わせてください(主に中国語の場合は中国語、それ以外の場合は英語)。

  • max_rounds = 1 の場合:expanded_prompt をもとに説明を作成します(インフォグラフィックに何が描かれているかに焦点を当てます)。
  • max_rounds > 1 の場合:rank=1 のラウンドの result と violations をもとに説明を作成します。
    • result=PASS:肯定的な表現にします。
    • result=FAIL(違反が 1~2 件):具体的な問題点を簡潔に指摘します。
    • result=FAIL(違反が 3 件以上):主な問題点を客観的に要約します。

画像:rank=1 の単一画像。

詳細モード
Quality ranking result (high -> low)
---
Expanded prompt: [expanded | not expanded, using original prompt]
<expanded_prompt>
---
#1 round=<n> result=<PASS|FAIL> violations=<n> [early terminated]
#2 round=<n> result=<PASS|FAIL> violations=<n>
...
---
Time statistics: Total <total>s | Prompt evaluation <t>s | Content analysis <t>s | Prompt expansion <t>s | Image generation <t>s×<n> rounds | VLM review <t>s×<n> rounds
---
Images (sent in rank order)

置換ルール:

プレースホルダールール
[expanded | not expanded, using original prompt]Return JSON に prompts_expand_skipped=true が含まれる場合は not expanded, using original prompt、それ以外の場合は expanded です。
<expanded_prompt>Return JSON の expanded_prompt フィールドをそのまま使用します。
#k round=<n> result=… violations=…rounds[] の各エントリにつき1行を、順位順(k = 1..len(rounds))に並べます。<n> は rounds[i].round です。
[early terminated]早期終了を実際に引き起こしたラウンド(ループを打ち切った result=PASS のラウンド)にのみ追加します。それ以外の行には追加しません。Return JSON に early_terminated が存在しない場合、このタグは表示されません。
Total <total>stiming.total_elapsed_seconds です。必ず表示します。
Prompt evaluation <t>stiming.prompt_evaluation.elapsed_seconds です。Return JSON に prompt_evaluation が存在しない場合、この | Prompt evaluation … セグメント全体を省略します。
Content analysis <t>stiming.content_analysis.elapsed_seconds です。存在しない場合はセグメント全体を省略します。
Prompt expansion <t>stiming.prompt_expand.elapsed_seconds です。存在しない場合はセグメント全体を省略します。
Image generation <t>s×<n> rounds<t> は rounds[].timing.image_generation.elapsed_seconds の合計、<n> は len(rounds) です。
VLM review <t>s×<n> rounds<t> は rounds[].timing.vlm_review.elapsed_seconds の合計(このフィールドが存在するラウンドのみ)、<n> は VLM レビューを行ったラウンド数です。max_rounds=1(VLM レビューが行われない場合)は、セグメント全体を省略します。
Images (sent in rank order)セクション見出しです。画像の配信自体は、ホストランタイムのチャネル規約に従います。

呼び出し関係

  • 最下層の依存関係:sn-image-base → sn-image-generate、sn-image-recognize、sn-text-optimize

参考資料

  • references/analysis-framework.md - 分析手法
  • references/base-prompt.md - プロンプトテンプレート
  • references/evaluation-standard.md - 評価基準
  • references/layout-style-selection.md - レイアウトとスタイルの選択ルール
  • references/prompts-expand-system.md - プロンプト拡張システムプロンプト
  • references/prompts-critic-system.md - プロンプト批評システムプロンプト
  • references/runtime-parameters.md - ランタイムパラメーター
  • references/structured-content-template.md - 構造化コンテンツテンプレート
  • references/layouts/<layout>.md - レイアウト定義(87種類)
  • references/styles/<style>.md - スタイル定義(66種類)

(Step 2.3 では)選択されたレイアウト/スタイルファイルのみを読み込みます。選択のためにこの2つのディレクトリをまとめて読み込まないでください。選択は名前レベルでランダムに行われます(Step 2.1)。

コメント