
تطبيق يضع الخصوصية أولاً ويزيل العلامات المائية للذكاء الاصطناعي من المحتوى الذي تملكه.
_ _ _ ____ ___ ____ ____ _ _ ____ ____ _ _ ____ ____ ____ _ _ ____ _ _ ____ ____
| | | |__| | |___ |__/ |\/| |__| |__/ |_/ [__ __ |__/ |___ |\/| | | | | |___ |__/
|_|_| | | | |___ | \ | | | | | \ | \_ ___] | \ |___ | | |__| \/ |___ | \
مهارة وكيل + خدمة Python بالمكتبة القياسية لإزالة علامات المصدر متعددة الموردين للذكاء الاصطناعي من النصوص والملفات — من أجل الخصوصية والنظافة على المحتوى الذي تملكه. المهارة عبارة عن عميل رفيع: تقود الآلية عبر HTTP، لذا لا يحتاج مضيف الوكيل إلى Python.
الموردون / الأنظمة البيئية (على مستوى الفئة): 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. النص الذي
يوجد فقط في سجل الدردشة لا يزال يعتمد على سير عمل المهارة،
والذي يقوم على تعليمات النموذج وبالتالي فهو بأفضل جهد ممكن.
المستودع هو أيضًا 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/skills على جهازك — بل تحمّل
المهارات الممكّنة لحساب claude.ai الخاص بك، والتي تتم مزامنتها عند بدء الجلسة.
لذا ثبّت هناك عن طريق رفع حزمة:```bash
python3 install_skill.py --skill remove-ai-marks --target cowork
ثم، في تطبيق 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
mkdir -p .grok/skills ln -sfn "$(pwd)/skills/remove-ai-marks" .grok/skills/remove-ai-marks
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
راجع docs/windows-autostart.md للتشغيل التلقائي للخدمة عند تسجيل الدخول إلى ويندوز بدون Docker.
للاطلاع على البنية التحتية الكاملة (النواة + الواجهات الاختيارية/الخلفيات الثقيلة)، راجع Docker / compose أدناه.
أدوات النظام الاختيارية (تُستخدم تلقائيًا عند توفرها — مثبّتة مسبقًا في صورة Docker الأساسية):
تحتاج السكربتات الأساسية إلى Python 3.10+ من المكتبة القياسية فقط. استدعاءات نموذج الطبقة B اختيارية.
SCRIPTS=service/scripts
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
python3 "$SCRIPTS/inspect_text.py" draft.md python3 "$SCRIPTS/clean_text.py" draft.md -o draft.cleaned.md --stats
python3 "$SCRIPTS/rewrite_text.py" draft.md --backend print-prompt --tactic paraphrase
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 من المكتبة القياسية (service/scripts/server.py) — الواجهة التي تستخدمها المهارة والطريقة التي يمكن لأي تطبيق ويب التكامل بها دون تضمين:
تُكرّر نقاط النهاية المجمّعة نفس خط الأنابيب لكل ملف كما في /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.
يتطلب تنظيف النصوص إعداد 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".
make docker-synthid-build
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). هذا للكشف/التقييم فقط — فهو لا يزيل العلامات المائية
على مستوى البكسل.
بالنسبة للعلامات المائية للصور في نطاق البكسل (فئة 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 المثبّت على
النسخ الموجودة، وليس فقط النسخ الجديدة.
SCRIPTS=service/scripts
"$SCRIPTS/setup_ctrlregen.sh"
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 (خطوات إزالة الضوضاء الفعالة ≈ الخطوات × الشدة).
CtrlRegen هو ControlNet لـ Stable Diffusion 1.5 بحجم 512×512. تقوم الخلفية بحل هذا للمدخلات العشوائية، لذا لا يتم كشف أي تجزئة إضافية هنا:
الصور الكبيرة جداً (مثل 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 الرسمي هو المرجع النهائي.
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 عادةً يحتاج مساحات عناوين كبيرة.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 ذلك.
لإجراء تجارب مضبوطة على الصور، تُغلِّف أداة خارجية اختيارية
THU-BPM/MarkDiffusion (Apache-2.0)،
وهي مجموعة أدوات لعلامات الماء التوليدية لنماذج الانتشار الكامن (تُضمِّن العلامات
— ولا تزيلها). نستخدمها لثلاثة أغراض:
DiffusionPurification
الخاص بها متاح عبر clean_image.py --remove-pixel diffusion، كبديل
لـ CtrlRegen. إنه إعادة توليد عمياء (بدون تهيئة ControlNet)،
لذا يُحرِّف محتوى الصورة أكثر من CtrlRegen — القيمة الافتراضية للشدة
محافظة (0.3)، ويُعامَل كخيار احتياطي/للمقارنة، وليس ضمانًا أبدًا.الخلفية غير مضمَّنة. يُنشئ setup_markdiffusion.sh بيئة افتراضية
ويثبِّت markdiffusion==1.0.2 من PyPI (مثبَّتة)، مع تثبيت torch من
فهرس المنصة الصحيح؛ أما --checkout فيثبِّت نسخة قابلة للتحرير عند
التزام مثبَّت بدلًا من ذلك. يُنزَّل نموذج Stable Diffusion (الافتراضي
huanzi05/stable-diffusion-2-1-base) من Hugging Face عند أول تشغيل.```bash
SCRIPTS=service/scripts
"$SCRIPTS/setup_markdiffusion.sh"
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
MARKDIFFUSION_DIR=~/markdiffusion
~/markdiffusion/.venv/bin/python "$SCRIPTS/markdiffusion_harness.py" purify
wm.png -o wm.purified.png --purification-intensity 0.3 --json
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.
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
كلا المرورين أعلاه يعملان على المستند: قاموس 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 هو الخيار
لمستند يجب أن تبقى تدفقاته دون مساس.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 + علامة مائية غير محسوسة): دليل Institute of AI PM.
مدققات يوفّرها المورّد للتحقق مما إذا كان المحتوى يحمل علامات مصدر الذكاء الاصطناعي:
المصفوفة: skills/remove-ai-marks/references/removal-matrix.md.
انظر skills/remove-ai-marks/references/ethics.md. للخصوصية والبحث على محتواك — وليس للاحتيال الأكاديمي أو ادعاءات كاذبة بأنه "مكتوب بشريًا".
الاستخدام المسؤول: هذا المشروع مخصص للمحتوى الذي تملكه أو المصرّح لك بمعالجته. يجب على المستخدمين الالتزام باللوائح المحلية واستخدامه بمسؤولية. يخلي المطوّرون مسؤوليتهم عن أي إساءة استخدام محتملة من المستخدمين.
مشاريع طرف ثالث تغلّف هذا المستودع أو تكمّله، مُدرجة لأغراض الاكتشاف فقط. لا تتم صيانتها أو المصادقة عليها أو دعمها من هذا المشروع. لا يراجع هذا المشروع شيفرتها، ولا يضمن سلوكها أو تعهداتها، ولا يتحمل مسؤولية أي شيء تثبّته أو تشغّله من هذه القائمة. كل مشروع يخضع لرخصته وصيانته ووثائقه الخاصة — اقرأها قبل استخدامه.
MetaClean تطبيق سطح مكتب مستقل بترخيص MIT مكتوب بـ Rust/Tauri (Windows، macOS، Linux) يوفّر واجهة رسومية أصلية مُحزَّمة لتنظيف البيانات الوصفية بالسحب والإفلات، مع أيقونة في شريط النظام وتكامل مع مستكشف الملفات. إنه قاعدة شيفرة منفصلة: لا يستدعي خدمة Python في هذا المستودع، وصيغه المدعومة وضمانات التنظيف فيه تختلف عن هذا المشروع. راجع README الخاص به للتفاصيل.
unmark-web عميل ويب ثابت مستقل بترخيص MIT. يزيل علامات Unicode غير المرئية من النص ويجرد البيانات الوصفية للمصدر من الصور بالكامل في المتصفح، ويمكنه اختياريًا استدعاء خدمة HTTP في هذا المستودع للصيغ التي لا يتعامل معها محليًا. إنه قاعدة شيفرة منفصلة وغير مرتبط بهذا المشروع؛ راجع README الخاص به للنطاق والحدود.
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
repos:
يفشل `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
/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 باسم .المعيار
سرقة العلامات المائية
الصوت / الفيديو / الصورة
uuid لمصدر المحتوى C2PA معروف على MP4/MOV/AVIF/HEIC (#264).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).الأمان
CI والأدوات والوثائق
تغطية الصيغ والحاويات
NETSCAPE2.0 وكتل الرسوم المتحركة الأخرى؛ تُسقَط بيانات TIFF IFD الوصفية (XMP/EXIF/GPS/IPTC/MakerNote) مع تصفير الحمولات وإبقاء إزاحات التجريد، لكلٍّ من classic وBigTIFF؛ تُقتطع البيانات الوصفية الزائدة في BMP مع إعادة كتابة حقل حجم الملف (#107)docProps في DOCX دائمًا؛ تقليم العلاقات المعلّقة بعد إزالة customXml؛ تشغيل الطبقة A على نص جسم DOCX/ODT؛ فك ترميز كيانات XML قبل كشط الطبقة A (#91، #100، #76، #83، #73، #80، #74، #81، #142)تقوية الطبقة A (Unicode غير المرئي)
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 بعرض فارغ لم يرها المُلتقِط الشامل (، ، ). لكلٍّ منها نفس الحفظ السياقي كما لأشقائها المُغطّاة بالفعل، بحيث لا يتلف نص المقطع الجزئي، ويُطبَّق كلٌّ منها على محرك الخدمة ونسخة المهارة الخفيفة المُضمَّنةإعادة كتابة الطبقة 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)detect_gumbel.py الجديد المعتمد على المكتبة القياسية فقط اختبار الإعادة بلا نموذج (u = PRF(Hash(key, window), token)؛ قيمة p دقيقة لذيل Gamma؛ إخفاء النافذة المتكرر) بلا GPU أو نموذج أو logits. يجعل (المتغير ، المفضّل) منه مُقيِّم الحلقة التكرارية (الأسبقية: gumbel > markllm > التباعد المعجمي) ويُكشَف عنه كـ في و. لنفس المفتاح فقط — ليس أوراكل مورّد؛ لا يُسجَّل المفتاح أبدًا (#190)التوزيع: الإضافة والخطافات وتثبيتات المهارة
.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 البيانات الوصفية دون CLIinstall_skill.py خيار --target (claude-code، claude-project، cowork، cursor) ومُحدِّد يغطي كلتا المهارتين المُصدَّرتين، بالإضافة إلى و و. تبني وجهة حزمة رفع قابلة لإعادة الإنتاج (، دليل مهارة واحد في المستوى الأعلى)؛ تتحقق كل وجهة من قواعد تغليف Agent Skills وحد الرفع 30 MB. أهداف الجديدة: ، ، ، ، خدمة 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)إصلاحات الموثوقية والصحة
--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 ومنع العمليات الفرعية من فتح نافذة وحدة تحكم على Windowsbench-synthid-text المبتلع؛ تبسيط تمرير الأعلام لمسبار Ghostscript وnoqa غير الضروري في clean_text (lint)CI / الأدوات / الوثائق
watermarks-remover-clean / clean_staged.py): استخدام ملخصات المحتوى (SHA-256) وكشف الإجراء النشط بحيث تُعرَف الملفات النظيفة على القرص دون المطالبة بإعادة تجهيز لا نهائية (#173)<AppVersion> سليمًا في docProps/app.xml أثناء تنظيف بيانات DOCX وXLSX وPPTX الوصفية لتلبية قيود مخطط ECMA-376 وتجنب أخطاء "المحتوى غير القابل للقراءة" في Microsoft Word/Office (#283)توزيع الخدمة / Docker
skills/remove-ai-marks/) أصبحت الآن عميلًا بعيدًا بلا كود عبر HTTP؛ انتقل كل التنفيذ إلى service/scripts/ ويعمل خلف server.py، نقطة دخول HTTP من المكتبة القياسية (/health، /inspect، /clean، /capabilities)service/scripts/server.py خط أنابيب التنظيف عبر JSON/base64؛ التقوية تحاكي واجهات CLI (حدود الحجم، حارس ثنائي، كتابات ذرية، افتراضي loopback، مصادقة bearer اختيارية WATERMARKS_SERVER_API_KEY)GET /openapi.json مواصفة OpenAPI 3.0.3 مُولَّدة ديناميكيًا (مبنية من جدول المسارات + التهيئة الحية، بحيث لا تنحرف أبدًا عن نقاط النهاية الحقيقية)؛ يتحقق CI منها بـ openapi-spec-validatorservice/Dockerfile): خدمة تنظيف كاملة مع exiftool / qpdf / c2patool مثبّتة مسبقًا؛ يبقى أي CLI قابلًا للتشغيل بتجاوز الأمرأداة 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 (تثبيت PyPI 1.0.2؛ استنساخ --checkout قابل للتحرير عند commit مثبّت) + requirements-markdiffusion.txt + Dockerfile.markdiffusion وأهداف Makefile bootstrap-markdiffusion / / / أداة MarkLLM للعلامات المائية النصية (اختيارية)
THU-BPM/MarkLLM، Apache-2.0): detect_text_watermark.py مع أوامر فرعية detect / watermark لمخططي KGW وSynthIDrewrite_text.py --markllm-scheme الكشف قبل/بعد حول إعادة كتابة الطبقة B والكشف لكل مرشح عندما --candidates N>1 (مبوَّب بالبيئة؛ يُبلّغ عن cleared)setup_markllm.sh + requirements-markllm.txt (تبعيات مثبّتة) + Dockerfile.markllm وأهداف Makefile bootstrap-markllm / smoke-markllm / docker-markllm-build / docker-markllm-helpإصلاحات وصقل- Layer B: يرسل rewrite_text.py الآن reasoning_effort: "none" افتراضيًا لواجهات openai-compatible الخلفية (--reasoning-effort / WATERMARKS_REWRITE_REASONING_EFFORT؛ off يحذفه). نماذج الاستدلال مثل deepseek-v4-flash تستهلك خلاف ذلك ~100 ثانية من سلسلة التفكير في إعادة كتابة سطر واحد (9,894 مقابل 12 رمز إكمال)
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.markdiffusionsafetensors==0.4.3، transformers==4.37.2 → tokenizers<0.19) لا توفر عجلات Python 3.14، لذا أصبحت الصورة الأساسية الآن python:3.11-slim (مثبّتة بالبصمة، متعددة المعماريات)Dockerfile.markllm و أبدًا إلى (خطأ موجود مسبقًا) — تمت الإضافةإزالة بكسل CtrlRegen الاختيارية (واجهة خلفية خارجية)
mertizci/noai-watermark: محوّل clean_ctrlregen.py + bootstrap setup_ctrlregen.sh (commit مثبّت، sparse checkout، venv، تحقق SHA)، بالإضافة إلى Dockerfile.ctrlregen و make bootstrap-ctrlregen / docker-ctrlregen-build / smoke-ctrlregenclean_image.py --remove-pixel ctrlregen ينفّذ تجريد البيانات الوصفية → إزالة CtrlRegen → تقييم اختياري لـ reverse-SynthID قبل/بعد؛ inspect_image.py يلمّح إلى العلامة عند ارتفاع نتيجة SynthID0.25 (إعدادات مسبقة 0.15/0.25/0.35/0.5/0.7)؛ خط الأنابيب الأصلي 512×512 يُقسّم تلقائيًا بواسطة الواجهة الخلفية للصور الأكبر؛ تحصل عملية torch الفرعية على حدود موارد أعلى قابلة للتجاوز عبر متغيرات البيئةnoai-watermark لا يتضمن ملف LICENSE (يُعامل كجميع الحقوق محفوظة)، ويتم تجاوز مسارات التثبيت/إعادة التشغيل التلقائي باستخدام مباشرةثقة النتائج والتدقيقات المجمّعة
confirmed / probable / informational / likely_false_positive، وتُعرض في JSON الخاص بالنص/الصورة/الحاوية والتقارير البشريةaudit_dir.py الجديد (شجرة تعاودية) و audit_website.py (اكتشاف sitemap + زحف) يجمعان التقارير؛ موثّقان في SKILL.mdإصلاحات الإيجابيات الكاذبة
docProps/customXml فقط، وليس الجسم المرئي (#14)VS16/ZWJ بعد قاعدة الإيموجي؛ علامة --strip-emoji-glue الجديدة المتشددة (#22)دعم Windows
preexec_fn و os.fchmod الخاصين بـ POSIX فقط بحيث تعمل الكتابات والأدوات الاختيارية على Windows (#15، #23)الوثائق وسلسلة التوريد
safe_write_bytes / safe_write_text)، ويرفض الوجهات ذات الروابط الرمزية، وينشئ نسخًا احتياطية .bak عبر المسار الآمن نفسه — لم تعد الروابط الرمزية الموضوعة مسبقًا (مثلًا في /tmp أو مجلدات التنزيل) قادرة على إعادة توجيه كتابة نظيفة إلى ملف عشوائيrewrite_text.py: تُرفض عمليات إعادة التوجيه كليًا، بحيث لا يمكن أبدًا إعادة إرسال مفتاح API في ترويسة Authorization إلى مضيف غير موثّق؛ نقاط النهاية غير المحلية مرفوضة افتراضيًا (اشترك عبر --allow-remote أو WATERMARKS_REWRITE_ALLOW_REMOTE=1)؛ تُقبل مخططات http(s) فقط؛ أُزيل --api-key — المفاتيح عبر متغيرات البيئة فقط من خلال WATERMARKS_REWRITE_API_KEYRLIMIT_AS/ على العمليات الفرعية exiftool/c2patool/SynthID (كل الحدود قابلة للتجاوز عبر متغيرات البيئة)rewrite_text.py تنفّذ الآن هجوم اختيار الكلمات + البنية صريحًا (ترتيب الجمل، أدوات الربط، كلمات الانتقال، حدود الجمل، الكلمات الوظيفية) بدلًا من إعادة كتابة عامة--tactic humanize الجديد: تمريرة "اكتب كإنسان" بدون أمثلة تستهدف الصياغة النمطية بأسلوب AI--tactic code الجديد: يعيد كتابة التعليقات و docstrings وحرفيات النصوص، ويعيد تسمية المعرّفات المحلية مع الحفاظ على السلوك وأسماء API العامة--temperature الجديد (افتراضي 0.9) لكل من واجهات Ollama و OpenAI-compatible الخلفية--candidates N الجديد: يولّد N إعادة كتابة ويختار الأكثر تباعدًا معجميًا (مسافة bigram Jaccard) مع حارس انحراف الطولSKILL.md و removal-matrix.md و vendor-notes.md؛ تغطي الاختبارات المطالبات الجديدة وتقييم التباعد واختيار المرشحينaloshdenny/reverse-SynthID (score_synthid.py)؛ يظهر في inspect_image.py / clean_image.py مع REVERSE_SYNTHID_DIR أو --synthid-dirsetup_synthid.sh (تبعيات المُقيّم فقط؛ --full يثبّت متطلبات المنبع)؛ Dockerfile.synthid بالإضافة إلى make docker-synthid-build / docker-synthid-helpsmoke-synthid و bootstrap-synthid في Makefileimage_meta.py: لم يعد has_manifest يعتبر Error: No claim found / No JUMBF data found بيانًا (خطأ أسبقية العمليات: العلامات السلبية الآن تُبطل كل فرع إيجابي)tests/test_c2patool_report.py الجديد (4 حالات: لا claim، لا JUMBF، بيان حقيقي، الأداة غائبة)c2patool (انتقل المستودع إلى contentauth/c2pa-rs)؛ إضافة إخلاء مسؤولية حول تكلفة الجودة لإزالة العلامة المائية النصيةMakefile (test / smoke / install-skill) و pytest.iniremove-ai-marks (تحل محل remove-claude-marks الخاص بـ Claude فقط)inspect_text / clean_text)rewrite_text.py الاختياري (print-prompt، Ollama، OpenAI-compatible)inspect_file.py / clean_file.py الموحّدان| الطبقة | الهدف | الكيفية |
|---|
| 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 |
| الأداة | الدور |
|---|
c2patool | فحص بيانات C2PA الوصفية |
exiftool | إزالة البيانات الوصفية المتبقية (خاصة PDF) |
qpdf | إعادة بناء هيكلية PDF — مطلوب لإزالة حقيقية من PDF (انظر أدناه) |
| 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", ...}}, ...]} |
| القناة | ما نزيله | ما قد يبقى | فحص خارجي (أمثلة) |
|---|
| C2PA / EXIF / XMP المرتبط ارتباطًا صلبًا | نعم | علامات مرتبطة ارتباطًا ناعمًا / علامات البكسل | c2patool، Content Credentials verify |
| وسائط من فئة SynthID | إزالة اختيارية للبكسل (CtrlRegen خارجي)؛ وإلا فالنتيجة المحلية | علامة مائية صوتية/مرئية؛ علامة مائية متبقية في البكسل بعد الإزالة | أدوات المزوّد (مثل Google SynthID / كاشف Vertex حيثما توفّر)؛ مُقيّم محلي اختياري reverse-SynthID |
| النص الإحصائي | إعادة كتابة بأفضل جهد | علامات قوية بعد تعديل طفيف | لا يوجد كاشف عالمي عام؛ أدوات المورّد عند توفّرها |
| الخيار | ما يزيله | ملاحظات |
|---|
| تنظيف Unicode (الطبقة A) | ZWSP، bidi، tags، مسافات غريبة، … | الافتراضي الآمن للنص |
| إعادة الكتابة (الطبقة B) | علامات الرموز الإحصائية (بأفضل جهد) | تُقدَّم دائمًا بواسطة المهارة؛ تكلّف أسلوبًا — انظر إخلاء المسؤولية |
| إزالة الحاوية/البيانات الوصفية | مصدر الملف | انظر جدول الصيغ |
| إزالة بكسل CtrlRegen (اختياري) | علامات الصور في نطاق البكسل (فئة SynthID، StegaStamp، Tree-Ring، StableSignature) | خلفية خارجية؛ حوسبة ثقيلة؛ شدة افتراضية متحفظة |
| إزالة بكسل DiffusionPurification (اختياري) | علامات الصور في نطاق البكسل (فئة Tree-Ring) | خلفية MarkDiffusion؛ إعادة توليد عمياء (انحراف أكثر من CtrlRegen)؛ شدة افتراضية متحفظة |
| نماذج محلية مفتوحة الأوزان | تجنّب إعادة الختم بالنموذج الأصلي | بديل تشغيلي |
--strategyCfU+180FU+3164U+FFA0U+13430–U+1343F)، وعناصر تحكم اختزال Duployan (U+1BCA0–U+1BCA3)، وعناصر تحكم العارضة/الرباط/الانحناء/العبارة الموسيقية (U+1D173–U+1D17A) تُحافَظ عليها الآن عند مجاورتها لكتابتها الخاصة وتُجرَّد (وتُعلَّم) عند طفوها بين نص غير ذي صلة؛ لا يزال الوضع المتفشي --strip-emoji-glue يجرّدها في كل مكانrewrite_text.py --gumbel-keyWATERMARKS_GUMBEL_KEYgumbel/capabilities/detectparaphrase:3؛ يحمل التقرير وCSV المحاولات لكل مستند (أعمدة mean_attempts / att، attempts / evaluator / passed)؛ يعكس --rewrite-loops الخيار --max-loops--skill--list--linkCLAUDE_CONFIG_DIRcoworkdist/<skill>.zipmakeinstall-claude-code-skillinstall-claude-code-text-skillinstall-claude-project-skillpackage-cowork-skillpackage-cowork-text-skillPostToolUse (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. لا يزال الخطاف عاجزًا عن إعادة كتابة رسالة دردشة المساعد — لا توجد نقطة خطاف كهذه — لذا يبقى ذلك المسار بأفضل جهدclean-user-facing-text يسمّي Cursor كمضيف وحيد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 الجديد من المكدس العامل (رمز الخروج فقط).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/smoke-markdiffusiondocker-markdiffusion-builddocker-markdiffusion-helptests/test_markdiffusion_harness.py) — بلا torch في CI؛ وثيقة مرجعية references/markdiffusion.mdremoval-matrix.md وmarkdiffusion.md--offline (بلا خروج HF، بلا كود بعيد)، حد تهيئة 1 MiB، WATERMARKS_MARKLLM_RLIMIT_AS اختياري على العملية الفرعية لإعادة الكتابة، torch مثبّت في Dockerfile، والتحقق من SHA للاستنساخ في Dockerfile.markllmtests/test_markllm_detect.py، 21 حالة) — بلا torch في CI؛ تحذير أداة التحقق (نفس التهيئة فقط، ليس أوراكل كاشف مورّد) موثّق في README وSKILL.md وremoval-matrix.md وvendor-notes.mdDockerfile.markdiffusioncommon.py/appC2PA و XMP و EXIF وملف تعريف ICC (#37)NETSCAPE2.0؛ تُسقط بيانات TIFF IFD الوصفية (XMP/EXIF/GPS/IPTC/MakerNote) مع تصفير الحمولات والإبقاء على إزاحات الشرائح، لكل من classic و BigTIFF؛ تُقتطع البيانات الوصفية الزائدة في BMP مع إعادة كتابة حقل حجم الملف--force-text يتجاوز ذلك (#24)--json لم يعد يكتم رمز الخروج الخاص بالإشارة المتبقية (#30)inspect_file يطبع اسم الملف في مخرجاته (#50)CtrlRegenEngineRLIMIT_FSIZEpermissions: contents: read، تبعيات تطوير مثبّتة (requirements-dev.txt)، خطوة pip-audit، وسير عمل CodeQL جديد؛ صورة Docker تعمل الآن كمستخدم غير مميّز مع pip مثبّت