Skip to content

v1.7.0 —— 视觉输入 + BYPASS 名副其实

Choose a tag to compare

@javaside javaside released this 03 Aug 01:38
· 151 commits to main since this release

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。发布包内随附 LICENSENOTICE(含所分发第三方库:Spring AI / Spring Boot / spring-ai-community 为 Apache 2.0,TamboUI 为 MIT)。本版未引入任何新的第三方依赖——视觉链路全部用 JDK 自带的 ImageIO 与既有的 Apache Tika。

环境:JDK 17+,macOS / Linux / Windows。