火山引擎 Coding Plan 用量监控指南:Ark CLI 查询与 Python 可视化脚本
火山引擎提供了便捷的 Ark CLI 工具,并配套 Claude Code Skill,让开发者可以直接在 Claude Code 环境中快速查询 Coding Plan 的用量情况。
效果如下:

借助 arkcli 命令行,我们能轻松查看当前账号下 Coding Plan 的订阅及使用详情。

不过首次使用前需通过 Ark CLI 完成登录,登录后 SSO 状态会持续有效约 47 小时,无需反复认证。
ark cli 安装:
npm install -g @volcengine/ark-cli
登录:
# 登录方式 :火山 SSO 浏览器登录 (推荐)
arkcli auth login volc-sso
安装 skills :
# 装 skill 到你常用的 AI Agent (Claude Code / Cursor / trae / Gemini CLI 等)
arkcli +connect
为了日常使用更方便,我又对官方 skill 进行了一层轻量封装,以适配自己的工作流。

封装好的 skill 使用效果如下:

经过测试,该封装背后实际执行的命令为:
$ arkcli usage plan
不过原始命令的输出格式可读性较差,使用起来不够直观:

除了借助安装好的 skill,我还编写了一个 Python 脚本来解析和美化命令输出,提升使用体验。
处理后的效果:

