mcpField
A
agentforce-architecture-analyze

agentforce-architecture-analyze

github.com/forcedotcomエージェントワークフロー1.1k5.8k 回インストール1 日前に更新
リポジトリ

agentforce-architecture-analyze について

Salesforce Agentforce の宣言済みメタデータを読み取り、アーキテクチャのスナップショットを作成するスキルです。構成を調べる開発者向けに、プランナー、トピック、アクション、Flow、Apex、プロンプト、プラグインをツリーとレポートにまとめます。実行時監査レコードは読み取りません。

主な機能

  1. 01Agentforce の宣言済みメタデータを読み取り
  2. 02プランナーからプラグインまでの依存ツリーを構築
  3. 03並列クエリとキャッシュに対応
  4. 04JSON ツリーと Markdown レポートを生成
  5. 05未解決参照と部分的な結果を提示

活用シーン

  1. 01エージェントの宣言済みアーキテクチャを整理
  2. 02トピック、アクション、Flow の依存関係を確認
  3. 03エージェントのメタデータ構成を文書化
  4. 04未解決のメタデータ参照を特定

インストール

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

$npx skills add https://github.com/forcedotcom/sf-skills --skill agentforce-architecture-analyze

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

同梱ファイル

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

README.md
assets/cli/
describe_sobject.yaml
describe_tooling_sobject.yaml
list_metadata_genaiprompttemplate.yaml
org_display.yaml
retrieve_genai_plugin.yaml
show_access_token.yaml
assets/mermaid/
action_tree.mmd
data_flow.mmd
dependency_graph.mmd
invocation_sequence.mmd
planner_state.mmd
assets/soql/
apex_class_bodies_by_ids.soql
apex_class_bodies_by_names.soql
bot_definition_details.soql
bot_version_lookup.soql
flow_definition_by_ids.soql
flow_definition_ids_by_names.soql
flow_definition_view_by_durable_ids.soql
flow_metadata_by_id.soql
functions_by_plugins.soql
planner_attrs_by_parent_ids.soql
planner_bundle_functions.soql
planner_definition_by_agent_chain.soql
plugin_functions_by_plugin_ids.soql
plugin_instructions_by_plugin_ids.soql
plugins_by_planner.soql
references/
architecture_sections.md
contract.json
soql_fields.md

詳細

agentforce-architecture-analyze — 宣言されたアーキテクチャのスナップショット

1 つの Agentforce エージェントの設計時メタデータツリーです:プランナー → トピック → アクション → フロー → Apex → プロンプト → NGA プラグイン。宣言されたメタデータのみを読み取ります — BotDefinition、GenAiPlanner*、GenAiPlugin*、GenAiFunction*、Flow、ApexClass、GenAiPromptTemplate。実行時の監査レコードは読み取りません。

実行時間の目安:通常 30~45 秒、上限 60 秒(参照フィクスチャ使用時)。逐次処理の場合は 90~220 秒かかりますが、Tooling SOQL の並列ファンアウトにより 3~5 倍高速化されます。多くのフローを含む大規模なボットでは、おおむね線形に処理時間が増加します — フローのメタデータ取得ごとに 1 回の往復が発生します。

インラインで実行されます — サブエージェントは使いません。すべてのフェーズで決定論的なファイル処理を行います。

ユーザーがエージェントを指定していない場合

トリガー条件:$ARGUMENTS にエージェント名がない場合(--agent フラグも、文章中に完全一致する BotDefinition API 名もない場合)。エージェント名を推測してはいけません。代わりに、組織に実際に含まれているエージェントを検出します — 読み取り専用のクエリを 1 つ実行し、パイプラインはまだ実行しません。--org エイリアスが指定されていればそれを使い、指定されていなければデフォルトのターゲット組織を使います(--target-org は省略します):

sf data query --query "SELECT DeveloperName, MasterLabel FROM BotDefinition ORDER BY DeveloperName" \
  [--target-org <alias>] --json

ユーザーが組織エイリアスを指定していない場合、パイプラインブロックでは --org が CLI のデフォルトのターゲット組織自体に設定されます(sf config get target-org --json → .result[0].value)。解決する必要はありません。

