斯坦福大学推出无失真大模型水印技术
1.引言
大语言模型在文本生成领域展现出强大的能力,但随之而来的挑战是如何可靠地识别和追踪由模型生成的内容。现有的logits水印方法(以红绿词表机制为代表)往往通过直接修改模型输出的概率分布来嵌入水印,这不可避免地会带来输出失真,甚至影响生成质量。针对这一问题,斯坦福大学计算机系推出的论文Robust Distortion-free Watermarks for Language Models提出了一种大模型无失真水印方法。该方法的核心思想是不改变语言模型的原始分布,而是用共享的随机密钥序列替代普通采样中的随机数,实现无损的水印嵌入。相比 logits水印可能造成的可检测失真,无失真水印既保持了模型输出的自然性和真实性,又能通过对齐检验实现高鲁棒性的水印检测。
2 论文亮点
该论文提出了一种新颖的无失真水印方法,相较于传统的logits水印方法具有显著优势,实现了高质量文本生成与高鲁棒性水印检测的兼顾。其主要亮点如下:
- 无失真性
- 传统logits水印通过调整模型输出概率分布嵌入水印,往往会引入失真,影响生成质量。
- 该论文方法完全保留语言模型原始分布,仅用共享密钥序列替代采样随机数,从而保证输出自然性与真实性。
- 鲁棒性强
- 该论文提出的对齐检验能够在文本经过替换、插入、删除甚至翻译后仍然有效检测水印。
- 相比只依赖逐token检查的检测方法,鲁棒性大大增强。
- 通用性强
- 该论文方法不依赖具体模型架构,可直接应用于 OPT、LLaMA、Alpaca 等多种大语言模型。
- 只需在采样阶段替换随机数即可,无需修改模型参数。
3 论文方法解析
该论文核心思想是利用随机密钥序列驱动大模型输出的采样过程,从而在不改变语言模型原始分布的前提下,在输出中嵌入隐含的统计特征。
3.1 问题设定
设词表为 V = { 1 , 2 , … , N } V = \{1,2,\dots,N\} V={1,2,…,N},大语言模型在给定前缀 x x x时输出下一个token的条件分布为 p ( ⋅ ∣ x ) p(\cdot | x) p(⋅∣x);水印密钥序列记为 ξ = ( ξ 1 , ξ 2 , … , ξ m ) \xi = (\xi_1, \xi_2, \dots, \xi_m) ξ=(ξ1,ξ2,…,ξm),其中每个 ξ i \xi_i ξi独立同分布,来自某个分布 ν \nu ν(如均匀分布)。该论文的目标是在生成阶段利用 ξ \xi ξ产生带水印的输出序列 Y Y Y,并在验证阶段无需依赖Prompt,仅通过相同的 ξ \xi ξ即可完成水印检测。
3.2 无失真采样的数学定义
该论文给出无失真解码器的定义为:
定义 1:若解码器 Γ : Ξ × Δ ( V ) → V \Gamma : \Xi \times \Delta(V) \to V Γ:Ξ×Δ(V)→V 满足
P ( Γ ( ξ , μ ) = y ) = μ ( y ) , ∀ μ ∈ Δ ( V ) , y ∈ V \mathbb{P}(\Gamma(\xi, \mu) = y) = \mu(y), \quad \forall \mu \in \Delta(V), \; y \in V P(Γ(ξ,μ)=y)=μ(y),∀μ∈Δ(V),y∈V则称 Γ \Gamma Γ对分布 ν \nu ν是无失真的。
无失真解码器的直观理解: 已知 Δ ( V ) \Delta(V) Δ(V)为词表 V V V上的概率分布集合, Ξ \Xi Ξ为随机数的集合, ξ ∈ Ξ \xi \in \Xi ξ∈Ξ为水印密钥序列中的一个随机数,解码器 Γ ( ξ , μ ) \Gamma(\xi, \mu) Γ(ξ,μ)接收随机数 ξ \xi ξ与分布 μ \mu μ并输出一个token。如果对于任意 y ∈ V y \in V y∈V,都有 P ( Γ ( ξ , μ ) = y ) = μ ( y ) \mathbb{P}(\Gamma(\xi,\mu)=y)=\mu(y) P(Γ(ξ,μ)=y)=μ(y),则说明即使用水印密钥 ξ \xi ξ替代普通随机数来驱动采样,输出结果的概率分布依然保持不变,因此水印不会扭曲模型的分布,用户也无法察觉异常。
大模型普通采样与无失真采样异同:二者在生成结果的分布上完全相同,都会严格遵循语言模型给出的条件概率分布,因此用户无法分辨差别;不同之处在于普通采样使用系统生成的随机数 u u u,而无失真采样使用共享的水印密钥序列 ξ \xi ξ作为随机性来源,从而在不改变输出分布的前提下,使生成文本与 ξ \xi ξ存在隐含的统计相关性,便于后续检测。
3.3 生成过程(Generate)
论文中的生成方法可以拆解为如下所示:
- 初始条件:给定 prompt 的前缀 x x x,模型输出条件分布 μ 1 = p ( ⋅ ∣ x ) \mu_1 = p(\cdot | x) μ1=p(⋅∣x)。
- 第一步采样:取 ξ 1 \xi_1 ξ1,解码得到 Y 1 = Γ ( ξ 1 , μ 1 ) . Y_1 = \Gamma(\xi_1, \mu_1) . Y1=Γ(ξ1,μ1).
- 迭代更新:将 Y 1 Y_1 Y1拼接到上下文,计算 μ 2 = p ( ⋅ ∣ x , Y 1 ) \mu_2 = p(\cdot | x, Y_1) μ2=p(⋅∣x,Y1)。 利用 ξ 2 \xi_2 ξ2,得到 Y 2 = Γ ( ξ 2 , μ 2 ) . Y_2 = \Gamma(\xi_2, \mu_2) . Y2=Γ(ξ2,μ2).
- 继续采样:重复此过程,直到生成 m m m个 token,得到完整序列 Y = ( Y 1 , … , Y m ) Y=(Y_1,\dots,Y_m) Y=(Y1,…,Ym)。
3.4 水印验证过程
水印验证任务是判断文本 Y Y Y是否依赖于密钥 ξ \xi ξ。该论文提出了对齐检验方法,定义一个代价函数 d ( y , ξ ) d(y,\xi) d(y,ξ),衡量文本片段 Y Y Y与密钥子序列 ξ \xi ξ的匹配程度,其中检测统计量为: φ ( Y , ξ ) = min i , j d ( Y i : i + k , ξ j : j + k ) , \varphi(Y, \xi) = \min_{i,j} d(Y_{i:i+k}, \xi_{j:j+k}), φ(Y,ξ)=i,jmind(Yi:i+k,ξj:j+k),即在所有长度为 k k k的子串对齐中,取最小代价,通过置换检验计算 p p p值:若 p ≤ α p \leq \alpha p≤α,则认为该文本包含水印。
4 示例介绍
4.1 大模型输出概率分布
为了更直观地理解该论文方法的核心原理,可以通过一个简单的数学示例来展示该方法如何缓解大语言模型的过度拒绝问题。假设用户向大语言模型提问:请给我推荐一些经典的电影。大模型在接收到该提问后,会在内部推理过程中基于训练语料对可能的候选电影进行概率建模。为了简化例子,假设它的候选词表只有三个电影名称:
V = { Godfather , Matrix , Inception } V = \{\text{Godfather}, \text{Matrix}, \text{Inception}\} V={Godfather,Matrix,Inception} 在这个上下文中,大模型预测下一个token(即电影名)的条件概率分布如下:
p ( Godfather ) = 0.45 , p ( Matrix ) = 0.35 , p ( Inception ) = 0.20 p(\text{Godfather}) = 0.45, \quad p(\text{Matrix}) = 0.35, \quad p(\text{Inception}) = 0.20 p(Godfather)=0.45,p(Matrix)=0.35,p(Inception)=0.20 由此可见,大模型推荐Godfather(教父)
的可能性最大,其次是 Matrix(黑客帝国)
,而 Inception(盗梦空间)
的概率最低。
4.2 大模型基于随机采样输出(不含水印)
在大模型预测下一个token的解码过程中,既可以采用贪心解码策略,也可以采用随机采样策略。前者在每一步都选择概率最大的token,因而结果确定性强,适合机器翻译、摘要生成等需要稳定性和一致性的任务,但其缺点是输出单一、缺乏多样性。相比之下,随机采样策略在每一步按照概率分布抽取候选,更加接近人类语言的自然表达,能够生成多样化的内容。由于问答和对话任务更强调灵活性与自然性,因此在这一类场景下,大模型通常采用随机采样策略,具体过程可以分为以下三步:
- 首先,在区间 [ 0 , 1 ] [0,1] [0,1] 内生成一个均匀随机数 u ∼ Uniform [ 0 , 1 ] u \sim \text{Uniform}[0,1] u∼Uniform[0,1]。
- 其次,将整个区间 [ 0 , 1 ] [0,1] [0,1] 按照模型给出的概率分布切分成与词表大小相同数量的子区间,并且每个子区间对应一个 token。
- 最后,根据随机数 u u u 落入的区间,确定输出的 token,从而保证每个 token 被选中的概率严格符合模型的概率分布。
(1)基于Prompt生成第1个token
- 大模型输入Prompt获得的初始输出分布为:
p ( Godfather ) = 0.45 , p ( Matrix ) = 0.35 , p ( Inception ) = 0.20 p(\text{Godfather}) = 0.45, \quad\quad p(\text{Matrix}) = 0.35, \quad\quad p(\text{Inception}) = 0.20 p(Godfather)=0.45,p(Matrix)=0.35,p(Inception)=0.20- 整个区间 [ 0 , 1 ] [0,1] [0,1]被划分为3个子区间且每个区间对应一个token:
[ 0.00 , 0.45 ) → Godfather , [ 0.45 , 0.80 ) → Matrix , [ 0.80 , 1.00 ) → Inception [0.00, 0.45) \;\;\rightarrow\; \text{Godfather}, \quad\quad [0.45, 0.80) \;\;\rightarrow\; \text{Matrix},\quad\quad[0.80, 1.00) \;\;\rightarrow\; \text{Inception} [0.00,0.45)→Godfather,[0.45,0.80)→Matrix,[0.80,1.00)→Inception- 随机数 u 1 = 0.30 ∈ [ 0 , 0.45 ) u_1 = 0.30 \in [0,0.45) u1=0.30∈[0,0.45),进而可知第1次的采样结果为
Godfather
。
(2)继续生成第2个token
- 由于已经选过
Godfather
,大模型会降低这个token的概率,新的条件分布为:
p ( Godfather ∣ Godfather ) = 0.15 , p ( Matrix ∣ Godfather ) = 0.45 , p ( Inception ∣ Godfather ) = 0.30 p(\text{Godfather} \mid \text{Godfather}) = 0.15, \quad p(\text{Matrix} \mid \text{Godfather}) = 0.45, \quad p(\text{Inception} \mid \text{Godfather}) = 0.30 p(Godfather∣Godfather)=0.15,p(Matrix∣Godfather)=0.45,p(Inception∣Godfather)=0.30- 整个区间 [ 0 , 1 ] [0,1] [0,1]被划分为3个子区间且每个区间对应一个token:
[ 0.00 , 0.15 ) → Godfather , [ 0.15 , 0.70 ) → Matrix , [ 0.70 , 1.00 ) → Inception [0.00, 0.15) \;\;\rightarrow\; \text{Godfather}, \quad\quad [0.15, 0.70) \;\;\rightarrow\; \text{Matrix},\quad\quad[0.70, 1.00) \;\;\rightarrow\; \text{Inception} [0.00,0.15)→Godfather,[0.15,0.70)→Matrix,[0.70,1.00)→Inception- 随机数 u 2 = 0.62 ∈ [ 0.15 , 0.70 ) u_2 = 0.62 \in [0.15,0.70) u2=0.62∈[0.15,0.70),进而可知第2次的采样结果为
Matrix
。
(3)继续生成第3个token
- 由于大模型已经生成过
Godfather
,Matrix
,进一步降低它们的联合概率,新的条件分布为: p ( Godfather ∣ Godfather, Matrix ) = 0.05 , p ( Matrix ∣ Godfather, Matrix ) = 0.05 , p ( Inception ∣ Godfather, Matrix ) = 0.90 p(\text{Godfather} \mid \text{Godfather, Matrix}) = 0.05, \text{ } p(\text{Matrix} \mid \text{Godfather, Matrix}) = 0.05, \text{ } p(\text{Inception} \mid \text{Godfather, Matrix}) = 0.90 p(Godfather∣Godfather, Matrix)=0.05, p(Matrix∣Godfather, Matrix)=0.05, p(Inception∣Godfather, Matrix)=0.90- 整个区间 [ 0 , 1 ] [0,1] [0,1]被划分为3子个区间且每个区间对应一个token: [ 0.00 , 0.05 ) → Godfather , [ 0.05 , 0.10 ) → Matrix , [ 0.70 , 1.00 ) → Inception [0.00, 0.05) \;\;\rightarrow\; \text{Godfather}, \quad\quad [0.05, 0.10) \;\;\rightarrow\; \text{Matrix},\quad\quad[0.70, 1.00) \;\;\rightarrow\; \text{Inception} [0.00,0.05)→Godfather,[0.05,0.10)→Matrix,[0.70,1.00)→Inception
- 随机数 u 3 = 0.80 ∈ [ 0.10 , 0.90 ) u_3 = 0.80 \in [0.10,0.90) u3=0.80∈[0.10,0.90),进而可知第3次的采样结果为
Inception
。
此时用户与大模型的对话结果为:
User:请给我推荐一些经典的电影。
LLM:Godfather(教父),Matrix(黑客帝国),Inception(盗梦空间)
4.3 大模型基于水印密钥序列输出(含水印)
在采用该论文提出的水印嵌入方法时,核心思想是不再使用普通随机数 u u u,而是使用由双方共享密钥生成的水印密钥序列 ξ = ( ξ 1 , ξ 2 , ξ 3 , … ) \xi = (\xi_1, \xi_2, \xi_3, \dots) ξ=(ξ1,ξ2,ξ3,…) 来驱动采样。这样既保证了输出分布与原模型一致(无失真),又在输出序列中嵌入了隐蔽的统计特征,便于检测。在该示例中,令水印密钥序列为 ξ = ( ξ 1 , ξ 2 , ξ 3 ) = ( 0.18 , 0.74 , 0.31 ) , \xi = (\xi_1, \xi_2, \xi_3) = (0.18, 0.74, 0.31), ξ=(ξ1,ξ2,ξ3)=(0.18,0.74,0.31), 则大模型的水印嵌入过程如下:
(1)基于Prompt生成第1个token(带水印)
- 大模型输入Prompt获得的初始输出分布为:
p ( Godfather ) = 0.45 , p ( Matrix ) = 0.35 , p ( Inception ) = 0.20 p(\text{Godfather}) = 0.45, \quad p(\text{Matrix}) = 0.35, \quad p(\text{Inception}) = 0.20 p(Godfather)=0.45,p(Matrix)=0.35,p(Inception)=0.20- 整个区间 [ 0 , 1 ] [0,1] [0,1] 被划分为:
[ 0.00 , 0.45 ) → Godfather , [ 0.45 , 0.80 ) → Matrix , [ 0.80 , 1.00 ) → Inception [0.00, 0.45) \;\;\rightarrow\; \text{Godfather}, \quad [0.45, 0.80) \;\;\rightarrow\; \text{Matrix}, \quad [0.80, 1.00) \;\;\rightarrow\; \text{Inception} [0.00,0.45)→Godfather,[0.45,0.80)→Matrix,[0.80,1.00)→Inception- 水印随机数设定为 ξ 1 = 0.18 ∈ [ 0 , 0.45 ) \xi_1 = 0.18 \in [0,0.45) ξ1=0.18∈[0,0.45),因此输出为
Godfather
。
(2)继续生成第2个token(带水印)
- 由于已经选过
Godfather
,模型会降低它的概率,新的条件分布为:
p ( Godfather ∣ Godfather ) = 0.15 , p ( Matrix ∣ Godfather ) = 0.45 , p ( Inception ∣ Godfather ) = 0.40 p(\text{Godfather} \mid \text{Godfather}) = 0.15, \quad p(\text{Matrix} \mid \text{Godfather}) = 0.45, \quad p(\text{Inception} \mid \text{Godfather}) = 0.40 p(Godfather∣Godfather)=0.15,p(Matrix∣Godfather)=0.45,p(Inception∣Godfather)=0.40- 区间被划分为:
[ 0.00 , 0.15 ) → Godfather , [ 0.15 , 0.60 ) → Matrix , [ 0.60 , 1.00 ) → Inception [0.00, 0.15) \;\;\rightarrow\; \text{Godfather}, \quad [0.15, 0.60) \;\;\rightarrow\; \text{Matrix}, \quad [0.60, 1.00) \;\;\rightarrow\; \text{Inception} [0.00,0.15)→Godfather,[0.15,0.60)→Matrix,[0.60,1.00)→Inception- 水印随机数 ξ 2 = 0.74 ∈ [ 0.60 , 1.00 ) \xi_2 = 0.74 \in [0.60,1.00) ξ2=0.74∈[0.60,1.00),因此输出为
Inception
。
(3)继续生成第3个token(带水印)
- 由于大模型已经生成过
Godfather
和Inception
,进一步降低它们的概率,新的条件分布为:
p ( Godfather ∣ Godfather, Inception ) = 0.10 , p ( Matrix ∣ Godfather, Inception ) = 0.50 , p ( Inception ∣ Godfather, Inception ) = 0.40 p(\text{Godfather} \mid \text{Godfather, Inception}) = 0.10, \text{ } p(\text{Matrix} \mid \text{Godfather, Inception}) = 0.50, \text{ } p(\text{Inception} \mid \text{Godfather, Inception}) = 0.40 p(Godfather∣Godfather, Inception)=0.10, p(Matrix∣Godfather, Inception)=0.50, p(Inception∣Godfather, Inception)=0.40- 区间被划分为:
[ 0.00 , 0.10 ) → Godfather , [ 0.10 , 0.60 ) → Matrix , [ 0.60 , 1.00 ) → Inception [0.00, 0.10) \;\;\rightarrow\; \text{Godfather}, \quad [0.10, 0.60) \;\;\rightarrow\; \text{Matrix}, \quad [0.60, 1.00) \;\;\rightarrow\; \text{Inception} [0.00,0.10)→Godfather,[0.10,0.60)→Matrix,[0.60,1.00)→Inception- 水印随机数 ξ 3 = 0.31 ∈ [ 0.10 , 0.60 ) \xi_3 = 0.31 \in [0.10,0.60) ξ3=0.31∈[0.10,0.60),因此输出为
Matrix
。
此时用户与嵌入水印的大模型对话结果为:
User:请给我推荐一些经典的电影。
LLM:Godfather(教父),Inception(盗梦空间),Matrix(黑客帝国)
水印验证:
已知水印密钥序列 ξ = ( 0.18 , 0.74 , 0.31 ) \xi = (0.18, 0.74, 0.31) ξ=(0.18,0.74,0.31)和大模型的输出结果,具体的验证过程如下所示:
- 检查第1个分词
Godfather
,已知第1个水印密钥为 ξ 1 = 0.18 ∈ [ 0 , 0.45 ) → Godfather \xi_1 = 0.18 \in [0,0.45) \;\rightarrow\; \text{Godfather} ξ1=0.18∈[0,0.45)→Godfather。 ✅ 匹配- 检查第2个分词
Inception
,已知第2个水印密钥为 ξ 2 = 0.74 ∈ [ 0.60 , 1 ) → Inception \xi_2 = 0.74 \in [0.60,1) \;\rightarrow\; \text{Inception} ξ2=0.74∈[0.60,1)→Inception。 ✅ 匹配- 检查第3个分词
Matrix
,已知第3个水印密钥为 ξ 3 = 0.31 ∈ [ 0.10 , 0.60 ) → Matrix \xi_3 = 0.31 \in [0.10,0.60) \;\rightarrow\; \text{Matrix} ξ3=0.31∈[0.10,0.60)→Matrix。 ✅ 匹配
根据以上水印验证过程可得水印匹配率为
S = number of matched tokens total number of tokens = 3 3 = 1.0 S =\frac{\text{number of matched tokens}}{\text{total number of tokens}}= \frac{3}{3} = 1.0 S=total number of tokensnumber of matched tokens=33=1.0 这远高于随机情况下的基线阈值 0.33 0.33 0.33,因此判定该文本带有水印,即该文本由大模型生成。
5. 代码实现
下面代码实现了基于无失真水印原理 的大模型文本生成与检测流程,即使用共享密钥序列驱动采样替代随机数,从而在不改变模型输出分布的情况下嵌入并验证水印。
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
import numpy as np
import argparse
# ========== 1. 工具函数 ==========
def get_next_token_distribution(model, tokenizer, prompt):
"""给定 prompt,返回下一个 token 的概率分布"""
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
with torch.no_grad():
outputs = model(**inputs)
logits = outputs.logits[:, -1, :] # 最后一个 token 的预测
probs = torch.softmax(logits, dim=-1).squeeze().cpu().numpy()
return probs
def inverse_transform_sampling(probs, r):
"""逆变换采样:CDF 区间查找,防止越界"""
cdf = np.cumsum(probs)
token_id = np.searchsorted(cdf, r)
if token_id >= len(cdf): # 防止越界
token_id = len(cdf) - 1
return token_id
def watermark_sample(model, tokenizer, prompt, xi_seq, max_new_tokens=20):
"""水印采样:使用 xi_seq 替代随机数"""
generated_tokens = []
text = prompt
xi_iter = iter(xi_seq)
for _ in range(max_new_tokens):
probs = get_next_token_distribution(model, tokenizer, text)
xi = next(xi_iter) # 从密钥序列取一个随机数
token_id = inverse_transform_sampling(probs, xi)
generated_tokens.append(token_id)
text += tokenizer.decode([token_id])
return tokenizer.decode(generated_tokens), generated_tokens
def normal_sample(model, tokenizer, prompt, max_new_tokens=20):
"""普通采样:使用真正的随机数"""
generated_tokens = []
text = prompt
for _ in range(max_new_tokens):
probs = get_next_token_distribution(model, tokenizer, text)
u = np.random.rand()
token_id = inverse_transform_sampling(probs, u)
generated_tokens.append(token_id)
text += tokenizer.decode([token_id])
return tokenizer.decode(generated_tokens), generated_tokens
def watermark_detect(tokens, xi_seq, tokenizer, prompt, model):
"""检测水印:通过区间检查匹配率"""
match_count = 0
text = prompt
for token_id, xi in zip(tokens, xi_seq):
probs = get_next_token_distribution(model, tokenizer, text)
cdf = np.cumsum(probs)
# 查找 token_id 的区间
if token_id >= len(cdf): # 防止越界
continue
lower = 0.0 if token_id == 0 else cdf[token_id - 1]
upper = cdf[token_id]
# 如果 xi 落在对应区间,则判定为匹配
if lower <= xi < upper:
match_count += 1
text += tokenizer.decode([token_id])
return match_count / len(tokens)
# ========== 2. 主程序 ==========
def main():
parser = argparse.ArgumentParser(description="Distortion-free watermarking demo")
parser.add_argument("--model_path", type=str, default="./models/Phi-3-mini-128k-instruct", help="Path to the model")
parser.add_argument("--prompt", type=str, default="Please recommend some classic movies.", help="Prompt text")
parser.add_argument("--max_new_tokens", type=int, default=20, help="Max new tokens to generate")
args = parser.parse_args()
# 加载模型
tokenizer = AutoTokenizer.from_pretrained(args.model_path)
model = AutoModelForCausalLM.from_pretrained(args.model_path, dtype=torch.float16, device_map="auto")
# 随机生成水印密钥序列
xi_seq = np.random.rand(1000)
# 普通采样
normal_text, normal_tokens = normal_sample(model, tokenizer, args.prompt, max_new_tokens=args.max_new_tokens)
print("\n普通采样输出:", normal_text)
# 水印采样
wm_text, wm_tokens = watermark_sample(model, tokenizer, args.prompt, xi_seq, max_new_tokens=args.max_new_tokens)
print("\n水印采样输出:", wm_text)
# 检测
match_rate = watermark_detect(wm_tokens, xi_seq, tokenizer, args.prompt, model)
threshold = 1 / len(wm_tokens)
print(f"\n水印匹配率: {match_rate:.2f} (阈值={threshold:.2f})")
if match_rate > threshold:
print("✅ 该文本可能是 AI 生成的。")
else:
print("❌ 未检测到显著水印,可能不是AI生成的。")
if __name__ == "__main__":
main()
🔹 运行示例
python distortion_free.py --model_path ./models/Phi-3-mini-128k-instruct --prompt "Please recommend some classic movies."
🔹 输出结果:
普通采样输出: As anubhuti produced.Comedy,Drama,Thriller These classicmovies
水印采样输出: I love movie. Sure, here are some classicmovies to
水印匹配率: 0.95 (阈值=0.05)
✅ 该文本可能是 AI 生成的。
更多推荐
所有评论(0)