liuyuyang fcf8669978 security: 凭据链第0级自举——sessionToken 动态换取 fmode API token 1 week ago
..
scripts fcf8669978 security: 凭据链第0级自举——sessionToken 动态换取 fmode API token 1 week ago
README.md fcf8669978 security: 凭据链第0级自举——sessionToken 动态换取 fmode API token 1 week ago
SKILL.md fcf8669978 security: 凭据链第0级自举——sessionToken 动态换取 fmode API token 1 week ago

README.md

Fmode Vision Skill — 维护文档

项目结构

.claude/skills/fmode-vision/
├── SKILL.md                       # 技能入口,Claude 读取后知道何时及如何使用本技能
├── README.md                      # 本文件:开发者维护文档
├── .skillfish.json                # 技能元信息(版本、来源仓库)
└── scripts/
    ├── vision-client.mjs           # 核心:通用视觉 API 客户端
    └── prompts/
        └── room-measurement.mjs    # 领域模块:毛坯房量尺 5-pass 提示词

核心逻辑

1. Token 解析链 (resolveApiToken)

第0级自举 + 四级回落,短路返回:

【第0级 自举】FMODE_SESSION_TOKEN 环境变量 或 ~/.fmode/config.json 的 sessionToken
  → 调 fmode API 动态换取 API token(登录 FMODE Studio 即可,无需手工配置;
    token 仅内存持有,不落盘不进日志;换取失败明确报错后回落)
FMODE_API_TOKEN 环境变量
  → ~/.fmode/config.json 的 fmodeApiToken / newapiToken 字段
    → ~/.claude/settings.json(含 settings.local.json / 项目级 .claude/)
      的 env.ANTHROPIC_AUTH_TOKEN(sk- 开头、非 sk-ant-、base 指向 fmode)
      (Claude Code 会话内注入的进程环境变量也在此级命中)
      → <project>/.fmode/config.json 的 fmodeApiToken / newapiToken 字段
        → 抛出异常(提示用户配置)

设计原因:环境变量适合 CI/CD;用户级配置适合个人开发机;Claude Code 的 sk- token 零配置自动命中;项目级配置适合团队共享(加入 .gitignore)。

1.5 模型选择策略 (detectHostVisionModel / resolveVisionModel / analyze)

宿主多模态优先,回落 Fmode API:

resolveVisionModel():
  显式传入 model 参数        → { provider:'fmode', model }(强制 Fmode API)
  ③ FMODE_VISION_MODEL 环境变量 → { provider:'host', model }(用户显式指定)
  ① Claude Code settings 的 model / env.ANTHROPIC_MODEL
     命中多模态名单且在会话内  → { provider:'host', model }
  ② ~/.codex/config.toml 的 model 命中名单 → { provider:'host', model }
  未命中                    → { provider:'fmode', model:'glm-5.3-flash' }

analyze() 是总入口:provider==='host' 且在 Claude Code / Codex 会话内时返回 { provider:'host', model, instruction, imagePath },AI 用自己的 Read 工具读图完成分析 (不调 GLM、不消耗 Fmode token);否则走 Fmode API。探测到宿主多模态但独立脚本运行时 回落 Fmode API。多模态能力名单见 vision-client.mjsHOST_VISION_MODEL_PATTERNS

2. API 调用流程 (callVisionAPI)

输入: imagePath | imageBase64 | imageUrl | videoUrl
  |
  ├─ 解析 token
  ├─ 构造 messages 数组
  │   ├─ system prompt
  │   └─ user content:
  │       ├─ text part(用户提示词)
  │       └─ image_url / video_url part(视觉内容)
  ├─ POST https://api.fmode.cn/v1/chat/completions
  │   body: { model, messages, temperature, max_tokens }
  ├─ 响应的 content 字符串 → extractJSON()
  └─ 返回 { raw, parsed, error, usage }

3. 多轮分析模式 (callMultiPass)

核心理念:每轮独立调用 API,各自聚焦一个分析维度,最后一轮合并。这比单轮全量分析精度更高。

