הקונטיינר והשרת ה-HTTP הם שתי דלתות לאותו קורפוס, ולכן חייבים לדרג אותו. הם לא עשו זאת: הקונטיינר רץ `MULTIMODAL_ENABLED=true`, בעוד שרת ה-MCP נפל לברירת-המחדל `false` — `~/.env` אינו מגדיר את הדגל כלל. סוכן ו-UI יכלו לענות על אותה שאלה מתוך קבוצות-תוצאות שונות, בלי שאיש ידע (G2 — מסלול מקביל שמתפצל בשקט). מה שונה - `MULTIMODAL_ENABLED: "true"` בקונפיג ה-pm2 של legal-mcp-http. - תיעוד השירות ב-operations-runbook (פורט, שער-Bearer, ו**האזהרה שהחשובה מכולן**: קוד ב-`mcp-server/` לא נכנס לתוקף עד restart — זהו תהליך ארוך-חיים, בניגוד ל-stdio שנטען בכל סשן. שלושה מיזוגים היום רצו על קוד ישן עד שהבחנתי). מה **לא** שונה, ולמה `STORAGE_BACKEND` נשאר לא-מוגדר. הקונטיינר רץ `s3`, השרת נופל ל-`filesystem`, וזה דריפט אמיתי — סוכן שכותב דרך MCP כותב לדיסק בעוד ה-web קורא מ-S3. אבל הפיכת הדגל דורשת `MINIO_ENDPOINT`/`ACCESS_KEY`/ `SECRET_KEY`, ואלה **אינם ב-Infisical** — רק ב-env של Coolify. בלעדיהם `MINIO_ENDPOINT` נפתר לברירת-המחדל `http://minio:9000` (שם-רשת פנימי של דוקר, לא נגיש מהמארח) וכל פעולת-בלוב נשברת. יצירת סוד חדש מחייבת אישור חיים לשם/תיקייה/תגית, ולכן הדריפט מתועד בבירור ב-runbook עם בדיוק מה שצריך כדי לסגור אותו — במקום להישאר ידיעה שבעל-פה. MinIO **כן** נגיש מהמארח: `https://s3.nautilus.marcusgroup.org` מחזיר 200 ב-`/minio/health/live` (אומת), כך שהחסם היחיד הוא הסודות. invariants: G2 (שתי דלתות, דירוג אחד) · §6 (הדריפט הנותר מתועד, לא נבלע)
19 KiB
Operations Runbook — עוזר משפטי
תוכן תפעולי-עומק שהוצא מ-
CLAUDE.mdכדי לרזות את ההקשר הנטען בכל סשן (TaskMaster #107.1). ה-CLAUDE.md מחזיק את הכללים הקריטיים בקצרה; כאן נמצאים הפרטים המלאים, הפקודות, וטבלאות-הייחוס. כשעובדים על Deploy, Paperplip-ops, או adapters — לקרוא את הסעיף הרלוונטי כאן.
שרת Nautilus (158.178.131.193)
| שירות | תפקיד | כתובת |
|---|---|---|
| Coolify | ניהול containers | http://158.178.131.193:8000 |
| PostgreSQL + pgvector | בסיס נתונים ראשי | legal-ai-postgres (localhost:5433, user legal_ai) |
| Redis | תור משימות | legal-ai-redis |
| Gitea | מאגר קוד | gitea.nautilus.marcusgroup.org/ezer-mishpati |
| ezer-mishpati-web | ממשק העלאת מסמכים (Docker/Coolify) | legal-ai.nautilus.marcusgroup.org |
| Paperclip | סוכן AI — מריץ Claude Code agents (pm2, מקומי) | localhost:3100 |
| legal-chat-service | גשר claude CLI לטאב הצ'אט ב-/training (pm2, loopback) | 127.0.0.1:8770 |
| Infisical | ניהול סודות — פרויקט All Infrastructure (2c462576-b125-4279-b0ec-7220dbf51ccf), env main. סודות המערכת מפוזרים על שלוש תיקיות-אחיות: /apps/legal-ai · /apps/paperclip · /apps/paperclip-hermes |
secret.marcus-law.co.il |
ארכיטקטורת Deploy — חובה לקרוא
שלושה מודלי-הרצה דרים יחד. ערבוב ביניהם הוא הטעות הנפוצה ביותר.
עוזר משפטי (Legal-AI) — Docker container דרך Coolify
- UUID:
gyjo0mtw2c42ej3xxvbz8zio(build_pack:dockerimage, לאdockerfile) - שינוי קוד ב-
web/אוweb-ui/לא נכנס לתוקף עד ש:- עושים
git commit+git push origin main - Gitea Actions בונה image → דוחף ל-registry → מפעיל redeploy ב-Coolify (
mcp__coolify__deploy) - ממתינים ~2-4 דקות לבנייה
- עושים
- אסור לנסות להריץ uvicorn /
next devמקומית — אין סביבת Python על המכונה - ה-container מריץ Next.js (
:3000, חשוף) + FastAPI (:8000, פנימי) - בדיקה:
curl https://legal-ai.nautilus.marcusgroup.org/api/health - runbook מלא של ה-pipeline:
~/CI-CD-MIGRATION-GUIDE.md
Paperclip — מקומית דרך pm2
- פורט:
localhost:3100, DB:localhost:54329(Postgres embedded) - שינויי קוד נכנסים לתוקף אחרי
pm2 restart paperclip - אין צורך ב-Docker או Coolify (מיגרציה ל-Coolify נוסתה 2026-04-04 והוחזרה 2026-04-08)
- תרגום/RTL:
~/.paperclip/hebrew/→bash ~/.paperclip/hebrew/apply-hebrew.sh && pm2 restart paperclip
legal-chat-service — מקומית דרך pm2 (מאפריל 2026)
- פורט:
127.0.0.1:8770(loopback בלבד) - שירות aiohttp קצר שעוטף את
claudeCLI ב-streaming + session continuation, ומשרת את הטאב "שיחה" בדף/training. הקונטיינר משדל אליו proxy דרךhost.docker.internal:8770. - קוד:
mcp-server/src/legal_mcp/chat_service/ - התקנה:
pm2 start /home/chaim/legal-ai/scripts/legal-chat-service.config.cjs && pm2 save - בריאות:
curl http://127.0.0.1:8770/health→{"ok":true,...} - שינויי קוד:
pm2 restart legal-chat-service - אפס עלות API — claude CLI משתמש ב-claude.ai subscription של chaim. הנחת היסוד של
claude_session.py(claude CLI מקומי בלבד) נשמרת. - Coolify dependency: ה-Service Definition של legal-ai חייב להכיל
extra_hosts: host.docker.internal:host-gateway(אחרת ה-proxy יקבל ConnectError).
legal-mcp-http — שרת ה-MCP ב-HTTP (מאוגוסט 2026)
- פורט:
127.0.0.1:8790(loopback בלבד) · קונפיג:scripts/legal-mcp-http.config.cjs - הדלת של סוכני הפלטפורמה אל 108 כלי ה-MCP. סשן אינטראקטיבי מגיע לאותו שרת ב-stdio דרך
~/.claude.json— אותו קוד, שתי תחבורות (G2). - שער Bearer (
MCP_HTTP_SHARED_SECRET); השרת מסרב לעלות בלי טוקן. בדיקה: ללא טוקן →401, עם טוקן →200. - ⚠️ שינוי קוד ב-
mcp-server/לא נכנס לתוקף עדpm2 restart legal-mcp-http. זה תהליך ארוך-חיים, בניגוד ל-stdio שנטען מחדש בכל סשן. אחרי מיזוג שנוגע ב-mcp-server/— להפעיל מחדש, אחרת הסוכנים ירוצו על קוד ישן בשקט.
🚨 דריפט תצורה פתוח —
STORAGE_BACKENDהקונטיינר רץ
STORAGE_BACKEND=s3; שרת ה-MCP נופל לברירת-המחדלfilesystem, כי~/.envאינו מגדיר אותו. כלומר סוכן שכותב קובץ דרך MCP כותב לדיסק, בעוד ה-web קורא מ-S3 — שני מסלולים שמתפצלים (G2 / INV-STG1).למה זה עדיין לא תוקן: ההעברה ל-s3 דורשת
MINIO_ENDPOINT+MINIO_ACCESS_KEY+MINIO_SECRET_KEY, ואלה אינם קיימים ב-Infisical — רק ב-env של Coolify. הפיכת הדגל בלעדיהם תפתור אתMINIO_ENDPOINTלברירת-המחדלhttp://minio:9000(שם-רשת פנימי של דוקר, לא נגיש מהמארח) ותשבור כל פעולת-בלוב.מה שצריך כדי לסגור: ליצור את שלושת המפתחות ב-Infisical תחת
/apps/legal-ai(תגיתcredentials, באישור חיים — ראה כלל "Saving a NEW secret" ב-~/CLAUDE.md), לטעון אותם בקונפיג ה-pm2 כמוMCP_HTTP_SHARED_SECRET, ואז להוסיףSTORAGE_BACKEND: "s3". MinIO כן נגיש מהמארח:https://s3.nautilus.marcusgroup.org(אומת —/minio/health/liveמחזיר 200).
מבנה תיקיות
/home/chaim/legal-ai/
├── CLAUDE.md ← אינדקס דק (כללים קריטיים + מצביעים)
├── docs/operations-runbook.md ← הקובץ הזה (עומק תפעולי)
├── Dockerfile ← Docker build
├── docs/ ← תיעוד + לקחים
│ ├── architecture.md ארכיטקטורה
│ ├── block-schema.md 12 בלוקים (המסמך החשוב ביותר)
│ ├── migration-plan.md תוכנית מעבר vault → DB
│ ├── legal-decision-lessons.md לקחים מ-3 החלטות
│ └── memory.md הקשר כללי — skills, פרויקטים
├── skills/ ← כלי עבודה ומדריכים
│ ├── decision/ מדריך סגנון + references + 12 בלוקים
│ ├── assistant/ קטלוג מסמכים
│ ├── docx/ עיצוב DOCX
│ ├── dafna-decision-template/ export DOCX לפי תבנית Word של דפנה
│ └── new-company-setup/ blueprint הוספת חברה חדשה
├── .claude/
│ └── agents/ ← הוראות סוכנים + HEARTBEAT.md (symlinks ב-Paperclip)
│ ├── HEARTBEAT.md checklist הפעלה משותף לכל הסוכנים
│ ├── legal-ceo.md תזמורן + בקרת זרימה
│ ├── legal-writer.md כתיבת בלוקים בסגנון דפנה
│ ├── legal-analyst.md ניתוח משפטי + חילוץ טענות
│ ├── legal-researcher.md חיפוש תקדימים
│ ├── legal-qa.md 7 שערי איכות
│ ├── legal-proofreader.md תיקון OCR
│ ├── legal-exporter.md ייצוא DOCX סופי
│ └── hermes-curator.md סוכן Hermes לניתוח סגנון post-export
├── data/
│ ├── training/ ← 4 החלטות לאימון (DOCX)
│ ├── exports/ ← טיוטות DOCX מיוצאות
│ └── cases/{case-number}/ ← תיקי עררים (מבנה שטוח, סטטוס ב-DB)
├── web/ ← FastAPI backend (Python): 75+ API endpoints
│ ├── app.py ← API ראשי
│ ├── paperclip_api.py ← אינטגרציית Paperclip: `pc_request()` + `emit_case_status_webhook()`
│ ├── paperclip_client.py ← legacy client (ישן — השתמש ב-paperclip_api.py)
│ └── gitea_client.py ← אינטגרציית Gitea
├── web-ui/ ← Next.js frontend (TypeScript/React): ממשק המשתמש
│ └── next.config.ts ← proxy: /api/* → FastAPI :8000
├── mcp-server/ ← MCP server + services + tools
├── adapters/ ← Paperclip external adapters
│ └── deepseek-paperclip-adapter/ ← `deepseek_local` (Hermes-pinned ל-DeepSeek profile)
└── scripts/ ← סקריפטים וכלי עזר (ראה scripts/SCRIPTS.md)
└── .archive/ ← סקריפטים שהושלמו (לא להריץ)
Paperclip — כללי אינטגרציה (פירוט מלא)
הכללים הקריטיים בתמצית נמצאים ב-
CLAUDE.md. כאן הפירוט המלא, הדוגמאות, וה-"למה".
Wakeup API — תמיד דרך API, לעולם לא דרך DB
- הנתיב הנכון:
POST /api/agents/{agent-id}/wakeup(לא/wake!) - ⚠️ אסור:
INSERT INTO agent_wakeup_requestsישירות — זה יוצר רק רשומה בליheartbeat_run, והסוכן לא יתעורר לעולם - ⚠️ חובה לשלוח
payloadעםissueId— בלי זה הסוכן מתעורר בלי הקשר (בלי תיק, בלי issue, בלי cwd נכון) - דוגמה נכונה:
{"source": "automation", "triggerDetail": "system", "reason": "...", "payload": {"issueId": "...", "mutation": "comment", "commentId": "..."}} - Board API Key: שמור ב-DB (
board_api_keys), auth:Authorization: Bearer pbk_...
ניתוב comments דרך CEO
- כשמשתמש כותב תגובה על issue ב-Paperclip, הפלאגין (
plugin-legal-ai) מעיר את ה-CEO דרךctx.agents.invoke() - ה-CEO קורא את ה-comment, מחליט על ניתוב, ויוצר issue לסוכן המתאים
- כל הסוכנים חייבים לקרוא comments אחרונים לפני שהם מתחילים לעבוד (HEARTBEAT שלבים 2b-2c)
קריאות API — תמיד דרך helper, לעולם לא curl ישיר
- bash (סוכנים):
~/legal-ai/scripts/pc.sh <METHOD> <PATH> [BODY_JSON]— מוסיף Authorization, X-Paperclip-Run-Id, Content-Type, base URL. ראהHEARTBEAT.md §0. - Python (FastAPI):
from web.paperclip_api import pc_request; await pc_request("POST", "/api/...", json={...})— שימוש ב-board API key. - אסור
curl ... $PAPERCLIP_API_URLישיר ב-bash; אסורhttpx.AsyncClientישיר ל-Paperclip ב-Python. - למה: ה-skill הרשמי דורש
X-Paperclip-Run-Idבכל קריאה משנה issue. אצלנו ה-audit trail עבד ממילא דרך JWT claims (runId: runIdHeader || claims.run_id), אבל ה-helper מבטיח עקביות + תאימות ל-board API keys (long-lived) שלא נושאות JWT claims.
Cross-company agent sync — אחרי כל שינוי הגדרות
- יש 14 סוכנים = 7 × 2 חברות (CMP=1xxx, CMPA=8xxx). Paperclip מחייב
agents.company_id NOT NULL— אין shared agents. - Master = CMP (1xxx), Mirror = CMPA (8xxx).
- אחרי כל שינוי ב-
adapter_config,runtime_config,budget_monthly_cents, או skills של סוכן ב-master (UI, SQL, או API), חובה להריץ:PAPERCLIP_BOARD_API_KEY=$(...infisical...) \ python ~/legal-ai/scripts/sync_agents_across_companies.py --verify # לבדיקה PAPERCLIP_BOARD_API_KEY=$(...) \ python ~/legal-ai/scripts/sync_agents_across_companies.py --apply # לסנכרן - הסקריפט מסנן local skills שלא קיימים ב-CMPA (מציג אזהרה), משתמש ב-API (לא DB ישיר), יוצר revisions, idempotent.
- שאלות ה-skill הרשמי של Paperclip —
paperclipskill תחתpaperclipai/paperclip.
Webhook יוצא — עדכון סטטוס תיק לפלאגין
כשסטטוס תיק משתנה דרך PUT /api/cases/{case_number}, הבקאנד שולח webhook אסינכרוני לפלאגין:
PUT /api/cases/{case_number} → emit_case_status_webhook() [BackgroundTask]
→ POST /api/plugins/marcusgroup.legal-ai/webhooks/case-status
→ plugin-legal-ai/onWebhook()
→ comment בעברית על issue + CEO wakeup (כשסטטוס = qa_failed)
- הקוד ב-
web/paperclip_api.py(emit_case_status_webhook), fire-and-forget, timeout 5s - הפלאגין שומר idempotency key ב-state עם TTL 5 דקות למניעת spam על retry
GET /api/cases/stale?days=N— תיקים שלא עודכנו N ימים; מוחרגים:new,final,exportedGET /api/chair-feedback/weekly-summary— סיכום פידבק YU"R לשבוע האחרון
Scheduled Jobs (plugin-legal-ai)
| Job | לוח זמנים | מה עושה |
|---|---|---|
stale-case-reminder |
יומי 08:00 | שולח comment אזהרה על תיקים תקועים >3 ימים |
weekly-feedback-analysis |
ראשון 19:00 | מעיר CEO לניתוח פידבק YU"R ועדכון docs/legal-decision-lessons.md |
sync-case-status |
כל 30 דק' | מסנכרן סטטוסי תיקים בין legal-ai ל-Paperclip |
CEO שמתעורר מ-weekly-feedback-job כותב לקובץ בלבד — אין לו issueId, אל תנסה לפרסם comment או לסגור issue.
External adapters — deepseek_local
- מיקום ה-package:
adapters/deepseek-paperclip-adapter/(לא ב-node_modules). - רישום ב-Paperclip: רשומה ב-
~/.paperclip/adapter-plugins.json(נטען אוטומטית ב-startup דרךbuildExternalAdapters). אין צורך בעריכתnode_modules. - מה ה-adapter עושה: spawnל-
hermes chatעםHERMES_HOME=/home/chaim/.hermes/profiles/deepseekכך שה-CLI טוען אתconfig.yaml(base_url=https://api.deepseek.com/v1,provider=custom,key_env=DEEPSEEK_API_KEY) ואת.env(שמכיל את ה-key). - מודלים זמינים (lookup ב-DeepSeek
/v1/models):deepseek-v4-pro(default),deepseek-v4-flash. יופיעו כדרופ-דאון ב-UI. - התקנה מחדש / עדכון:
curl -X POST -H "Authorization: Bearer pcapi_legal_install_key_2026" -H "Content-Type: application/json" -d '{"packageName":"/home/chaim/legal-ai/adapters/deepseek-paperclip-adapter","isLocalPath":true}' http://localhost:3100/api/adapters/install. לעדכון hot —POST /api/adapters/deepseek_local/reload. - ⚠ Cross-company sync:
sync_agents_across_companies.pyמדלג על סוכנים עםadapter_typeשונה בין CMP ל-CMPA. כשעוברים סוכן ל-deepseek_localחובה להחיל ידנית בשתי החברות לפני sync. - תוספת adapters עתידיים (OpenAI ישיר, Anthropic ישיר, וכו'): אותו דפוס. ה-package הראשי חייב לייצא
createServerAdapter()שמחזיר{ type, label, models, agentConfigurationDoc, execute, testEnvironment, sessionCodec, listSkills, syncSkills, ... }. ראה אתadapters/deepseek-paperclip-adapter/dist/index.jsכתבנית.
External adapters — Hermes Curator (curator-cmp / curator-cmpa)
- פרופילי Hermes נפרדים לסוכן
hermes-curator— מנתח החלטות סופיות ומציע עדכוני SKILL.md/lessons.md - מיקום:
~/.hermes/profiles/curator-cmp/+~/.hermes/profiles/curator-cmpa/ - מופעל אחרי export סופי; אינו מעדכן קבצים ישירות
- תהליך אישור הצעות: הצעות ה-curator מגיעות כ-comment ב-Paperclip → חיים בוחן ומאשר ידנית → commits ל-
SKILL.mdו-docs/legal-decision-lessons.md
הערות יו"ר (Chair Feedback)
מנגנון לתיעוד הערות דפנה על טיוטות:
- DB: טבלת
chair_feedback(case_id, block_id, feedback_text, category, lesson_extracted) - API:
GET/POST /api/feedback,PATCH /api/feedback/{id}/resolve - MCP tools:
record_chair_feedback,list_chair_feedback - UI: דף ניהול ב-
/feedback(ב-Next.js) - קטגוריות: missing_content, wrong_tone, wrong_structure, factual_error, style, other
ניהול משימות — Gitea Issues (פירוט)
TaskMaster AI הוצא משימוש ב-2026-08-05. מקור-האמת לעבודה הוא Gitea Issues. ה-workflow, מערכת התיוג ו-Session-Start Protocol מתועדים ב-
CLAUDE.md; טבלת המיפוי TaskMaster→issue ב-taskmaster-to-gitea-migration.md.
עבודה מול ה-API
הטוקן: ~/.git-credentials (שורת gitea.nautilus) או Infisical /apps/legal-ai GITEA_TOKEN.
API=https://gitea.nautilus.marcusgroup.org/api/v1/repos/ezer-mishpati/legal-ai
T=$(grep -m1 'gitea.nautilus' ~/.git-credentials | sed 's|.*://[^:]*:\([^@]*\)@.*|\1|')
# מה הבא בתור (דילוג על in-progress/in-review/blocked/deferred/needs-decision)
curl -s -H "Authorization: token $T" "$API/issues?state=open&labels=priority:p1-high"
# issue חדש
curl -s -X POST -H "Authorization: token $T" -H "Content-Type: application/json" \
"$API/issues" -d '{"title":"...","body":"## What & Why\n...\n## Acceptance Criteria\n...","labels":[<ids>]}'
# תוויות (id ← שם)
curl -s -H "Authorization: token $T" "$API/labels?limit=100"
ארכיון TaskMaster
~/legal-ai/.taskmaster/tasks/tasks.json מוקפא לקריאה-בלבד — 166 משימות שהושלמו,
4 שבוטלו, ו-26 הפתוחות שהועברו. שימושי לחפירה היסטורית ("למה עשינו X"), אבל
אין לכתוב אליו. אם צריך הקשר ממשימה ישנה — לחפש בקובץ הזה, ולפתוח issue חדש
אם נדרשת עבודה.