This skill is used for cost anomaly analysis, fluctuation explanation, and root cause analysis (RCA) in FinOps scenarios. It is an orchestration-type analysis skill, but its internal tool calls follow stricter tool schema design principles:
- First perform structured task identification
- Then decide which analysis action to call
- Chain multiple actions when necessary
- Expose unified, stable, and minimized parameter semantics to the model
- Map to the underlying OpenAPI interface through an adapter
- All numerical conclusions come only from tool results; the model is not allowed to fabricate them
- Finally output structured, explainable FinOps conclusions
Applicable scenarios
Use this skill when the user expresses any of the following intentions:
- "Why did the cost go up / down"
- "Help me do an RCA / root cause analysis"
- "Which product, region, instance, or entity caused the anomaly"
- "Was the anomaly caused by unit price, usage, discount, or other metrics"
- "Help me locate the source of the cost anomaly"
- "Help me look at the reason for the cost fluctuation of a certain customer / account / billing period"
- "Help me explain a certain bill anomaly"
Not applicable to:
- Pure terminology explanation
- Pure permission issues
- Static data queries that do not involve anomaly analysis
Runtime environment and identity
Requires Python 3.9+ and an available ve CLI in PATH, as well as the current user authentication context provided and isolated by the Host (CLI profile or environment credentials). It does not read, output, or copy AK/SK/token, and does not modify the global CLI login configuration.
Orchestration actions, script inputs, and OpenAPI Actions uniformly use QueryCostChangeDimensions、QueryCostChangeAmountComponents、QueryCostChangeDrivers,Always use --force generic invocation, with fixed billing、2022-01-01、POST and JSON Content-Type. It is not compatible with old action names or old analysis states; please create a new analysis task.--force only skips CLI metadata validation; the server still needs to publish the new interface and grant access permissions to the current identity. When the new interface is unavailable, handle it according to the tool failure path, and do not fall back to old interfaces or internal interfaces.
Each session/analysis task uses an independent directory and state-file. Initialization can receive --environment online|boe、--region、--endpoint、--profile、--timeout-seconds, and only uses the configuration provided by the Host/test environment; business context and hooks do not carry these fields. BOE must explicitly provide the endpoint. Initialization writes non-sensitive configuration to <state-file>.artifacts/openapi_config.json, and subsequent tool_command automatically follows it; do not rewrite the configuration. After switching identity, environment, or account authorization, create a new task and do not reuse the old analysis state.
For detailed configuration and response contracts, see references/api_mapping.md。
If the call returns VE_CLI_UPGRADE_REQUIRED(ve does not support --force), you must clearly prompt the user to first upgrade ve to a version that supports --force generic invocation, and then re-initiate the analysis; do not automatically retry repeatedly, remove --force , or fall back to old interfaces. Reporting only "Action not supported" cannot be directly determined as the version being too low; it is still necessary to distinguish among CLI, platform proxy, and interface release issues.
Default execution entry: run_rca_workflow.py
RCA analysis must be executed through the scripts/run_rca_workflow.py process of init / step / resume . This script is the default orchestration entry point of the skill, responsible for advancing the fixed workflow, calling analysis_state.py to complete state transitions, managing hook artifacts, validating input, and outputting the next action.
It is forbidden to bypass run_rca_workflow.py and directly manually call analysis_state.py next-step / apply-result according to the old process, or directly assemble call_openapi.py parameters.analysis_state.py remains the only state transition entry point for canonical state, but it is called by run_rca_workflow.py , not manually chained by the LLM.
Standard process:
1. 初始化:
python3 scripts/run_rca_workflow.py init \
--question "<用户问题>" \
--context-json '<current_period/base_period 等业务上下文>' \
--state-file <state.json>
2. 根据脚本输出 action 执行:
- action=await_hook:读取 hook_request_file,按 hook_type 生成 hook_response.json,
然后执行 resume --hook-response-file。
- action=execute_tool:按脚本输出的 tool_command 参数数组执行 scripts/call_openapi.py,
将工具结果写入 tool_result.json,然后执行 resume --tool-result-file。
- action=final:输出脚本返回的 final_answer。
3. 每次 resume 后,继续按脚本返回的 action 执行,直到 action=final。
Three LLM hooks:
choose_task: frompending_tasksselect oneselected_task_id, and providedecision_reason。interpret_result: interpret the tool result, outputresult_summary、findings、candidate_tasks、llm_decision。draft_answer: after the script confirmsstop_condition.final.should_stop=true, generate the final natural language answer based on findings.
Hook response is not analysis_state. The LLM can only return the lightweight JSON required by the hook schema, and must not hand-write the complete analysis_state, FSM state, or task queue. For the complete orchestrator design and hook examples, see references/rca_orchestrator_design.md。
Tool Definitions
This skill internally has 3 analysis tools:QueryCostChangeDimensions、QueryCostChangeAmountComponents、QueryCostChangeDrivers.
Here only the information necessary for the model to perform Tool Choice is retained; the underlying OpenAPI request body, field mapping, and return structure details are uniformly placed in references/api_mapping.md。
General invocation principles
Model-layer unified parameters
When calling any tool, the model layer uniformly uses the following field names:
current_period: string, formatYYYY-MM-01base_period: string, formatYYYY-MM-01dimension: string, only used inQueryCostChangeDimensionsused infilters: object, filter condition, optional, used to constrain the upper-level scopecharge_item_code: string, only used inQueryCostChangeAmountComponents/QueryCostChangeDriversused in
General constraints:
- By default, analyze the currently authorized account, do not ask the user for the account ID, and do not pass
account_id/AccountIDto OpenAPI. When the user specifies another account, first explain the current identity boundary; if there is no Host authorization switching capability, you cannot query on behalf of the user or silently change the query to the current account. filtersOnly the following keys are allowed:product、instance_no、charge_item_code、project、region_code- The model only uses unified field names; underlying field differences are handled by the adapter or script layer
QueryCostChangeDimensions
Purpose: locate which objects the anomaly is mainly concentrated on.
Applicable questions:
- Which product caused the increase / decrease
- Which region is anomalous
- Which instance is the most anomalous
- Which objects contributed most of the fluctuation
- Help me locate the source of the anomaly
Inapplicable questions:
- The user is asking about changes in amount composition → do not prioritize using
QueryCostChangeDimensions - The user is asking about usage / unit price drivers → do not prioritize using
QueryCostChangeDimensions
Prerequisites:
- Requires a billing period
- Requires a clear localization dimension; if it is generalized RCA, by default start from
dimension="product"start
QueryCostChangeAmountComponents
Purpose: analyze the amount composition changes of a certain charge_item_code .
Applicable questions:
- Why did the payable amount increase / decrease
- How did amount items such as original price, discounted price, refund, and voucher each change
- What changed at the amount structure level
Prerequisites:
- Can only be called when
charge_item_codeis known
Inapplicable questions:
- The user is only asking where the anomaly is → do not use
QueryCostChangeAmountComponents - The user is asking about usage / unit price drivers → switch to
QueryCostChangeDrivers
QueryCostChangeDrivers
Purpose: analyze a certain charge_item_code change is caused by which driving factors.
Applicable questions:
- Is it a volume issue or a unit price issue
- Which factor is driving the increase / decrease
- How much did usage, unit_price, and impact_factor each contribute
Preconditions:
- Only when
charge_item_codeis known can it be called
Non-applicable questions:
- The user is only asking about anomaly localization → do not use
QueryCostChangeDrivers - The user is asking about amount composition → switch to
QueryCostChangeAmountComponents
Tool Choice routing rules
This section is the core of this skill.
The model does not directly schedule the next round of tools; whether to continue is determined by run_rca_workflow.py based on the canonical state outputting the next action. The model only makes narrow decisions when the script outputs a hook.
Intermediate state constraints
run_rca_workflow.py is the RCA main process orchestration entry point;analysis_state.py is the canonical analysis_state ofthe only migration entry point. The LLM is the policy decision-maker and does not directly generate/modify the complete analysis_state, nor manually chain low-level state commands.
The new version of analysis_state adopts the analysis-state-v0.3 protocol, and the core division of labor is:
- The LLM is responsible for: responding to the hook output by
run_rca_workflow.py, providingselected_task_id/decision_reason、findings/result_summary/candidate_tasks/llm_decision, as well as the finalfinal_answer - The LLM is prohibited from: manually writing the complete
analysis_state, derivingfsm.stateon its own, maintaining the task_queue on its own, or judging on its own to "give a restricted answer" to bypass the failure path - The Orchestrator is responsible for: deciding the next action, managing hook/tool artifacts, validating hook response, calling
analysis_state.py, and atomically writing back the canonical state - The Runtime is responsible for: state structure validation,
pending/current/completed/failedmigration,task_keydeduplication, precondition validation, hard guardrails such as max steps / no executable tasks / tool failure retries, FSM advancement, and stop condition calculation. The Runtime is the only writer of the canonical state.
Each round of state must contain top-level fields such as question、context、fsm、current_task、pending_tasks、completed_tasks、failed_tasks、findings、scheduler、progress_signals、stop_condition、dedup . For the complete JSON Schema, field descriptions, and state transition examples, see references/analysis_state.md。
During initialization, pass in the user question and context such as run_rca_workflow.py init through current_period/base_period . The Runtime will generate the initial task; when necessary business context is missing, the Runtime will generate the blocked state, clear the executable task pointer, and the Agent should ask the user to fill in the missing fields rather than continue calling tools.
Mandatory workflow (must be followed every round, driven by the action output by the orchestrator):
run_rca_workflow.py init
→ action=await_hook/choose_task
→ run_rca_workflow.py resume --hook-response-file
→ action=execute_tool
→ call_openapi.py
→ run_rca_workflow.py resume --tool-result-file
→ action=await_hook/interpret_result
→ run_rca_workflow.py resume --hook-response-file
→ [若 continue] action=await_hook/choose_task
→ [若 stop] action=await_hook/draft_answer
→ run_rca_workflow.py resume --hook-response-file
→ action=final
Failure handling rules:
run_rca_workflow.py initfailure is a blocking error and must be retried, the user must be asked to fill in the missing context, or it must be terminated; it is not allowed to directly callcall_openapi.py。call_openapi.pywhen there is no canonical state. When a call fails, the failure result must be written totool_result.jsonand enter the failure/interpretation path throughrun_rca_workflow.py resume --tool-result-file; it is not allowed to skip the state transition and directly give a restricted answer or proceed to the next round.run_rca_workflow.py resumefailure is a blocking error and must be retried or terminated after correcting the hook response/tool result according to the error; it is not allowed to manually write state files, FSM states, or task queues.- Tool parameters must come from
execute_tool.tool_args, and it is not allowed to bypass the orchestrator and manually assemble parameters. - In
stop_condition.final.should_stop = falsethe final answer must not be given directly.
First-round selection principles
- The initial task is generated by
run_rca_workflow.py initcalling runtime. - If the script outputs
action=await_hookandhook_type=choose_task, you may only choose onehook_request_filefrom thepending_tasksofselected_task_id。 - When selecting tasks, follow business priority: generalized RCA usually locates the product dimension first; for amount composition/driver factor questions, if
charge_item_codeis missing, first fill in the billing items through a locating task.
Judgment principles after each round
After each tool result is obtained, the result must be written to tool_result.json and run_rca_workflow.py resume --tool-result-filemust be executed. The script then outputs the interpret_result hook; the LLM only fills in the hook response and does not modify state on its own:
- The LLM provides a lightweight interpretation:
result_summary、findings、candidate_tasks、progress_signals、llm_decision - Runtime automatically completes:
current_task→completed_tasks/failed_tasks、findingsmerge,pending_tasksupdate,fsm.stateadvance,stop_conditioncalculation
If the finding indicates that the main billing item has been located, charge_item_code must be written to finding.scope.charge_item_code, and must not only be written in the summary text; Runtime derives progress_signals.has_primary_charge_itemonly based on the structured scope, and will not extract billing items from a natural language summary.
Runtime then updates stop_condition.runtime_guardrail and stop_condition.final. Continue only when the script outputs action=execute_tool or action=await_hook again; when the script outputs action=final , output the conclusion or explain the blocking reason based on the final_answer returned by the script.
Precondition rules
QueryCostChangeAmountComponentsandQueryCostChangeDriverscan only be called whencharge_item_codeis known- If
charge_item_codeis missing, it must first be filled in throughQueryCostChangeDimensionsFill in - When the user provides only a month and does not specify a year, use the current year by default; when there is clear historical context, use that year.
- If the billing period is missing, ask the user; handle credential or authorization failures according to OpenAPI errors, and adding an account ID cannot fix authentication.
- If the initialization state has entered
blockeddue to missing necessary context, you must ask for the missing fields based onstop_condition.final.reason, and must not callcall_openapi.py - It is not allowed to skip preconditions and directly call explanatory tools
- Tool parameters are synthesized by
analysis_state.context + analysis_state.current_task.scope, and the model must not bypass the state object to temporarily assemble parameters from natural language context
Runtime files and sensitive information
state-fileand its.artifactsdirectory store business state, hook requests, tool parameters/results, final replies, and non-sensitive OpenAPI configuration, and are used to advance the analysis process across commands.- Sensitive information such as Authorization, AK, SK, token, JWT, cookie, and API key must not be written to runtime files or console output.
Unified OpenAPI calls
All underlying analysis actions uniformly execute call_openapi.py through ve billing <Action> --force to call OpenAPI; the model does not directly assemble underlying HTTP requests or hand-write OpenAPI request bodies. Script inputs, action in the workflow, and gateway Actions all use the three new names, with no old-name mapping.
Execution entry principles
run_rca_workflow.pyis the default orchestration entry for RCA analysis, and the Agent must advance according to the action it outputscall_openapi.pyis the execution entry for underlying OpenAPI analysis actions, and can only be used afterrun_rca_workflow.pyoutputsaction=execute_toolaccording to the script-providedtool_commandCallanalysis_state.pyis the canonicalanalysis_stateinitialization, task selection, tool parameter construction, and state transitionsole authoritative entry point(not an auxiliary entry point), but is called byrun_rca_workflow.py, not manually chained by the LLM- Standard execution chain:
run_rca_workflow.py init→ handle by actionawait_hook / execute_tool / final→run_rca_workflow.py resume→ loop untilaction=final - The model is responsible for:
- In the
choose_taskhook, selectpending_tasksfromselected_task_id, and explaindecision_reason - In the
interpret_resulthook, interpret tool results and generatefindings/candidate_tasks/llm_decision - In the
draft_answerhook, generate the final answer - Do not directly hand-write the underlying OpenAPI request body; under the regular chain, do not directly assemble
call_openapi.py'smodel_args, but instead userun_rca_workflow.pyoutputexecute_tool.tool_args
- In the
call_openapi.pyis responsible for:- Validate Action and key prerequisite parameters
- When directly calling
QueryCostChangeDimensions, validatedimensioncan only beproduct、charge_item_code、instance_no、region_code、project - Execute the adapter mapping from model-layer parameters to the OpenAPI request body
- Handle underlying field naming differences across different Actions and fixed
Metric - Initiate the OpenAPI call and return the result
- Blocking prerequisites and task validity checks must be completed by indirectly calling
run_rca_workflow.pythroughanalysis_state.py next-step. - AdapterError is a script parameter error and is returned as structured failure JSON; the regular chain should use
run_rca_workflow.pyoutputexecute_tool.tool_args, with parameter construction and prerequisite validation completed indirectly by the orchestrator. - The model is not responsible for:
- Handling underlying field naming differences
- Assembling the OpenAPI request body
- Deciding fixed
Metricvalues - Supporting underlying payload structures for different Actions
Call constraints
- Must be called through the script; do not directly hand-write curl
- Use a tool that can execute local commands to run
tool_command; parameters must remain independent and be correctly quoted; do not treat JSON as shell code. - For Action mapping, adapter rules, request body format, field mapping, and return structure, see
references/api_mapping.md
Output specification
The final answer should be generated based on the script return result, usingconclusion first, then elaborationexpression style, prioritizing business explanations users can directly understand, rather than raw fields or JSON.
Output principles
- First use 1–2 sentences to state the overall conclusion: whether costs rose or fell, and mainly by what object or factor they were driven
- Then expand key evidence as needed, possibly combining short paragraphs and tables
- The narrative part should highlight causality, not merely list numbers
- Tables should show only key data, not pile up underlying fields
- Numeric values should uniformly retain two decimal places, and amounts should use thousands separators
- If anomalies are highly concentrated, show only Top1 and explain that concentration is high
- If the current results are insufficient to support a clear conclusion, directly state that the signal is insufficient or prerequisites are missing; do not fabricate reasons
Expression requirements
- Express for business users, prioritizing the explanation of "why"
- Do not directly paste raw JSON
- Do not expose underlying OpenAPI field names as the main body of the text
- In scenarios such as amount composition, attribution paths, and factor decomposition, tables may be selectively displayed as needed for the question
- At the end, 1–2 directions for further follow-up questions may be added
Relationship with api_mapping.md relationship with
Implementation details such as the field extraction method in the returned structure, TopN selection rules, amount composition sorting rules, and factor aggregation rules are uniformly described in references/api_mapping.md。
Constraints
- Directly writing SQL to query data is prohibited, and all data must be obtained through scripts
- Fabricating data is prohibited, and all numerical conclusions must come from the JSON output by the scripts
- If script execution fails, you must explain which step it got stuck at and what the error message is, and distinguish based on the error among credential expiration, no permission, parameter error, timeout, or service exception; failure must not be interpreted as zero cost
- When key prerequisites are missing (such as billing period,
charge_item_code), you must first ask the user or fill them in through prerequisite actions, and must not skip the conditions and directly output explanatory conclusions - If the user asks a follow-up question at a finer granularity (such as "analyze by region"), you should continue to follow the Tool Choice and prerequisite rules and select the appropriate next-round analysis action
- The final answer is expressed for business users and does not output underlying script commands, OpenAPI request bodies, or implementation-layer field mappings as the main body of the text