输入: imagePath + passes[{name, systemPrompt, userPrompt, maxTokens}]
  |
  for each pass:
  ├─ 检查 cacheDir/pass<N>.json 是否存在
  │   ├─ 存在 → 跳过,读取缓存
  │   └─ 不存在 → callVisionAPI() → 写入缓存
  ├─ sleep(delayMs) 避免限流
  |
  └─ 返回 results[]

缓存设计:

  • 每轮结果独立缓存,支持断点续跑
  • 缓存 key = pass 序号,与提示词内容无关
  • 如需强制重新分析,删除对应缓存文件即可
  • 提示词迭代时,建议手动清理缓存

4. JSON 提取 (extractJSON)

LLM 响应可能被 markdown 代码块包裹(json ...),也可能前后有解释文字。用正则 /\{[\s\S]*\}/ 提取第一个 JSON 对象。

5. 毛坯房 5-pass 专用流程 (room-measurement.mjs)

继承自 analyze-photos-v4.mjs,5 轮各有独立职责:

Pass 名称 分析焦点 tokens
1 spatial 空间结构:透视类型、墙面多边形、阴阳角、天地面 2000
2 ceiling 吊顶特征:cornice/trayStep/beam/bulkhead 1000
3 openings 门窗洞口:双层框架(outer+inner polygon) 2500
4 obstacles 障碍物:插座/开关/电箱/踢脚线/风口等 1500
5 merge 文本合并:场景描述、房间类型、测量计划、质量评估 2000

质量验证:

  • 踢脚线 height > 10% → 警告(应为 2-5%)
  • 吊顶特征 polygon 顶点 > 4 → 警告

配置说明

API Token

方式一:环境变量

export FMODE_API_TOKEN="sk-****(占位符,换成你自己的 token)"

方式二:用户级配置 ~/.fmode/config.json

{
  "fmodeApiToken": "sk-****(占位符,换成你自己的 token)"
}

方式三:项目级配置 <project>/.fmode/config.json(需加入 .gitignore)

{
  "fmodeApiToken": "sk-****(占位符,换成你自己的 token)"
}

可用模型

模型 ID 用途 备注
glm-5.3-flash 视觉理解(默认回落) Fmode API 默认视觉模型,替代旧 doubao
宿主配置模型 视觉理解(优先) Claude Code / Codex 配置的多模态模型,零额外计费
glm-4.6v 视觉理解 调用时传 model 参数覆盖

模型列表可能更新,以 Fmode API 返回为准。

扩展指南

添加新的提示词模板

scripts/prompts/ 下新建 .mjs 文件:

import { callVisionAPI, callMultiPass } from '../vision-client.mjs';

export const MY_SYSTEM_PROMPT = `...`;
export const MY_USER_PROMPT = `...`;

export async function analyzeSomething(imagePath) {
  const result = await callVisionAPI({
    imagePath,
    systemPrompt: MY_SYSTEM_PROMPT,
    userPrompt: MY_USER_PROMPT,
    maxTokens: 1000,
  });
  return result.parsed;
}

添加新模型

vision-client.mjsDEFAULT_CONFIG 中调整默认模型,或调用时传入 model 参数:

const result = await callVisionAPI({
  imagePath: '/path/to/img.jpg',
  systemPrompt: '...',
  userPrompt: '...',
  model: 'glm-4.6v',  // 覆盖默认模型(强制走 Fmode API)
});

多轮分析自定义

import { callMultiPass } from './vision-client.mjs';

const results = await callMultiPass({
  imagePath: '/path/to/img.jpg',
  cacheDir: '/tmp/my-analysis/img-001/',
  passes: [
    { name: 'overview', systemPrompt: '...', userPrompt: '描述整体场景', maxTokens: 500 },
    { name: 'details', systemPrompt: '...', userPrompt: '标注细节元素', maxTokens: 1500 },
    { name: 'verify',  systemPrompt: '...', userPrompt: '验证前两轮一致性', maxTokens: 1000 },
  ],
});

依赖

仅使用 Node.js 内置模块:fs, path, os。无需 npm install

全局 fetch 需要 Node.js 18+(已内置)。