v1.7.0 —— 视觉输入 + BYPASS 名副其实
springai-agentdemo v1.7.0
在 v1.6.0 基础上的功能版(minor)。核心交付物仍是终端编码智能体 springai-code-tui。
本版两件事:让模型真能看见图片,以及把 --dangerously-skip-permissions 改成名副其实。
⚠️ 先读这一条:权限行为变了v1.6.0 里
--dangerously-skip-permissions并不真的跳过全部检查——内置底线与 ask 规则仍会弹窗。
那份发版说明白纸黑字写着「deny 规则与内置底线在该模式下仍然生效,这是刻意的」。本版起它真的跳过全部检查:内置底线与 ask 规则都不再执行,只剩你自己写的 deny 规则。
这是安全性的净下降。 换来的是那个开关不再说谎、以及半无人值守场景可用。详见下方「权限」一节。
下载物仍是两个自包含运行包(解压即用,无需构建):
springai-code-tui-1.7.0-dist.tar.gz(macOS / Linux 首选)springai-code-tui-1.7.0-dist.zip(Windows 首选)
两者内容一致:启动脚本(bin/)+ 主 jar + 全部运行期依赖(lib/)+ LICENSE/NOTICE/README。运行时界面版本标识为 v1.7.0。
完整功能全景与
⚠️ 安全声明见 v1.3.0 发布说明 与 v1.0.0 发布说明;本文只列出相对 v1.6.0 的变化。
✨ 新功能:视觉输入(图片真正进模型)
支持视觉的模型现在能真正看见图片,而不只是收到一行文件路径。
两个入口
你自己贴的图——输入框里直接写路径,或把文件从访达/桌面拖进终端:
> 这个报错界面是怎么回事 docs/bug.png
⏎ 已附带 1 张图片(bug.png) · Ctrl+X 取消
拖拽白送支持——终端不传文件、只把路径当粘贴插到光标处,所以走的是同一条路。带空格的中文文件名也认(macOS 截图默认就带空格):反斜杠转义、单引号、双引号三种形态都吃。
工具产的图——Read 一张 png、MCP 截图工具的返回,都会作为图片交给模型。
图片从不进会话记忆
落盘的永远是一段结构化文本引用块,图片字节只在出站请求组装的最后一刻才挂上,且只挂当轮的。
因此聊多久都不会累积上下文:聊 100 轮、看过 50 张图,请求里也只有当轮那几张。想让模型重看历史图片,让它 Read 引用里的 path 即可——那条路本来就通,不需要额外命令。
硬上限(这是设计的核心)
每请求 用户贴图 ≤3 张 + 工具产图 ≤1 张(最新那张) + 6k 视觉 token
每回合 累计兑现 ≤12 张·次
单回合视觉花费的绝对上限因此约 21.6k token——跑飞的截图循环也就到这儿。
用户图先过预算且保底不淘汰:反过来会让「照这张稿子改」的稿子被随后 Read 的图挤掉。
同一回合内每次工具迭代都会重传当轮的图。 这是无状态请求的固有代价——正文与全部历史每次也在重传。只能靠缩图压单价 + 回合上限封顶,无法消除。这不是「已优化」,是承认它治不了。
会误附,所以给了 Ctrl+X
裸路径自动识别不可能只在你想附图时命中。把 docs/bug.png 复制到 tmp/ 这种句子里,路径独立成词、文件存在、魔数是图片——三条判据全中,必然被误认为附件。
这个代价是设计时明确接受的:由「附件行当场可见 + Ctrl+X 一键撤销」承担,不靠规则去消灭它。加语法标记能消灭误附,但也就同时消灭了「拖进来即可」。
(用 Ctrl+X 而非 Ctrl+D:本项目实现了 readline 键位,Ctrl+D 在 readline 里是「删除光标处字符」/ 空行时 EOF;Ctrl+X 在 readline 里是前缀键、单按无动作,不撞肌肉记忆。)
⚠️ 某些全局热键会抢占按键,撞上时症状是「按了没反应」或弹出别的窗口,不是本功能坏了。已知一例:Chrome 的 Gemini 扩展把Ctrl+G注册成 OS 级别的全局热键,键在任何终端应用看到它之前就被拿走——取消键原定Ctrl+G,正是因此改成了Ctrl+X。
图片处理
| 格式 | 处理 |
|---|---|
| PNG / JPEG / GIF | 长边 >1568px 等比缩后再发(磁盘原件不动) |
| BMP / TIFF | 解码后转码成 PNG |
| WebP | 原样发、不缩(JDK 解不了但各家 API 收);>4MB 不兑现 |
| HEIC / AVIF | 发不出去,只留引用(HEIC 是 iPhone 照片默认格式,会真遇到) |
超过 5000 万像素的图直接不兑现——判定只读文件头拿尺寸,绝不先解码再判断(200MB 的 PNG 解成 BufferedImage 是 GB 级)。
项目内指原文件,项目外复制一份
- 项目内的图 → 引用块里就是原路径,不复制。你回头更新了这个文件,模型再看到的就是新版
- 项目外的图(拖进来的桌面截图)→ 复制一份进
.codetui/artifacts/。这不是偏好:引用块解析器会拒掉一切指向项目外的路径(防外部内容注入path: ../../../etc/id_rsa),不复制的话那张图会无声地永远兑现不了
.codetui/artifacts/latest.png 是最近一张图的稳定软链,可直接 open 查看(内容寻址的文件名是 64 位 sha,人不好复制)。
逃生口与开销可见
export CODETUI_VISION=off # 全局关闭整条视觉链路/context 面板单列视觉占用(本回合几张、约多少 token、每回合上限多少),不混进文本估算——图片从不进会话存储,两个数字合并只会让「为什么请求比面板大」变得无法解释。
⚠️ 验证范围(别当成「五家都能用」)
「工具结果 → 合成一条 user 消息(带图)」这个消息序列,只在一个本地兼容中转网关 + gpt-5.6-sol 上做过真机验证:发纯红/绿/蓝三张图,模型三次全答对;对照组(同样序列但不挂图)明确回答「只看到图片文件引用,无法看到实际图像内容」。
api.openai.com 原生端点,以及 Anthropic / 千问 / 智谱三家,完全没有验证过(本机没有对应 API key)。它们第一次真用时仍可能返回 400。
另外:千问与智谱的内置模型清单里没有一个视觉模型(qwen3.7-max / glm-5.2 这些都不是),要在这两家用视觉得自己用 DASHSCOPE_MODELS / ZHIPU_MODELS 配一个 -vl / glm-4v 系的 id。
不支持的场景
- 终端里显示不了图片:模型截的图你只会看到一行路径,要看得自己开文件(系统提示已禁止模型写
这类 markdown——那在终端里只是一串原始文本) Ctrl+V粘贴剪贴板图片不支持(三个平台各一套外部命令,无头环境测不了)Bash生成图片文件不会自动产生引用:输出里出现一个路径不代表模型想看它;想看就Read它.codetui/artifacts/超过 500MB 时按最旧优先删除,只在启动时扫一次- 视频不投递:
supportsVideoInput是保留字段,恒为 false
⚠️ 权限:--dangerously-skip-permissions 现在名副其实
变了什么
| v1.6.0 | v1.7.0 | |
|---|---|---|
| deny 规则 | 硬拒 | 硬拒(不变) |
| 内置底线 | 强制询问(弹窗) | 不再执行 |
| ask 规则 | 询问(弹窗) | 不再执行 |
关键性质:这一档下永远不会停下来等人。 deny 命中时直接拒绝、把结果告诉模型(不弹窗),回合继续。
为什么改
v1.6.0 的设计原则是「护栏不是牢笼,人确认了就该能做」——那句话本身没错,但它默认了总有人在场。
有人在 TUI 里跑半无人值守的自动化开发(丢一个大任务给 agent 然后离开),发现即使开了这个参数仍会卡死等人应答:权限握手阻塞在无超时的队列上,只有人来点或回合被取消才能解开。
而更根本的问题是:一个名字里带 dangerously 的开关,用户已经承担了全部心理成本去打开它,结果发现它是假的。这比多弹几次窗伤害大得多。
为什么 deny 规则保留
三者来源不同:内置底线是本项目的意见(写死在代码里),deny / ask 规则是你写在 permissions.json 里的明令。BYPASS 的字面意思是「跳过审批」,不等于「无视我立过的禁令」。而且 <项目根>/.codetui/permissions.json 是仓库带来的——clone 别人的仓库时那些 deny 规则本来就是保护你的。
ask 一并跳过,是因为它的语义「每次都问我」与 BYPASS 的「不问」直接矛盾,此时按 BYPASS 走是唯一自洽的解释。
放弃拦截,但不放弃告知
BYPASS 放行一个踩到内置底线的操作时会留痕——即时打一行进对话区,回合末再汇总一次:
⚠ 本回合 BYPASS 放行了 3 个通常需要确认的操作:
· 写入 .git/ 内部:/p/.git/hooks/pre-commit
· 读取凭据:~/.aws/credentials
· rm -rf 变量目标:$BUILD_DIR
不阻塞、不询问,只让你回来时看得见不在期间发生了什么。
留痕只进对话区,/clear 或滚出屏幕就没了,不落盘。
想在无人值守下保留某些禁令怎么办
自己写 deny 规则进 permissions.json——那是这一档下唯一还拦得住的东西:
{ "deny": ["Write(~/.ssh/**)", "Bash(rm -rf ~:*)"] }另外三档一个字没变
默认 / 自动接受编辑 / 计划模式 三档的判定完全不变,内置底线在那里照常强制询问。
🔧 其它变化
- 联网测试改为显式开关门控(
CODETUI_LIVE_TESTS=1)。此前它们按「对应 API key 存在」放行,而 key 存在是正常安装的常态——于是每次mvn test都真的联网、花钱、看运气,其中一条还带 60 秒墙钟硬上限,约一半概率把构建打红、挡住打包。
⚠️ 安全声明(本版必须重读的部分)
上一版说过「权限层不是沙箱」,本版要再加一句:
--dangerously-skip-permissions下没有最后一道询问了。 提示注入把模型说服去rm -rf ~时,只有你自己写的 deny 规则能拦。留痕能让你事后发现,阻止不了。
视觉输入也带来一条新的外泄通道:
你贴的图会原样发给第三方模型 API。 截图里若有 API key、token、私密聊天记录、身份证件,它们就此离开这台机器——而且是以图像形式,本地任何基于文本的检查都看不出来。而路径是自动识别的,「只是提了一嘴文件名」的句子同样会附上。
引用块这条注入路径建了防线(path 须过项目根包含校验、只扫用户消息与工具结果、字段不齐或重复整块丢弃、文件名清洗控制字符),但它防的是「引用块被伪造」,不是「模型被网页里的话说服去做别的事」。
详见 code-tui README 的安全声明 与 SECURITY.md。
⬆️ 升级须知
- 无破坏性变更:旧会话、旧
permissions.json、旧mcp.json、旧技能目录照常可用。 - 但
--dangerously-skip-permissions的行为变了(见上)。如果你此前依赖「BYPASS 下危险操作仍会问我」,那个保障没有了——把需要的禁令写成 deny 规则。 - 视觉是加法:不用视觉模型、不贴图,行为与 v1.6.0 完全一致。
CODETUI_VISION=off可全局关闭。
🔐 校验(SHA-256)
0302e5066dd11357f295b27385c3d24ca3d5743681a7402b246e07e4b9423f17 springai-code-tui-1.7.0-dist.tar.gz
a6b7b62b124380857c2b6f7044d40ee994946bea2628903eb70bfd67143f9899 springai-code-tui-1.7.0-dist.zip
shasum -a 256 -c <<'EOF'
0302e5066dd11357f295b27385c3d24ca3d5743681a7402b246e07e4b9423f17 springai-code-tui-1.7.0-dist.tar.gz
a6b7b62b124380857c2b6f7044d40ee994946bea2628903eb70bfd67143f9899 springai-code-tui-1.7.0-dist.zip
EOF📄 许可
Apache License 2.0。发布包内随附 LICENSE 与 NOTICE(含所分发第三方库:Spring AI / Spring Boot / spring-ai-community 为 Apache 2.0,TamboUI 为 MIT)。本版未引入任何新的第三方依赖——视觉链路全部用 JDK 自带的 ImageIO 与既有的 Apache Tika。
环境:JDK 17+,macOS / Linux / Windows。