その後、返された行に応じて分岐します:

  • エージェントが 0 件 — 組織に Agentforce エージェントがないことをユーザーに伝え、停止します。エージェントやツリーを捏造してはいけません。
  • エージェントがちょうど 1 件、または DeveloperName/MasterLabel がユーザーの説明(例:「サポート」「サービス」「営業」)と一致するエージェントがちょうど 1 件 — そのエージェントを使い、どのエージェントを選んだか、またその理由を明示してから、以下のパイプラインブロックを実行します。最初の行に ARG_AGENT=<DeveloperName> を設定し(ユーザーが組織を指定した場合は ARG_ORG=<alias> も設定)、例:ARG_AGENT=Customer_Support_Agent。ブロックは環境変数から ARG_* を読み取ります。$ARGUMENTS のフラグは引き続き優先されます。
  • それ以外 — 以下のブロックを出力し、最初の行の下に、検出されたエージェントを番号付きの DeveloperName — MasterLabel テーブルとして列挙して、ユーザーの選択を待ちます。
  • クエリに失敗した場合(デフォルト組織がない、認証エラーなど)— 一覧を付けずに以下のブロックを出力します。

どのエージェントを、どの組織でドキュメント化しますか?

必要な情報:

  • エージェント API 名 — BotDefinition の DeveloperName(例:MyAgent、MySalesAgent)。ラベルではありません。
  • 組織エイリアス — sf CLI 認証用(sf org login で設定したエイリアス)

任意:

  • バージョン — v5 のような agent_version_api_name。省略した場合は、有効な BotVersion を解決します。
  • --force — キャッシュされたツリーを無視して、すべて再取得します。
  • --reprobe — 7 日間のチャネルプローブキャッシュを再実行します(Salesforce のリリース後にのみ必要です)。

メタデータパイプラインはインラインで実行します。成果物は ~/.vibe/data/agentforce-architecture-analyze/<org_id15>/<agent_api_name>__<agent_version>/ に保存されます(--data-dir で変更できます)。

パイプラインの実行

エージェントが確定したら、$ARGUMENTS の --agent <api_name> を使うか、検出後に ARG_AGENT=<DeveloperName> を設定して、このブロックを 1 回実行します。--org(または ARG_ORG)は任意です。指定しない場合は、CLI のデフォルトのターゲット組織が使われます。1 回の python3 呼び出しでパイプライン全体を実行します。main.py が .emit_ctx.json を書き込み、emit_result.py がそれを読み取って、最後に === RESULT === ブロックを標準出力に出力します。

set -euo pipefail

# zsh arrays are 1-indexed by default; bash arrays are 0-indexed.
# This block uses 0-indexed semantics throughout (_args[$i] starting at i=0),
# so under zsh + `set -u` the very first read of `_args[0]` would trip
# `parameter not set`. KSH_ARRAYS makes zsh treat arrays as 0-indexed,
# matching the bash shebang's expectation. No-op under bash.
[ -n "${ZSH_VERSION:-}" ] && setopt KSH_ARRAYS

# <SKILL_DIR> = absolute path of this skill's own directory (the folder holding
# this SKILL.md); substitute it from the skill path in context. Runtimes install
# skills in different places, so also probe the known install roots and use the
# first that actually contains this skill's scripts. An exported SKILL_ROOT wins.
_skill=agentforce-architecture-analyze
for _c in "${SKILL_ROOT:-}" "<SKILL_DIR>" \
          "${CLAUDE_PLUGIN_ROOT:+$CLAUDE_PLUGIN_ROOT/skills/$_skill}" \
          "${VIBES_SKILLS_DIR:+$VIBES_SKILLS_DIR/$_skill}" \
          "$HOME/.claude/skills/$_skill" "$PWD/.claude/skills/$_skill" \
          "${PLUGIN_ROOT:-$HOME/.vibe/skills}/$_skill"; do
  [ -n "$_c" ] || continue
  # Some stagers (ADK eval) nest the bundled files under <skill>/artifacts/.
  for _r in "$_c" "$_c/artifacts"; do
    [ -f "$_r/scripts/main.py" ] && { SKILL_ROOT="$_r"; break 2; }
  done
done
[ -f "${SKILL_ROOT:-}/scripts/main.py" ] || { echo "$_skill: scripts not found — set SKILL_ROOT to this skill's directory" >&2; exit 1; }

# Argument parser. Accepts both `--org foo` and `--org=foo`.
# `$ARGUMENTS` is the raw user input Claude Code substitutes; it may be unset
# or plain prose. Each ARG_* starts from the environment, so a value resolved
# during discovery can be preset (e.g. `ARG_AGENT=MyAgent` on the first line);
# flags in $ARGUMENTS still override.
ARG_ORG="${ARG_ORG:-}"
ARG_AGENT="${ARG_AGENT:-}"
ARG_VERSION="${ARG_VERSION:-}"
ARG_FORCE="${ARG_FORCE:-}"
ARG_REPROBE="${ARG_REPROBE:-}"
ARG_PARALLELISM="${ARG_PARALLELISM:-}"
ARG_MAX_MERMAID="${ARG_MAX_MERMAID:-}"

