watermarks-remover:名字在说"去水印",实际在做交付前体检
项目地址:https://github.com/guillaumemeyer/watermarks-remover
看到 watermarks-remover 这个名字,第一反应是"又一个去水印工具"。但 README 的开篇条件写得很明确:只处理自己拥有或已获授权的内容,定位是隐私与内容卫生。也就是说,这个项目解决的是自有内容在交付前的隐藏痕迹问题——不是把别人作品上的署名擦掉。
先把边界立住:内容卫生不是内容洗白。
架构:skill 只是薄客户端,逻辑在独立服务里
Agent skill(无核心逻辑)
│ HTTP
▼
stdlib Python HTTP 服务(默认 http://127.0.0.1:8765)
skill 只做一件事:在 Agent 工作流里发起 HTTP 请求。真正的处理逻辑跑在本地 Python 服务里,因此宿主环境不需要装 Python。这个分层是清醒的——skill 层属于提示词工程、容易漂移,而后端是可以用测试固定下来的确定性代码。
三层处理,三种确定性
| 层 | 处理对象 | 方式 | 确定性 |
|---|---|---|---|
| A 层 | 不可见 Unicode/特殊空格/bidi 控制符/标签字符 | 字符级规则脚本 | 确定性 |
| B 层 | 统计文本水印(token-sampling 等) | Agent 改写 + rewrite_text.py hook | best-effort |
| 文件层 | C2PA/EXIF/XMP/文档属性 | 剥离/重建 | 确定性(受依赖工具影响) |
文件层覆盖格式很广:PNG/JPEG/WebP/AVIF/HEIC/BMP/GIF/TIFF/SVG/PDF/DOCX/XLSX/PPTX/EPUB/ODT/HTML/MD,以及 MP4/MOV/WAV/MP3/FLAC 等音视频容器。
值得注意的工程边界
hooks 挂在 PostToolUse,匹配 Write|Edit|MultiEdit|NotebookEdit。check 模式默认只报告不修改(退出码 2),clean 模式原地剥离。hook 这部分是确定性的"半壁江山"。
但 hook 改不了 chat 消息——Stop hook 是只读的。Agent 对话里已经出现的文本,没法靠这套机制回滚,只能由 skill 工作流在生成前尽量干预。这不是遗漏,是架构性边界;它决定了 B 层的清理本质上是 best-effort。
上手
# 安装 skill(Cursor 用 --target cursor)
python3 install_skill.py --skill remove-ai-marks --target claude-code
# 启动后端服务
make serve
可选系统依赖:
c2patool:处理 C2PA 签名exiftool:EXIF/XMP 深层操作qpdf:PDF 结构性重建——只有依赖 qpdf 做重建,PDF 才算真正剥离,单纯覆盖页面不算
适用边界
| 适合 | 不适合 |
|---|---|
| 维护 AI 生成文档、多格式交付链路的团队 | 处理第三方作品水印、署名、来源信息 |
| 关心自有文件隐私、格式稳定性与留痕的团队 | 想伪装"纯人工创作"的人 |
| 正在给 AI 产物建检查门槛的治理流程 | 想绕过平台或版权规则的人 |
它提出的问题比它的功能更有价值
C2PA、EXIF、XMP 不该被当"垃圾"一刀切。它们可能是排障线索、隐私变量,也可能是权利链路的一部分。正确流程是:先识别,再判定,最后留痕。交付前三问:
- 隐藏标记真的影响交付,还是只是"看着不干净"?
- 我是否有权处理这份内容?
- 处理前状态、处理依据、处理后版本,是否都留了记录?
仓库在 2026-08-27 时约 18.7k Star / 2.2k Fork,最新 v0.6.0,MIT 协议。它把"先检查、再决定"做进了工具设计(默认检查、保存备份、安全写入),这比任何"一键清理"都值得借鉴。
留一个后续问题:既然 chat 消息层的文本无法被确定性清洗,那 Agent 交互留下的"行为性痕迹",是不是从一开始就该走生成时治理的路径,而不是等事后清理?