
手头刚好有一个很契合当下合规趋势的小需求给图片和视频加上欧盟 AI 内容标签。最近在梳理生成式 AI 落地流程时发现内容溯源和 AI 披露已经从“可选项”变成了很多业务的“必答题”。今天就把一套可运行的标签添加方案拆开讲清楚包含核心概念、环境准备、完整代码、运行验证和常见坑点。无论你是做内容平台、多媒体工具还是想给自己的 AI 应用补齐合规能力这篇都能直接照着做。1. 背景与核心概念1.1 什么是 EU AI content labelEU AI content label 是欧盟《人工智能法案》EU AI Act推进过程中提出的内容标识机制。它的目的很明确当一段文字、图片、音频或视频由 AI 生成或深度伪造时需要向用户提供清晰的标识信息让用户知道“这不是传统意义上由人类直接创作的内容”。这个标签不是某个单一平台的“已加 AI”按钮而是需要嵌入到文件本身的一种元数据体系。也就是说图片或视频文件在导出后即使被下载、转发、二次上传标签信息仍然保留在文件内部便于校验和追溯。从实现角度看官方推荐的技术路径通常包括三类元数据标注将 AI 生成信息写入 EXIF、XMP、IPTC 等标准元数据字段。水印技术在图像或视频画面中叠加可感知或不可感知的水印。加密签名与溯源信息基于 C2PACoalition for Content Provenance and Authenticity等标准记录内容的来源、编辑历史和生成工具。本文的实战部分将以“元数据标注”为主线并给出视频文件的标签添加方式。对于需要更强溯源能力的场景我会补充 C2PA 的工程思路。1.2 为什么现在需要关注内容标签目前生成式 AI 工具已经非常普及。用户可以用 Stable Diffusion、Midjourney、各类视频生成模型在几分钟内生产出非常逼真的内容。随之而来的问题是虚假信息、冒名内容、深度伪造视频开始增多。在这种情况下内容平台和工具开发者需要考虑几个问题用户看到的内容是否由 AI 生成如果内容被用于新闻、医疗、法律等严肃场景能否追溯生成链条平台如何向监管方证明自己已经执行了披露义务当监管要求逐步收紧后AI 内容标签会成为基础能力。如果等到政策强制落地再补整个内容管线的改造压力会大很多。1.3 常见应用场景这类标签能力的典型应用场景包括场景说明AI 绘图工具用户导出图片时自动写入 AI 生成标签视频生成平台成品视频的元数据中标注 AI 生成信息社交媒体审核平台检测并展示 AI 内容标识新闻与版权系统区分人类创作与 AI 合成内容便于版权认定企业素材库在 DAM数字资产管理系统中标记 AI 资产无论你是在做客户端工具还是后端处理服务下面这套方案都能提供参考。2. 环境准备与版本说明2.1 运行环境本文示例使用 Python 3.10 编写命令行工具。操作环境为 Ubuntu 22.04 / macOS但 Windows 下同样适用只需要注意命令行工具的安装方式。需要说明的是不同操作系统中exiftool 和 ffmpeg 的安装命令不同因此建议先在本地终端验证这两个命令是否可执行。2.2 依赖工具清单工具用途安装方式Python 3.10编写主逻辑官网下载或包管理器安装ExifTool读写图片/视频元数据macOS:brew install exiftoolUbuntu:sudo apt install libimage-exiftool-perlWindows: 下载安装包FFmpeg处理视频元数据macOS:brew install ffmpegUbuntu:sudo apt install ffmpegWindows: 下载安装包PillowPython 端图像快速验证pip install Pillow如果你只是测试图片标签可以不安装 FFmpeg视频部分才需要。2.3 示例项目结构为了保持逻辑清晰我们把项目组织成下面这样eu-ai-label-tool/ ├── requirements.txt ├── README.md └── ai_label_tool.pyai_label_tool.py作为单文件命令工具包含图片和视频两套处理函数。这样做的好处是便于复制、学习和二次改造如果后续需要集成到 Django/FastAPI 服务中再按模块拆分即可。3. 核心实现原理3.1 为什么选择 XMP / 通用元数据EXIF、IPTC、XMP 是多媒体文件中常用的三类元数据标准。其中EXIF 主要用于存储相机参数比如光圈、快门、ISO。IPTC 常用于新闻图片的标题、作者、版权说明。XMP 是 Adobe 提出的可扩展元数据平台灵活度最高被很多内容平台接受。给 AI 内容做标签时首选 XMP 和 IPTC 字段。因为它们不依赖某个厂商私有格式跨平台工具都能读取。为了不编造并不确定的字段名我们采用保守但通用的做法在Description中加入 AI 生成声明。在Label或版权相关字段中写入标签信息。在Comment中写清楚“本内容由 AI 生成”。如果你的业务对标准字段有严格规范建议查阅欧盟 AI 内容标签官方技术说明或 C2PA 规范按正式字段配置而不是仅依赖自定义字段。3.2 ExifTool 的工作方式ExifTool 是目前最常用的元数据读写工具。它通过命令行参数指定要写入的标签和值执行后可以覆盖写原文件或输出到新文件。一个最简单的图片标签写入命令如下exiftool -DescriptionAI generated content according to EU AI Act -LabelAI-generated input.jpg默认情况下ExifTool 会生成一个_original备份文件避免原文件被破坏。在自动化脚本中我们可以通过-overwrite_original压掉备份也可以保留备份以便出问题时回滚。3.3 FFmpeg 在视频侧的做法视频文件和图片不同它通常封装为 MP4/MOV 等容器格式。FFmpeg 可以不改编码、不做转码仅通过-metadata参数修改容器级元数据。ffmpeg -i input.mp4 -metadata titleAI generated content -metadata commentEU AI Act notice -c copy output.mp4-c copy表示复制视频流和音频流不做重新编码速度非常快也不会造成画质损失。3.4 如何校验标签是否写入成功写入完成后必须回头读取元数据来验证结果。图片用exiftool output.jpg视频用ffprobe -show_format output.mp4这两条命令会输出文件内部的元数据信息。如果我们在输出中看到对应的字段值说明标签已经成功嵌入。4. 完整实战案例下面我们实现一个可运行的命令行工具。它提供两个子命令add-image给图片添加 EU AI 内容标签。add-video给视频添加 EU AI 内容标签。verify检查文件是否包含 AI 内容标签。4.1 创建项目文件首先创建项目目录mkdir eu-ai-label-tool cd eu-ai-label-tool然后创建ai_label_tool.py把下面的代码复制进去。#!/usr/bin/env python3 # -*- coding: utf-8 -*- EU AI content label tool. 给图片和视频写入欧盟 AI 内容标签的示例工具。 import argparse import subprocess import sys from pathlib import Path def run_command(cmd: list) - None: 执行外部命令失败时抛异常。 print([CMD], .join(cmd)) result subprocess.run(cmd, capture_outputTrue, textTrue) if result.returncode ! 0: print([STDERR], result.stderr, filesys.stderr) raise RuntimeError(fCommand failed: { .join(cmd)}) if result.stdout: print([STDOUT], result.stdout) def add_image_label(input_path: Path, output_path: Path, model_name: str unknown) - None: 为图片文件写入 EU AI 内容标签。 说明 这里通过 ExifTool 写入通用的 XMP/IPTC 字段。 如果业务有严格合规要求需要按官方规范补充签名与溯源字段。 if not input_path.exists(): raise FileNotFoundError(fInput image not found: {input_path}) output_path.parent.mkdir(parentsTrue, exist_okTrue) # 基于 XMP 的通用 AI 生成声明 description AI generated content (EU AI Act label) label AI-generated creator_tool model_name cmd [ exiftool, f-Description{description}, f-Label{label}, f-CreatorTool{creator_tool}, -overwrite_original, -o, str(output_path), str(input_path), ] run_command(cmd) def add_video_label(input_path: Path, output_path: Path, model_name: str unknown) - None: 为视频文件写入 EU AI 内容标签。 说明 使用 FFmpeg 的 metadata 机制不做重新编码。 注意不同容器和播放器对 comment 字段的支持程度不同。 if not input_path.exists(): raise FileNotFoundError(fInput video not found: {input_path}) output_path.parent.mkdir(parentsTrue, exist_okTrue) cmd [ ffmpeg, -y, -i, str(input_path), -metadata, ftitleAI generated content (EU AI Act label), -metadata, fcommentAI-generated by {model_name}, -c, copy, str(output_path), ] run_command(cmd) def verify_label(file_path: Path) - None: 检查文件是否包含 AI 内容标签。 if not file_path.exists(): raise FileNotFoundError(fFile not found: {file_path}) suffix file_path.suffix.lower() if suffix in {.jpg, .jpeg, .png, .tiff, .webp, .heic}: cmd [exiftool, str(file_path)] run_command(cmd) elif suffix in {.mp4, .mov, .mkv, .avi}: cmd [ffprobe, -show_format, str(file_path)] result subprocess.run(cmd, capture_outputTrue, textTrue) if result.returncode ! 0: print([STDERR], result.stderr, filesys.stderr) raise RuntimeError(ffprobe command failed) print([STDOUT]) print(result.stdout) else: print(fUnsupported file type: {suffix}) def main() - None: parser argparse.ArgumentParser(descriptionEU AI content label tool) subparsers parser.add_subparsers(destcommand, requiredTrue) parser_img subparsers.add_parser(add-image, helpAdd EU AI label to an image) parser_img.add_argument(input, typePath, helpInput image path) parser_img.add_argument(output, typePath, helpOutput image path) parser_img.add_argument(--model, defaultunknown, helpAI model name) parser_vid subparsers.add_parser(add-video, helpAdd EU AI label to a video) parser_vid.add_argument(input, typePath, helpInput video path) parser_vid.add_argument(output, typePath, helpOutput video path) parser_vid.add_argument(--model, defaultunknown, helpAI model name) parser_ver subparsers.add_parser(verify, helpVerify AI label in a file) parser_ver.add_argument(file, typePath, helpImage or video path) args parser.parse_args() if args.command add-image: add_image_label(args.input, args.output, args.model) print(Image label added successfully.) elif args.command add-video: add_video_label(args.input, args.output, args.model) print(Video label added successfully.) elif args.command verify: verify_label(args.file) else: parser.print_help() if __name__ __main__: main()这段代码的核心逻辑如下使用argparse解析子命令。调用系统安装的exiftool或ffmpeg完成标签写入。run_command函数统一打印命令和输出方便调试。输出路径的父目录会自动创建避免目录不存在导致的失败。图片处理时使用-o输出到新文件不直接覆盖原文件更加安全。4.2 安装第三方依赖本项目实际只用到标准库第三方依赖并非必须。如果你需要额外用 Pillow 做图像快速判断可以创建requirements.txtPillow10.3.0安装命令pip install -r requirements.txt4.3 准备测试素材为了验证效果我们可以先用 FFmpeg 生成一张测试图片和一个测试视频。生成一张纯色测试图片ffmpeg -f lavfi -i colorcblue:s640x360:d1 -frames:v 1 test_input.jpg -y生成一个 3 秒测试视频ffmpeg -f lavfi -i testsrcduration3:size640x360:rate30 test_input.mp4 -y这样我们就有了一份不涉及版权问题的测试素材。4.4 运行标签添加给图片添加标签python3 ai_label_tool.py add-image test_input.jpg labeled_image.jpg --model MyAITool-v1给视频添加标签python3 ai_label_tool.py add-video test_input.mp4 labeled_video.mp4 --model VideoGenModel-v2执行后当前目录会生成labeled_image.jpg和labeled_video.mp4。4.5 验证结果查看图片元数据python3 ai_label_tool.py verify labeled_image.jpg在输出中你会看到类似下面的关键字段Description : AI generated content (EU AI Act label) Label : AI-generated Creator Tool : MyAITool-v1查看视频元数据python3 ai_label_tool.py verify labeled_video.mp4输出中的format区块会包含title和comment字段证明标签写入成功。4.6 扩展批量处理目录实际项目中可能需要批量处理一个目录下的所有图片。可以在命令行工具基础上增加一个循环或者用 Shell 脚本调用。下面是一个简单的 Python 批量示例from pathlib import Path from ai_label_tool import add_image_label input_dir Path(./images) output_dir Path(./labeled_images) for image_path in input_dir.glob(*.jpg): output_path output_dir / image_path.name add_image_label(image_path, output_path, model_namebatch-model)这里的重点是每个文件都单独调用exiftool优点是单文件失败不会影响整个批次缺点是速度较慢适合中小规模文件集。如果文件量非常大可以考虑并发方案。5. 常见问题与排查思路5.1 常见问题对照表问题现象常见原因解决思路exiftool: command not foundExifTool 未安装根据系统安装 ExifToolffmpeg: command not foundFFmpeg 未安装根据系统安装 FFmpeg图片输出后元数据为空输出路径或参数顺序错误检查-o参数是否正确视频title写入后某些播放器不显示播放器不支持容器元数据在应用层读取并展示标签PNG 元数据不支持某些 XMP 字段文件格式本身限制改用 JPG/TIFF 或使用 C2PA 方案原文件被覆盖没有使用-o且没有备份改用-o输出新文件保留原始素材批量处理时权限报错输出目录权限不足使用可写目录并在脚本中创建父目录5.2 图片写入后看不到标签如果执行成功但看不到标签先用exiftool input.jpg看原文件本身是否支持 XMP 写入。部分极简 PNG 文件或 WebP 文件在旧版本 ExifTool 下可能支持不完整。解决办法是升级 ExifTool 到最新版本或者转换格式后再写入。exiftool -ver如果版本过旧建议升级到 12.60 以上。具体版本以官方发布为准。5.3 视频标签写入后校验不到FFmpeg 的-metadata会写入容器级元数据但不同容器的支持程度不同MP4支持title、comment等字段。MOV通常支持良好。MKV字段名在不同工具下可能解析不一致。AVI支持有限。如果我们添加的是自定义字段玩家可能忽略它。因此视频方案更推荐“容器元数据 画面内水印/片头声明”的组合。5.4 如何避免原文件被破坏在 ExifTool 中最安全的方式是像本文示例一样使用-o参数输出到新文件。这样原文件始终保留出问题时可以重新处理。如果出于存储考虑想直接覆盖可以在确认测试无误后再去掉-o并使用-overwrite_original。6. 最佳实践与工程建议6.1 在内容生成管线中默认加标签AI 内容标签不应是事后手动补的步骤而应该是内容生成流水线的一部分。也就是说当用户点击“生成图片”或“生成视频”时系统在导出阶段就应该自动写入标签。这样可以避免两个问题人工忘记添加标签。用户下载后再补标签链路不可控。在工程实现上可以在模型服务返回结果后紧接着执行一个标签写入模块。这个模块可以是独立的微服务也可以是一个异步任务。6.2 组合使用元数据与水印元数据最大的弱点是文件一旦被截图、屏幕录制或转码标签可能丢失。因此对于需要强披露的场景建议组合使用元数据写入。图像/视频边界水印。C2PA 签名溯源。C2PA 是目前比较受关注的内容溯源标准。它可以记录内容从哪里来、经过哪些编辑、由什么工具生成。如果你的项目需要做到严格可信的溯源建议调研 C2PA 官方 SDK并将 C2PA 信息嵌入到文件中。注意C2PA 的 API 和工具链仍在快速演进接入时要以官方仓库和文档为准。6.3 日志与追溯每次给内容打标签时建议记录以下日志信息文件原始哈希值。输出文件哈希值。使用的模型名称与版本。标签写入时间。处理人/调用方标识。这样可以形成一条可审计的内容处理链路。即使内容后续出现问题也能快速定位是哪一批数据、哪一个环节出了问题。下面是一个简单的日志字段示例{ source_file: /data/raw/abc.jpg, output_file: /data/labeled/abc.jpg, source_md5: d41d8cd98f00b204e9800998ecf8427e, output_md5: fbbc9b6e5f9c7f6f0f9c0b7d6f9f4f2e, model_name: stable-diffusion-xl, label_time: 2025-01-15T10:30:00Z }6.4 安全边界与权限控制给内容加标签通常不会涉及复杂权限但要注意以下几点不要允许用户任意删除或伪造“人类创作”标签尤其在生产环境。标签写入操作必须经过服务端校验不能只靠前端传参。如果标签用于审计或合规需要保护原始文件不被篡改必要时保存哈希链。简单说加标签是普通能力但标签的“权威性”才是合规价值所在。6.5 性能优化ExifTool 和 FFmpeg 都是外部进程每次调用都有一定的进程启动开销。在高并发场景下建议使用进程池复用外部进程。对图片批量处理时一次 ExifTool 调用可以传入多个文件。对视频处理避免重复编码始终使用-c copy。将耗时操作放入消息队列避免阻塞 API 请求。如果处理的文件量一天超过几十万建议使用 C/Go/Rust 实现底层处理模块或直接调用对应库而不是频繁拉起外部命令。6.6 兼容性测试清单上线前至少对以下组合做一轮兼容性验证JPG 与 PNG。MP4 与 MOV。常用播放器是否读取元数据。图片压缩后标签是否保留。视频二次转码后标签是否保留。平台审核系统是否能识别标签。这些测试决定了你的标签方案是“给自己看”还是“给生态看”。7. 总结与后续学习方向这篇教程从一个实际需求出发实现了给图片和视频添加 EU AI 内容标签的最小工具。你不仅了解了 AI 内容标签产生的背景还掌握了基于 ExifTool 和 FFmpeg 的元数据写入方法以及如何通过verify子命令验证标签是否生效。如果你想继续深入可以从下面几个方向入手完整阅读 EU AI Act 中关于生成式 AI 透明度义务的条款理解合规边界。学习 C2PA 技术规范了解如何生成带签名和证书链的内容溯源信息。调研主流内容平台对 AI 内容标签的识别规则设计更通用的标签策略。把这个命令行工具改造成 FastAPI 服务集成到现有的内容生产系统中。动手在本地图片和视频上试一遍再用exiftool和ffprobe查看输出文件你会更直观地理解“内容标签”在文件层面到底做了什么。这对后续做 AI 产品、内容平台或合规系统都很有帮助。