mcpField
A
agentforce-architecture-analyze

agentforce-architecture-analyze

github.com/forcedotcomAgent 工作流1.1k5.8k 次安装1 天前更新
代码仓库

关于 agentforce-architecture-analyze

用于读取 Salesforce Agentforce Agent 声明式元数据并生成架构快照的技能,面向需要了解 Agent 组件关系的开发者。它将规划器、主题、操作、Flow、Apex、提示词和插件整理为树与文档,不读取运行时审计记录。

主要功能

  1. 01读取 Agentforce 声明式元数据
  2. 02构建规划器到插件的依赖树
  3. 03并行查询并支持缓存
  4. 04生成 JSON 架构树与 Markdown 文档
  5. 05标记未解析引用和部分结果

适用场景

  1. 01梳理 Agent 的声明式架构
  2. 02查看主题、操作与 Flow 的依赖
  3. 03为 Agent 元数据生成文档
  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 — 已声明的架构快照

一个 Agentforce Agent 的设计时元数据树:规划器 → 主题 → 操作 → 流 → Apex → 提示词 → NGA 插件。仅读取已声明的元数据:BotDefinition、GenAiPlanner*、GenAiPlugin*、GenAiFunction*、Flow、ApexClass、GenAiPromptTemplate。不会读取运行时审计记录。

运行时耗时预算:通常为 30–45s,硬上限为 ≤60s(基于参考 fixture)。顺序基线需要 90–220s;并行 Tooling SOQL 扇出可带来 3–5× 的加速。包含许多流的大型 Bot 会近似线性扩展——每次流元数据检索都需要一次往返。

内联运行——不使用子 Agent。每个阶段都是确定性的文件处理。

如果用户未指定 Agent

触发条件:$ARGUMENTS 未指定任何 Agent(没有 --agent 标志,且描述中没有完全匹配的 BotDefinition API 名称)。切勿臆造 Agent 名称。应改为发现该组织实际包含的内容——只执行一次只读查询,暂不运行流程。如有提供 --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)——无需解析。

然后根据返回的行进行分支处理:

  • 没有 Agent — 告知用户该组织没有 Agentforce Agent,然后停止。不要编造 Agent 或架构树。
  • 只有一个 Agent,或只有一个 Agent 的 DeveloperName/MasterLabel 与用户描述相符(例如“support”“service”“sales”)— 使用该 Agent,明确说明选择了哪个 Agent 以及原因,然后运行下方的流程块,并将 ARG_AGENT=<DeveloperName>(如果用户指定了组织,还需设置 ARG_ORG=<alias>)作为首行,例如 ARG_AGENT=Customer_Support_Agent。该代码块从环境读取 ARG_*;$ARGUMENTS 中的标志仍具有覆盖优先级。
  • 其他情况 — 打印下方代码块,并在首行下方以编号 DeveloperName — MasterLabel 表格列出发现的 Agent,然后等待用户选择。
  • 查询失败(没有默认组织、认证错误)— 打印下方代码块,不附列表。

应记录哪个 Agent,以及在哪个组织中?

我需要:

  • Agent API 名称 — BotDefinition 的 DeveloperName(例如 MyAgent、MySalesAgent),不是标签。
  • 组织别名 — 用于 sf CLI 认证的别名(通过 sf org login 配置的别名)

可选项:

  • 版本 — 类似 v5 的 agent_version_api_name。如果省略,我会解析活动的 BotVersion。
  • --force — 忽略缓存的架构树;重新获取所有内容。
  • --reprobe — 重新运行 7-day channel-probe 缓存(仅在 Salesforce 发布新版本后需要)。

我会内联运行元数据流程。产物将写入 ~/.vibe/data/agentforce-architecture-analyze/<org_id15>/<agent_api_name>__<agent_version>/(可通过 --data-dir 覆盖)。

流程调用