# Word-split $ARGUMENTS (zsh needs SH_WORD_SPLIT for that) with globbing off
# so prose like `*` never expands to filenames.
[ -n "${ZSH_VERSION:-}" ] && setopt SH_WORD_SPLIT
set -f
# shellcheck disable=SC2206
_args=(${ARGUMENTS:-})
set +f
i=0
while [ $i -lt ${#_args[@]} ]; do
 tok="${_args[$i]}"
 case "$tok" in
 --org=*) ARG_ORG="${tok#--org=}" ;;
 --org) i=$((i+1)); ARG_ORG="${_args[$i]:-}" ;;
 --agent=*) ARG_AGENT="${tok#--agent=}" ;;
 --agent) i=$((i+1)); ARG_AGENT="${_args[$i]:-}" ;;
 --version=*) ARG_VERSION="${tok#--version=}" ;;
 --version) i=$((i+1)); ARG_VERSION="${_args[$i]:-}" ;;
 --parallelism=*) ARG_PARALLELISM="${tok#--parallelism=}" ;;
 --parallelism) i=$((i+1)); ARG_PARALLELISM="${_args[$i]:-}" ;;
 --max-mermaid-nodes=*) ARG_MAX_MERMAID="${tok#--max-mermaid-nodes=}" ;;
 --max-mermaid-nodes) i=$((i+1)); ARG_MAX_MERMAID="${_args[$i]:-}" ;;
 --force) ARG_FORCE="1" ;;
 --reprobe) ARG_REPROBE="1" ;;
 esac
 i=$((i+1))
done

# No org named anywhere: fall back to the sf CLI default target org
# (`sf config get target-org --json` -> .result[0].value). Extracted with sed
# so the block needs neither jq nor an inline interpreter; any failure leaves
# ARG_ORG empty and the usage block below fires.
if [ -z "$ARG_ORG" ] && command -v sf >/dev/null 2>&1; then
 _cfg="$(sf config get target-org --json 2>/dev/null || true)"
 _cfg="$(printf '%s\n' "$_cfg" | sed -n 's/.*"value"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' || true)"
 ARG_ORG="${_cfg%%$'\n'*}"
fi

# Usage block if required flags missing. Agent reads stderr,
# prints verbatim, and stops — does NOT pre-run main.py.
if [ -z "$ARG_ORG" ] || [ -z "$ARG_AGENT" ]; then
 cat >&2 <<'USAGE'
> Which agent should I document, and in which org?
>
> I need:
> - **Agent API name** — the BotDefinition.DeveloperName (e.g. `MyAgent`)
> - **Org alias** — for `sf` CLI auth (the alias you configured with `sf org login`);
>   only needed when no default target org is set (`sf config set target-org <alias>`)
>
> Optional flags:
> - `--version v5` — pin a specific BotVersion (default: Active+highest)
> - `--force` — bypass cache
> - `--reprobe` — force channel-probe refresh
> - `--parallelism N` — ThreadPoolExecutor size (default 5)
> - `--max-mermaid-nodes N` — cap Mermaid node count (default 80)
USAGE
 exit 2
fi

# Fresh work dir per invocation. Epoch + random suffix avoids collisions
# between concurrent runs on the same host.
WORK_DIR="/tmp/agentforce-architecture-analyze-$(date +%s)-$RANDOM"
mkdir -p "$WORK_DIR"

# Input validation at the boundary, BEFORE any python3 call.
# fs_guard exits 1 and prints an INVALID_INPUT RESULT block on failure;
# `|| exit 1` is mandatory — bare calls silently continue past failures.
python3 "$SKILL_ROOT/scripts/_shared/fs_guard.py" "$ARG_AGENT" agent_api_name api_name || exit 1
python3 "$SKILL_ROOT/scripts/_shared/fs_guard.py" "$ARG_ORG" org_alias not_empty || exit 1
python3 "$SKILL_ROOT/scripts/_shared/fs_guard.py" "$WORK_DIR" WORK_DIR symlink || exit 1
python3 "$SKILL_ROOT/scripts/_shared/fs_guard.py" "$WORK_DIR" WORK_DIR owned || exit 1
if [ -n "$ARG_VERSION" ]; then
 python3 "$SKILL_ROOT/scripts/_shared/fs_guard.py" "$ARG_VERSION" agent_version api_name || exit 1
fi

