name: fmode-vision description: "通过 Fmode API 调用视觉模型对图片、视频进行分析。适用场景:(1) 图片内容识别与结构化提取, (2) 多轮聚焦分析获取高精度结果, (3) 视频帧分析, (4) 视觉素材批量处理"
本技能封装视觉识别能力:优先用宿主 Agent 自带的多模态模型读图(Claude Code / Codex 配置的模型支持视觉时直接用,不产生任何额外调用),否则回落 Fmode API (api.fmode.cn) 的视觉模型 glm-5.3-flash,支持单轮和多轮分析。用户可能要求你分析图片、处理视频帧、或对视觉素材进行结构化信息提取。
技能初始化时先探测运行环境,决定视觉识别走哪条路:
detectHostVisionModel() 探测顺序:
① Claude Code 配置:./.claude/settings.json 或 ~/.claude/settings.json
(含 settings.local.json)的 model 字段 / env.ANTHROPIC_MODEL
② Codex 配置:~/.codex/config.toml 的 model 字段
③ 环境变量 FMODE_VISION_MODEL(用户显式指定的视觉模型,直接采纳)
claude-4* / claude-opus / claude-sonnet-4 / gpt-4o / gpt-5* / gemini-2* / gemini-3* / o3 等)且运行在 Claude Code / Codex 会话内
→ 直接用宿主模型读图:用你自己的 Read 工具读取图片文件,结合提示词完成分析。
不调 GLM、不发网络请求、不消耗 Fmode token。 输出时注明「已用宿主多模态模型」。glm-5.3-flash(替代旧的 doubao-seed-2-0-pro-260215)。代码入口:
import { resolveVisionModel, analyze } from './scripts/vision-client.mjs';
resolveVisionModel(); // => { provider: 'host'|'fmode', model, source }
// host: 宿主多模态模型;fmode: glm-5.3-flash(默认回落)
// analyze() 是总入口:自动按上面的策略分发
const result = await analyze({ imagePath, systemPrompt, userPrompt });
if (result.provider === 'host') {
// 用 Read 工具读 result.imagePath,按 systemPrompt+userPrompt 分析,输出注明「已用宿主多模态模型」
} else {
// result.raw / result.parsed —— Fmode API 返回
}
仅 Fmode API 路径需要 token(宿主多模态路径零 token)。按以下优先级查找(第0级自举 → 回落):
FMODE_SESSION_TOKEN 或 ~/.fmode/config.json 的 sessionToken → 调 fmode API 动态换取 API token(登录 FMODE Studio 即可,无需手工配置;token 仅内存持有,不落盘不进日志)环境变量 FMODE_API_TOKEN
echo $FMODE_API_TOKEN
用户级配置 ~/.fmode/config.json → fmodeApiToken / newapiToken 字段
cat ~/.fmode/config.json
Claude Code 配置 ~/.claude/settings.json(含 settings.local.json / 项目级 .claude/)的 env.ANTHROPIC_AUTH_TOKEN —— 即 Claude Code 的 sk- token(sk- 开头、非 sk-ant-、base 指向 fmode 时自动采纳,无需手动配置)
项目级配置 <project>/.fmode/config.json → fmodeApiToken / newapiToken
如果四级都未找到,提示用户提供 token。仓库与文档中不出现任何真实密钥。
https://api.fmode.cnPOST /v1/chat/completionsAuthorization: Bearer <token>glm-5.3-flashmodel 参数覆盖(如 glm-4.6v,以账号可用列表为准){
"model": "glm-5.3-flash",
"messages": [
{ "role": "system", "content": "系统提示词" },
{
"role": "user",
"content": [
{ "type": "text", "text": "用户提示词" },
{ "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,<base64>" } }
]
}
],
"temperature": 0.12,
"max_tokens": 2000
}
| 类型 | 传递方式 | 适用场景 |
|---|---|---|
| 本地图片 | data:image/<fmt>;base64,<data> |
jpg/png/webp |
| 远程图片 | 直接 URL | 需模型支持公网访问 |
| 视频 | type: "video_url" |
模型自动抽帧 |
需要分析视觉内容?
├── 宿主多模态命中(Claude Code / Codex 视觉模型)→ Read 工具直接读图
├── 简单描述/单维度提取 → 单轮分析 (analyze / callVisionAPI)
├── 多维度精确标注 → 多轮聚焦分析 (callMultiPass)
│ ├── 每轮独立调用,专注一个维度
│ ├── 中间结果写入缓存目录
│ └── 最后一轮合并所有结果
└── 批量处理 → 遍历 + 单轮/多轮
使用 scripts/vision-client.mjs 的 analyze()(推荐,自动选路)或 callVisionAPI()(强制走 Fmode API):
import { analyze, resolveApiToken } from './scripts/vision-client.mjs';
const result = await analyze({
imagePath: '/path/to/image.jpg',
systemPrompt: '你是一位影像分析专家...',
userPrompt: '请描述这张图片中的关键元素...',
maxTokens: 1000,
});
// result = { provider, model, raw, parsed, error, usage, instruction? }
// provider==='host' 时按 instruction 用 Read 工具读图完成分析
使用 callMultiPass() 封装,适用于需要从不同维度精确分析的场景:
import { callMultiPass } from './scripts/vision-client.mjs';
const passes = [
{ name: 'structure', systemPrompt: '...', userPrompt: '...', maxTokens: 2000 },
{ name: 'details', systemPrompt: '...', userPrompt: '...', maxTokens: 1000 },
];
const results = await callMultiPass({
imagePath: '/path/to/image.jpg',
passes,
cacheDir: '/tmp/analysis/image-id/',
});
始终要求模型输出严格 JSON,在 system prompt 中给出完整 schema:
## 输出格式(严格JSON,无markdown代码块)
{
"field1": "value",
"field2": [{ "sub": "value" }]
}
多轮分析中每轮只关注一个维度,明确告知模型忽略其他内容:
## 规则
1. 只标注 X 类元素,忽略 Y、Z 等其他所有元素
2. 每个元素标注精确的 boundingBox
模型可能包裹 markdown 代码块,使用 extractJSON() 提取:
import { extractJSON } from './scripts/vision-client.mjs';
const parsed = extractJSON(rawResponse);
多轮分析支持中间结果缓存:
cacheDir/pass<N>.jsonscripts/prompts/room-measurement.mjs 提供 5-pass 量尺分析提示词:
Pass 5: 合并 + 测量计划
import { processPhoto } from './scripts/prompts/room-measurement.mjs';
const merged = await processPhoto('/path/to/photo.jpg', 'photo-001', 'photo-001.jpg');