用 Litefuse 追踪 MiniMax Agent
MiniMax Agent 是 MiniMax 的桌面编码 agent。它的 agent 守护进程 Mavis 基于 MiniMax-M2 系列模型运行 coder / general / verifier 等 agent,并提供一套 hook 系统和本地 SQLite 存储。本集成把两者结合起来:hook 事件做触发器、提供真实墙钟时间,SQLite 提供每次 LLM 调用的消息、token 用量与成本。采集器是一个零依赖(纯标准库)的单文件 Python 脚本,直接把 span 发往 Litefuse 的 OTLP 端点——不改 Mavis 源码、不装 SDK、不建虚拟环境。
每个用户回合生成一条 Litefuse trace:每次 LLM API 调用一个 generation、每次工具执行一个 tool observation、每次子 agent 委派一棵完整子树。
给 AI —— 自动安装
如果你此刻正在和某个 AI agent 对话,粘贴这句话,agent 会端到端完成安装:
Read https://litefuse.ai/SKILL.md and follow the instructions to install and configure Litefuse for MiniMax Agent.
skill 会向你索取 Litefuse API Key(没有账号会引导注册),然后就地完成全部配置。想手动一步步装,继续往下看。
采集内容
| 数据 | 形式 | 说明 |
|---|---|---|
| 用户 prompt | trace input | 来自 UserPromptSubmit hook |
| 每次 LLM API 调用 | generation observation | plan (n tools) #N / response / think #N,按模型行为命名;输出保留 thinking / text / tool_call 块结构 |
| 工具执行(输入 + 输出) | tool observation | tool: bash (git) #N——名字带关键信息,完整参数在 input;start/end 是 hook 记录的真实墙钟时间 |
子 agent(task 工具) | 子树 | tool (1 subagent) #N → subagent 容器 → 子会话自己的 plan/tool/response 步骤,从 SQLite 重建;子 agent 用量汇入父 trace。支持递归。 |
| Token 用量 | generation 的 usage_details | Anthropic 风格 key(input / output / cache_read_input_tokens / cache_creation_input_tokens),来自 Mavis 的 token_usage 表 |
| 成本 | generation 的 cost_details | Mavis 原生计算每次调用的美元成本,原样转发 |
| 模型名 | generation 的 model | 如 MiniMax-M2.7;provider 放 agent_provider metadata |
| 工具报错 | tool observation,level=ERROR | 附状态消息预览 |
| 中止 / 出错的回合 | root span level=WARNING / ERROR | 执行中的工具以 WARNING 收尾(“turn ended before tool completed”) |
| 会话分组 | trace session_id | Mavis 会话 id(mvs_…);team-plan 子会话归入父会话分组 |
| 用户身份 | trace user_id | $LITEFUSE_USER_ID,回退到操作系统用户名 |
| 上下文水位 | trace metadata | 取末条消息 usage 的 agent_context_tokens / agent_context_window |
Trace 结构
一个委派了子 agent 的回合,trace 长这样(真实示例):
Mavis Coder — Turn 16 (AGENT root span,携带 trace header)
├── plan (1 tool) #1 (generation——usage、成本、真实延迟)
├── tool (1 subagent) #2 (tool——父进程视角的委派)
│ └── subagent (AGENT 容器——从子会话重建)
│ ├── plan (1 tool) #1 (容器内编号从 #1 重新开始)
│ ├── tool: glob (hooks) #2
│ ├── plan (2 tools) #3
│ ├── tool: read (litefuse_hook.py) #4
│ ├── tool: read (litefuse_hook.py) #5
│ └── subagent response (generation——子 agent 的最终回答)
├── plan (1 tool) #3
├── tool: bash (ls) #4
└── response (generation——最终回答,回合到此结束)设计要点:
- hook 触发,SQLite 供数。 Mavis 的 hook payload 不带 token 用量、消息边界,(大多)也不带模型名——但这些全部落在
~/.mavis/sqlite.db(session_messages+token_usage)。采集器用 hook 实时记时间,回合结束时从数据库组装 trace。对数据库的依赖是只读的,且只涉及两张表。 - 每次 LLM API 调用一个 generation,按模型做了什么命名——请求工具时是
plan (n tools) #N,最终文本回答是response,只有 thinking 是think #N——绝不用模型名进名字(那是model属性)。 - 每个 agent 容器一个步骤计数器。
#N是 generation 和 tool 共用的单一时序编号,严格按数据库消息顺序分配;每个子 agent 容器从#1重计。工具的agent_plan_stepmetadata 指向发起它的 generation 的agent_step_index。 - 子 agent 子树。
task工具派生的 opencode 内部子会话不经过 Mavis hook bridge——采集器在委派工具返回时,从结果中解析子会话 id(ses_…),从 SQLite 重建完整三层子树。委派工具 span 包裹容器是有意设计:工具 span 时长 − 容器时长 = 委派的真实开销。嵌套委派(子再派孙)按同样规则递归。 - 能拿到的都是真实时间戳。 父回合的工具 span 用 hook 墙钟时间。子树里的子工具如果有 hook 计时(子会话的工具 hook 会被记录,只是不单独发射)也用真实时间,否则按消息时间戳估算并标记
agent_times_estimated。 - generation 的 input 是增量。 完整请求体(system prompt + 历史)在 Mavis hook 里不可见。每个 generation 的 input 是模型新收到的内容——首次调用是用户 prompt,之后是上一步的工具结果——标记
agent_input_is_delta。 - 降级而不失联。 假如 Mavis 的 SQLite schema 变化导致读取失败,trace 仍会从纯 hook 数据发出(root + tools + 合成的
response),并标记agent_degraded+agent_degraded_reason,看板上一眼可见。 - 打平的
agent_*metadata。 所有集成字段都放在 metadata 顶层、带agent_前缀——与其它 Litefuse agent 集成共用同一套 key,一个看板查询通用于所有 agent。
Trace 何时出现?
回合的全部 span 在回合结束时(Mavis 每回合触发一次 SessionEnd)作为一批上传;回合进行中不可见。这是有意的取舍:Mavis 的工具 hook 经由 opencode 代理到达,远快于 daemon 的消息落库,只有等回合消息在 SQLite 落定后才能保证编号正确。与 Claude Code 集成的取舍相同。
快速开始
前置条件
- 已安装 MiniMax Agent 桌面 App(数据目录
~/.mavis/存在)。 - Python ≥ 3.8——任意
python3都行。零第三方依赖:不装 SDK、不建虚拟环境、不跑 pip。 - 一个 https://litefuse.cloud 的 Litefuse 项目及其 public + secret key。
下载采集脚本
mkdir -p ~/.mavis/hooks
curl -fsSL https://litefuse.ai/integrations/minimax-agent/litefuse_hook.py \
-o ~/.mavis/hooks/litefuse_hook.py同一 URL 也可以直接在浏览器里阅读源码——部署前不妨先看一遍。
在 ~/.mavis/.env 配置凭据
cat > ~/.mavis/.env <<'EOF'
TRACE_TO_LITEFUSE=true
LITEFUSE_PUBLIC_KEY=pk-lf-xxx
LITEFUSE_SECRET_KEY=sk-lf-xxx
LITEFUSE_HOST=https://litefuse.cloud
EOF
chmod 600 ~/.mavis/.env注册全局 hook
六个小 hook 文件把 Mavis 事件路由到采集器。全局 hook(直接放在 ~/.mavis/hooks/ 下)对所有 agent 生效——coder、general、verifier 以及 team-plan 委派出的会话:
for E in SessionStart UserPromptSubmit PreToolUse PostToolUse MessageComplete SessionEnd; do
L=$(echo "$E" | tr '[:upper:]' '[:lower:]')
cat > ~/.mavis/hooks/litefuse-$L.md <<EOF
---
hookEvent: $E
type: script
priority: 50
timeout: 30000
---
\`\`\`bash
set -a && source ~/.mavis/.env && set +a && python3 ~/.mavis/hooks/litefuse_hook.py $E
\`\`\`
EOF
done验证注册(无需重启——Mavis 每次事件都会重新读取 hook 文件):
~/.mavis/bin/mavis hook list --human | grep litefuse
# 预期:六行记录,AGENT 列为 "*"验证
在 MiniMax Agent App 里发一条消息,等回复完成,然后看采集器日志:
MAVIS_LITEFUSE_DEBUG=true # 可选:写进 ~/.mavis/.env 开启详细日志
tail ~/.mavis/hooks/litefuse_hook.log
# 预期:"SessionEnd mvs_... reason=finished emitted=N"打开 Litefuse 项目——每条用户消息对应一条 Mavis <Agent> — Turn N trace,结构如上文所示。
从 v1 升级
旧版 hook 依赖虚拟环境里的 Langfuse Python SDK,hook 注册在 ~/.mavis/agents/coder/hooks/ 下且仅对 coder 生效,每回合只发一条合并的 “LLM response”,token 靠字符数估算。v2 全部不需要:
- 备份并移除旧的 per-agent hook 文件:
mv ~/.mavis/agents/coder/hooks/litefuse-*.md <备份目录>/——留在原地会与全局 hook 重复触发。 - 用新脚本覆盖
~/.mavis/hooks/litefuse_hook.py(如有自定义改动请先备份)。 - 按快速开始注册全局 hook。
- 旧虚拟环境可以删掉——已无任何依赖。
v2 重命名了 observation(plan (n tools) #N / response 取代 user message / LLM response)、从 Mavis 数据库读取真实 usage 与成本(不再估算)、metadata 打平为 agent_* key——记得更新保存过的看板过滤器。
环境变量
从 ~/.mavis/.env 读取(hook 文件会 source 它)。LITEFUSE_* 优先;同名 LANGFUSE_* 作为生态兼容 fallback。
| 变量 | 必填 | 说明 |
|---|---|---|
LITEFUSE_PUBLIC_KEY | 是 | Litefuse 项目 public key(pk-lf-...)。 |
LITEFUSE_SECRET_KEY | 是 | Litefuse 项目 secret key(sk-lf-...)。 |
LITEFUSE_HOST | 否 | 默认 https://litefuse.cloud。别名:LITEFUSE_BASE_URL。 |
LITEFUSE_TRACING_ENVIRONMENT | 否 | trace 写入的 Litefuse environment。默认 production;实验用 development 以免污染生产看板。 |
LITEFUSE_USER_ID | 否 | 覆盖 trace user_id。回退到操作系统用户名。 |
LITEFUSE_EXTRA_TARGETS | 否 | 额外写入目标的 JSON 数组([{"host", "public_key", "secret_key", "environment"}]),可双写(如自托管 + cloud)。 |
TRACE_TO_LITEFUSE | 否 | 设为 "false" 可不卸载直接停用。只要 key 在,追踪默认开启。 |
MAVIS_LITEFUSE_DEBUG | 否 | 设为 "true" 开启详细日志(错误日志始终记录)。 |
MAVIS_LITEFUSE_MAX_CHARS | 否 | span input/output 的截断阈值(字符数)。默认 1000000。 |
MAVIS_LITEFUSE_DB | 否 | 覆盖 SQLite 路径(测试用)。默认 ~/.mavis/sqlite.db。 |
Metadata 参考
所有集成字段都是带 agent_ 前缀的顶层 metadata key(与其它 Litefuse agent 集成共用)。源数据中不存在的字段完全不出现,绝不用 null 占位。
Trace root:agent_turn_number、agent_session_id、agent_cwd、agent_model、agent_provider、agent_api_calls、agent_tool_calls、agent_steps、agent_message_count、agent_duration_ms、agent_context_tokens、agent_context_window;team-plan 子会话另有 agent_parent_session_id + agent_subagent;降级模式另有 agent_degraded + agent_degraded_reason。
Generation:agent_turn_number、agent_step_index、agent_provider、agent_stop_reason、agent_tool_call_count、agent_thinking_chars、agent_context_tokens、agent_input_is_delta、截断标记。
Tool:agent_turn_number、agent_step_index、agent_plan_step(join 键:tool.agent_plan_step == generation.agent_step_index)、agent_tool_name、agent_tool_call_id、agent_duration_ms(hook 计时)或 agent_times_estimated、agent_is_error;委派工具另有 agent_subagent_session_id。
Subagent 容器:agent_subagent: true、agent_session_id(子会话 ses_… id),以及该次运行的 agent_api_calls / agent_tool_calls / agent_steps / agent_duration_ms。
工作原理
采集器注册六个 Mavis hook,按会话在 ~/.mavis/hooks/litefuse_state/ 维护状态:
UserPromptSubmit开启回合:生成 trace/root id、回合序号(按该会话的用户消息数推导,续接会话自动接着数)、以及session_messages的高水位。PreToolUse/PostToolUse记录每个工具的真实起止时间、参数与结果——此时不发送任何数据。opencode 内部子会话(ses_…)触发的工具 hook 只记录计时、绝不单独发射(它们的 span 属于父回合的子树)。MessageComplete暂存最终回答文本。SessionEnd(每回合结束触发)做 finalize:从 SQLite 切出本回合的 assistant 消息、按消息顺序编号、按消息 id jointoken_usage取 usage 与成本、把task委派展开成子树,然后整批以 OTLP/HTTP JSON 发往<host>/api/public/otel/v1/traces(Basic 认证,10 秒超时)。trace header 随每个 span 携带。
采集器是 fail-open 的:任何意外错误只写 ~/.mavis/hooks/litefuse_hook.log 并返回 no-op JSON,绝不阻塞或拖慢 Mavis。状态文件用 flock 防并发。
已知局限
- generation 的 input 是增量而非完整请求体(
agent_input_is_delta)——Mavis hook 不暴露 provider 请求。根治此项(以及让 hook payload 携带 usage)需要 MiniMax 上游支持。 - 子 agent 目前无法嵌套。 opencode 的 subagent 会话没有
task工具,子 agent 无法再委派孙 agent——采集器的递归子树支持已就绪,等上游放开即可生效。 - 回合进行中不可见——见 Trace 何时出现?
排障
Litefuse 里看不到 trace。 tail ~/.mavis/hooks/litefuse_hook.log。日志为空说明 hook 没触发——用 ~/.mavis/bin/mavis hook list --human | grep litefuse 确认有六行。出现 send … failed: 说明 key 或网络有问题:检查 ~/.mavis/.env。
trace 带 agent_degraded: true。 采集器读不到 Mavis 的 SQLite(路径变了、schema 变了)。trace 结构由 hook 数据兜底;usage/成本/thinking 缺失。看日志里的 db_… 错误行并反馈 issue。
trace 的 root 是 WARNING。 该回合被中止或没有产出最终文本回答。WARNING 的状态消息会说明原因;这不是采集错误。
时间线视图里编号看起来交错。 按名字而不是开始时间排序:generation 的开始时间由相邻步骤推导,#N 跟随权威的消息顺序。
部分调用成本为 0。 Mavis 提供的 cost_details 会原样转发;没有时由 Litefuse 按模型名计算——确认 Litefuse 项目的 Settings → Models 里有对应模型(如 MiniMax-M2.7)的价格条目。
手动测试采集器(使用 development 环境,不污染生产):
set -a && source ~/.mavis/.env && set +a
export LITEFUSE_TRACING_ENVIRONMENT=development MAVIS_LITEFUSE_DEBUG=true
H=~/.mavis/hooks/litefuse_hook.py
echo '{"input":{"agentName":"coder","sessionId":"manual-test","prompt":"ping"}}' | python3 $H UserPromptSubmit
echo '{"input":{"agentName":"coder","sessionId":"manual-test","content":"pong","retryCount":0}}' | python3 $H MessageComplete
echo '{"input":{"agentName":"coder","sessionId":"manual-test","reason":"finished"}}' | python3 $H SessionEnd
tail ~/.mavis/hooks/litefuse_hook.log
# 预期:"SessionEnd manual-test reason=finished emitted=2"资源
- Litefuse Cloud
- 采集脚本源码:
litefuse_hook.py