# Single python3 call drives all pipeline phases. main.py writes
# `.emit_ctx.json` into $WORK_DIR — emit_result.py then renders the
# RESULT block from that ctx. No subprocess-per-phase.
_main_args=(--org-alias "$ARG_ORG" --agent "$ARG_AGENT" --work-dir "$WORK_DIR")
[ -n "$ARG_VERSION" ] && _main_args+=(--version "$ARG_VERSION")
[ -n "$ARG_FORCE" ] && _main_args+=(--force)
[ -n "$ARG_REPROBE" ] && _main_args+=(--reprobe)
[ -n "$ARG_PARALLELISM" ] && _main_args+=(--parallelism "$ARG_PARALLELISM")
[ -n "$ARG_MAX_MERMAID" ] && _main_args+=(--max-mermaid-nodes "$ARG_MAX_MERMAID")

# main.py returns nonzero on terminal failures; we DON'T short-circuit —
# emit_result still publishes the failure RESULT block. `set -e` is
# temporarily relaxed around this single call.
set +e
python3 "$SKILL_ROOT/scripts/main.py" "${_main_args[@]}"
_rc=$?
set -e

# Final RESULT block is emit_result.py's stdout — MUST be the last thing
# stdout sees. emit_result exits 0 on render success; the bash harness
# propagates main.py's rc for the agent's exit status.
WORK_DIR="$WORK_DIR" python3 "$SKILL_ROOT/scripts/emit_result.py"
exit "$_rc"

入力

入力フラグ必須デフォルト
org_alias--orgいいえsf CLI のデフォルトの対象組織(sf config get target-org)
agent_api_name--agentはい—
agent_version_api_name--versionいいえアクティブな BotVersion
force_refresh--forceいいえfalse(キャッシュを使用)
reprobe--reprobeいいえfalse(7 日間のチャネルプローブキャッシュを使用)
parallelism--parallelismいいえ5
max_mermaid_nodes--max-mermaid-nodesいいえ80
data_dir--data-dirいいえ~/.vibe/data/agentforce-architecture-analyze
cache_dir--cache-dirいいえ~/.vibe/cache/agentforce-architecture-analyze

出力

すべての成果物は ~/.vibe/data/agentforce-architecture-analyze/<org_id15>/<agent_api_name>__<agent_version>/ に保存されます(デフォルト。--data-dir <path> で変更できます)。

<agent>_<ver>_metadata_tree.json   primary artifact — normalized planner/topic/action/flow/apex/prompt/plugin tree
<agent>_<ver>_architecture.md      human-readable section-by-section rendering (H1 + 7 numbered sections, plus a conditional Dependency graph appendix). Mermaid diagrams are embedded inside the relevant sections (Action tree, Data flow, and Dependency graph)

パイプライン — インライン、サブエージェントなし

resolve_bot.py        → BotDefinition + BotVersion + planner name lookup
retrieve_planner.py   → Metadata API zip retrieve for GenAiPlannerBundle (+ NGA plugins if present)
parallel_retrieve.py  → 6 parallel Tooling SOQL channels fan out from the planner id
                          (resolved by the `planner_definition_by_agent_chain` seed query):
                          - plugins_by_planner (GenAiPluginDefinition)
                          - planner_bundle_functions (GenAiPlannerFunctionDef join)
                          - functions_by_plugins (GenAiFunctionDefinition)
                          - planner_attrs_by_parent_ids (GenAiPlannerAttrDefinition)
                          - plugin_functions_by_plugin_ids (GenAiPluginFunctionDef join)
                          - plugin_instructions_by_plugin_ids (GenAiPluginInstructionDef)
parse_bundle.py       → parse retrieved XML into normalized node shapes
parse_wave.py         → BFS expansion: flow/apex/prompt refs discovered in nodes
                          → SOQL for Flow/Apex bodies (batched by id list)
                          → Metadata retrieve ONLY for GenAiPromptTemplate (+ NGA external plugins conditionally)
finalize.py           → merge waves into metadata_tree.json
render_architecture.py → <agent>_<ver>_architecture.md + Mermaid invocation graph (capped at --max-mermaid-nodes)

チャネル戦略 — SOQL 優先。

  • 正規化されたツリーノード(planner、plugins、functions、plugin-functions、plugin-instructions、planner-functions、planner-attrs)のすべてに Tooling SOQL を使用します。planner id をキーとする 6 つの並列チャネルに加え、エージェントチェーンから planner id を解決する planner_definition_by_agent_chain のシードクエリを使用します。
  • Flow と Apex の本文(id または名前による)の取得には、バッチ処理で Data API SOQL を使用します。
  • Metadata retrieve は 2 つの場合にのみ使用します:(a)GenAiPromptTemplate(プロンプト本文は Tooling SOQL ではきれいに取得できないため)、(b)プランナーが Native Generative Agent 形式の場合の NGA 外部プラグイン(クラシック ReAct ではスキップされます)。