火山的 Coding Plan(GLM-5.2 模型)额度其实并不宽裕,一轮对话就可能消耗近 9.6% 的配额(查询前仍为 0%),而我编写该脚本的过程中也花掉了周额度的 1.3%。对于高频使用者来说,需要特别注意额度管理。我目前手头有两个账号轮流使用,当前这个账号用量较少,可作为一个参考基准。未来的编程辅助工具无疑会引入更多费用消耗,而“古法”手动编码反倒成了一种零成本的怀念。
脚本:
#!/usr/bin/env python3
"""arkcli usage plan 输出封装工具
方便查看 coding plan 用量
"""
import json
import subprocess
import sys
from datetime import datetime
def get_usage_plan():
"""调用 arkcli usage plan 获取数据"""
try:
result = subprocess.run(
["arkcli", "usage", "plan", "--format", "json"],
capture_output=True,
text=True,
check=True
)
return json.loads(result.stdout)
except FileNotFoundError:
print("错误: 未找到 arkcli 命令", file=sys.stderr)
sys.exit(1)
except subprocess.CalledProcessError as e:
print(f"错误: arkcli 命令执行失败", file=sys.stderr)
if e.stderr:
try:
err_json = json.loads(e.stderr)
if err_json.get("error", {}).get("message"):
print(f" {err_json['error']['message']}", file=sys.stderr)
except:
print(f" {e.stderr}", file=sys.stderr)
sys.exit(1)
except json.JSONDecodeError:
print("错误: 无法解析 arkcli 输出", file=sys.stderr)
sys.exit(1)
def format_timestamp(ts):
"""格式化时间戳 (自动处理秒/毫秒)"""
if ts == -1:
return "无数据"
try:
# 判断是秒还是毫秒
if ts > 9999999999: # 大于这个值说明是毫秒
dt = datetime.fromtimestamp(ts / 1000)
else:
dt = datetime.fromtimestamp(ts)
return dt.strftime("%Y-%m-%d %H:%M:%S")
except (ValueError, OSError):
return "无效时间"
def get_progress_bar(percent, width=30):
"""生成进度条"""
filled = int(width * percent / 100)
bar = "█" * filled + "░" * (width - filled)
return bar
def get_percent_color(percent):
"""根据百分比获取颜色代码"""
if percent >= 80:
return "\033[91m" # 红色
elif percent >= 50:
return "\033[93m" # 黄色
else:
return "\033[92m" # 绿色
def print_coding_plan(item):
"""打印 Coding Plan 详情"""
product_name = {
"coding-plan": "Coding Plan (个人版)",
"coding-plan-team": "Coding Plan (团队版)"
}.get(item["product"], item["product"])
print(f"\n\033[1;34m{product_name}\033[0m")
print("─" * 60)
if not item.get("subscribed", False):
print(" 未订阅")
return
if "updated_at" in item:
print(f" 数据更新: {format_timestamp(item['updated_at'])}")
print()
periods = item.get("periods", [])
if not periods:
print(" 暂无用量数据")
return
period_labels = {
"session": "会话周期",
"weekly": "每周额度",
"monthly": "每月额度",
"5h": "5小时额度"
}
for period in periods:
label = period_labels.get(period["label"], period["label"])
percent = float(period.get("percent", 0))
color = get_percent_color(percent)
reset = "\033[0m"
print(f" \033[1m{label}\033[0m")
print(f" 进度: {color}{get_progress_bar(percent)} {percent:.1f}%{reset}")
if "used" in period and "total" in period:
used = period["used"]
total = period["total"]
remaining = total - used
print(f" 用量: {used:,} / {total:,} (剩余: {remaining:,})")
print(f" 重置: {format_timestamp(period.get('reset_at', -1))}")
print()
def print_agent_plan(item):
"""打印 Agent Plan 详情"""
product_name = {
"agent-plan": "Agent Plan (个人版)",
"agent-plan-team": "Agent Plan (团队版)"
}.get(item["product"], item["product"])
tier = item.get("tier", "")
if tier:
product_name += f" - {tier}"
print(f"\n\033[1;35m{product_name}\033[0m")
print("─" * 60)
if not item.get("subscribed", False):
print(" 未订阅")
return
if "seat_id" in item:
print(f" 席位: {item['seat_id']}")
print()
periods = item.get("periods", [])
if not periods:
print(" 暂无用量数据")
return
period_labels = {
"session": "会话周期",
"weekly": "每周额度",
"monthly": "每月额度",
"5h": "5小时额度"
}
for period in periods:
label = period_labels.get(period["label"], period["label"])
percent = float(period.get("percent", 0))
color = get_percent_color(percent)
reset = "\033[0m"
print(f" \033[1m{label}\033[0m")
print(f" 进度: {color}{get_progress_bar(percent)} {percent:.1f}%{reset}")
if "used" in period and "total" in period:
used = period["used"]
total = period["total"]
remaining = total - used
print(f" 用量: {used:,} / {total:,} AFP (剩余: {remaining:,})")
print(f" 重置: {format_timestamp(period.get('reset_at', -1))}")
print()
def print_viewer(viewer):
"""打印查看者信息"""
print("\033[1;36m" + "=" * 60 + "\033[0m")
print("\033[1;36mARK 套餐用量查看\033[0m")
print("\033[1;36m" + "=" * 60 + "\033[0m")
print()
print("\033[1m当前身份:\033[0m")
auth_method = viewer.get("auth_method", "unknown")
auth_display = {
"sso": "SSO 登录",
"aksk": "AK/SK 登录",
"apikey": "API Key 登录",
"none": "未登录"
}.get(auth_method, auth_method)
print(f" 认证方式: {auth_display}")
if viewer.get("profile"):
print(f" 当前 Profile: {viewer['profile']}")
if viewer.get("region"):
print(f" 区域: {viewer['region']}")
if viewer.get("user_name"):
print(f" 用户: {viewer['user_name']}")
if viewer.get("account_id"):
print(f" 账号: {viewer['account_id']}")
def main():
"""主函数"""
data = get_usage_plan()
viewer = data.get("viewer", {})
items = data.get("items", [])
print_viewer(viewer)
if not items:
print("\n 未找到任何套餐数据")
return
# 先打印 coding plan,再打印 agent plan
coding_items = [i for i in items if "coding-plan" in i["product"]]
agent_items = [i for i in items if "agent-plan" in i["product"]]
for item in coding_items:
print_coding_plan(item)
for item in agent_items:
print_agent_plan(item)
print("─" * 60)
print()
if __name__ == "__main__":
main()