给 AI 编程代理集群加"眼睛":一次真实的多模态接入与健壮性加固实录
今天在自己的 opencode 多代理配置仓库里做了几轮迭代:给代理集群接入多模态视觉能力、根据真实会话日志修复编排器的几个坑、再补上配置校验工具。都是纯工程问题,把过程和踩坑点分享出来。
一、给代理集群接入视觉能力
opencode 支持自定义 subagent,每个代理可以绑定不同模型、不同权限、不同提示词。接入视觉模型本身不难,难的是三个细节:
1. modalities 声明是硬门槛
只在 provider 里注册模型是不够的。OpenCode 默认把模型当纯文本模型,调用读图工具直接报 Cannot read image (this model does not support image input)。必须在模型配置里显式声明能力:
"deepseek-v4-flash-vision-exp": {
"modalities": {
"input": ["text", "image"],
"output": ["text"]
},
"options": {
"temperature": 0,
"thinking": { "type": "disabled" }
}
}
视觉模型属于 flash 档次的变体,成本档位一致,所以沿用 flash 的策略:温度 0、关闭思考。推理开关是 provider/model 层级的配置,不是每个代理的 frontmatter 各自为政。
2. steps 按任务形态设计
视觉任务本质是"看一眼、说结论"的单发任务,不需要长链条工具调用。把 vision 代理的 steps 从 40 降到 25,既是成本约束,也防止模型在无意义的工具循环里空转。
3. 权限隔离:视觉代理是读者,不是写者
给 vision 代理加了严格的权限块:task: deny(不能再嵌套派发),bash 只放行 git status/diff/log/show、rg、Get-ChildItem/Get-Content 这类只读命令。同时在编排器侧把它从"writer agent"名单里移除——读写分离,视觉代理永远不该出现在并发写文件的冲突面上。
提示词里还专门加了"What You DON'T Handle"一节:深度推理、多文件实现、外部调研、视觉只是附带的任务,一律拒绝或升级给重型代理。给代理写清楚边界,比写清楚职责更重要——职责模糊最多慢一点,边界缺失会直接出错。
二、编排器健壮性:从三份真实会话日志里挖出来的
优化不是拍脑袋,而是回放了三份真实会话日志后的结论。
1. 空结果兜底(最高优先级)
子代理返回空结果且工作区没有任何变更时,正确动作是:换更小的单文件任务重试一次;再失败就停下,告诉用户子代理基础设施出了问题。明确禁止两件事:反复重试同一个任务;编排器自己下场执行重实现。后者尤其隐蔽——编排器亲自干活会烧掉它本应用于路由的上下文,而且往往干得不如专职代理。
2. 上下文卫生三原则
- 编排器不亲自探索。glob、grep、数行数这类活全部委派给廉价的探索代理,编排器的上下文只留给路由决策。
- 不加载领域技能给自己壮胆。加载了 skill 不等于授权自己动手,多文件修改照样路由给专职代理。
- 转发前先压缩。子代理的完整报告永远不要原样塞进下一个子代理的提示词——提取可执行的增量,做成紧凑的交接摘要。完整报告会让上下文膨胀、token 翻倍。
3. 已验证事实的传播
一个容易忽略的浪费:planner 辛苦验证了某个外部库的 API 语义,结果 reviewer、deep-worker、复审代理各自又重新验证一遍,同一事实被验证了三次。修复方式是把已验证的事实摘要写进后续每一次委派的提示词里。配套的规则还有:复审前先把实现者的总结与原始发现逐条对照,用廉价模型就能抓住"只修了一半"的问题,省掉一轮昂贵的复审。
4. 路由表补全
"评估代码库规模"→探索代理、"commit/push"→轻量编排器走专门的命令,这些之前都靠编排器即兴判断。即兴判断就是不一致的来源,能进路由表的场景就进表。
三、配置即代码:给 JSONC 写个靠谱的校验器
配置文件改多了总会翻车:尾随逗号、引号不配对。直接 JSON.parse 又不行,因为 JSONC 有注释和尾随逗号。写了一个字符串感知的剥离器,核心是状态机:
function stripJsonc(source) {
const out = [];
let inString = false;
let inBlockComment = false;
let escape = false;
// ...逐字符扫描
}
关键点:
- 在字符串内部时,
//和/*都是普通字符,不能当注释剥离(比如 URL 里的https://); - 反斜杠转义要单独处理,
"\""里的引号不能提前结束字符串; - 尾随逗号在剥离注释后再容忍性处理,最后走标准
JSON.parse。
脚本挂进配置修改 skill 的检查清单:每次改完配置、提交前先跑一遍。同时补上了 .gitignore——这种仓库最容易因为漏掉 node_modules 之类的目录而在某次手滑时污染历史。
顺带把 research skill 从 18 行扩到 78 行:强制一手来源优先、每条结论带引用、区分"已验证 / 转述 / 推断"三档可信度。给代理的 skill 文档和给人看的文档一样,写得越具体,执行偏差越小。
四、多代理协作下的 git 纪律
多个代理(或多个会话)共享同一个工作目录时,git 安全规则要比单人开发严格得多:
- 禁止
git add -A、git reset --hard、git checkout .、git clean -fd——这些命令会吞掉其他会话或工具留下的未提交工作; - 禁止
git add <目录>——目录级暂存会把无关改动一起带进去,必须写明确切文件路径; - 提交前必看
git status、git diff --staged、最近 10 条日志,只暂存本次会话修改的文件; - 禁止 force-push、禁止
--no-verify、未经明确要求不 amend。
这些规则写进全局 AGENTS.md,对所有代理生效。多代理环境里,默认值不是"方便",而是"不误伤"。
五、几条可迁移的经验
- 多模态接入先查能力声明(modalities),再看任务形态定 steps,最后用权限块锁死读写边界。
- 代理提示词里"不做什么"和"做什么"同等重要,甚至更重要。
- 编排器的价值在路由,不在执行——任何让它亲自下场的诱惑都应该变成一条委派规则。
- 优化要来自真实会话日志的回放,而不是想象中的用法。
- 配置仓库也要有 CI 思维:校验脚本 + 检查清单,把翻车拦在提交前。

浙公网安备 33010602011771号