成本盲区
单个 Skill 的成本很容易算:input_tokens × 单价 + output_tokens × 单价。
工作流里有 7 个 Phase,每个 Phase 可能有多个子 Agent,Phase 4 还有 3 个并发候选。运行一次工作流到底花了多少钱?大多数团队说不清楚,也就没有办法优化。
跨 Phase 成本追踪
在状态文件里记录 Token 消耗
每次子 Agent 调用完成后,把 Token 消耗记录进 workflow_state.json:
json
{
"workflow_id": "wf-bug-e2e-AE-33995-20260601",
"cost_tracking": {
"phase_1_jira": {
"model": "claude-sonnet-4-6",
"input_tokens": 850,
"output_tokens": 420,
"cost_usd": 0.0019
},
"phase_2_logs": {
"model": "claude-sonnet-4-6",
"input_tokens": 3200,
"output_tokens": 380,
"cost_usd": 0.0061
},
"phase_3_analyze": {
"model": "claude-opus-4-8",
"input_tokens": 15000,
"output_tokens": 500,
"cost_usd": 0.2625
},
"phase_4_candidate_a": {
"model": "claude-sonnet-4-6",
"input_tokens": 8000,
"output_tokens": 2000,
"cost_usd": 0.046
},
"phase_4_candidate_b": {
"model": "claude-sonnet-4-6",
"input_tokens": 8100,
"output_tokens": 1800,
"cost_usd": 0.0423
},
"phase_4_candidate_c": {
"model": "claude-sonnet-4-6",
"input_tokens": 8200,
"output_tokens": 2200,
"cost_usd": 0.0498
},
"total_usd": 0.5145
}
}
采集方式:调用 LLM API 时,response 对象里有 usage 字段,在子 Agent 完成后写入状态文件。
python
def invoke_subagent(phase_id: str, prompt: str, model: str) -> dict:
response = llm_client.messages.create(
model=model,
messages=[{"role": "user", "content": prompt}]
)
# 计算成本(以 Claude Sonnet 为例,实际单价从配置读取)
prices = {"claude-sonnet-4-6": {"input": 0.003, "output": 0.015},
"claude-opus-4-8": {"input": 0.015, "output": 0.075}}
input_tokens = response.usage.input_tokens
output_tokens = response.usage.output_tokens
price = prices[model]
cost_usd = (input_tokens * price["input"] + output_tokens * price["output"]) / 1000
return {
"output": response.content[0].text,
"cost": {
"model": model,
"input_tokens": input_tokens,
"output_tokens": output_tokens,
"cost_usd": round(cost_usd, 4)
}
}
成本热点分析
统计几次真实运行的数据后,通常会看到这样的分布:
bash
Phase 平均成本 占比
─────────────────────────────────────
phase_3_analyze $0.26 51% ← 最贵:使用 Opus,输入大(日志)
phase_4_fix (×3) $0.14 27% ← 第二贵:3 个候选并发
phase_2_logs $0.006 1%
phase_1_jira $0.002 0.4%
phase_5_commit $0.003 0.6%
phase_7_notify $0.002 0.4%
─────────────────────────────────────
总计 $0.51 100%
Phase 3 占 51% 的成本,原因是两个:使用了 Opus 模型,以及输入包含大量日志文本(15000 tokens)。Phase 4 占 27% 是因为并发跑 3 个候选。
优化方向
Phase 3 降成本:
python
# 当前:用 Opus 分析完整日志
phase_3_config = {"model": "claude-opus-4-8", "context": "full_logs"} # 贵
# 优化方案 A:先用 Sonnet 提取关键行,再用 Opus 分析关键行
# 第一步:Sonnet 提取关键 log lines(便宜)
# 第二步:Opus 分析关键 log lines(输入小,贵但少)
phase_3_config = {
"pre_filter": {"model": "claude-sonnet-4-6", "task": "extract_key_lines"},
"analysis": {"model": "claude-opus-4-8", "context": "key_lines_only"}
}
# 优化方案 B:先用 Sonnet 做分析,只有置信度低于阈值才升级到 Opus
phase_3_config = {
"model": "claude-sonnet-4-6",
"fallback_model": "claude-opus-4-8", # 置信度 < 0.7 时升级
"fallback_threshold": 0.7
}
Phase 4 降成本:
Phase 4 的 3 个候选是为了提高"至少 1 个通过"的概率。如果历史数据显示候选通过率超过 80%,可以先跑 1 个,通过了就不跑剩下的:
yaml
phase_4_fix:
strategy: lazy_parallel # 先跑 1 个,通过则结束;失败才跑剩余 2 个
max_candidates: 3
stop_on_first_pass: true # 有通过就停
故障排查方法论
Workflow 运行失败时,不要从头翻日志,先判断属于哪一类故障,再用对应的定向检查。
故障分类树
arduino
工作流没有完成
├── 从未开始
│ └── 触发条件问题
│ → 检查 AGENTS.md 的触发关键词是否匹配
│ → 检查输入参数格式(jira_key 格式是否正确)
│
├── 在某个 Phase 卡住
│ ├── 子 Agent spawn 失败
│ │ → 检查 sessions_spawn 的参数
│ │ → 检查网络和认证配置
│ │
│ ├── 子 Agent 超时(输出文件不存在)
│ │ → 检查 task prompt 长度(过长导致 LLM 响应慢)
│ │ → 检查模型的 RPM/TPM 限制
│ │
│ └── 子 Agent 失败(输出文件存在但 passed=false)
│ → 读取 output_file 中的 error 字段
│ → 检查对应 template 的输出契约声明
│
├── 在确认门等待超时
│ └── 检查 timeout_action 是否设置为 pause
│ → 如果是 continue,检查默认选项是否正确
│
└── 续接时从错误位置继续
└── 检查 workflow_state.json 的 phase/step 状态
→ in_progress 的 Phase 会被重新执行(正常)
→ 检查版本绑定(W3 篇)
5 步标准诊断
bash
# Step 1:看当前状态
cat $WS/workflow_state.json | python3 -m json.tool | grep -A3 '"phase"'
# Step 2:找第一个未完成的 Phase
cat $WS/workflow_state.json | python3 -c "
import json, sys
state = json.load(sys.stdin)
for phase_id, phase in state['phases'].items():
if phase.get('status') != 'done':
print(f'Stuck at: {phase_id} ({phase.get(\"status\", \"unknown\")})')
break
"
# Step 3:检查该 Phase 的输出文件
ls -la $WS/phase_4/
# Step 4:如果输出文件存在,读取 error 字段
cat $WS/phase_4/candidate_a.json | python3 -c "
import json, sys
r = json.load(sys.stdin)
if not r.get('passed'):
print('Error:', r.get('error', 'no error field'))
"
# Step 5:(如果有 Trace)在 Langfuse 搜索对应 workflow_id
# 直接在 Langfuse UI 里搜 workflow_id,查看各 Phase 的 Span 详情
常见故障场景速查
场景 1:工作流停在 Phase 3,没有输出文件
ini
症状:phase_3 status=in_progress,但 analysis_final.json 不存在
已等待超过 5 分钟
诊断:子 Agent 超时。可能原因:
1. task prompt 过长(日志文件全部注入了 prompt)→ 检查 Phase 3 的输入大小
2. 模型限速 → 检查 API 调用日志
3. 子 Agent spawn 失败但没有错误记录 → 检查 sessions_spawn 日志
修复:手动设置 phase_3 status=pending,重新触发续接
场景 2:Phase 4 的 3 个候选都 passed=false
bash
症状:candidate_a/b/c.json 都存在,但 passed=false
gate_B 被触发
诊断:修复策略失败。可能原因:
1. 根因分析有误(analysis_final.json 的 root_cause 不正确)
→ 读取 analysis_final.json 的 root_cause 字段,手动判断是否合理
2. 测试用例本身有问题(test_runner 报错不是修复代码的问题)
→ 读取 candidate_a.json 的 error 字段
修复:通过 Gate B 的确认门选择"重新分析根因"
月度成本报告
python
# tools/cost_report.py
import json
from pathlib import Path
from collections import defaultdict
def generate_monthly_report(state_dir: Path) -> dict:
"""汇总一个月内所有工作流运行的成本"""
totals = defaultdict(float)
run_count = 0
for state_file in state_dir.glob("**/workflow_state.json"):
state = json.loads(state_file.read_text())
cost_tracking = state.get("cost_tracking", {})
for phase_id, phase_cost in cost_tracking.items():
if phase_id != "total_usd" and isinstance(phase_cost, dict):
totals[phase_id] += phase_cost.get("cost_usd", 0)
totals["total"] += cost_tracking.get("total_usd", 0)
run_count += 1
return {
"run_count": run_count,
"total_cost_usd": round(totals["total"], 4),
"avg_cost_per_run": round(totals["total"] / run_count, 4) if run_count else 0,
"by_phase": {k: round(v, 4) for k, v in totals.items() if k != "total"},
"top_cost_driver": max(
(k for k in totals if k != "total"),
key=lambda k: totals[k],
default=None
)
}
设计 Checklist
成本追踪
- 每个子 Agent 调用后把 Token 消耗写入状态文件
- 状态文件包含
cost_tracking.total_usd字段 - 有工具汇总跨运行的成本,识别成本热点 Phase
成本优化
- 最贵的 Phase 评估是否可以用更便宜的模型(Sonnet 替换 Opus)
- 并发候选数量有历史通过率数据支撑
- 高输入量 Phase 评估是否可以先做预过滤再调用
故障排查
- 熟悉故障分类树(从未开始 / Phase 卡住 / 确认门超时 / 续接位置错误)
- 能在 5 分钟内用 4 步 shell 命令定位问题 Phase
- 常见故障场景有对应的修复操作记录
总结
- 成本热点通常集中在 1-2 个 Phase:Phase 3(高质量模型 + 大输入)和 Phase 4(并发候选 × 3)加起来通常占总成本的 75%,优化这两个比优化其他所有 Phase 更有效
- 故障分类先于排查:遇到问题先判断属于哪一类(从未开始/Phase 卡住/确认门/续接错误),每类都有对应的定向检查,比从头翻日志快得多
- 诊断工具要提前准备 :
cost_report.py和diagnose.sh在问题出现之前就应该存在,出问题时打开即用
欢迎访问 PrimeSkills ------ 一个精心策划的 AI Agent 与技能市场,所有内容均经过真实企业级工作流验证。没有噱头,只有真正有效的东西。
更多实用知识和有趣产品,欢迎访问我的个人主页