
watermarks-remover v0.5.0
تطبيق يضع الخصوصية أولاً ويزيل العلامات المائية للذكاء الاصطناعي من المحتوى الذي تملكه.
_ _ _ ____ ___ ____ ____ _ _ ____ ____ _ _ ____ ____ ____ _ _ ____ _ _ ____ ____
| | | |__| | |___ |__/ |\/| |__| |__/ |_/ [__ __ |__/ |___ |\/| | | | | |___ |__/
|_|_| | | | |___ | \ | | | | | \ | \_ ___] | \ |___ | | |__| \/ |___ | \
watermarks-remover
مهارة وكيل + خدمة Python بالمكتبة القياسية لإزالة علامات المصدر متعددة الموردين للذكاء الاصطناعي من النصوص والملفات — من أجل الخصوصية والنظافة على المحتوى الذي تملكه. المهارة عبارة عن عميل رفيع: تقود الآلية عبر HTTP، لذا لا يحتاج مضيف الوكيل إلى Python.
| الطبقة | الهدف | الكيفية |
|---|---|---|
| A | Unicode غير المرئي، المسافات الغريبة، bidi، محارف الوسوم | سكربتات Python حتمية |
| B | العلامات المائية النصية الإحصائية (أخذ عينات الرموز) | إعادة كتابة الوكيل + خطاف rewrite_text.py الاختياري |
| الملفات | C2PA / EXIF / XMP / خصائص المستند | PNG، JPEG، WebP، AVIF، HEIC، BMP، GIF، TIFF، SVG، PDF، DOCX، XLSX، PPTX، EPUB، ODT، HTML، Markdown، MP4/MOV/M4A/M4V، WAV، MP3، FLAC |
الموردون / الأنظمة البيئية (على مستوى الفئة): Claude، Gemini / SynthID-Text، أسطح المصدر الخاصة بـ OpenAI، علامات open-LLM بنمط Kirchenbauer (القائمة الخضراء) و keyed-Gumbel / EXP (Aaronson).
أحدث إصدار: v0.7.0
مسار المهارة: skills/remove-ai-marks/
مسار الخدمة: service/
(الترحيل: كان سابقًا remove-claude-marks؛ لا يزال الاسم المستعار /remove-claude-marks موثقًا)
التثبيت (مهارة الوكيل)
المهارة لا تشحن أي كود — بل تستدعي الخدمة عبر HTTP. ثبّت المهارة (markdown فقط) وابدأ الخدمة، ثم اضبط WATERMARKS_SERVICE_URL إذا لم تكن http://127.0.0.1:8765.
في Claude Code، أسرع طريق هو سوق الإضافات المضمّن — بدون استنساخ، ويحدّث في مكانه. في كل مكان آخر، مثبّت واحد يغطي كل مضيف مدعوم (Python 3.10+ بالمكتبة القياسية، بدون تبعيات):```bash python3 install_skill.py --skill remove-ai-marks --target claude-code
| المضيف | الهدف | مكان الاستقرار |
| --- | --- | --- |
| Claude Code (شخصي) | `--target claude-code` | `~/.claude/skills/<skill>` (يحترم `CLAUDE_CONFIG_DIR`) |
| Claude Code (مشروع) | `--target claude-project --project-dir PATH` | `PATH/.claude/skills/<skill>` |
| Cowork، claude.ai، الجلسات السحابية، الروتينات | `--target cowork` | `dist/<skill>.zip` للرفع تحت **Customize → Skills** |
| Cursor | `--target cursor` (افتراضي) | `~/.cursor/skills/<skill>` |
المهارات المُضمَّنة: `remove-ai-marks` (كاملة، مدعومة بخدمة) و
`clean-user-facing-text` (نصية فقط، مكتفية ذاتيًا). يطبعها `--list`.
تُحفظ التثبيتات الموجودة ما لم تمرر `--force`؛ يتم تجهيز الاستبدال
أولًا ويُحتفظ بالتثبيت السابق كنسخة احتياطية باسم فريد.
يقوم `--link` بإنشاء روابط رمزية لهذا المستودع بدلًا من النسخ، فتُلتقط التعديلات
مباشرة. على Windows، استخدم `py install_skill.py ...`؛ غلاف `install-skill.sh`
متوفر لأصداف macOS/Linux.
قبل كتابة أي شيء، يتحقق المُثبِّت من المهارة وفق قواعد تغليف
[Agent Skills](https://agentskills.io) التي تفرضها عمليات الرفع إلى claude.ai
وواجهة Skills API: بيانات أمامية مطابقة للمواصفة (`name`، `description`،
`license`، `compatibility`، `metadata`، `allowed-tools`)، و`name` بحروف صغيرة
مفصولة بشرطات لا يتجاوز 64 حرفًا ويطابق الدليل، و`description` غير فارغ
لا يتجاوز 1024 حرفًا. كما يجب أن تتوافق حزمة Cowork مع
حد الرفع البالغ 30 ميغابايت، وهو ما يفرضه المُحزِّم.
### التنظيف التلقائي عبر hook (حتمي)
المهارة تعليمة: النموذج يقرر ما إذا كان سيستدعيها، والنموذج هو
ذاته ما ينتج العلامات. أما الـ **hook** فينفذه الـ harness
عند كل استدعاء أداة مطابق، دون الحاجة إلى تعاون. وهذا ما يجعل الـ hook
النصف الحتمي من سير العمل هذا.
تسجّل الإضافة hook من نوع `PostToolUse` على `Write|Edit|MultiEdit|NotebookEdit`
يشغّل [`service/scripts/hook_written_file.py`](https://github.com/guillaumemeyer/watermarks-remover/blob/main/service/scripts/hook_written_file.py)
على الملف الذي كتبه الوكيل للتو. وضعان، بما يطابق عُرف pre-commit
القائم على الفحص افتراضيًا:
| الوضع | السلوك |
| --- | --- |
| `check` (افتراضي) | يبلّغ عن علامات المصدر، ويترك الملف كما هو. تذهب النتائج إلى النموذج (exit 2)، ليتمكن من عرض تنظيفها. |
| `clean` | يزيل العلامات في مكانها، ثم يخبر النموذج أن الملف على القرص قد تغيّر. |
اضبط الوضع من إعدادات الإضافة (**Hook mode** في `/plugin manage`،
يقرأه الـ hook باسم `CLAUDE_PLUGIN_OPTION_HOOK_MODE`)، أو عبر
`WATERMARKS_HOOK_MODE=clean` في البيئة. أمر الـ hook **لا** يُدرج
`${user_config.hook_mode}` عمدًا: يرفض Claude Code تشغيل
hook يشير إلى خيار لم يفتح المستخدم `/plugin manage` لضبطه — فوجود
`default` معلن لا يكفي — لذا فإن إدراجه يعني أن الـ hook لن يعمل بصمت أبدًا
على تثبيت جديد. تعيد الكشف استخدام `scan_file` / `is_actionable` من `audit_lib`،
فيتفق الـ hook وبوابة pre-commit وتصدير CI SARIF على ما يُعد
قابلًا للتنفيذ؛ ويستدعي التنظيف `clean_file.py`، فلا تتكرر
أي منطق تنظيف. يكتب وضع `clean` إلى ملف مؤقت مجاور ويستبدل فقط عند
وجود فرق حقيقي، فتحتفظ الملفات النظيفة أصلًا بطابعها الزمني ولا تعيد
تفعيل مراقبي الملفات.
بدون الإضافة، اربطه بنفسك في `~/.claude/settings.json` (أو في
`.claude/settings.json` الخاص بالمشروع):```json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit|NotebookEdit",
"hooks": [
{
"type": "command",
"command": "python3",
"args": ["/path/to/watermarks-remover/service/scripts/hook_written_file.py",
"--mode", "check"],
"timeout": 30
}
]
}
]
}
}
على Windows، استبدل python3 بـ py.
ما لا يمكن للـ hook فعله. لا يمكن لأي hook إعادة كتابة رسالة الدردشة الخاصة بالمساعد
قبل أن تقرأها. يتلقى hook Stop في Claude Code قيمة last_assistant_message
للقراءة فقط، ولا يوجد مرشّح ما قبل الإرسال للردود النهائية — وهو نفس القيد
الذي يوثّقه هذا المشروع بالفعل لقواعد Cursor. لذا فإن الضمان الحتمي
يغطي الملفات التي يكتبها الوكيل، بالإضافة إلى
بوابة pre-commit لأي شيء في طريقه إلى git. النص الذي
يوجد فقط في سجل الدردشة لا يزال يعتمد على سير عمل المهارة،
والذي يقوم على تعليمات النموذج وبالتالي فهو بأفضل جهد ممكن.
إضافة Claude Code (marketplace)
المستودع هو أيضًا plugin لـ Claude Code وmarketplace أحادي الإضافة
(.claude-plugin/)، لذا تُثبَّت كلتا المهارتين وتُحدَّثان بأمرين
فقط، دون الحاجة إلى clone أو script:```
/plugin marketplace add guillaumemeyer/watermarks-remover
/plugin install watermarks-remover@watermarks-remover
تُحمَّل المهارات بعد ذلك بنطاق أسماء: `/watermarks-remover:remove-ai-marks` و
`/watermarks-remover:clean-user-facing-text` (يعمل أيضًا `/remove-ai-marks` المجرّد
عندما لا يطالب به شيء آخر). يجلب `/plugin marketplace update
watermarks-remover` الإصدارات اللاحقة. وينطبق الأمر نفسه من واجهة سطر الأوامر باستخدام
`claude plugin marketplace add …` / `claude plugin install …`، ومن نسخة محلية
بتمرير مسار بدلًا من `owner/repo`.
للمشرفين: يشغّل `make plugin-validate` الأمر `claude plugin validate . --strict`
على كلا البيانين؛ ويغطي `tests/test_plugin_manifest.py` الملفات نفسها
دون الحاجة إلى واجهة سطر الأوامر.
### Claude Code```bash
# Personal — available in all your projects
python3 install_skill.py --skill remove-ai-marks --target claude-code
# or: make install-claude-code-skill
# Project — commit .claude/skills/ to share it with the repo
python3 install_skill.py --skill remove-ai-marks --target claude-project \
--project-dir /path/to/project
# or: make install-claude-project-skill PROJECT=/path/to/project
Claude Code يلتقط المهارات الشخصية ومهارات المشروع دون إعادة تشغيل؛ يسرد /skills
ما تم تحميله. استدعِ باستخدام /remove-ai-marks أو اطلب "إزالة العلامات المائية
للذكاء الاصطناعي / C2PA / علامات Claude / نص من فئة SynthID." تثبيت المشروع هو
أيضًا ما تقرأه جلسات السحابة،
حيث تستنسخ المستودع وتحمّل .claude/skills/ الخاص به.
Cowork (و claude.ai، جلسات السحابة، الروتينات)
جلسات Cowork لا تقرأ ~/.claude/skills على جهازك — بل تحمّل
المهارات الممكّنة لحساب claude.ai الخاص بك، والتي تتم مزامنتها عند بدء الجلسة.
لذا ثبّت هناك عن طريق رفع حزمة:```bash
python3 install_skill.py --skill remove-ai-marks --target cowork
writes dist/remove-ai-marks.zip (make package-cowork-skill)
ثم، في تطبيق Claude Desktop، افتح **Customize → Skills → Add** وارفع
ملف zip (إعدادات المهارة نفسها على claude.ai تعمل أيضًا). الحزمة
قابلة لإعادة الإنتاج وتحتوي على دليل واحد بمستوى أعلى `remove-ai-marks/` مع
`SKILL.md` في جذره، وهو التخطيط الذي يتوقعه الرفع.
تعد إمكانية الوصول إلى الخدمة أكثر أهمية هنا مقارنة بالتثبيت المحلي: فالمهارة
عميل HTTP رفيع، لذا يجب أن تكون الجلسة قادرة على الوصول إلى `WATERMARKS_SERVICE_URL`.
جلسات Cowork التي تعمل محليًا على جهازك تصل إلى `make serve` المحلي؛
أما الجلسات السحابية والروتينات فتعمل عن بُعد وتحتاج إلى عنوان URL للخدمة يمكن الوصول إليه من
هناك (مع تعيين `WATERMARKS_SERVER_API_KEY` عليه). إذا أردت مهارة بدون أي
خدمة على الإطلاق، فارفع `clean-user-facing-text` بدلاً منها — فهي نصية فقط
وتتضمن نصوصها البرمجية الخاصة:```bash
python3 install_skill.py --skill clean-user-facing-text --target cowork
Grok```bash
Grok Build / project-local
mkdir -p .grok/skills ln -sfn "$(pwd)/skills/remove-ai-marks" .grok/skills/remove-ai-marks
User-global Grok
mkdir -p ~/.grok/skills ln -sfn "$(pwd)/skills/remove-ai-marks" ~/.grok/skills/remove-ai-marks
### مهارة نصية اختيارية
[`skills/clean-user-facing-text/`](https://github.com/guillaumemeyer/watermarks-remover/blob/main/skills/clean-user-facing-text) هي
مهارة قائمة بذاتها للمخطوطات المصرّح بها، والتوثيق، والنصوص
الإعلانية. وهي تستثني أدوات الصور، وC2PA، والخدمة، والنماذج الخارجية، وتشغّل
نصوص Layer A المضمّنة الخاصة بها بدلاً من استدعاء الخدمة.```bash
python3 install_skill.py --skill clean-user-facing-text --target claude-code
python3 install_skill.py --skill clean-user-facing-text --target cursor
استدعاء المهارة يتم اختياره بواسطة النموذج. المشاريع التي تتبنى هذا
سير العمل صراحةً في Cursor يمكنها أيضاً نسخ القاعدة الاختيارية:```bash
mkdir -p /path/to/project/.cursor/rules
cp integrations/cursor/clean-user-facing-text.mdc
/path/to/project/.cursor/rules/clean-user-facing-text.mdc
بالنسبة لجميع المشاريع، ضع نفس التعليمات في **قواعد المستخدم** في Cursor بدلاً من ذلك.
تعمل القواعد على تحسين الاتساق لكنها تبقى تعليمات للنموذج؛ لا يوفر Cursor
مرشحًا حتميًا قبل الإرسال للردود النهائية في الدردشة.
### بدء الخدمة
أسرع مسار هو خادم HTTP محلي (Python 3.10+ stdlib فقط — بدون تبعيات، بدون Docker):```bash
make serve # http://127.0.0.1:8765
# or directly:
python3 service/scripts/server.py --host 127.0.0.1 --port 8765
ويندوز (بدون Docker)
راجع docs/windows-autostart.md للتشغيل التلقائي للخدمة عند تسجيل الدخول إلى ويندوز بدون Docker.
للاطلاع على البنية التحتية الكاملة (النواة + الواجهات الاختيارية/الخلفيات الثقيلة)، راجع Docker / compose أدناه.
أدوات النظام الاختيارية (تُستخدم تلقائيًا عند توفرها — مثبّتة مسبقًا في صورة Docker الأساسية):
| الأداة | الدور |
|---|---|
c2patool | فحص بيانات C2PA الوصفية |
exiftool | إزالة البيانات الوصفية المتبقية (خاصة PDF) |
qpdf | إعادة بناء هيكلية PDF — مطلوب لإزالة حقيقية من PDF (انظر أدناه) |
تحتاج السكربتات الأساسية إلى Python 3.10+ من المكتبة القياسية فقط. استدعاءات نموذج الطبقة B اختيارية.
الاستخدام السريع (السكربتات)```bash
SCRIPTS=service/scripts
Unified inspect / clean
python3 "$SCRIPTS/inspect_file.py" draft.md python3 "$SCRIPTS/clean_file.py" draft.md -o draft.cleaned.md python3 "$SCRIPTS/clean_file.py" photo.png -o photo.cleaned.png python3 "$SCRIPTS/clean_file.py" notes.docx -o notes.cleaned.docx
Text Layer A
python3 "$SCRIPTS/inspect_text.py" draft.md python3 "$SCRIPTS/clean_text.py" draft.md -o draft.cleaned.md --stats
Layer B rewrite hook (default: print prompt only — no model required)
python3 "$SCRIPTS/rewrite_text.py" draft.md --backend print-prompt --tactic paraphrase
Optional local Ollama (loopback only by default — remote endpoints require
WATERMARKS_REWRITE_ALLOW_REMOTE=1 or --allow-remote):
WATERMARKS_REWRITE_BACKEND=ollama WATERMARKS_REWRITE_MODEL=llama3.2 \
python3 "$SCRIPTS/rewrite_text.py" draft.md -o draft.rewritten.md
API keys are read from WATERMARKS_REWRITE_API_KEY only (never argv).
Images
python3 "$SCRIPTS/inspect_image.py" shot.png python3 "$SCRIPTS/clean_image.py" shot.png -o shot.cleaned.png
### أدوات النصوص ترفض المدخلات الثنائية
`inspect_text.py` و `clean_text.py` و `rewrite_text.py` تعمل على النصوص. عند توجيهها إلى ملف `.docx` أو `.pdf` أو صورة، كانت تفكّ ترميز البايتات المضغوطة وتُبلّغ عن أي نقاط ترميز تنتج — ضوضاء تتبع الضغط، لا المحتوى — ثم كان `clean_text.py` يكتب تلك البايتات المشوّهة مرة أخرى، مما يتلف الملف. الآن ترفض المدخلات الثنائية وتذكر الأداة التي تتعامل معها:```bash
python3 "$SCRIPTS/inspect_text.py" report.docx
# refusing to treat report.docx as text: it looks like a ZIP container (DOCX, ODT, …).
# Use inspect_file.py / clean_file.py, which route by format,
# or pass --force-text to scan the raw bytes anyway.
يتم الاكتشاف عبر رقم سحري بالإضافة إلى نسبة بايت التحكم، لذا يظل النص بترميزات غير UTF-8 يعمل. --force-text يتجاوز ذلك في كل مكان.
الصيغ غير المعروفة لا يتم تنظيفها تلقائيًا أبدًا
classify() يصنّف البايتات التي لا تطابق أي نص مدعوم أو صورة أو صيغة حاوية على أنها unknown — ولم تعد ترجع إلى "text". في الوضع التلقائي يرفض clean_file.py مثل هذه الملفات (exit 2، لا يتم كتابة أي مخرجات) بدلاً من فك ترميزها كـ UTF-8 وكتابة بايتات مشوّهة؛ --as text أو --force-text هما الاشتراك الصريح. يُبلّغ inspect_file.py عن الملف كـ unknown (exit 0)، وتجيب خدمة HTTP على /inspect بـ kind: "unknown" لكنها ترفض /clean للصيغ غير المعروفة (400 — أرسل اسم ملف بامتداد معروف، مثل notes.txt).
خدمة HTTP
تعمل نفس الآلية كخدمة HTTP من المكتبة القياسية (service/scripts/server.py) — الواجهة التي تستخدمها المهارة والطريقة التي يمكن لأي تطبيق ويب التكامل بها دون تضمين:
| Method | Path | Body | Returns |
|---|---|---|---|
| GET | /health | — | {"ok": true, "version": ...} |
| GET | /capabilities | — | optional tools / backends usable (each tool is version-probed, not just found on PATH) |
| GET | /openapi.json | — | dynamically generated OpenAPI 3.0.3 spec |
| POST | /inspect | {"file": "<base64>", "name": "notes.md"} | {"ok", "kind", "suspicious", "report"} |
| POST | /detect | {"file": "<base64>", "name": "notes.txt"} | {"ok", "kind", "detections": [...]} |
| POST | /clean | {"file": "<base64>", "name": "notes.md", "options": {...}} | {"ok", "kind", "cleaned": "<base64>", "report"} |
| POST | /watermark | {"text": "...", "keys": [118, 504, ...], "options": {...}} or {"file": "<base64>", ...} | {"ok", "kind", "watermarked_text", "report": {"scheme_used", ...}} |
| POST | /inspect/batch | {"files": [{"file": "<base64>", "name": "notes.md"}, ...]} | {"ok", "results": [{"name", "ok", "kind", "suspicious", "report"}, ...]} |
| POST | /detect/batch | {"files": [{"file": "<base64>", "name": "notes.txt"}, ...]} | {"ok", "results": [{"name", "ok", "kind", "detections", "report"}, ...]} |
| POST | /clean/batch | {"files": [{"file": "<base64>", "name": "notes.md", "options": {...}}, ...]} | {"ok", "results": [{"name", "ok", "kind", "cleaned", "report"}, ...]} |
| POST | /watermark/batch | {"files": [{"text": "...", "keys": [...]}, {"file": "<base64>"}, ...]} | {"ok", "results": [{"name", "ok", "kind", "watermarked_text", "report": {"scheme_used", ...}}, ...]} |
تُكرّر نقاط النهاية المجمّعة نفس خط الأنابيب لكل ملف كما في /inspect و/detect و/clean و/watermark، بحد أقصى WATERMARKS_MAX_BATCH_FILES ملفًا لكل طلب (الافتراضي 50). يظهر الإدخال المشوّه (base64 سيئ، خيار غير معروف، صيغة غير معروفة) كـ "ok": false لذلك الإدخال مع سلسلة "error" — ولا يُجهض أبدًا بقية الدفعة.```bash
WM="http://127.0.0.1:8765"
curl -s "$WM/health" # {"ok": true, "version": "..."}
curl -s "$WM/openapi.json" # machine-readable OpenAPI 3.0.3 contract
curl -s -X POST "$WM/clean" -H 'Content-Type: application/json'
-d "{"file": "$(base64 < notes.md | tr -d '\n')", "name": "notes.md"}"
تُوجّه الخدمة حسب امتداد اسم الملف ثم البايتات السحرية، لذا يتم الكشف التلقائي عن النص / الصورة / الحاوية. اضبط `WATERMARKS_SERVER_API_KEY` لطلب `Authorization: Bearer <key>` في كل طلب. الربط على loopback فقط افتراضيًا (`--host` للتجاوز)؛ مخصص لشبكة موثوقة.
### كشف العلامة المائية (`/detect` و `detect_before` / `detect_after`)
الكشف خطوة منفصلة عن التنظيف — لا تستدعي الخدمة واجهات
المورّد البرمجية إلا إذا طلبت منها ذلك:
- **`POST /detect`** يشغّل كاشفات العلامة المائية المُهيّأة على ملف.
نص → كاشفات المورّد + قياس الأسلوب؛ صورة → درجة بكسل SynthID.
- **`/inspect`** يقبل علامة `"detect": true` اختيارية تُلحق
نتائج الكاشف بتقرير النص (ويمكن أن تقلب `suspicious`).
- **`/clean`** يقبل خيارات `"detect_before"` / `"detect_after"` لتقييم
المدخل والمخرج المُنظَّف، حتى تقيس ما غيّره التنظيف فعليًا.
- **`/clean`** يشغّل إعادة كتابة النص Layer B بعد Layer A **افتراضيًا** (وهي
خطوة مطلوبة للنص). خيار **`"strategy"`** (قائمة مرتّبة من
`tactic@intensity`، مثل `"[email protected],[email protected]"`) يتجاوز
الافتراضي من ملف تهيئة الاستراتيجية (انظر أدناه). عندما لا تكون
الواجهة الخلفية/النموذج لإعادة الكتابة لخطوة ما مُهيّأة، يُرجع `/clean` خطأ 400.
كاشفات النص (انظر `/capabilities` → `text_detectors`):
كاشفات النص (انظر `/capabilities` → `text_detectors`):
| الكاشف | يُفعَّل بواسطة | ملاحظات |
| --- | --- | --- |
| `markllm` | `MARKLLM_DIR` (نسخة محلية على المضيف) | أداة بحثية (مخططات KGW / SynthID)، بنفس التهيئة فقط — وليس أوراكل مورّد. |
| `gumbel` | `WATERMARKS_GUMBEL_KEY` | إعادة تشغيل بنفس المفتاح بدون نموذج لمخطط keyed-Gumbel (Aaronson EXP) (انظر `detect_gumbel.py`)، بمكتبة قياسية فقط — محركات مستضافة ذاتيًا مثل arbi-serve؛ بنفس المفتاح فقط، وليس أوراكل مورّد. |
| `claude-text` | — (عنصر نائب) | أعلنت Anthropic عن واجهة برمجية لكشف العلامة المائية؛ يُفعَّل هذا المنفذ عند إطلاقها. |
تقييم الصور: عندما يكون `WATERMARKS_SYNTHID_SCORER_URL` مضبوطًا، تقيّم
الخدمة الصور عبر sidecar الخاص بـ `wr-synthid-score` (الملف الثقيل)؛ مع
`REVERSE_SYNTHID_DIR` محلي تستخدم النسخة المحلية مباشرة. الكشف
متسامح مع الفشل: الكاشفات غير المُهيّأة أو المنتهية مهلتها أو التي أخطأت
تُبلّغ `{"available": false, "error": ...}` ولا تعيق التنظيف أبدًا.
### توليد العلامة المائية (`/watermark` و `/watermark/batch`)
يولّد نصًا يحمل علامة مائية لتقييم المعايير واختبار الذهاب والإياب.
عندما يكون `WATERMARKS_SYNTHID_TEXT_URL` مضبوطًا، تفوّض الخدمة التوليد إلى
sidecar الخاص بـ `wr-synthid-text` (ملف الأداة)؛ مع `MARKLLM_DIR` محلي تستخدم
النسخة المحلية مباشرة. مثل الكشف، التوليد متسامح مع الفشل: المولّد غير المُهيّأ
يُبلّغ `{"ok": false, "error": ...}`.
## Docker / compose
الصور المنشورة (GHCR):
| وسم الصورة | المحتويات | منشورة؟ |
| --- | --- | --- |
| `ghcr.io/guillaumemeyer/watermarks-remover:<tag>` / `:latest` | خدمة HTTP الأساسية + جميع أدوات التنظيف + exiftool / qpdf / c2patool | نعم |
| `…:markllm-<tag>` / `:markllm-latest` | أداة العلامة المائية النصية MarkLLM (المصدر الأصلي Apache-2.0) | نعم |
| `…:markdiffusion-<tag>` / `:markdiffusion-latest` | أداة الصور MarkDiffusion (المصدر الأصلي Apache-2.0) | نعم |
| `watermarks-remover-ctrlregen:local` | إزالة بكسلات CtrlRegen — **لا تُنشر أبدًا** (`noai-watermark` لا يتضمن أي LICENSE) | بناء محلي فقط |
| `watermarks-remover-synthid-scorer:local` | مقيّم reverse-SynthID — **لا يُنشر أبدًا** (رخصة بحثية غير تجارية) | بناء محلي فقط (مقيّم CLI + sidecar HTTP اختياري `wr-synthid-score` ضمن ملف `heavy`) |
بناء وتشغيل الخدمة الأساسية:```bash
make docker-core-build
docker run --rm -p 127.0.0.1:8765:8765 --read-only --tmpfs /tmp watermarks-remover
# any CLI stays runnable by overriding the command:
docker run --rm -v "$(pwd):/data" watermarks-remover \
/app/scripts/clean_file.py /data/notes.md -o /data/notes.cleaned.md
بدء تشغيل البنية التحتية بالكامل:```bash docker compose up -d # core HTTP service only docker compose --profile harness up -d # + markllm / markdiffusion / wr-synthid-text sidecar docker compose --profile heavy up -d # + ctrlregen / synthid (local builds) docker compose --profile harness --profile heavy up -d # all services
يُوزّع الـ compose stack الخدمة الأساسية إلى `127.0.0.1:8765`. تعمل الخدمات الدائمة كخفيّات في الخلفية (`wr-core` و`wr-synthid-text` sidecar ضمن ملف harness التعريفي). أما بقية خدمات harness/heavy فهي أدوات CLI تُنفَّذ مرة واحدة — استدعِها عبر `docker compose run --rm <service> …` عند الحاجة إلى التحقق أو العمل على البكسلات.
تحقّق من صحة الـ stack قيد التشغيل (رمز الخروج فقط، دون مخرجات عند النجاح):```bash
make compose-check # or: ./compose-check.sh
يتحقق من wr-core عبر GET /health ويشغّل كل خدمة harness/heavy باستخدام --help، مع اشتراط رمز الخروج 0.
الإعداد (متغيرات البيئة لـ docker compose)
يتطلب تنظيف النصوص إعداد Layer B — إعادة كتابة Layer B هي
خطوة مطلوبة لـ POST /clean على النصوص، لذا تحتاج الخدمة الأساسية إلى إعداد
الواجهة الخلفية لإعادة الكتابة، وإلا فإن تنظيف النصوص يُرجع HTTP 400. يعمل تنظيف
بيانات الصور/الحاويات الوصفية مباشرةً. أما بالنسبة للنصوص فيجب عليك إعداد
تبعيات استراتيجية Layer B: transformers + roberta-large (لخطوة mlm الافتراضية) و
إعداد LLM الخاص بـ WATERMARKS_REWRITE_* (لخطوة paraphrase):```bash
echo "Hello\u200bWorld\u00ad!" > /tmp/sample.txt
curl -s -X POST http://127.0.0.1:8765/clean -H 'Content-Type: application/json'
-d "{"file": "$(base64 < /tmp/sample.txt | tr -d '\n')", "name": "sample.txt"}"
اللغات التي تعتمد طباعتها على مسافة غير فاصلة (الفرنسية `« … »`، والمسافة قبل `; : ! ?`) ينبغي أن تمرّر `"options": {"normalize_spaces": false}`، وهو المكافئ عبر HTTP للخيار `clean_text.py --no-normalize-spaces`. لا تزال الحوامل غير المرئية تُزال؛ ويُتخطّى فقط إعادة كتابة المسافات.
كل ما عدا ذلك اختياري ويوجد في ملف `.env` في جذر المستودع. يقوم `docker compose` **بتحميل `.env` تلقائيًا** واستبدال مراجع `${VAR}` في `compose.yaml` منه (تفوز متغيرات shell المُصدَّرة على `.env` إذا كان كلاهما معيَّنًا).```bash
cp .env.example .env # then edit
docker compose up -d # picks up .env automatically
ملف .env مُتجاهَل بواسطة git (الرفض افتراضيًا) — لا تُودِعه في المستودع أبدًا. لتشغيلات CLI على المضيف (rewrite_text.py، والمهارة)، صدّر الملف نفسه إلى البيئة:```bash
set -a; . ./.env; set +a; python3 service/scripts/rewrite_text.py /tmp/x.txt -o /tmp/x.rewritten.txt
| المتغير | يصل إلى | الغرض |
| --- | --- | --- |
| `WATERMARKS_SERVER_API_KEY` | `wr-core` (عبر compose `environment`) | يتطلب `Authorization: Bearer <key>` على واجهة HTTP API |
| `WATERMARKS_GEMINI_*` | — | أُزيل في أغسطس 2026: أوقفت Google علامة SynthID المائية النصية على الـ API (انظر `vendor-notes.md`) |
| `WATERMARKS_SYNTHID_SCORER_URL` | `wr-core` | توجيه النواة إلى الـ sidecar `wr-synthid-score` لتقييم صور SynthID (مثل `http://wr-synthid-score:8766` تحت الملف الثقيل) |
| `WATERMARKS_SYNTHID_SCORER_API_KEY` | `wr-core` + `wr-synthid-score` | مفتاح bearer مشترك للـ scorer sidecar (فارغ = بدون مصادقة) |
| `WATERMARKS_SYNTHID_TEXT_URL` | `wr-core` | توجيه النواة إلى الـ sidecar `wr-synthid-text` للعلامة المائية النصية SynthID (مثل `http://wr-synthid-text:8767` تحت ملف harness) |
| `WATERMARKS_SYNTHID_TEXT_API_KEY` | `wr-core` + `wr-synthid-text` | مفتاح bearer مشترك للـ sidecar العلامة المائية النصية (فارغ = بدون مصادقة) |
| `WATERMARKS_SYNTHID_TEXT_TIMEOUT` | `wr-core` | عدد الثواني للانتظار من أجل الـ sidecar `wr-synthid-text` (الافتراضي 120) |
| `WATERMARKS_MARKLLM_SCHEME` | `text_detectors.py` (المضيف) | مخطط MarkLLM لـ `/detect`: `kgw` (الافتراضي) / `synthid` |
| `HF_TOKEN` | خدمات harness/heavy | رمز Hugging Face للنماذج المقيّدة |
| `WATERMARKS_SERVICE_URL` | العميل فقط (skill / curl) | مكان الوصول إلى الخدمة؛ الافتراضي `http://127.0.0.1:8765` |
| `WATERMARKS_REWRITE_BACKEND` | خطاف `rewrite_text.py` | `print-prompt` (الافتراضي) / `ollama` / `openai-compatible` |
| `WATERMARKS_REWRITE_MODEL` | خطاف `rewrite_text.py` | اسم النموذج (مثل `deepseek-v4-flash`) |
| `WATERMARKS_REWRITE_BASE_URL` | خطاف `rewrite_text.py` | قاعدة الـ API (مثل `https://api.deepseek.com`) |
| `WATERMARKS_REWRITE_API_KEY` | خطاف `rewrite_text.py` | مفتاح الـ API — عبر البيئة فقط، وليس على argv أبدًا |
| `WATERMARKS_REWRITE_ALLOW_REMOTE` | خطاف `rewrite_text.py` | `1` للسماح بنقاط نهاية غير loopback |
| `WATERMARKS_REWRITE_REASONING_EFFORT` | خطاف `rewrite_text.py` | `none` (الافتراضي) / `low` / `medium` / `high` / `off` |
| `WATERMARKS_CLEAN_STRATEGY_FILE` | `server.py` `/clean` | مسار ملف JSON الخاص بإعداد استراتيجية الطبقة B (الافتراضي `config/clean_strategy.json`) |
| `WATERMARKS_GUMBEL_KEY` | `detect_gumbel.py` / `text_detectors.py` | مفتاح سري لـ keyed-Gumbel (EXP) لإعادة تشغيل نفس المفتاح (مثل `0x…`)؛ مفضّل على argv — لا يُسجَّل أبدًا |
**الطبقة B مطلوبة لتنظيف النص.** يطبّق `/clean` دائمًا الاستراتيجية الافتراضية
(من `config/clean_strategy.json`، `{"default_strategy": "[email protected],[email protected]"}`) على ملف نصي بعد الطبقة A، ما لم يمرر الطلب خيار `"strategy"` الخاص به (قائمة `tactic@intensity` مرتبة). خطوة الاستراتيجية هي `tactic@intensity`؛ خطوة `mlm` تحتاج `transformers` + `roberta-large`، وأي خطوة LLM (`paraphrase`، `humanize`، …) تحتاج إعداد `WATERMARKS_REWRITE_*`. إذا لم يكن الـ backend/النموذج المطلوب مُهيَّأً — أو لم تتوفر أي استراتيجية — فإن `/clean` **يرفض الطلب بخطأ 400**. أسبقية مسار الإعداد: علم CLI `--strategy-config` > متغير البيئة `WATERMARKS_CLEAN_STRATEGY_FILE` > الافتراضي `config/clean_strategy.json`.
تُنشر الصور تلقائيًا على وسوم `v*` عبر [`.github/workflows/release-images.yml`](https://github.com/guillaumemeyer/watermarks-remover/blob/main/.github/workflows/release-images.yml).
## تقييم بكسلات SynthID الاختياري
يمكن لـ `inspect_image.py` و `clean_image.py` الإبلاغ عن درجة ثقة SynthID
في نطاق البكسل عندما يتوفر checkout خارجي لـ
[`aloshdenny/reverse-SynthID`](https://github.com/aloshdenny/reverse-SynthID).
الـ scorer **غير مُضمَّن**: يُحمَّل في وقت التشغيل من الـ checkout الخاص بك، ويبقى كوده تحت رخصة
البحث غير التجاري الخاصة بالمشروع الأصلي.
### الخيار 1: bootstrap بأمر واحد (بدون Docker)```bash
SCRIPTS=service/scripts
# Clones upstream, creates a venv, and installs scorer-only dependencies.
"$SCRIPTS/setup_synthid.sh"
# Score an image (default checkout: ~/reverse-SynthID).
REVERSE_SYNTHID_DIR=~/reverse-SynthID \
~/reverse-SynthID/.venv/bin/python "$SCRIPTS/score_synthid.py" shot.png
# Or surface the score from inspect / clean (same venv Python).
REVERSE_SYNTHID_DIR=~/reverse-SynthID \
~/reverse-SynthID/.venv/bin/python "$SCRIPTS/inspect_image.py" shot.png
setup_synthid.sh يقبل --dir PATH و--ref REF و--full (لتثبيت
requirements.txt الكامل من المنبع، والذي يضيف torch/diffusers لتجاوز
VAE من المنبع الذي لا يستخدمه هذا المشروع).
على Windows استخدم setup_synthid.ps1 (-Dir، -Ref، -Full)، والذي ينشئ
البيئة الافتراضية في .venv\Scripts\ — وهو التخطيط الذي يبحث عنه image_meta.py
بالفعل عند os.name == "nt".
الخيار 2: بناء Docker محلي```bash
make docker-synthid-build
Run unprivileged and with a read-only rootfs; the scorer only needs to read
/data and write to stdout/tmp.
docker run --rm
--user "$(id -u):$(id -g)"
--read-only --tmpfs /tmp
-v "$(pwd):/data"
watermarks-remover-synthid-scorer /data/shot.png
يتم بناء الصورة محليًا من المصدر الأصلي في وقت البناء. وهي غير
منشورة، لذا فهي لا تعيد توزيع الكود الأصلي.
### الخيار 3: HTTP scorer sidecar (docker compose)
تحت ملف التعريف `heavy`، يقوم stack الخاص بـ compose أيضًا بتشغيل scorer كـ HTTP
sidecar (`wr-synthid-score`) بحيث يمكن لـ **خدمة core المنشورة** تقييم
الصور قبل/بعد التنظيف دون تضمين الكود الأصلي غير التجاري. وجّه `wr-core` إليه وشارك
مفتاح bearer (انظر `.env.example`):```bash
# .env
WATERMARKS_SYNTHID_SCORER_URL=http://wr-synthid-score:8766
WATERMARKS_SYNTHID_SCORER_API_KEY=change-me
docker compose --profile heavy up -d
ثم POST /clean مع {"options": {"detect_before": true, "detect_after": true}} يُرجع synthid_before / synthid_after في
التقرير، وPOST /detect على صورة يُرجع درجة SynthID. الفشل اللين:
إذا كان الـ sidecar متوقفًا أو غير مُهيأ، تحمل التقارير
{"available": false, "error": ...} ويظل التنظيف ناجحًا.
يستخدم تقييم V4 ملف artifacts/spectral_codebook_v4.npz من نسخة upstream
(`220 MB). هذا للكشف/التقييم فقط — فهو لا يزيل العلامات المائية
على مستوى البكسل.
إزالة بكسلات CtrlRegen الاختيارية
بالنسبة للعلامات المائية للصور في نطاق البكسل (فئة SynthID، StegaStamp، Tree-Ring،
StableSignature)، تُشغّل واجهة خلفية خارجية اختيارية خط أنابيب CtrlRegen
(ControlNet + DINOv2 IP-Adapter controllable regeneration). الواجهة الخلفية هي
mertizci/noai-watermark، وهي
إعادة تنفيذ مُصانة لطريقة ICLR 2025
CtrlRegen مع تقسيم تلقائي إلى بلاطات.
الواجهة الخلفية غير مُضمّنة ولا تُرفق ملف LICENSE، لذا تُعامل
كجميع الحقوق محفوظة: تُستنسخ عند commit مثبّت وتُحمّل وقت التشغيل.
تثبيتات التبعيات من حقبة البحث (requirements-ctrlregen.txt — مثل
transformers==4.37.2، diffusers==0.27.2) تحمل تنبيهات أمنية منشورة
وهي غير محدّثة عمدًا، لذا لا تُثبّت أبدًا إلا داخل
الـ venv المخصص الذي ينشئه هذا السكربت ولا تدخل أبدًا في صورة الخدمة
الرئيسية؛ كما يعيد setup_ctrlregen.sh التحقق من الـ commit المثبّت على
النسخ الموجودة، وليس فقط النسخ الجديدة.
Bootstrap```bash
SCRIPTS=service/scripts
Clones upstream (pinned commit), creates a venv, installs torch + deps.
"$SCRIPTS/setup_ctrlregen.sh"
Standalone removal (default checkout: ~/noai-watermark).
NOAI_WATERMARK_DIR=~/noai-watermark
~/noai-watermark/.venv/bin/python "$SCRIPTS/clean_ctrlregen.py" shot.png -o shot.ctrlregen.png
على Windows استخدم `setup_ctrlregen.ps1` (نفس الأعلام مثل `-Dir`، `-Ref`، `-Python`)؛
تُنشأ البيئة الافتراضية في `.venv\Scripts\`، وهو المسار الذي يحلّه `clean_image.py` بالفعل.
يفحص فهارس حزم PyTorch المنشورة ويختار الأعلى منها عند أو
أقل من إصدار CUDA الذي يطبعه `nvidia-smi` والذي يوجد فعلاً — هذا الرقم
هو الحد الأقصى الذي يدعمه *المشغّل*، والمشغّلات متوافقة مع الإصدارات السابقة، لذا فإن
مشغّلاً يُبلّغ عن 13.1 (لا يوجد `cu131` منشور) يثبّت `cu130`. تحت قدرة الحوسبة
7.5 يفرض `cu126`، وهو آخر فهرس لا تزال حزمه تحمل نوى
Maxwell/Pascal/Volta. يثبّت `torch` **و** `torchvision`
معاً من ذلك الفهرس حتى لا يمكن لعملية تثبيت التبعيات استبدالهما ببنى CPU
من PyPI، ثم يتحقق بعد التثبيت من أن `torch.cuda.is_available()`
صحيح — إذا تم اكتشاف GPU لكن انتهى torch بوضع CPU فقط، يحذّر السكربت
بصوت عالٍ ويخرج بقيمة غير صفرية بدلاً من التظاهر بأن الإعداد نجح.
### من `clean_image.py````bash
NOAI_WATERMARK_DIR=~/noai-watermark \
~/noai-watermark/.venv/bin/python "$SCRIPTS/clean_image.py" shot.png \
-o shot.cleaned.png --remove-pixel ctrlregen
ترتيب العمليات: إزالة البيانات الوصفية أولاً، ثم إزالة بكسلات CtrlRegen، ثم
درجة reverse-SynthID الاختيارية قبل/بعد (عندما يتم تعيين REVERSE_SYNTHID_DIR
أيضاً).
الشدة محافظة افتراضياً (--ctrlregen-intensity 0.25)، لأن الشدة الأعلى
تزيل المزيد من العلامة المائية لكنها تعيد توليد المزيد من الصورة.
الإعدادات المسبقة الموثقة: 0.15 أدنى / 0.25 افتراضي / 0.35 متوازن /
0.5 عدواني / 0.7 أقصى (القيمة الافتراضية للخلفية هي 0.5). --ctrlregen-steps
افتراضياً 50 (خطوات إزالة الضوضاء الفعالة ≈ الخطوات × الشدة).
حجم الصورة (حد 512×512 الأصلي)
CtrlRegen هو ControlNet لـ Stable Diffusion 1.5 بحجم 512×512. تقوم الخلفية بحل هذا للمدخلات العشوائية، لذا لا يتم كشف أي تجزئة إضافية هنا:
- ≤512 بكسل: تمريرة واحدة — قص مركزي/تغيير الحجم إلى 512، إعادة التوليد، تغيير الحجم مرة أخرى.
- >512 بكسل: تجزئة تلقائية متداخلة (تجزيئات 512 بكسل، تداخل 192 بكسل)، العرض/الارتفاع محاذيان لمضاعفات 8، ثم دمج الحواف بتمازج جيبي.
- كلا المسارين: يتم تغيير حجم المخرجات إلى الحجم الأصلي ومطابقة الألوان مع الصورة الأصلية.
الصور الكبيرة جداً (مثل 4K) تنتج العديد من التجزيئات، لذا تتوسع عمليات التشغيل مع عدد التجزيئات (أبطأ وذاكرة VRAM أعلى). قم بتصغير المدخلات الكبيرة مسبقاً عندما يكون ذلك عملياً؛ حجم التجزئة والتداخل مثبتان في المنبع وغير مكشوفين كأعلام.
الحوسبة والنماذج المقيدة والتحقق
توقع حوالي 10 جيجابايت من تنزيلات النماذج؛ يوصى بشدة باستخدام GPU وتشغيلات CPU
بطيئة. بعض النماذج في المنبع مقيدة، لذا قم بتصدير HF_TOKEN (متغير بيئة فقط —
أبداً argv). يرفض clean_ctrlregen.py التثبيت التلقائي للتبعيات؛ قم بتشغيل
setup_ctrlregen.sh أولاً.
لا يوجد كاشف محلي لـ StegaStamp/Tree-Ring/StableSignature، لذا فإن الإشارة المحلية
الوحيدة هي درجة reverse-SynthID (بديل). عند توفرها،
يبلغ clean_image.py --remove-pixel ctrlregen عن تلك الدرجة قبل/بعد؛ يبقى
فحص Google SynthID الرسمي هو المرجع النهائي.
Docker```bash
make docker-ctrlregen-build
docker run --rm -e HF_TOKEN="$HF_TOKEN"
--user "$(id -u):$(id -g)"
-v "$(pwd):/data"
watermarks-remover-ctrlregen /data/shot.png -o /data/shot.ctrlregen.png
## التحقق الاختياري من العلامة المائية النصية عبر MarkLLM
بالنسبة إلى **التجارب المضبوطة**، تُغلِّف أداة خارجية اختيارية
[`THU-BPM/MarkLLM`](https://github.com/THU-BPM/MarkLLM) (Apache-2.0) لوضع علامة مائية على نص الاختبار وإعادة اكتشافها بعد إعادة كتابة Layer B — على سبيل المثال إثبات أن
علامة KGW (Kirchenbauer، صف "open-LLM" الخاص بك) أو SynthID-Text (صف Gemini) تختفي تحت إعادة الكتابة الخاصة بك. إنها **أداة تحقق، وليست أوراكل**:
اكتشاف MarkLLM صالح فقط مقابل *نفس* إعدادات المخطط + المفاتيح المستخدمة عند
التوليد، ولا يمكنه ضمان فشل كاشف البائع.
الخادم الخلفي **غير مضمَّن**. يقوم `setup_markllm.sh` بنسخ المستودع الأصلي عند commit مثبَّت،
وإنشاء venv، وتثبيت التبعيات المثبَّتة (torch + transformers)؛ ويتم تنزيل
نموذج التقييم (الافتراضي `facebook/opt-1.3b`، Apache-2.0) من Hugging
Face عند أول تشغيل.```bash
SCRIPTS=service/scripts
# Bootstrap (clones upstream, creates ~/MarkLLM/.venv, installs deps).
"$SCRIPTS/setup_markllm.sh"
# Generate watermarked + unwatermarked sample text under the KGW scheme.
MARKLLM_DIR=~/MarkLLM \
~/MarkLLM/.venv/bin/python "$SCRIPTS/detect_text_watermark.py" watermark prompt.txt \
--scheme kgw -o wm.txt -o2 plain.txt
# Detect the scheme mark in a text file.
MARKLLM_DIR=~/MarkLLM \
~/MarkLLM/.venv/bin/python "$SCRIPTS/detect_text_watermark.py" detect wm.txt --scheme kgw --json
التحقق حول إعادة كتابة Layer B: مرّر --markllm-scheme إلى
rewrite_text.py (مع --markllm-dir)، ويسجّل كشف MarkLLM
قبل/بعد بالإضافة إلى علامة cleared:```bash
export WATERMARKS_REWRITE_BACKEND=ollama WATERMARKS_REWRITE_MODEL=llama3.2
MARKLLM_DIR=~/MarkLLM
python3 "$SCRIPTS/rewrite_text.py" wm.txt -o wm.rewritten.txt
--markllm-scheme kgw --markllm-dir "$HOME/MarkLLM" --json-stats
**إعادة الكتابة التكرارية الموجهة بالكشف:** تقوم الطبقة B الآن بإعادة الكتابة بشكل تكراري وتتوقف بمجرد أن ينجح محاولة في التقييم. تُنتج كل جولة تقييم متغيرات `--candidates` (الافتراضي **1**، `WATERMARKS_REWRITE_CANDIDATES`) ويحدد `--max-loops` عدد الجولات التي تُنفَّذ قبل إرجاع أفضل متغير ممكن (الافتراضي **1**، `WATERMARKS_REWRITE_LOOPS`). كل متغير هو استدعاء إعادة كتابة واحد بالإضافة إلى تقييم واحد، وتنتهي الجولة مبكرًا عند أول محاولة يُبلغ المُقيِّم بأنها غير موسومة — لذا فإن رفع `--max-loops` يعيد المحاولة بمتغيرات جديدة حتى ينجح تقييم (إعادة كتابة نظيفة نموذجية تكلف محاولة واحدة). يتم اختيار المُقيِّم حسب الأولوية:
1. **MarkLLM** — كشف بحثي بنفس الإعدادات، عند تمرير `--markllm-scheme` (مع `--markllm-dir`). تم حجز خانة كاشف المورّد فوق MarkLLM لكاشف SynthID-text من Google، والذي أوقفته Google على واجهة API الخاصة به في أغسطس 2026 — يمكن لنقطة نهاية مورّد مستقبلية أن تُوصَل هناك.
2. **التباعد المعجمي bigram-Jaccard** — عند عدم تكوين أي كاشف؛ لا يوجد حكم بالنجاح/الفشل، لذا تُولَّد كل محاولة ويُختار الأكثر تباعدًا معجميًا (السلوك الأصلي).
يُبلّغ `--json-stats` عن المُقيِّم، والمحاولات المُنفَّذة، والنجاح/الفشل، وسجلات كل محاولة:```json
{
"evaluator": "markllm",
"candidates": 1,
"max_loops": 2,
"attempts_made": 2,
"passed": true,
"candidate_scores": [
{
"lexical_divergence": 0.91,
"selection_score": 0.91,
"selected": false,
"passed": false,
"evaluation": {"detector": "markllm", "available": true, "scheme": "kgw",
"is_watermarked": true, "score": 4.3, "threshold": 3.0}
},
{
"lexical_divergence": 0.84,
"selection_score": 0.84,
"selected": true,
"passed": true,
"evaluation": {"detector": "markllm", "available": true, "scheme": "kgw",
"is_watermarked": false, "score": 1.7, "threshold": 3.0}
}
],
"markllm": {"scheme": "kgw", "before": {"...": "..."}, "after": {"...": "..."},
"cleared": true, "note": "same-config only"}
}
كاشف غير مُهيأ، أو تنتهي مهلته، أو ينتج أخطاء يُنتج مدخلة
"available": false مع سبب error ولا يُفشل إعادة الكتابة أبدًا — تلك
المحاولة ببساطة لا يمكن أن تنجح، وتتراجع الحلقة إلى اختيار التباعد
المعجمي. عندما يُستنفد الحد الأقصى دون نجاح، تُعاد المحاولة الأقل علامة
مائية (أدنى درجة) كأفضل جهد مع ملاحظة.
إذا كانت الواجهة الخلفية غير مُهيأة أو كانت تبعياتها مفقودة، تستمر إعادة الكتابة ويشير التقرير إلى أن التحقق كان غير متاح. يُوصى باستخدام GPU؛ تعمل عمليات CPU لكنها بطيئة، وتنزيل النموذج يبلغ بضعة جيجابايت.
مقابض التقوية:
--offlineعلى المحوّل (أو أي تشغيل MarkLLM) يحمّل نموذج التقييم من ذاكرة Hugging Face المؤقتة فقط — صفر خروج شبكي؛ يفشل بسرعة إذا لم يكن مخزّنًا مؤقتًا. لا يُنفَّذ الكود البعيد المخصص أبدًا (لا يُفعَّلtrust_remote_codeفي transformers أبدًا).WATERMARKS_MARKLLM_RLIMIT_AS=<bytes>(متغير بيئة، POSIX) يطبّق حدًا لمساحة العنوان على العملية الفرعية لكاشف MarkLLM. معطّل افتراضيًا لأن torch/CUDA عادةً يحتاج مساحات عناوين كبيرة.- ملفات التهيئة محدودة بـ 1 MiB؛ نسخة المستودع المصدرية والصورة الأساسية مثبّتتان بواسطة SHA/digest.
Docker```bash
make docker-markllm-build
docker run --rm --user "$(id -u):$(id -g)" -v "$(pwd):/data"
watermarks-remover-markllm detect /data/wm.txt --scheme kgw --json
### التحقق من Keyed-Gumbel (Aaronson EXP) بنفس المفتاح
يصف [التقرير التقني لـ ARBI](https://arbicity.com/news/ai-text-watermarking-for-self-hosted-ai/)
العلامة المائية النصية keyed-Gumbel ("الأُسّية") — التي تُشحن الآن في محرك
arbi-serve مفتوح المصدر (`ARBI_WATERMARK_KEY`) — حيث تُشتقّ ضوضاء المُعيّن
من تجزئة مفتاحية لنافذة السياق المكوّنة من آخر 4 رموز. الكشف هو
**إعادة تشغيل بلا نموذج**: أعد حساب `u = PRF(Hash(key, window), token)` من
النص وحده واختبر ذيل Gamma، لذا لا يحتاج إلى GPU أو نموذج أو logits.
يُشحن هذا المستودع ذلك الكاشف باسم `detect_gumbel.py` (بالمكتبة القياسية فقط؛
القيمة الاحتمالية p هي متطابقة مجموع بواسون الدقيقة لشكل Gamma صحيح):```bash
# Text mode (deterministic word/run tokenizer) — quick checks and rewrite-loop
# evaluation; exact replay against a real engine needs its tokenizer:
python3 service/scripts/detect_gumbel.py draft.txt --key 0x... --json
# Exact replay: pass the engine's token ids (JSON array or one per line).
python3 service/scripts/detect_gumbel.py ids.json --tokens --key 0x... --json
نفس التحذير الخاص بالصدق كما في MarkLLM: هذا إعادة تشغيل بنفس المفتاح — صالح فقط ضد نفس المفتاح، والمُرمِّز (tokenizer)، وتخطيط PRF المستخدم عند التوليد، والنتيجة السلبية لا تُثبت شيئًا. تخطيط HMAC-SHA256 هنا هو تجسيد قابل للتدقيق، وليس متوافقًا بتًا مع أي نواة محرك محددة (راجع docstring الوحدة لمعرفة ما يجب تكييفه لإعادة التشغيل الدقيقة).
إعادة الكتابة الموجهة بالكشف: مرِّر --gumbel-key إلى rewrite_text.py
(متغير البيئة: WATERMARKS_GUMBEL_KEY، وهو المفضّل) وتُدار حلقة إعادة الكتابة
التكرارية بواسطة إعادة تشغيل Gumbel بنفس المفتاح — تصبح أولوية المُقيِّم
gumbel > MarkLLM > lexical divergence — مع تقرير gumbel.before/after/cleared:```bash
export WATERMARKS_REWRITE_BACKEND=ollama WATERMARKS_REWRITE_MODEL=llama3.2
export WATERMARKS_GUMBEL_KEY=0x...
python3 "$SCRIPTS/rewrite_text.py" wm.txt -o wm.rewritten.txt --json-stats
المفتاح لا يظهر أبدًا في الإحصاءات أو السجلات. يمكن للمشغّلين المستضيفين ذاتيًا الذين يحملون مفتاح محرّكهم التحقق من أن إعادة الكتابة أزالت علامة Gumbel؛ أما الجميع غيرهم فيتعاملون مع الطبقة B على أنها بذل أفضل جهد فقط.
## معيار اختياري لإزالة SynthID-text
[`bench_synthid_text.py`](https://github.com/guillaumemeyer/watermarks-remover/blob/main/service/scripts/bench_synthid_text.py) يقيس مدى
فعالية إعادة كتابة الطبقة B في إزالة العلامات المائية من فئة SynthID-text وبأي
تكلفة. يولّد عينات موسومة + غير موسومة باستخدام مخطط MarkLLM
SynthID (كشف بنفس الإعداد، مقيّد بفحص سلامة)، ويشغّل متغيرات إعادة الكتابة لديك
(تكتيك × الحد الأقصى لمحاولات إعادة الكتابة؛ تتوقف الحلقة مبكرًا عند النجاح) بالإضافة إلى
عناصر التحكم (بدون إزالة، الطبقة A فقط، فحص إعادة الوسم الاختياري)، ويكتب
`report.md` /
`results.json` / `results.csv` قابلًا للمشاركة. الدليل الكامل:
[`docs/synthid-text-benchmark.md`](https://github.com/guillaumemeyer/watermarks-remover/blob/main/docs/synthid-text-benchmark.md).
يتطلب نسخة MarkLLM (`setup_markllm.sh` / `MARKLLM_DIR`) و
خلفية إعادة كتابة. **نموذج إعادة الكتابة هو LLM تقوم أنت بإعداده** — نفس
خلفية `rewrite_text.py` التي تستخدمها المهارة. النموذج الافتراضي في MarkLLM
`facebook/opt-1.3b` (`--markllm-model`) هو فقط مولّد/كاشف
العلامة المائية؛ ولا يقوم أبدًا بإعادة الكتابة. قم بإعداد نموذج إعادة الكتابة عبر متغيرات
البيئة أو أعلام المعيار (وهي تعكس
[جدول الإعداد](#configuration-env-vars-for-docker-compose) أعلاه):
| متغير البيئة | علم المعيار | الافتراضي | المعنى |
| --- | --- | --- | --- |
| `WATERMARKS_REWRITE_BACKEND` | `--rewrite-backend` | `ollama` | `ollama` أو `openai-compatible` |
| `WATERMARKS_REWRITE_MODEL` | `--rewrite-model` | *(مطلوب)* | الـ LLM الذي ينفّذ إعادة الكتابة (مثل `llama3.2`، `deepseek-v4-flash`) |
| `WATERMARKS_REWRITE_BASE_URL` | `--rewrite-base-url` | `http://127.0.0.1:11434` | نقطة النهاية؛ الافتراضي في Ollama هو loopback |
| `WATERMARKS_REWRITE_API_KEY` | `--rewrite-api-key` | — | مفتاح API (من البيئة فقط في العملية الفرعية، وليس من argv أبدًا) |
| `WATERMARKS_REWRITE_ALLOW_REMOTE=1` | `--rewrite-allow-remote` | معطّل | مطلوب لإرسال المحتوى إلى نقاط نهاية غير loopback |```bash
# Ollama (loopback):
python3 service/scripts/bench_synthid_text.py --markllm-dir ~/MarkLLM \
--rewrite-backend ollama --rewrite-model llama3.2
# OpenAI-compatible API (remote):
WATERMARKS_REWRITE_API_KEY=... python3 service/scripts/bench_synthid_text.py \
--markllm-dir ~/MarkLLM --rewrite-backend openai-compatible \
--rewrite-model deepseek-v4-flash --rewrite-base-url https://api.deepseek.com \
--rewrite-allow-remote
استخدم نموذجًا غير أصلي لإعادة الصياغة (لا تعد الصياغة باستخدام نفس النموذج المعلَّم بالماء الذي أنتج النص) وإلا فقد تعيد إعادة الصياغة وسم المخرجات؛ يقيس --restamp-control ذلك.
أداة MarkDiffusion الاختيارية لعلامات الماء على الصور
لإجراء تجارب مضبوطة على الصور، تُغلِّف أداة خارجية اختيارية
THU-BPM/MarkDiffusion (Apache-2.0)،
وهي مجموعة أدوات لعلامات الماء التوليدية لنماذج الانتشار الكامن (تُضمِّن العلامات
— ولا تزيلها). نستخدمها لثلاثة أغراض:
- أداة تحقق (مثل MarkLLM، لكن للصور): ضع علامة ماء على صورة اختبار بمخطط ما، شغِّل الإزالة، ثم أعد الكشف باستخدام نفس إعدادات المخطط — أي إثبات أن علامة من فئة Tree-Ring تُمحى عبر خط معالجتك. إنها أداة تحقق، وليست مرجعًا قاطعًا: يتطلب الكشف النموذج المولِّد (والمفاتيح للمخططات المعتمدة على المفاتيح)، لذا لا يمكنها الجزم بأن كاشف مورّد ما سيفشل على صورة عشوائية.
- محرك اختياري لإزالة البكسلات: هجوم إعادة التوليد
DiffusionPurificationالخاص بها متاح عبرclean_image.py --remove-pixel diffusion، كبديل لـ CtrlRegen. إنه إعادة توليد عمياء (بدون تهيئة ControlNet)، لذا يُحرِّف محتوى الصورة أكثر من CtrlRegen — القيمة الافتراضية للشدة محافظة (0.3)، ويُعامَل كخيار احتياطي/للمقارنة، وليس ضمانًا أبدًا. - كاشف محلي بنفس المخطط لعلامات من فئة Tree-Ring، يسدّ جزئيًا فجوة "لا كاشف محلي لـ StegaStamp/Tree-Ring/StableSignature" (فهو يغطي Tree-Ring/Ring-ID/Gaussian-Shading وغيرها، وليس StegaStamp / StableSignature / SynthID-media).
الخلفية غير مضمَّنة. يُنشئ setup_markdiffusion.sh بيئة افتراضية
ويثبِّت markdiffusion==1.0.2 من PyPI (مثبَّتة)، مع تثبيت torch من
فهرس المنصة الصحيح؛ أما --checkout فيثبِّت نسخة قابلة للتحرير عند
التزام مثبَّت بدلًا من ذلك. يُنزَّل نموذج Stable Diffusion (الافتراضي
huanzi05/stable-diffusion-2-1-base) من Hugging Face عند أول تشغيل.```bash
SCRIPTS=service/scripts
Bootstrap (PyPI pin default; creates ~/markdiffusion/.venv, installs deps).
"$SCRIPTS/setup_markdiffusion.sh"
1. Generate a Tree-Ring watermarked image (+ unwatermarked control).
echo "a red fox in snow" > /tmp/prompt.txt
MARKDIFFUSION_DIR=~/markdiffusion
~/markdiffusion/.venv/bin/python "$SCRIPTS/markdiffusion_harness.py" watermark
/tmp/prompt.txt -o wm.png -o2 plain.png --scheme tr --json
2. Remove with the DiffusionPurification regeneration attack.
MARKDIFFUSION_DIR=~/markdiffusion
~/markdiffusion/.venv/bin/python "$SCRIPTS/markdiffusion_harness.py" purify
wm.png -o wm.purified.png --purification-intensity 0.3 --json
3. Re-detect with the SAME scheme config.
MARKDIFFUSION_DIR=~/markdiffusion
~/markdiffusion/.venv/bin/python "$SCRIPTS/markdiffusion_harness.py" detect
wm.purified.png --scheme tr --detector-type l1_distance --json
أو قم بتشغيل التنقية كجزء من خط أنابيب الصور العادي:```bash
MARKDIFFUSION_DIR=~/markdiffusion \
~/markdiffusion/.venv/bin/python "$SCRIPTS/clean_image.py" shot.png \
-o shot.cleaned.png --remove-pixel diffusion
تُحاكي مقابض التقوية إطار MarkLLM: --offline يحمّل النموذج من ذاكرة Hugging Face المؤقتة فقط (بدون خروج شبكي، بدون كود بعيد)، وHF_TOKEN من البيئة فقط (وليس من argv أبدًا)، وتُحدَّد إعدادات الخوارزمية بحد أقصى 1 MiB، وتحصل العملية الفرعية على نفس حدود الموارد الأعلى مثل CtrlRegen.
Docker```bash
make docker-markdiffusion-build
docker run --rm --user "$(id -u):$(id -g)" -v "$(pwd):/data"
watermarks-remover-markdiffusion detect /data/wm.png --scheme tr --json
تقوم الصورة بتثبيت CPU torch؛ يجب على مستخدمي CUDA تشغيل `setup_markdiffusion.sh`
على المضيف بدلاً من ذلك. لا تزال تنزيلات النموذج تصل إلى HF hub عند التشغيل الأول.
## مصفوفة التغطية
| القناة | Claude | Gemini/SynthID | OpenAI | Open-LLM |
| --- | --- | --- | --- | --- |
| نص Unicode / قائم على التعديل | الطبقة A | الطبقة A | الطبقة A | الطبقة A |
| **نص أخذ العينات الإحصائي** | الطبقة B بأفضل جهد (Claude seam عندما تُطلق Anthropic واجهة برمجة تطبيقات الكشف الخاصة بها) | الطبقة B بأفضل جهد (+ MarkLLM same-config harness؛ تقاعدت Google من كاشف البائع في أغسطس 2026) | الطبقة B إن وُجدت | الطبقة B بأفضل جهد + MarkLLM harness اختياري |
| C2PA / بيانات وصفية للملف | نعم (التنسيقات المدرجة) | نعم عند وجودها | نعم عند وجودها | نعم عند وجودها |
| علامات الصور بالبكسل | خارج النطاق | درجة SynthID اختيارية + إزالة CtrlRegen (خارجي)؛ كشف MarkDiffusion same-scheme اختياري + إزالة DiffusionPurification (خارجي) | خارج النطاق | إزالة CtrlRegen / MarkDiffusion اختيارية (خارجي) |
| أبواب خلفية للتدريب | خارج النطاق | خارج النطاق | خارج النطاق | خارج النطاق |
التفاصيل: [`skills/remove-ai-marks/references/vendor-notes.md`](https://github.com/guillaumemeyer/watermarks-remover/blob/main/skills/remove-ai-marks/references/vendor-notes.md)، [`mark-classes.md`](https://github.com/guillaumemeyer/watermarks-remover/blob/main/skills/remove-ai-marks/references/mark-classes.md).
---
## كيف تعمل علامة النص (مختصر)
غالبًا ما تخفي العلامات المائية الحديثة للنماذج اللغوية الكبيرة إشارة في **الرموز التي يتم اختيارها** (التحيز التوليدي / في أخذ العينات)، وليس فقط في الأحرف غير المرئية. تحقن المخططات القائمة على التعديل قواعد Unicode أو المرادفات. تُرفق مخططات الملفات **C2PA** أو بيانات وصفية للمولّد.
- **الطبقة A** تزيل حاملات Unicode القائمة على التعديل (قابلة للاختبار).
- **الطبقة B** تهاجم العلامات المائية لأخذ العينات عبر إعادة كتابة مكثفة (بأفضل جهد؛ هجمات معيارية في الأدبيات مثل إعادة الصياغة / الترجمة العكسية).
- **منظفات الملفات** تزيل C2PA/XMP/props من الحاويات المدعومة.
حتى تُطلق البائعون كواشف ومفاتيح عامة، **لا يمكن لأي أداة أن تشهد بأمانة** "أن هذا يفشل الفحص الرسمي". يجب أن تفصل التقارير بين العمل القابل للتحقق والعمل بأفضل جهد.
فضّل نموذجًا **غير أصلي** للطبقة B (لا تعد كتابة نص Claude بـ Claude إذا كنت تحاول تجنب إعادة الختم).
---
## إخلاء المسؤولية: ما تكلفة إزالة علامة النص المائية
تعيش العلامات المائية للنص في **الصياغة نفسها**: الإشارة موزعة عبر اختيارات الرموز، لذا تحمل كل جملة تقريبًا القليل منها. تتبع نتيجتان، وهما السبب في وصف الطبقة B بأمانة بأنها *بأفضل جهد* بدلاً من ممحاة سحرية.
1. **الإزالة تعني إعادة الصياغة، وليس إعادة الهيكلة.** خلط الفقرات، أو تغيير العناوين، أو التحسينات الطفيفة بالكاد تحرك الإشارة. يتطلب إزالة علامة إحصائية إعادة كتابة جزء كبير من النص — جملة بجملة، وليس قسمًا بقسم.
2. **إعادة الصياغة تُدهور النسخة.** أي إعادة كتابة تستبدل اختيارات الكلمات الأصلية باختيارات نموذج إعادة الكتابة، مما يسطح النبرة والصوت والدقة. في النسخ الإنتاجية (SEO، التسويق، أعمال العملاء) يكون هذا التدهور حقيقيًا وغالبًا مرئيًا للأشخاص الذين يهتمون بالكتابة أكثر من غيرهم. إنه مثل أخذ نص من نموذج من الطبقة الأولى وطلب من نموذج أقل قدرة إعادة كتابته من الصفر: لا يمكن للنتيجة أن تتجاوز سقف نموذج إعادة الكتابة.
وهذا يقود إلى السؤال الصادق الكامل:
> إذا كانت الخطة هي إعادة كتابة النص بنموذج أرخص على أي حال، فلماذا تدفع مقابل نموذج متميز في المقام الأول؟ التوليد مباشرة بالنموذج الأرخص أبسط وأرخص وينتج نفس النتيجة — أو أفضل.
تكون الطبقة B منطقية عندما تريد تحديدًا **تفكير وصياغة** النموذج المتميز وتقبل تمريرة إعادة كتابة لتلبية متطلب نظافة أو خصوصية — وليس كطريق رخيص لنص خالٍ من العلامات.
**متى تتخطى الطبقة B:**
- **الجودة تهم أكثر من النظافة:** استخدم المسار غير الفاقد — تنظيف Unicode للطبقة A بالإضافة إلى منظفات البيانات الوصفية للملفات — واحتفظ بالنص الأصلي.
- **إعادة الكتابة على أي حال:** استخدم نموذجًا **غير أصلي** (إعادة الكتابة بالنموذج الأصلي يمكن أن تعيد ختم النص)، وتذكر أن المخاطر المتبقية تبقى — لا يمكن لأي أداة أن تشهد بأن كاشف البائع سيفشل.
---
## تنسيقات الملفات
| التنسيق | الفحص | التنظيف |
| --- | --- | --- |
| PNG / JPEG / WebP | C2PA chunks / APP11 / RIFF `C2PA`، تلميحات AI XMP | إسقاط مقاطع البيانات الوصفية |
| AVIF / HEIC | ISOBMFF `jumb` / XMP `uuid` boxes | إسقاط الصناديق |
| BMP | بايتات زائدة غير صورة (لا قناة موحدة) | اقتطاع البيانات الوصفية الزائدة، إصلاح حقل حجم الملف |
| GIF | تعليق / امتدادات تطبيق XMP | إسقاط التعليق و XMP، الاحتفاظ بحلقة `NETSCAPE2.0` |
| TIFF (كلاسيكي + BigTIFF) | وسوم IFD: XMP، EXIF، GPS، IPTC، MakerNote | إسقاط الوسوم، تصفير الحمولات، الاحتفاظ بالشرائط |
| SVG | `<metadata>`، XMP | تجريد الكتل |
| PDF | Byte/XMP + أدوات اختيارية | **exiftool** ثم **qpdf**، ثم **ghostscript** للبيانات الوصفية داخل الصور المدمجة؛ كل أداة مفقودة تُدهور طبقة مختلفة (تجريد المستند، إعادة الكتابة الهيكلية، الصور المدمجة) |
| DOCX | docProps / customXml | تنظيف props، إسقاط customXml |
| EPUB | بيانات OPF الوصفية، XHTML meta/JSON-LD، وسائط مدمجة | تنظيف OPF، تجريد XHTML meta، تنظيف الوسائط + الطبقة A (تتخطى الأجزاء المشفرة) |
| ODT | meta.xml | إسقاط المولّد / meta الشبيهة بالذكاء الاصطناعي |
| HTML | meta، JSON-LD، data-ai* | تجريد الوسوم/السمات |
| Markdown | مفاتيح AI في YAML frontmatter | إسقاط المفاتيح + الطبقة A للنص |
| MP4 / MOV / M4A / M4V | صناديق ISOBMFF `jumb`/`uuid` (نفس آلية AVIF/HEIC) + وسوم مولّد `moov/udta` | إسقاط الصناديق |
| WAV | مقاطع RIFF `C2PA` / `LIST INFO`، مقطع `id3\x20` مدمج | إسقاط المقاطع |
| MP3 | إطارات ID3v2 (v2.3/v2.4 لكل إطار؛ v2.2 للوسم كاملًا) | إسقاط الإطارات المطابقة أو الوسم كاملًا |
| FLAC | بيان C2PA في إطار ID3v2 `GEOB` | إسقاط الإطار المطابق أو وسم ID3v2 كاملًا |
يغطي دعم FLAC حامل ID3v2 الموحد لـ C2PA. تُترك كتل بيانات FLAC الوصفية الأصلية،
وVorbis Comments، والعلامات المائية في نطاق الشكل الموجي دون مساس.
#### لماذا يحتاج PDF إلى qpdf، وليس فقط exiftool
يكتب ExifTool ملفات PDF **بشكل تزايدي**. يُلحق `exiftool -all=` كتلة
`%BeginExifToolUpdate` تحرر كائن Info وتُسقط `/Info` من
المقطورة — لكن بايتات البيانات الوصفية الأصلية تبقى في الملف حرفيًا، و
يمكن لـ exiftool نفسه التراجع عن التعديل بـ `-PDF-update:all=`. يخرج الأمر
بـ `0`، ولا تعرض العارضات أي بيانات وصفية، ويصبح الملف *أكبر*، وهذا هو الدليل.
بالنسبة لأداة تجريد المصدر، يعد ذلك تسريبًا صامتًا، لذا يتبع `clean_pdf`
تمريرة exiftool بـ `qpdf --linearize`، الذي يعيد تسلسل المستند
من رسم الكائنات الخاص به ويُسقط الكائنات غير المُشار إليها الآن. بدون تثبيت `qpdf`
يظل التنظيف يعمل، لكنه يصرح بذلك:```
warning: exiftool PDF edits are incremental — the original metadata bytes
remain recoverable; install qpdf for a structural rewrite
لماذا لا يكفي qpdf للصور داخل ملف PDF
كلا المرورين أعلاه يعملان على المستند: قاموس Info، وحزمة XMP،
ومخطط الكائنات. لا ينزل أي منهما إلى XObject الخاص بالصورة، لذا فإن مسحًا ضوئيًا أو
تصديرًا من Photoshop — صفحة هي صورة JPEG واحدة كبيرة — يحتفظ بكل ما تحمله الصورة.
على ملف PDF حقيقي مُصدَّر من Photoshop، يترك ذلك 27 وسمًا في مكانها بعد
تنظيف "ناجح"، من بينها IFD0:Software، وطوابع وقت الالتقاط، وصورة مصغّرة معاينة؛
كما تنجو منه أيضًا حزمة C2PA المرفقة بالصورة نفسها.
لذلك يضيف clean_pdf مرورًا ثالثًا، deep_images، مدفوعًا بـ pdfwrite من Ghostscript.
يعمل على درجتين ويتوقف بمجرد أن يصبح الملف نظيفًا:
- بلا فقدان.
pdfwriteمع التمرير المباشر يعيد بناء المستند من مخطط الكائنات بينما ينسخ بيانات الصورة المضغوطة بايتًا بايتًا — وقد تم التحقق من ذلك عبر تجزئة التدفقات قبل وبعد. هذا يزيل كل ما غلّفه PDF حول الصورة. يغطي التمرير المباشر برامج الترميز التي يدعمها Ghostscript له، وهي JPEG (DCTDecode) وJPEG2000 (JPXDecode)؛ أما صور Flate وCCITT وLZW فيتم فك ترميزها وإعادة ترميزها، وهو بلا فقدان عمليًا لتلك البرامج لكنه ليس مطابقًا بايتًا بايتًا.neverهو الخيار لمستند يجب أن تبقى تدفقاته دون مساس. - إعادة الترميز، فقط عند وجود دليل. أي شيء يعيش في مقاطع APPn الخاصة بـ JPEG
نفسها — EXIF في APP1، وحزمة C2PA في APP11، وموارد Photoshop في APP13 — ينتقل
مع البايتات المرتبط بها، لذا يحافظ التمرير المباشر عليه. تشغّل الدرجة 2 نفس المرور
مع إيقاف التمرير المباشر، وفقط عندما تترك الدرجة 1 شيئًا خلفها بشكل مُثبت: علامة
AI/C2PA في أي وضع، أو، تحت
always، أي بيانات وصفية APPn باقية. يُترك APP0 (JFIF) وAPP2 (ICC) دون مساس — الأول بنيوي والثاني يحدد كيفية قراءة الألوان. تُنفق البكسلات على الدليل، لا على الشك.
يأخذ deep_images القيم auto (الافتراضي: الدرجة 1 فقط عندما تنجو العلامات من
تجريد المستند، ثم الدرجة 2 إذا نجت من ذلك)، وalways (الدرجة 1 لكل
PDF، مع التصعيد إلى الدرجة 2 لبيانات EXIF الخاصة بالكاميرا والمحرر أيضًا)، وlossless
(الدرجة 1 فقط — لا إعادة ضغط أبدًا، والإبلاغ عن أي شيء ينجو عبر الحقول المعتادة
still_has_c2pa / post_findings) وnever. تُرفض أي قيمة غير معروفة بدلًا من
معاملتها بهدوء كـ auto. يوضح التقرير أي الدرجات عملت
عبر meta.deep_image_pass وmeta.images_reencoded، وعندما يتم
تخطي المرور فإنه يسمّي الخيار الذي قد يذهب أبعد:```text
deep image pass not needed for AI/C2PA markers; pass deep_images="always"
to also clear non-AI EXIF inside images
بدون تثبيت Ghostscript، لا يزال التنظيف يعمل ويوضح ما تعذّر الوصول إليه:```text
warning: metadata inside embedded images left in place; install ghostscript
for the deep image pass
إزالة العلامة المائية في نطاق البكسل متاحة الآن كخلفية CtrlRegen خارجية اختيارية (انظر أعلاه)؛ إنها مزيل مُعيد للتوليد، وليست ضمانًا. يبقى الربط الناعم C2PA (علامة مائية داخل المحتوى يمكنها إعادة ربط بيان بيانات اعتماد المحتوى البعيد بعد إزالة البيانات الوصفية) خارج النطاق. إزالة C2PA المرتبط ارتباطًا صلبًا لا تمسح تلك القنوات.
المخاطر المتبقية بعد التنظيف
يُبلّغ هذا الأداة عن عمليات إزالة قابلة للتحقق (أعداد Unicode، إجراءات البيانات الوصفية) وعمليات إعادة كتابة الطبقة B بأفضل جهد. ولا يمكنها ضمان فشل كواشف المورّد.
للتحقق من الإشارات المتبقية بنفسك (اختياري، خارجي):
| القناة | ما نزيله | ما قد يبقى | فحص خارجي (أمثلة) |
|---|---|---|---|
| C2PA / EXIF / XMP المرتبط ارتباطًا صلبًا | نعم | علامات مرتبطة ارتباطًا ناعمًا / علامات البكسل | c2patool، Content Credentials verify |
| وسائط من فئة SynthID | إزالة اختيارية للبكسل (CtrlRegen خارجي)؛ وإلا فالنتيجة المحلية | علامة مائية صوتية/مرئية؛ علامة مائية متبقية في البكسل بعد الإزالة | أدوات المزوّد (مثل Google SynthID / كاشف Vertex حيثما توفّر)؛ مُقيّم محلي اختياري reverse-SynthID |
| النص الإحصائي | إعادة كتابة بأفضل جهد | علامات قوية بعد تعديل طفيف | لا يوجد كاشف عالمي عام؛ أدوات المورّد عند توفّرها |
سياق الصناعة ثنائي الطبقات (C2PA + علامة مائية غير محسوسة): دليل Institute of AI PM.
كواشف العلامات المائية
مدققات يوفّرها المورّد للتحقق مما إذا كان المحتوى يحمل علامات مصدر الذكاء الاصطناعي:
- Claude: Check if a file was made with Claude — يقرأ بيانات اعتماد محتوى C2PA في الصور والفيديو والصوت للإبلاغ عمّا إذا كان Claude قد شارك في إنتاج الملف؛ يعمل في المتصفح. واجهة برمجة تطبيقات كشف العلامة المائية النصية من Claude في معاينة خاصة حاليًا.
- OpenAI: Verify OpenAI-generated content — ارفع صورة أو ملف صوتي وتحقق من إشارات مصدر OpenAI (بيانات C2PA الوصفية وعلامات SynthID المائية). تتوفر أيضًا واجهة برمجة تطبيقات برمجية.
- Google DeepMind: SynthID — تقنية العلامات المائية من Google للصور والصوت والنصوص والفيديو المولّدة بالذكاء الاصطناعي، مع نظرة عامة على كيفية تضمين العلامات غير المحسوسة وكشفها.
- Gemini: Verify AI-generated images, videos, and audio — دليل Google للتحقق من الملفات في تطبيق Gemini باستخدام علامات SynthID المائية وبيانات اعتماد المحتوى، بما في ذلك حدود الرفع وكيفية قراءة النتائج.
خيارات الإزالة (ملخص)
| الخيار | ما يزيله | ملاحظات |
|---|---|---|
| تنظيف Unicode (الطبقة A) | ZWSP، bidi، tags، مسافات غريبة، … | الافتراضي الآمن للنص |
| إعادة الكتابة (الطبقة B) | علامات الرموز الإحصائية (بأفضل جهد) | تُقدَّم دائمًا بواسطة المهارة؛ تكلّف أسلوبًا — انظر إخلاء المسؤولية |
| إزالة الحاوية/البيانات الوصفية | مصدر الملف | انظر جدول الصيغ |
| إزالة بكسل CtrlRegen (اختياري) | علامات الصور في نطاق البكسل (فئة SynthID، StegaStamp، Tree-Ring، StableSignature) | خلفية خارجية؛ حوسبة ثقيلة؛ شدة افتراضية متحفظة |
| إزالة بكسل DiffusionPurification (اختياري) | علامات الصور في نطاق البكسل (فئة Tree-Ring) | خلفية MarkDiffusion؛ إعادة توليد عمياء (انحراف أكثر من CtrlRegen)؛ شدة افتراضية متحفظة |
| نماذج محلية مفتوحة الأوزان | تجنّب إعادة الختم بالنموذج الأصلي | بديل تشغيلي |
المصفوفة: skills/remove-ai-marks/references/removal-matrix.md.
الأخلاقيات وإخلاء المسؤولية
انظر skills/remove-ai-marks/references/ethics.md. للخصوصية والبحث على محتواك — وليس للاحتيال الأكاديمي أو ادعاءات كاذبة بأنه "مكتوب بشريًا".
الاستخدام المسؤول: هذا المشروع مخصص للمحتوى الذي تملكه أو المصرّح لك بمعالجته. يجب على المستخدمين الالتزام باللوائح المحلية واستخدامه بمسؤولية. يخلي المطوّرون مسؤوليتهم عن أي إساءة استخدام محتملة من المستخدمين.
المنظومة
مشاريع طرف ثالث تغلّف هذا المستودع أو تكمّله، مُدرجة لأغراض الاكتشاف فقط. لا تتم صيانتها أو المصادقة عليها أو دعمها من هذا المشروع. لا يراجع هذا المشروع شيفرتها، ولا يضمن سلوكها أو تعهداتها، ولا يتحمل مسؤولية أي شيء تثبّته أو تشغّله من هذه القائمة. كل مشروع يخضع لرخصته وصيانته ووثائقه الخاصة — اقرأها قبل استخدامه.
MetaClean — واجهة رسومية لسطح المكتب
MetaClean تطبيق سطح مكتب مستقل بترخيص MIT مكتوب بـ Rust/Tauri (Windows، macOS، Linux) يوفّر واجهة رسومية أصلية مُحزَّمة لتنظيف البيانات الوصفية بالسحب والإفلات، مع أيقونة في شريط النظام وتكامل مع مستكشف الملفات. إنه قاعدة شيفرة منفصلة: لا يستدعي خدمة Python في هذا المستودع، وصيغه المدعومة وضمانات التنظيف فيه تختلف عن هذا المشروع. راجع README الخاص به للتفاصيل.
unmark-web — واجهة ويب في المتصفح
unmark-web عميل ويب ثابت مستقل بترخيص MIT. يزيل علامات Unicode غير المرئية من النص ويجرد البيانات الوصفية للمصدر من الصور بالكامل في المتصفح، ويمكنه اختياريًا استدعاء خدمة HTTP في هذا المستودع للصيغ التي لا يتعامل معها محليًا. إنه قاعدة شيفرة منفصلة وغير مرتبط بهذا المشروع؛ راجع README الخاص به للنطاق والحدود.
DropMarks — واجهة رسومية لـ macOS
DropMarks تطبيق macOS SwiftUI مستقل بترخيص MIT. يستدعي inspect_file.py / clean_file.py في هذا المستودع (واختياريًا rewrite_text.py) عبر نسخة مُضمَّنة من تلك النصوص البرمجية القياسية. إنه قاعدة شيفرة منفصلة وغير مرتبط بهذا المشروع؛ راجع README الخاص به للنطاق والحدود.
إضافة مشروع
لتسجيل مشروع هنا، افتح PR يضيف مدخلًا قصيرًا — اسم المشروع، وما يغلّفه أو يضيفه، ورابطًا لمستودعه الخاص. اجعل المدخلات موجزة وواقعية؛ لا تدّعِ التوافق مع هذا المشروع أو المصادقة منه. ينبغي أن يبني المشروع المُدرج على هذا المستودع أو يدمجه — على سبيل المثال، باستدعاء خدمته أو إعادة استخدام محرك الكشف الخاص به — بدلًا من مجرد معالجة المشكلة نفسها بشكل مستقل. يُرجى تجنّب الأسماء التي تبدأ بـ watermarks-remover أو تشبهه عن قرب — فالأسماء المتشابهة تجعل من الصعب التمييز بين المشاريع.
خطاف ما قبل الالتزام
بوابة CI موجودة بالفعل (تصدير SARIF من audit_dir.py، انظر سياق مصفوفة التغطية) — خطافات pre-commit أدناه تلتقط نفس فئة المشكلة مبكرًا، قبل حتى الالتزام بملف مُعلَّم. كلاهما يغلّف واجهات CLI الموجودة (audit_dir.py / clean_file.py) — بلا منطق كشف منفصل.```yaml
.pre-commit-config.yaml
repos:
- repo: https://github.com/guillaumemeyer/watermarks-remover
rev: v0.5.0 # pin to a tag/commit
hooks:
- id: watermarks-remover-check # fails the commit if marks are found
- id: watermarks-remover-clean # opt-in: cleans staged files in place instead
يفشل `watermarks-remover-check` عملية الـ commit ويسرد النتائج؛ بينما `watermarks-remover-clean` اختياري ويعيد كتابة الملفات المُجهَّزة في مكانها (يخرج بالرمز 1 حتى تراجع الـ diff وتعيد التجهيز — نفس الاصطلاح المتبع في خطافات الإصلاح التلقائي مثل `ruff --fix`). عندما يتعذر على المنظّف معالجة ملف ما على الإطلاق — سواء انهار، أو تم إنهاؤه، أو لم يُنتج أي تقرير — فإن `watermarks-remover-clean` يذكر اسم ذلك الملف ويخرج بالرمز 3 بدلاً من ذلك، حتى لا يُخطئ أبداً في اعتبار منظّف فشل كملف نظيف بالفعل. شغّل أياً منهما يدوياً باستخدام `python3 service/scripts/check_staged.py <files...>` / `clean_staged.py <files...>`.
## الاختبارات```bash
python3 -m venv .venv && .venv/bin/pip install pytest
.venv/bin/python -m pytest # or: make test
make smoke # quick CLI smoke on fixtures
سجل التغييرات
v0.7.0 — إعادة كتابة الطبقة B في /clean، وحدة سرقة العلامات المائية، إزالة العلامات المائية من الصوت/الفيديو، واتساع المعايير/الأدوات
يُدخل الإصدار v0.7.0 إعادة كتابة العلامة الإحصائية للطبقة B داخل خدمة /clean نفسها، مدفوعًا باستراتيجية قابلة للتهيئة ومضبوطة بالمعايير ([email protected],[email protected]). وإلى جانبه: وحدة سرقة العلامات المائية بالصندوق الأسود، وإزالة تدميرية للعلامات المائية من الصوت ومن كل إطار في الفيديو، ومعيار إعادة كتابة أغنى بكثير، ومجموعة من إصلاحات التقوية والأمان والأدوات.
إعادة كتابة الطبقة B في الخدمة
- يشغّل
/cleanإعادة كتابة الطبقة B للنص بعد الطبقة A. يأتي الافتراضي منconfig/clean_strategy.json؛ ويتجاوزهoptions.strategyلكل طلب، ويرفض/cleanبخطأ 400 عندما لا تكون الواجهة الخلفية المطلوبة مهيأة (#315). أسبقية التهيئة:--strategy-config>WATERMARKS_CLEAN_STRATEGY_FILE>config/clean_strategy.json. - تكتيك إعادة كتابة
mlmالجديد: إخفاء جزء من كلمات المحتوى وملؤها بـroberta-large— تعديل محلي غير انحداري تلقائي، بحيث يخلط الناتج تدفق الرموز الأصلي مع تنبؤات النموذج اللغوي المُقنَّع (#311). - يطبّق تكتيك
humanizeالآن تمريرة مهارة الأنْسَنة بشكل حتمي (علامات اقتباس مستقيمة، بلا شرطات en/em، انهيار الحشو،utilize→use) ويسمّي قواعد الكاتب البشري في التوجيه (#311). اكتسبrewrite_text.pyمسار CLI باسم--strategy. - صحة إعادة الكتابة: ترميز الكلمات بترميز Unicode في التباعد المعجمي (#305)؛ مقارنة الهوامش الخام قبل التقريب وتسجيل بيانات وصفية للاختيار / قيم p المرتبة (#249).
المعيار
- بحث وصفة SynthID + قياس متين (#280)؛ إعادة تسمية مفردات إعادة الكتابة، والبحث عبر المدخلات، وترتيب الأنْسَنة أخيرًا (#302)؛ التوصية فقط بالاستراتيجيات التي لا تزال تُجيز بعد صقل الأنْسَنة (#307).
- واجهة Pangram الجماعية كواجهة خلفية للتشابه البشري (#296)؛ معيار مستوى إعادة الكتابة الأدنى المُقوّى مع مجموعة من 30 مستندًا (#257)؛ شبكة أوزان مُتحقَّق منها + بحث وصفة موسّع (#294)؛ مجموعة معيار باللغة البولندية (#295).
سرقة العلامات المائية
- وحدة سرقة العلامات المائية بالصندوق الأسود الجديدة ومُحمِّل مجموعة التوجيهات (#303)؛ مسح الحالة القديمة عند فشل مسبار البدء من جديد (#310).
الصوت / الفيديو / الصورة
- سلسلة إزالة تدميرية للعلامات المائية الصوتية لـ silentcipher/AudioSeal/WavMark (الإيقاع + النغمة + EQ + إعادة ترميز بمعدل بت منخفض → M4A) (#266).
- تنقية فيديو TrustMark لكل إطار تُسقط التصويت الزمني (#265).
- صندوق
uuidلمصدر المحتوى C2PA معروف على MP4/MOV/AVIF/HEIC (#264). - الحفاظ على ذيول MP4 المقتطعة أثناء التجريد (#242)؛ إبقاء وجهة إعادة ترميز الصوت منفصلة عن وجهة تنظيف الحاوية (#278).
- تخطي مخرجات exiftool المُهمَلة وSynthID الزائد في الفحص بعد التنظيف (#261)؛ التدهور بسلاسة عندما لا يستطيع exiftool معالجة ملف PDF (#281).
- تحديد
zTXt/iTXtفي PNG المُفكَّك الضغط عند 1 MiB (#308)؛ تجريد تصريحات DOCTYPE/ENTITY في SVG XML (#288)؛ إبقاء أعضاء DOCX الثنائية آمنة بايتيًا (#314)؛ الحفاظ علىAppVersionفي OOXML (#289).
خدمة HTTP وCLI
- خيار
/cleanللحفاظ على المسافات الغريبة، بما يحاكي CLI (#274)؛ يكشف/inspectفئات الأدلة الصريحة في الحمولة المشبوهة (#277)؛ الطوابع الزمنية في سجلات طلبات HTTP (#256)؛ تمرير بايتات الحمولة إلى تقييم SynthID عبر HTTP وinspect_*لتجنب قراءة عكسية زائدة. - اكتسب
clean_file.pyالخيارات-q/--quiet/--only-changed(#254).
المهارات والإضافة والخطافات
- تقييم الأسلوبية وروافع الكاشف لـ
clean-user-facing-text(#258)؛ جعل مشغّل خطاف PostToolUse متعدد المنصات (#255)؛ يعامل خطاف pre-commit الملفات غير النصية النظيفة المتطابقة بايتيًا كمتغيّرة (#238).
التدقيق
- يفحص
audit_dir.pyملفات المصدر والوثائق وi18n التي تجاوزها الموجّه (#284)؛ يفحص.ts/.tsx/.jsx/.gdويوائم ثقة المسافات عبر الصيغ (#273)؛ دعمaudit_website.py --sarif(#194)؛ تقوية النسخ الاحتياطية في المكان، وحالة الملف النظيف، وحكم SynthID، وID3v2 المقتطع، وتوجيه zip (#201).
الأمان
- إزالة ReDoS متعدد الحدود في فحوص data-URI وJSON-LD (#306)؛ حظر إعادات توجيه HTTP في مُقيِّم SynthID لمنع SSRF (#252).
CI والأدوات والوثائق
- يفشل CI عندما يتعذّر حل متطلبات الواجهة الخلفية الاختيارية (#301)؛ صورة Docker تُبلّغ عن ffmpeg كقابل للاستخدام وتثبّت Ghostscript (#272)؛ ترقيات التبعيات (cython #299، scipy #298، ruff #297، docker/setup-buildx-action #237).
- الوثائق: قسم كاشفات العلامات المائية، مرجع مدونة ETH SRI "Probing SynthID"، سياسة النظام البيئي (إسقاط ClaudeWatermarks؛ اشتراط أن تستخدم المشاريع المُدرَجة هذا المستودع) (#292).
v0.6.0 — تغطية صيغ أوسع، تقوية الطبقة A، توزيع الإضافة والخطافات، وإعادة كتابة موجّهة بالكشف
تغطية الصيغ والحاويات
- AVIF / HEIC: تجريد أصلي للبيانات الوصفية وC2PA من المكتبة القياسية (#84، #85)
- BMP / GIF / TIFF: كشف وفحص وتنظيف بيانات وصفية من المكتبة القياسية — تُسقَط امتدادات تعليق/XMP في GIF بينما يُحافَظ على تكرار
NETSCAPE2.0وكتل الرسوم المتحركة الأخرى؛ تُسقَط بيانات TIFF IFD الوصفية (XMP/EXIF/GPS/IPTC/MakerNote) مع تصفير الحمولات وإبقاء إزاحات التجريد، لكلٍّ من classic وBigTIFF؛ تُقتطع البيانات الوصفية الزائدة في BMP مع إعادة كتابة حقل حجم الملف (#107) - EPUB: تنظيف حاويات من المكتبة القياسية — كشط بيانات OPF الوصفية وXHTML meta/JSON-LD، وتجريد الوسائط النقطية/SVG المضمّنة، وتطبيق الطبقة A على نص جسم XHTML، وإسقاط أجزاء البيانات الوصفية الحاملة للعلامات، وتمرير الأجزاء المشفّرة بـ OCF دون مساس (#107)
- XLSX / PPTX / DOCX (OOXML): كشط أصلي للبيانات الوصفية للحاويات والنص والوسائط المضمّنة من المكتبة القياسية؛ إفراغ حقول مصدر
docPropsفي DOCX دائمًا؛ تقليم العلاقات المعلّقة بعد إزالةcustomXml؛ تشغيل الطبقة A على نص جسم DOCX/ODT؛ فك ترميز كيانات XML قبل كشط الطبقة A (#91، #100، #76، #83، #73، #80، #74، #81، #142) - حاويات SGML/المتجهات: تجريد بيانات وصفية بزمن خطي لـ SVG/ODT (GHSA-7vpp-96qp-j9wh) (#147)؛ فحص وتنظيف متكرر لبيانات URI النقطية المضمّنة في SVG وHTML وMarkdown (#87، #88)
- الصوت / الفيديو: تجريد بيانات AI/C2PA الوصفية لـ MP4/MOV وWAV وMP3 (#139)؛ كشف وإزالة كتلة C2PA في WAV RIFF؛ دعم بيانات C2PA الوصفية في FLAC؛ رفض التحليل الجزئي لإطار ID3v2 (#232)؛ الحفاظ على إزاحات وسائط MP4 عند تجريد البيانات الوصفية (#183)
- PDF: الوصول إلى البيانات الوصفية التي تعيش داخل الصور المضمّنة وإيقاف تغيير حجم PDF لتجريد XMP؛ تشغيل تمريرة الصورة العميقة سواء كان exiftool مثبّتًا أم لا؛ احترام بايتات حشو علامات JPEG ومشاركة ماشي مقاطع واحد
- PNG: كشف أسماء منتجات مولّدات AI في بيانات PNG النصية الوصفية؛ كشف علامات AI في نص PNG المضغوط (#127)؛ إبقاء الذيل المقتطع بدلًا من إسقاطه في تجريدات png/isobmff (#182)
تقوية الطبقة A (Unicode غير المرئي)
- تقوية الطبقة A الموحّدة (#133): تجريد نقاط ترميز
Default_Ignorableالمحجوزة دون استخدام تبادل مشروع (U+2065،U+FFF0–U+FFF8،U+E0000،U+E0080–U+E00FF،U+E01F0–U+E0FFF— يُبلَّغ عنها كـreserved_ignorable)، والأحرف الـ66 غير الحرفية (U+FDD0–U+FDEFبالإضافة إلىU+FFFE/U+FFFFلكل مستوى — يُبلَّغ عنها كـnoncharacter)، وثلاثة حاملات Default_Ignorable بعرض فارغ لم يرها المُلتقِط الشاملCf(U+180F،U+3164،U+FFA0). لكلٍّ منها نفس الحفظ السياقي كما لأشقائها المُغطّاة بالفعل، بحيث لا يتلف نص المقطع الجزئي، ويُطبَّق كلٌّ منها على محرك الخدمة ونسخة المهارة الخفيفة المُضمَّنة - إيقاف تجريد عناصر تحكم التخطيط المرئي المجاورة لكتابتها الخاصة: عناصر تحكم مربعات الهيروغليفية المصرية (
U+13430–U+1343F)، وعناصر تحكم اختزال Duployan (U+1BCA0–U+1BCA3)، وعناصر تحكم العارضة/الرباط/الانحناء/العبارة الموسيقية (U+1D173–U+1D17A) تُحافَظ عليها الآن عند مجاورتها لكتابتها الخاصة وتُجرَّد (وتُعلَّم) عند طفوها بين نص غير ذي صلة؛ لا يزال الوضع المتفشي--strip-emoji-glueيجرّدها في كل مكان - صقل الإيموجي / الكتابة: الحفاظ على VS16 بعد الإيموجي المفرد خارج نطاقات الكتل؛ الحفاظ على روابط الكتابة وإيموجي الأعلام وعلامات Cf العربية؛ الحفاظ على Unicode متعدد اللغات أثناء تنظيف النص (#34)
إعادة كتابة الطبقة B وكشف العلامات المائية
- إعادة كتابة الطبقة B التكرارية الموجّهة بالكشف: يولّد كل جولة متغيرات
--candidates(الافتراضي 1،WATERMARKS_REWRITE_CANDIDATES) ويحدّ--max-loops(الافتراضي 1،WATERMARKS_REWRITE_LOOPS) جولات التقييم، متوقفًا بمجرد اجتياز محاولة للكشف. أسبقية المُقيِّم: MarkLLM (--markllm-scheme) > التباعد المعجمي bigram-Jaccard (احتياطي). يُبلّغrewrite_text.py --json-statsالآن عنevaluator/max_loops/attempts_made/passedوcandidate_scoresلكل محاولة (#153) - التحقق بنفس المفتاح لـ Keyed-Gumbel (Aaronson EXP): يطبّق
detect_gumbel.pyالجديد المعتمد على المكتبة القياسية فقط اختبار الإعادة بلا نموذج (u = PRF(Hash(key, window), token)؛ قيمة p دقيقة لذيل Gamma؛ إخفاء النافذة المتكرر) بلا GPU أو نموذج أو logits. يجعلrewrite_text.py --gumbel-key(المتغيرWATERMARKS_GUMBEL_KEY، المفضّل) منه مُقيِّم الحلقة التكرارية (الأسبقية: gumbel > markllm > التباعد المعجمي) ويُكشَف عنه كـgumbelفي/capabilitiesو/detect. لنفس المفتاح فقط — ليس أوراكل مورّد؛ لا يُسجَّل المفتاح أبدًا (#190) - المعايير: معيار نصي MarkLLM متعدد المخططات وكشف (#188) ومعيار قابل لإعادة الإنتاج لإزالة نص SynthID (#145)؛ المتغيرات الافتراضية
paraphrase:3؛ يحمل التقرير وCSV المحاولات لكل مستند (أعمدةmean_attempts/att،attempts/evaluator/passed)؛ يعكس--rewrite-loopsالخيار--max-loops - الكشف: كشف العلامات المائية النصية للمورّدين (Gemini SynthID، Claude seam، MarkLLM) بالإضافة إلى مُلحَق مُقيِّم صور SynthID (#109)؛ كاشف نص AI إحصائي وأسلوبي جديد بلا LLM لـ CI والتدقيق (#68، #69)
التوزيع: الإضافة والخطافات وتثبيتات المهارة
- أصبح المستودع الآن إضافة Claude Code وسوقًا أحادية الإضافة (
.claude-plugin/plugin.json+marketplace.json)، بحيث تُثبَّت كلتا المهارتين بـ/plugin marketplace add guillaumemeyer/watermarks-removerثم/plugin install watermarks-remover@watermarks-remover، وتُحدَّث في المكان. يشغّلmake plugin-validateالأمرclaude plugin validate . --strict؛ يفحصtests/test_plugin_manifest.pyالبيانات الوصفية دون CLI - اكتسب
install_skill.pyخيار--target(claude-code،claude-project،cowork،cursor) ومُحدِّد--skillيغطي كلتا المهارتين المُصدَّرتين، بالإضافة إلى--listو--linkوCLAUDE_CONFIG_DIR. تبني وجهةcoworkحزمة رفع قابلة لإعادة الإنتاج (dist/<skill>.zip، دليل مهارة واحد في المستوى الأعلى)؛ تتحقق كل وجهة من قواعد تغليف Agent Skills وحد الرفع 30 MB. أهدافmakeالجديدة:install-claude-code-skill،install-claude-code-text-skill،install-claude-project-skill،package-cowork-skill،package-cowork-text-skill - تنظيف تلقائي حتمي عبر خطاف
PostToolUse(hooks/hooks.json+service/scripts/hook_written_file.py): بعد أن يكتب الوكيل ملفًا يشغّل الحاضنة الخطاف سواء تعاون النموذج أم لا. يُبلّغcheck(الافتراضي) النموذج بالعلامات؛ يجرّدهاcleanفي المكان ويخبر النموذج أن الملف تغيّر، مبادلًا فقط عند اختلاف حقيقي بحيث تحتفظ الملفات النظيفة بـ mtime الخاص بها. يأتي الوضع من إعدادhook_modeفي الإضافة أوWATERMARKS_HOOK_MODE؛ يعيد الكشف استخدامaudit_lib.scan_file/is_actionable، بحيث يتفق الخطاف وبوابة pre-commit وتصدير CI SARIF. لا يزال الخطاف عاجزًا عن إعادة كتابة رسالة دردشة المساعد — لا توجد نقطة خطاف كهذه — لذا يبقى ذلك المسار بأفضل جهد - تكامل خطاف pre-commit لفحص/تنظيف الملفات المُجهَّزة (#138)؛ مهارة نص Cursor خفيفة (#35)؛ لم يعد وصف
clean-user-facing-textيسمّي Cursor كمضيف وحيد
خدمة HTTP
- نقاط نهاية الدفعات:
POST /clean/batch،/inspect/batch(#137) وPOST /detect/batch(#151) - الحفاظ على امتدادات صيغ الصور في
/cleanواستخدام كتابات آمنة فيav_meta(#150)؛ استخدام base64 محمول في مثال curl لـ/detect(وإصلاح قابلية نقلrealpathفي macOS في أدوات التمهيد، #185)
التدقيق / الفحص والأمان
- اكتسب
audit_dir.pyتزامنًا متعدد العمال وتصدير SARIF 2.1.0 (#101، #102) - توجيه صيغ موقع الويب الثنائية إلى ماسحاتها الحقيقية (#177)؛ رفض قنابل DTD/entity في محلل sitemap (GHSA-pjg6-92pm-mmcf) (#146)؛ منظّف تعطّل يحجب الالتزام بدلًا من أن يُقرأ كنظيف (#179)؛ ملف نصي غير قابل للقراءة هو فحص فاشل، وليس فحصًا نظيفًا (#169)
إصلاحات الموثوقية والصحة
- تحافظ تشغيلة
--in-placeالثانية على.bakالأصلي؛ الحفاظ على الأدلة المجمّعة عندما يفشل عضو zip لاحق في القراءة (#175)؛ لا تزال حاويات ISOBMFF المقتطعة تشغّل احتياطي المسح البايتي لـ C2PA (#176)؛ التمييز بين منظّف فاشل وملف نظيف بالفعل (#159، #161)؛ معاملة تشغيلة c2patool الفاشلة كغير حاسمة بدلًا من "لا C2PA" (#156)؛ التحقق من أنواع خيارات التنظيف (#111)؛ عدم الاختيار التلقائي لجهاز MPS لكشف العلامات المائية النصية (#99)؛ قابلية النقل في macOS — مخرجات--jsonنقية لـ stdout لمُقيِّم SynthID ومسبارrealpathفي BSD (#70)؛ إصلاح مسارsubprocess_creationflagsفي Windows في_ghostscript_usableومنع العمليات الفرعية من فتح نافذة وحدة تحكم على Windows - تقوية سلوكية: الحفاظ على وضع الإبقاء لتعليقات JPEG الحميدة؛ إصلاح علم
bench-synthid-textالمبتلع؛ تبسيط تمرير الأعلام لمسبار Ghostscript وnoqa غير الضروري في clean_text (lint)
CI / الأدوات / الوثائق
- فحص وتنسيق Ruff مع فرض في CI (#103)؛ إضافة macOS إلى مصفوفة الاختبار (#152)؛ إضافة تهيئة CodeRabbit لمراجعات PR الآلية (#222)؛ CODEOWNERS لـ CODE_OF_CONDUCT/LICENSE ومالكي المراجعة الرئيسية؛ نسب حقوق النشر إلى Guillaume Meyer والمساهمين (#228)
- الوثائق: إرشادات إعادة الكتابة الحافظة للصوت وحماية خيارات الصوت/إمكانية الوصول؛ إضافات النظام البيئي (ClaudeWatermarks، unmark-web) وملاحظة تثني عن الأسماء المشابهة؛ مرجع arXiv 2402.14904؛ دليل البدء التلقائي في Windows عبر Task Scheduler؛ base64 محمول في أمثلة curl؛ تثبيت محرك النص لمهارة Cursor المُضمَّنة على نسخة الخدمة (#96)
غير مُصدَر
- خطاف التنظيف pre-commit (
watermarks-remover-clean/clean_staged.py): استخدام ملخصات المحتوى (SHA-256) وكشف الإجراء النشط بحيث تُعرَف الملفات النظيفة على القرص دون المطالبة بإعادة تجهيز لا نهائية (#173) - الحفاظ على حاوية OOXML: إبقاء
<AppVersion>سليمًا فيdocProps/app.xmlأثناء تنظيف بيانات DOCX وXLSX وPPTX الوصفية لتلبية قيود مخطط ECMA-376 وتجنب أخطاء "المحتوى غير القابل للقراءة" في Microsoft Word/Office (#283)
v0.5.0 — توزيع الخدمة وDocker، وواجهة HTTP API، وأدوات التحقق
توزيع الخدمة / Docker
- فصل المهارة/الخدمة: المهارة (
skills/remove-ai-marks/) أصبحت الآن عميلًا بعيدًا بلا كود عبر HTTP؛ انتقل كل التنفيذ إلىservice/scripts/ويعمل خلفserver.py، نقطة دخول HTTP من المكتبة القياسية (/health،/inspect،/clean،/capabilities) - خدمة HTTP: يكشف
service/scripts/server.pyخط أنابيب التنظيف عبر JSON/base64؛ التقوية تحاكي واجهات CLI (حدود الحجم، حارس ثنائي، كتابات ذرية، افتراضي loopback، مصادقة bearer اختياريةWATERMARKS_SERVER_API_KEY) - OpenAPI: يقدّم
GET /openapi.jsonمواصفة OpenAPI 3.0.3 مُولَّدة ديناميكيًا (مبنية من جدول المسارات + التهيئة الحية، بحيث لا تنحرف أبدًا عن نقاط النهاية الحقيقية)؛ يتحقق CI منها بـopenapi-spec-validator - صورة Docker الأساسية (
service/Dockerfile): خدمة تنظيف كاملة مع exiftool / qpdf / c2patool مثبّتة مسبقًا؛ يبقى أي CLI قابلًا للتشغيل بتجاوز الأمر - Docker / compose: يُشغّل
compose.yamlالبنية التحتية كاملة (coreدائمًا؛markllm/markdiffusionخلفprofile: harness؛ctrlregen/synthidخلفprofile: heavyكبنيات محلية فقط)؛ الخدمات مسبوقة بـwr-؛ خدمات harness/heavy افتراضيًاcommand: ["--help"]بحيث يخرجdocker compose up --profile harness --profile heavyبسلاسة (تُشغَّل واجهات CLI لمرة واحدة بـdocker compose run)؛ يتحققmake compose-check/compose-check.shالجديد من المكدس العامل (رمز الخروج فقط) - نشر GHCR: ينشر
.github/workflows/release-images.ymlصورcoreوmarkllmوmarkdiffusionعلى وسومv*؛ لا يُنشَرctrlregen/synthidأبدًا (ترخيص المنبع) - تهيئة البيئة:
.env.example+ دليل تهيئة الخدمة؛ يحمّلdocker composeملف.envتلقائيًا؛.envمُتجاهَل في git (رفض افتراضيًا) - نظافة المستودع:
.gitignoreوservice/.dockerignoreالآن رفض افتراضيًا — فقط المسارات المسموح بها صراحةً يمكن الالتزام بها أو إرسالها في سياق بناء (سياقات الصور تشحن فقطservice/scripts/، وهو كل ما تنسخه ملفات Dockerfile) - الاختبارات:
tests/test_http_server.py(13 حالة) لخدمة HTTP؛ أُعيد توجيه كل المجموعات إلىservice/scripts/
أداة MarkDiffusion للعلامات المائية في الصور (اختيارية)
- أداة اختيارية جديدة (خارجية
THU-BPM/MarkDiffusion، Apache-2.0):markdiffusion_harness.pyمع أوامر فرعيةwatermark/detect/purifyلتسعة مخططات صور (Tree-Ring، Ring-ID، ROBIN، WIND، SFW، Gaussian-Shading، GaussMarker، PRC، SEAL) - يشغّل
clean_image.py --remove-pixel diffusionهجوم إعادة التوليدDiffusionPurificationمن MarkDiffusion كمحرك بديل لإزالة البكسل (شدة محافظة 0.3 افتراضيًا) - تمهيد
setup_markdiffusion.sh(تثبيت PyPI1.0.2؛ استنساخ--checkoutقابل للتحرير عند commit مثبّت) +requirements-markdiffusion.txt+Dockerfile.markdiffusionوأهداف Makefilebootstrap-markdiffusion/smoke-markdiffusion/docker-markdiffusion-build/docker-markdiffusion-help - اختبارات قائمة على المحاكاة (
tests/test_markdiffusion_harness.py) — بلا torch في CI؛ وثيقة مرجعيةreferences/markdiffusion.md - الوثائق: تحذير التحقق بنفس المخطط فقط (ليس أوراكل كاشف مورّد) وتحذير انحراف إعادة التوليد العمياء في README وSKILL.md و
removal-matrix.mdوmarkdiffusion.md
أداة MarkLLM للعلامات المائية النصية (اختيارية)
- أداة اختيارية جديدة (استنساخ خارجي
THU-BPM/MarkLLM، Apache-2.0):detect_text_watermark.pyمع أوامر فرعيةdetect/watermarkلمخططي KGW وSynthID - يشغّل
rewrite_text.py --markllm-schemeالكشف قبل/بعد حول إعادة كتابة الطبقة B والكشف لكل مرشح عندما--candidates N>1(مبوَّب بالبيئة؛ يُبلّغ عنcleared) - تمهيد
setup_markllm.sh+requirements-markllm.txt(تبعيات مثبّتة) +Dockerfile.markllmوأهداف Makefilebootstrap-markllm/smoke-markllm/docker-markllm-build/docker-markllm-help - التقوية: تحميل النموذج من الذاكرة المؤقتة فقط بـ
--offline(بلا خروج HF، بلا كود بعيد)، حد تهيئة 1 MiB،WATERMARKS_MARKLLM_RLIMIT_ASاختياري على العملية الفرعية لإعادة الكتابة، torch مثبّت في Dockerfile، والتحقق من SHA للاستنساخ فيDockerfile.markllm - اختبارات قائمة على المحاكاة (
tests/test_markllm_detect.py، 21 حالة) — بلا torch في CI؛ تحذير أداة التحقق (نفس التهيئة فقط، ليس أوراكل كاشف مورّد) موثّق في README وSKILL.md وremoval-matrix.mdوvendor-notes.md
إصلاحات وصقل- Layer B: يرسل rewrite_text.py الآن reasoning_effort: "none" افتراضيًا لواجهات openai-compatible الخلفية (--reasoning-effort / WATERMARKS_REWRITE_REASONING_EFFORT؛ off يحذفه). نماذج الاستدلال مثل deepseek-v4-flash تستهلك خلاف ذلك ~100 ثانية من سلسلة التفكير في إعادة كتابة سطر واحد (9,894 مقابل 12 رمز إكمال)
- إصلاح بناء صورة markllm: ثبّت
requirements-markllm.txtالإصدارtokenizers==0.23.1، الذي يتعارض معtransformers==5.15.0(يحددtokenizers<=0.23.0؛ لا يوجد إصدار 0.23.0) — الآن مثبّت علىtokenizers==0.22.2؛ نُقل torch إلى فهرس عجلة CPU (torch==2.13.0.*) بحيث تكون الصورة CPU-only مثلDockerfile.markdiffusion - إصلاح بناء صورة ctrlregen: تثبيتات البحث من حقبة 2023 (
safetensors==0.4.3،transformers==4.37.2→tokenizers<0.19) لا توفر عجلات Python 3.14، لذا أصبحت الصورة الأساسية الآنpython:3.11-slim(مثبّتة بالبصمة، متعددة المعماريات) - إصلاح صور harness في وقت التشغيل: لم ينسخ
Dockerfile.markllmوDockerfile.markdiffusionأبدًاcommon.pyإلى/app(خطأ موجود مسبقًا) — تمت الإضافة - WebP: فحص وتنظيف بيانات وصفية يعتمد على المكتبة القياسية فقط لقطع RIFF
C2PAو XMP و EXIF وملف تعريف ICC (#37) - BMP / GIF / TIFF: كشف وفحص وتنظيف بيانات وصفية يعتمد على المكتبة القياسية فقط — تُسقط امتدادات تعليق/XMP في GIF بينما يُحافظ على تكرار
NETSCAPE2.0؛ تُسقط بيانات TIFF IFD الوصفية (XMP/EXIF/GPS/IPTC/MakerNote) مع تصفير الحمولات والإبقاء على إزاحات الشرائح، لكل من classic و BigTIFF؛ تُقتطع البيانات الوصفية الزائدة في BMP مع إعادة كتابة حقل حجم الملف - EPUB: تنظيف حاوية يعتمد على المكتبة القياسية فقط — تُنقّى بيانات OPF الوصفية و XHTML meta/JSON-LD، وتُجرّد الوسائط النقطية/SVG المضمّنة، ويُطبّق Layer A على نص جسم XHTML، وتُسقط أجزاء البيانات الوصفية الحاملة للعلامات، وتمر الأجزاء المشفّرة بـ OCF دون تغيير
- تعقيم أسماء الملفات: ترفض خدمة HTTP أسماء الإخراج غير الآمنة المقدّمة من العميل
- إصلاح منظّف frontmatter في markdown الذي كان ينهار ويسرّب مفاتيح AI المتداخلة (#25)
- أدوات النصوص ترفض المدخلات الثنائية؛
--force-textيتجاوز ذلك (#24) --jsonلم يعد يكتم رمز الخروج الخاص بالإشارة المتبقية (#30)inspect_fileيطبع اسم الملف في مخرجاته (#50)- الحفاظ على وسوم meta الخاصة بمولّد CMS المختلطة الحالة (#42)
- الحفاظ على المحارف غير المرئية الحاملة للسكربت، وإزالة PUA في Layer A (#38، #52)
- الحفاظ على وصلات السكربت، وإيموجي الأعلام، وعلامات Cf العربية في Layer A (#28)
- تحصين تدقيق الموقع ضد SSRF وقنابل gzip (#49)
- SECURITY.md يشير فقط إلى قناة التنبيهات الخاصة (#51)
- Windows: منافذ PowerShell لسكربتات bootstrap الإعدادية (#40)
- الوثائق: إضافة شارات stars/forks وإزالة مخطط star-history؛ إضافة MarkLLM إلى مراجع README؛ قالب طلب السحب؛ خطة لنشر Docker CLI + API
v0.4.0 — إزالة البكسل، ثقة النتائج، إصلاحات Windows والإيجابيات الكاذبة
إزالة بكسل CtrlRegen الاختيارية (واجهة خلفية خارجية)
- إزالة اختيارية لعلامة مائية في نطاق البكسل عبر نسخة خارجية من
mertizci/noai-watermark: محوّلclean_ctrlregen.py+ bootstrapsetup_ctrlregen.sh(commit مثبّت، sparse checkout، venv، تحقق SHA)، بالإضافة إلىDockerfile.ctrlregenوmake bootstrap-ctrlregen/docker-ctrlregen-build/smoke-ctrlregen clean_image.py --remove-pixel ctrlregenينفّذ تجريد البيانات الوصفية → إزالة CtrlRegen → تقييم اختياري لـ reverse-SynthID قبل/بعد؛inspect_image.pyيلمّح إلى العلامة عند ارتفاع نتيجة SynthID- شدة افتراضية متحفظة
0.25(إعدادات مسبقة 0.15/0.25/0.35/0.5/0.7)؛ خط الأنابيب الأصلي 512×512 يُقسّم تلقائيًا بواسطة الواجهة الخلفية للصور الأكبر؛ تحصل عملية torch الفرعية على حدود موارد أعلى قابلة للتجاوز عبر متغيرات البيئة - لا تُضمّن الواجهة الخلفية أبدًا:
noai-watermarkلا يتضمن ملف LICENSE (يُعامل كجميع الحقوق محفوظة)، ويتم تجاوز مسارات التثبيت/إعادة التشغيل التلقائي باستخدامCtrlRegenEngineمباشرة
ثقة النتائج والتدقيقات المجمّعة
- تُصنّف النتائج الآن
confirmed/probable/informational/likely_false_positive، وتُعرض في JSON الخاص بالنص/الصورة/الحاوية والتقارير البشرية audit_dir.pyالجديد (شجرة تعاودية) وaudit_website.py(اكتشاف sitemap + زحف) يجمعان التقارير؛ موثّقان في SKILL.md
إصلاحات الإيجابيات الكاذبة
- DOCX: فحص
docProps/customXmlفقط، وليس الجسم المرئي (#14) - Text Layer A: الحفاظ على إيموجي
VS16/ZWJبعد قاعدة الإيموجي؛ علامة--strip-emoji-glueالجديدة المتشددة (#22) - HTML: معاملة وسوم مولّد CMS كمعلوماتية، وليست بيانات AI وصفية (#13)
- PDF: استبعاد حمولات stream من فحص بايتات علامة AI (#13)
- تقارير الفحص تشير إلى المسارات غير المدعومة/بأفضل جهد
دعم Windows
- تقييد
preexec_fnوos.fchmodالخاصين بـ POSIX فقط بحيث تعمل الكتابات والأدوات الاختيارية على Windows (#15، #23) - إعادة تهيئة stdio إلى UTF-8 بحيث لا ترفع تدفقات Windows المعاد توجيهها أخطاءً على Unicode غير المرئي؛ مسار CI الخاص بـ Windows + تشغيل CLI التجريبي (#23)
الوثائق وسلسلة التوريد
- قسم CtrlRegen في README + مراجع بحثية (CtrlRegen، UnMarker، تحذير التخفي الجنائي)، إخلاء مسؤولية الاستخدام المسؤول؛ تحديثات SKILL/matrix/vendor-notes/ethics
- إعداد Dependabot + CODEOWNERS لمسار الأمان؛ ترقية scipy/numpy/opencv-python/scikit-learn/pywavelets والصورة الأساسية إلى Python 3.14-slim
- اختبارات CtrlRegen قائمة على المحاكاة (بدون torch في CI)
v0.3.2 — تحصين أمني (كتابات آمنة، عميل HTTP، سلسلة توريد CI)
- كتابات إخراج آمنة وذرّية: كل منظّف يكتب الآن عبر ملف مؤقت + إعادة تسمية ذرّية (
safe_write_bytes/safe_write_text)، ويرفض الوجهات ذات الروابط الرمزية، وينشئ نسخًا احتياطية.bakعبر المسار الآمن نفسه — لم تعد الروابط الرمزية الموضوعة مسبقًا (مثلًا في/tmpأو مجلدات التنزيل) قادرة على إعادة توجيه كتابة نظيفة إلى ملف عشوائي - تحصين عميل HTTP في
rewrite_text.py: تُرفض عمليات إعادة التوجيه كليًا، بحيث لا يمكن أبدًا إعادة إرسال مفتاح API في ترويسةAuthorizationإلى مضيف غير موثّق؛ نقاط النهاية غير المحلية مرفوضة افتراضيًا (اشترك عبر--allow-remoteأوWATERMARKS_REWRITE_ALLOW_REMOTE=1)؛ تُقبل مخططات http(s) فقط؛ أُزيل--api-key— المفاتيح عبر متغيرات البيئة فقط من خلالWATERMARKS_REWRITE_API_KEY - حدود الموارد: الحد الأقصى الافتراضي للمدخلات 1 GiB → 256 MiB، حد جديد لـ stdin بـ 64 MiB، ميزانية zip لـ DOCX/ODT من 512 MiB → 128 MiB، وتطبيق
RLIMIT_AS/RLIMIT_FSIZEعلى العمليات الفرعية exiftool/c2patool/SynthID (كل الحدود قابلة للتجاوز عبر متغيرات البيئة) - سلسلة التوريد: إجراءات CI مثبّتة بـ SHA مع
permissions: contents: read، تبعيات تطوير مثبّتة (requirements-dev.txt)، خطوةpip-audit، وسير عمل CodeQL جديد؛ صورة Docker تعمل الآن كمستخدم غير مميّز مع pip مثبّت - تبعيات المُقيّم: ترقية Pillow من 10.4.0 → 12.3.0 (24 ثغرة CVE معروفة)؛ تم التحقق من استخدام API مقابل commit المنبع المثبّت
- الاختبارات: 18 اختبار انحدار أمني جديد (60 إجمالًا، جميعها ناجحة)
v0.3.1 — إعادة كتابة أقوى للعلامة المائية الإحصائية في Layer B
- إعادة الصياغة الافتراضية في
rewrite_text.pyتنفّذ الآن هجوم اختيار الكلمات + البنية صريحًا (ترتيب الجمل، أدوات الربط، كلمات الانتقال، حدود الجمل، الكلمات الوظيفية) بدلًا من إعادة كتابة عامة --tactic humanizeالجديد: تمريرة "اكتب كإنسان" بدون أمثلة تستهدف الصياغة النمطية بأسلوب AI--tactic codeالجديد: يعيد كتابة التعليقات و docstrings وحرفيات النصوص، ويعيد تسمية المعرّفات المحلية مع الحفاظ على السلوك وأسماء API العامة- التمريرة البنيوية تُنتج الآن "نثرًا بشريًا طبيعيًا ومتنوعًا" بدلًا من "الأسلوب المهني الواضح" النموذجي لـ AI
--temperatureالجديد (افتراضي0.9) لكل من واجهات Ollama و OpenAI-compatible الخلفية--candidates Nالجديد: يولّد N إعادة كتابة ويختار الأكثر تباعدًا معجميًا (مسافة bigram Jaccard) مع حارس انحراف الطول- نظافة نموذج أقوى: تفضيل النماذج المحلية مفتوحة الأوزان وتجنّب أي مورّد معروف بعلامة مائية، وليس فقط المصدر المشتبه به
- تقارير المخاطر المتبقية تميّز الآن بين النص القصير/القابل للتنبؤ بشدة (خطر أقل) والنثر الطويل عالي الإنتروبيا (خطر أعلى)
- تحديث الوثائق في
SKILL.mdوremoval-matrix.mdوvendor-notes.md؛ تغطي الاختبارات المطالبات الجديدة وتقييم التباعد واختيار المرشحين
v0.3.0 — تقييم اختياري لبكسل SynthID
- مُقيّم اختياري لـ SynthID في نطاق البكسل عبر نسخة خارجية من
aloshdenny/reverse-SynthID(score_synthid.py)؛ يظهر فيinspect_image.py/clean_image.pyمعREVERSE_SYNTHID_DIRأو--synthid-dir - bootstrap
setup_synthid.sh(تبعيات المُقيّم فقط؛--fullيثبّت متطلبات المنبع)؛Dockerfile.synthidبالإضافة إلىmake docker-synthid-build/docker-synthid-help - أهداف
smoke-synthidوbootstrap-synthidفي Makefile - اختبارات لمحوّل المُقيّم، ومسار CLI غير المتاح، وتحليل JSON، وأخطاء وقت التشغيل
- الوثائق: الكشف/التقييم فقط (بدون إزالة البكسل)؛ كود المنبع غير مضمّن ويبقى تحت رخصته البحثية غير التجارية
v0.2.0 — إصلاح إيجابية كاذبة في c2patool
image_meta.py: لم يعدhas_manifestيعتبرError: No claim found/No JUMBF data foundبيانًا (خطأ أسبقية العمليات: العلامات السلبية الآن تُبطل كل فرع إيجابي)tests/test_c2patool_report.pyالجديد (4 حالات: لا claim، لا JUMBF، بيان حقيقي، الأداة غائبة)- الوثائق: إصلاح روابط
c2patool(انتقل المستودع إلىcontentauth/c2pa-rs)؛ إضافة إخلاء مسؤولية حول تكلفة الجودة لإزالة العلامة المائية النصية
v0.1.0 — تحسين التغليف + صدق المصدر
Makefile(test/smoke/install-skill) وpytest.ini- عينات fixtures لـ Markdown و HTML و SVG؛ اختبار تنظيف PDF المتدهور
- الوثائق: نموذج الطبقتين الصناعي (C2PA صلب الربط مقابل الربط الناعم / SynthID-media)
- جدول المخاطر المتبقية في README + روابط لأدوات التحقق الخارجية
- مرجع: دليل C2PA/SynthID من Institute of AI PM
- العلامات المائية للربط الناعم والبكسل/الصوت/الفيديو خارج النطاق صراحةً في skill/matrix/ethics
v0.0.1 — الإصدار الأولي متعدد المورّدين
- مهارة الوكيل
remove-ai-marks(تحل محلremove-claude-marksالخاص بـ Claude فقط) - Layer A: Unicode غير المرئي / bidi / محارف الوسوم / homoglyphs المسافات (
inspect_text/clean_text) - Layer B: إرشادات إعادة الكتابة +
rewrite_text.pyالاختياري (print-prompt، Ollama، OpenAI-compatible) - الملفات: تجريد بيانات C2PA/AI الوصفية لـ PNG و JPEG و SVG و PDF و DOCX و ODT و HTML و Markdown
inspect_file.py/clean_file.pyالموحّدان- وثائق متعددة المورّدين (Claude، Gemini/SynthID-class، OpenAI، open-LLM)