确定 Agent 后运行此代码块——Agent 可以通过 $ARGUMENTS 中的 --agent <api_name> 指定,也可以在发现后预设为 ARG_AGENT=<DeveloperName>。--org(或 ARG_ORG)为可选项;不指定时使用 CLI 默认目标组织。一次 python3 调用即可驱动完整流程。main.py 会写入 .emit_ctx.json;emit_result.py 会读取该文件,并在 stdout 的末尾打印最终的 === 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否active 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)

流水线 — 内联,无子 Agent

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 优先。

  • Tooling SOQL 用于每个规范化树节点(planner、plugins、functions、plugin-functions、plugin-instructions、planner-functions、planner-attrs)——按 planner id 进行 6 个并行通道查询,另有用于从 agent chain 解析 planner id 的 planner_definition_by_agent_chain 种子查询。
  • Data API SOQL 用于 Flow(按 id)和 Apex(按 id 或名称)主体——采用批处理。
  • Metadata retrieve 仅用于两种情况:(a) GenAiPromptTemplate(通过 Tooling SOQL 无法清晰获取提示词主体),以及 (b) 当 planner 属于 Native Generative Agent 类型时的 NGA 外部插件(classic ReAct 会跳过)。

3–5 倍的速度提升正是由此而来。朴素的实现会依次通过 Metadata API zip 检索所有内容;并行 Tooling SOQL 则可通过单次扇出查询覆盖约 80% 的树。

Planner 类型 — classic ReAct 与 NGA

该技能会将两种 planner 系列规范化为统一的树形结构:

类型GenAiPlannerDefinition.PlannerTypeInvocationTarget 风格NGA 插件?
Classic ReActReactAiPlannerV1 / SequentialPlannerIntentClassifier / 等DeveloperName 字符串否
NGAConcurrentMultiAgentOrchestration / AnthropicCompatibleV1 / 等有时为 15/18 位 Id(按 ID 前缀路由)是(通过 Metadata retrieve 获取外部插件)

resolve_invocation_target.py 中的 ID 前缀路由器可区分两者:看起来像 id 的 NGA InvocationTarget(01p… = ApexClass、301… = Flow 等)会通过限定 id 的 SOQL 解析;DeveloperName 目标则通过限定名称的 SOQL 查询。未知前缀会以 _unresolved[] 的形式显示,并带有 reason="unknown-id-prefix:<prefix>"——绝不会被静默丢弃。

缓存

  • 树缓存:除非传入 --force,否则会复用 metadata_tree.json。缓存键包含此技能捆绑的每个 .soql / .yaml / .mmd 模板的资产哈希值——更新模板后会自动使缓存失效。
  • 通道探测缓存:每个组织的 sf sobject describe 结果会缓存 7 天,用于验证 SOQL 资源引用的每个字段名称。如果 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 的字段参考(2 个 Data API + 11 个 Tooling),标注了 [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。只有清单时间戳会在重复运行时发生变化。
  • 仅向前遍历。 每个发现的引用都从规划器指向子项。不进行反向查找。
  • 部分结果会被呈现,而非隐藏。 任何未解析的引用都会写入 _unresolved[],并包含 reason=...。如果任何通道失败,则 STATUS=PARTIAL_OK;只有运行顺利完成时才为 STATUS=OK。
  • 循环检测按分支进行。 如果某个流程沿自身的祖先链再次被访问,则会发出 _cycle_back_to:<path>,而不是继续递归。防御性的 MAX_BFS_DEPTH=20 限制为按分支维护的祖先集合提供后备保护;实际 Agent 通常远在触及任一限制前就会遍历结束。(早期文档曾声称硬性上限为 5;这是历史限制,后来已弃用,因为像 handleFlowFault 这样的共享实用流程会在每个嵌套树中触发该限制——原因见 config.MAX_BFS_DEPTH。)
  • 子项按 api_name 字母顺序排列(不区分大小写)。 在根级别,Topics 排在非 Topic 规划器操作之前。Flow 的 actionCall 顺序不会排序——它反映的是流程作者设定的执行顺序。

评论

支持 Markdown