ここで 3~5 倍の高速化を実現します。単純な実装では Metadata API の zip を使ってすべてを順次取得しますが、並列 Tooling SOQL により、単一のファンアウトでツリーの約 80%を取得できます。

プランナー形式 — クラシック ReAct と NGA

このスキルは、2 種類のプランナーを単一のツリー形式に正規化します。

形式GenAiPlannerDefinition.PlannerTypeInvocationTarget の形式NGA プラグイン?
クラシック ReActReactAiPlannerV1 / SequentialPlannerIntentClassifier / などDeveloperName 文字列いいえ
NGAConcurrentMultiAgentOrchestration / AnthropicCompatibleV1 / など15/18 文字の Id の場合あり(ID プレフィックスでルーティング)はい(Metadata retrieve 経由の外部プラグイン)

resolve_invocation_target.py の ID プレフィックスルーターは、2 つの形式を区別します。ID のように見える NGA InvocationTarget(01p… = ApexClass、301… = Flow など)は ID 範囲の SOQL で解決され、DeveloperName のターゲットは名前範囲の SOQL で処理されます。不明なプレフィックスは _unresolved[] として reason="unknown-id-prefix:<prefix>" とともに示され、黙って破棄されることはありません。

キャッシュ

  • ツリーキャッシュ:--force が指定されない限り、metadata_tree.json を再利用します。キャッシュキーには、スキルに含まれるすべての .soql/.yaml/.mmd テンプレートのアセットハッシュが含まれます。テンプレートを更新すると、キャッシュは自動的に無効になります。
  • チャネルプローブキャッシュ:SOQL アセットが参照するすべてのフィールド名を検証する、組織ごとの sf sobject describe 結果に 7 日間の TTL を設定します。Salesforce の四半期リリースでフィールド名が変更または削除されると、status: PROBE_FAILED が発生します。--reprobe を指定すると更新を強制できます。

前提条件

ツール必須
sf CLI(対象組織に対して認証済み)はい — sf org login web --alias <alias> を実行してください。また、CLI が sf org auth show-access-token を提供する必要があります(起動時の事前チェックで確認されます)。
Python 3.10+はい

必要に応じて読み込むリファレンスドキュメント

先読みしないでください。ユーザーの質問で必要になった場合に読み込んでください。

  • references/soql_fields.md — このスキルが扱う 13 個の sObject(Data API 2 個 + Tooling 11 個)のフィールドリファレンスです。[mandatory] と [optional] のタグが付いています。特定のフィールドについて質問された場合、または INVALID_FIELD SOQL エラーをデバッグする場合に読み込んでください。
  • references/contract.json — metadata_tree.json の機械可読スキーマです。このツリーを利用する下流ツールを書く場合に読み込んでください。
  • references/architecture_sections.md — レンダリングされた <agent>_<ver>_architecture.md のセクションごとの構成です。

事前に把握しておくべき不変条件

  • パイプラインは決定論的です。 同じ (org, agent, version) と静的な組織メタデータであれば、バイト単位で同一の <agent>_<ver>_metadata_tree.json と <agent>_<ver>_architecture.md が生成されます。再実行時に変わるのはマニフェストのタイムスタンプだけです。
  • 前方向のみをたどります。 検出されたすべての参照は、プランナーから子へ向かいます。逆方向の検索はありません。
  • 部分的な結果は隠さずに提示されます。 解決できない参照はすべて reason=... とともに _unresolved[] に記録されます。いずれかのチャネルで失敗すると STATUS=PARTIAL_OK となり、すべて正常に完了した場合のみ STATUS=OK となります。
  • 循環検出はブランチごとに行われます。 同じフローが自身の祖先チェーン内で再訪されると、再帰せずに _cycle_back_to:<path> を出力します。祖先セットに加え、防御策として MAX_BFS_DEPTH=20 のガードも設けられています。実際のエージェントは、どちらの上限にも達する前に処理が終了します。(以前のドキュメントにはハード上限が 5 と記載されていましたが、それは過去の上限です。handleFlowFault のような共有ユーティリティフローがネストされたツリー内で毎回この上限に達したため、廃止されました。理由については config.MAX_BFS_DEPTH を参照してください。)
  • 子は api_name のアルファベット順(大文字と小文字を区別しない)に並べられます。 ルートレベルでは、Topic が plannerActions 以外の項目より先に並びます。フローの actionCall の順序はソートされません。フロー作成者が指定した実行順序が維持されます。

コメント