Un agent autonome runnable qui crée des PDF depuis du Markdown/JSON/texte, modifie des PDF existants (fusion, ajout de page, filigrane, métadonnées), et extrait le contenu PDF vers JSON — en CLI ou en mode écoute.
Table of contents
What's in this pack
| File | Purpose |
|---|---|
README.md |
This file. Overview, quick-start, runnable agent. |
SOUL.md |
Mission, valeurs, OODA loop, anti-patterns de l'agent PDF. |
PROMPTS.md |
Prompts système prêts à l'emploi pour piloter l'agent en langage naturel. |
SPEC.md |
Spec technique : sous-commandes, interfaces, codes de sortie. |
CHECKLIST.md |
Checklist de déploiement pas-à-pas. |
DEPLOYMENT.md |
Self-deploy (venv + deps) et mode « attente d'instructions ». |
pdf_agent.py |
Agent runnable : create / modify / extract / listen. |
requirements.txt |
Dépendances pip réellement utilisées par pdf_agent.py. |
deploy.sh |
Self-deploy : venv + pip install -r requirements.txt + smoke-test. |
pdf-creator-v1.json |
Manifest machine-readable du pack. |
Target audience
- Agents A2A qui doivent produire un livrable PDF (facture, rapport, contrat) à la volée.
- Back-offices : fusionner annexes + document principal, tamponner un filigrane « CONFIDENTIEL ».
- Pipelines documentaires : convertir une sortie JSON/Markdown d'un autre agent en PDF final.
- Tout workflow sans humain qui reçoit des instructions et doit écrire/modifier des fichiers PDF.
Agent architecture
Instruction (CLI / stdin / JSON)
│
▼
┌──────────────────┐
│ OBSERVE │ → lire l'entrée (fichier .md/.json/.txt ou PDF source)
└────────┬─────────┘
▼
┌──────────────────┐
│ ORIENT │ → détecter le type d'entrée + la commande (create | modify | extract)
└────────┬─────────┘
▼
┌──────────────────┐
│ DECIDE │ → choisir le moteur : fpdf2 (riche) ou générateur pur-Python (offline)
└────────┬─────────┘
▼
┌──────────────────┐
│ ACT │ → create : Markdown/JSON → PDF
│ │ → modify : fusion / +page / filigrane / métadonnées (pypdf)
│ │ → extract : PDF → JSON (texte + métadonnées)
└────────┬─────────┘
▼
┌──────────────────┐
│ VERIFY │ → le PDF existe, démarre par %PDF, taille > 0
└──────────────────┘
Quick-start
- Copy all files to your agent workspace.
bash deploy.shto create the venv and install dependencies.python pdf_agent.py --help— affiche l'aide, aucun appel réseau, ne crash jamais.python pdf_agent.py create --in doc.md --out doc.pdf— premier PDF.- Read
SPEC.mdto wirelisten/ JSON instructions into your A2A pipeline.
Runnable agent
python pdf_agent.py --help # aide, jamais de crash
python pdf_agent.py create --in doc.md --out doc.pdf # Markdown → PDF
python pdf_agent.py create --in data.json --out data.pdf --title "Rapport"
python pdf_agent.py modify --in a.pdf --add b.pdf --out merged.pdf # fusion
python pdf_agent.py modify --in a.pdf --watermark "CONFIDENTIEL" --out wm.pdf
python pdf_agent.py modify --in a.pdf --meta Title=Facture --meta Author=Bot --out m.pdf
python pdf_agent.py extract --in document.pdf --out document.json # extraction JSON
echo 'create --in doc.md --out doc.pdf' | python pdf_agent.py listen # mode écoute
Dépendances : fpdf2 (création riche, retour à la ligne) et pypdf (modification). Au
lancement d'une action, l'agent vérifie/installe ses dépendances (désactivable via
--no-install). Sans fpdf2 ni réseau, create bascule sur un générateur PDF pur-Python
déterministe — utilisé par les tests et garanti hors-ligne. modify et extract requièrent pypdf.
Use a local model (Ollama)
The optional LLM step can also run on your own machine through Ollama, so no text leaves your server. No account and no API key.
ollama pull <model> # any chat model you like
export PACK_LLM=ollama # prefer Ollama even if a coding-agent CLI is installed
export PACK_OLLAMA_MODEL=<model> # optional: default is the first chat model the server lists
python3 -c "import llm_adapter; print(llm_adapter.detect())" # prints: ollama
PACK_OLLAMA_HOSTpoints to another server (defaulthttp://127.0.0.1:11434);OLLAMA_HOSTis also read whenPACK_LLM=ollama.PACK_OLLAMA_NUM_CTXsets the context size if your reports or prompts are long.- Without
PACK_LLM, a local Ollama server is used only when no Claude Code, Kimi Code or Codex CLI is found. - If the server is down or the model is missing, the pack keeps working in its deterministic mode.
Learning memory (local and private)
Every LLM call this pack makes is journaled in ./.pack_memory/ next to the code, so the pack can improve for you. Nothing is ever sent anywhere.
- What is kept: the task name, the masked input, the masked answer, status and latency. Keys, tokens, e-mails, phone numbers, IP addresses, card and IBAN numbers and personal paths are masked before writing; the system prompt is never stored.
- Teach it:
python3 pack_memory.py feedback last okconfirms a result,python3 pack_memory.py feedback last bad --fix "the right answer"corrects it. Confirmed and corrected runs are reused as validated examples when a similar input comes in. Unrated runs are never injected into a prompt. - Find similar cases:
python3 pack_memory.py similar "some text"andpython3 pack_memory.py clusters(groups of near-identical failures) work in plain Python, no model needed. Optional meaning-based matching uses an embedding model you already run locally:python3 pack_memory.py enable-semantic(Ollama on this machine only). - Share, only if you want:
python3 pack_memory.py exportwrites an anonymised summary (counts, statuses, size buckets, no text) for you to review. - Control:
PACK_MEMORY=0turns it off,PACK_MEMORY_STORE_TEXT=0keeps fingerprints only,python3 pack_memory.py purge --yeswipes it.
