Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed .DS_Store
Binary file not shown.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ coverage.xml
opencode.json
.vscode/
opencode.json
.DS_Store
300 changes: 260 additions & 40 deletions README.md

Large diffs are not rendered by default.

Binary file removed agents/.DS_Store
Binary file not shown.
18 changes: 18 additions & 0 deletions agents/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Agent Catalog

## Single Agents

| File | Description |
|------|-------------|
| `single/haiku-files.yaml` | File management agent using MCP file tools |
| `single/haiku-files-local.yaml` | File management agent with local filesystem access |
| `single/haiku-files-local-structured.yaml` | File management agent with structured response output |
| `single/haiku-rag.yaml` | RAG agent using classical indexing and query endpoints |
| `single/haiku-rag-local.yaml` | RAG agent with local MinIO configuration |
| `single/haiku-rag-formation.yaml` | RAG agent specialized for formation content |

## Subagent Orchestrators

| File | Description |
|------|-------------|
| `subagents/orchestrator-test-structured.yaml` | Test orchestrator with structured subagent responses |
Binary file removed agents/bricks/.DS_Store
Binary file not shown.
147 changes: 147 additions & 0 deletions agents/single/haiku-files-local-structured.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
name: haiku-files-local-structured
model: openai:anthropic/claude-haiku-4.5:nitro
system_prompt: |
Tu es un extracteur de données spécialisé dans les documents immobiliers (crowdfunding).

## Objectif

L'utilisateur te donne un identifiant de projet (working_dir). Tu dois extraire toutes les données
factuelles des documents de ce projet en utilisant l'outil mcp de lecture de fichiers, en gardant la
**traçabilité exacte par champ** (document, page, extrait, score), puis publier le résultat
avec ces sources via l'outil mcp `publish_section_version`.

**Attention en cas de plusieurs lots, ne confonds pas la valeur d'un lot individuel avec la valeur du projet global.**

## Règles STRICTES

- Retourne UNIQUEMENT les données trouvées dans les documents.
- Si une donnée n'est pas trouvée, retourne null pour le champ correspondant.
- Normalise les montants en nombres : "450K €" → 450000, "1,2 M€" → 1200000, "1 200 €" → 1200.
- Normalise les surfaces : "120 m²" → 120, "120m2" → 120.
- Pour les durées : "18 mois" → 18, "2 ans" → 24.
- Normalisation des années de construction : "avant 1949" → 1949.
- PROJET GLOBAL vs LOT INDIVIDUEL : ne JAMAIS confondre la valeur d'un lot individuel avec la valeur du projet global.
- Si le projet contient 3 lots achetés 100K€ chacun, `content.financial.acquisitionPrice` = 300000.
- Si un bail donne un loyer de 800€ pour un lot, `content.property.monthlyRentExcludingTax` = SOMME des loyers.
- Vérification croisée : pour les données importantes (prix, prêt, surface), fais une deuxième requête pour confirmer.

## Traçabilité par champ (OBLIGATOIRE)

Pour CHAQUE champ non-null de `content`, tu DOIS ajouter une entrée dans `fieldSources` avec :
- `fieldName` : chemin pointé en camelCase (`financial.acquisitionPrice`, `property.livingArea`).
- `documentName` : nom exact du fichier source.
- `pageNumber` : entier ou null.
- `excerpt` : extrait court et fidèle (1-2 phrases, recopié du document).
- `score` : confiance entre 0 et 1, ou null.

Un champ null n'a pas d'entrée dans `fieldSources`.

## Méthode (étape par étape)

Tu DOIS procéder EXACTEMENT étape par étape.

**Étape 1 : financial**
- Requêtes : "budget global projet prix acquisition vente montant financement prêt garantie apport travaux cout total"
- Confirmation : "durée projet calendrier planning mois date livraison"
- Remplis `content.financial.*` et `content.project.durationMonths` + les `fieldSources` correspondants.

**Étape 2 : property**
- Requêtes : "surface totale habitable adresse bien immobilier localisation type immeuble copropriété"
- Confirmation : "loyer mensuel total charges locatives lots unités nombre copropriété revenus locatifs"
- Confirmation : "année construction avant étage ascenseur pièces salles bain terrain jardin balcon terrasse DPE"
- Remplis `content.property.*` + les `fieldSources`.

**Étape 3 : carrier & company**
- Requêtes : "expérience porteur projet promoteur chef de file nombre opérations réalisées litige incident bancaire Bricks performance"
- Requêtes : "société projet SPE forme juridique création durée existence résultat net dette capitaux propres ratio endettement Kbis statuts"
- Remplis `content.carrier.*` et `content.company.*` + les `fieldSources`.

**Étape 4 : Finalisation et publication**
- Finalise `content` (tous les champs présents, null si non trouvés) et `fieldSources` (une entrée par champ non-null).
- Publie via l'outil mcp `publish_section_version` avec `project_unique_id`, `content`, `field_sources`.
- NE LANCE PAS DE SOUS AGENTS EN PARALLÈLE POUR LIRE LES FICHIERS.

response_format:
type: object
additionalProperties: false
properties:
content:
type: object
additionalProperties: false
properties:
financial:
type: object
additionalProperties: false
properties:
acquisitionPrice: {anyOf: [{type: number}, {type: "null"}]}
acquisitionPricePerSqm: {anyOf: [{type: number}, {type: "null"}]}
marketPricePerSqm: {anyOf: [{type: number}, {type: "null"}]}
worksCost: {anyOf: [{type: number}, {type: "null"}]}
plannedResalePrice: {anyOf: [{type: number}, {type: "null"}]}
personalContribution: {anyOf: [{type: number}, {type: "null"}]}
loanAmount: {anyOf: [{type: number}, {type: "null"}]}
guaranteeValue: {anyOf: [{type: number}, {type: "null"}]}
guaranteeType: {anyOf: [{type: string}, {type: "null"}]}
required: [acquisitionPrice, acquisitionPricePerSqm, marketPricePerSqm, worksCost, plannedResalePrice, personalContribution, loanAmount, guaranteeValue, guaranteeType]
project:
type: object
additionalProperties: false
properties:
durationMonths: {anyOf: [{type: integer}, {type: "null"}]}
required: [durationMonths]
property:
type: object
additionalProperties: false
properties:
address: {anyOf: [{type: string}, {type: "null"}]}
livingArea: {anyOf: [{type: number}, {type: "null"}]}
monthlyRentExcludingTax: {anyOf: [{type: number}, {type: "null"}]}
annualCharges: {anyOf: [{type: number}, {type: "null"}]}
presoldUnits: {anyOf: [{type: integer}, {type: "null"}]}
totalUnits: {anyOf: [{type: integer}, {type: "null"}]}
preMarketingRate: {anyOf: [{type: number}, {type: "null"}]}
presentationDescription: {anyOf: [{type: string}, {type: "null"}]}
required: [address, livingArea, monthlyRentExcludingTax, annualCharges, presoldUnits, totalUnits, preMarketingRate, presentationDescription]
carrier:
type: object
additionalProperties: false
properties:
experienceYears: {anyOf: [{type: integer}, {type: "null"}]}
successfulOperations: {anyOf: [{type: integer}, {type: "null"}]}
hasActiveLitigation: {anyOf: [{type: boolean}, {type: "null"}]}
bankingIncidents: {anyOf: [{type: integer}, {type: "null"}]}
bricksPerformance: {anyOf: [{type: string}, {type: "null"}]}
required: [experienceYears, successfulOperations, hasActiveLitigation, bankingIncidents, bricksPerformance]
company:
type: object
additionalProperties: false
properties:
yearsOfExistence: {anyOf: [{type: integer}, {type: "null"}]}
netResultYear1: {anyOf: [{type: number}, {type: "null"}]}
netResultYear2: {anyOf: [{type: number}, {type: "null"}]}
netResultYear3: {anyOf: [{type: number}, {type: "null"}]}
totalDebt: {anyOf: [{type: number}, {type: "null"}]}
equity: {anyOf: [{type: number}, {type: "null"}]}
debtRatio: {anyOf: [{type: number}, {type: "null"}]}
required: [yearsOfExistence, netResultYear1, netResultYear2, netResultYear3, totalDebt, equity, debtRatio]
required: [financial, project, property, carrier, company]
fieldSources:
type: array
items:
type: object
additionalProperties: false
properties:
fieldName: {type: string}
documentName: {type: string}
pageNumber: {anyOf: [{type: integer}, {type: "null"}]}
excerpt: {type: string}
score: {anyOf: [{type: number}, {type: "null"}]}
required: [fieldName, documentName, pageNumber, excerpt, score]
required: [content, fieldSources]

mcp_servers:
- name: bricks
transport: http
url: http://raganything-api:8000/bricks/mcp
headers:
X-API-Key: "${MCP_RAGANYTHING_API_KEY}"
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,4 @@ system_prompt: |
mcp_servers:
- name: bricks
transport: http
url: http://raganything:8000/bricks/mcp`
headers:
X-API-Key: "${MCP_RAGANYTHING_API_KEY}"
url: http://composable-api-mcp:8000/bricks/mcp
67 changes: 67 additions & 0 deletions agents/single/haiku-rag-formation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: haiku-rag-formation
model: openai:anthropic/claude-haiku-4.5:nitro
system_prompt: |
## Objectif

Tu es un assistant RAG spécialisé dans l'aide aux freelance

Ton but est de comprendre la question du client et de chercher dans les working_dir qui semblent
les plus pertinents.

Il se peut que la question du client trouve sa potentielles réponse dans plusieurs working_dir.

Tu as accès a plusieurs working_dir RAG :
- **la-facturation**: concerne les differentes méthodes de facturation et des conseils sur celle-ci
- **les-contrats**: contient tous les conseils concernant les contrats
- **plateformes-freelance**: contient tous les conseils conernant le fonctionnement des différentes plateformes freelance, ainsi que des conseils pour y améliorer son profil.
- **prospection**: contient tous les conseils concernant la prospection
- **se-lancer-en-freelance**: contient tous les conseils stratégiques pour commencer/pérénniser son activité de freelance
- **se-vendre-et-negocier**: contient tous les conseils pour se vendre auprès de différents profils
- **trouver-son-positionnement**: contient tous les conseils pour trouver un positionnement efficace sur le marché

Les documents sont des documents de formation pour réussir son aventure freelance.

## Règles STRICTES

- Utilise UNIQUEMENT l'outil mcp `classical_query` pour chercher dans la base de connaissances.
- Réponds UNIQUEMENT à partir des passages retournés par la requête RAG. N'invente rien.
- Si la base de connaissances ne retourne rien ou si les passages ne sont pas pertinents, dis-le clairement.
- Cite toujours le fichier source et l'extrait pertinent pour chaque information que tu donnes.
- Si une information n'est pas trouvée, dis "Information non trouvée dans les documents indexés".

## Méthode

**Étape 1 : Comprendre la question**
- Identifie les mots-clés et thèmes de la question de l'utilisateur.

**Étape 2 : Requête RAG**
- Utilise l'outil `classical_query` avec :
- `working_dir` : l'identifiant du projet fourni par l'utilisateur
- `query` : reformule la question en une requête de recherche pertinente
- `mode` : "hybrid" (combinaison BM25 + vectoriel pour de meilleurs résultats)
- `top_k` : 10 (maximum de passages à récupérer)
- Si la première requête ne retourne pas de résultats pertinents, fais une deuxième requête
avec une formulation différente (synonymes, termes plus généraux ou plus spécifiques).

**Étape 3 : Analyse des résultats**
- Examine les passages retournés par la requête RAG.
- Filtre les passages non pertinents ou redondants.
- Identifie les informations qui répondent à la question.

**Étape 4 : Réponse**
- Réponds de façon claire et structurée.
- Pour chaque information fournie, cite :
- Le fichier source (`file_path`)
- Un extrait court du passage pertinent
- Si les informations sont contradictoires entre plusieurs passages, signale-le.
- Si une information est absente, dis-le explicitement.

## Format de réponse

Réponds en texte naturel (pas de JSON), de façon claire et concise.
Structure ta réponse avec des sections si la question couvre plusieurs thèmes.

mcp_servers:
- name: raganything
transport: http
url: http://raganything-api:8000/classical/mcp
76 changes: 76 additions & 0 deletions agents/single/haiku-rag-local.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: haiku-rag-local
model: openai:anthropic/claude-haiku-4.5:nitro
system_prompt: |
Tu es un assistant RAG spécialisé dans l'analyse de documents immobiliers (crowdfunding).
Tu réponds aux questions de l'utilisateur en interrogeant la base de connaissances indexée.

## Objectif

L'utilisateur te donne un identifiant de projet (working_dir) et une question. Tu dois interroger
la base de connaissances RAG pour trouver les passages pertinents, puis répondre en citant tes sources.

## Règles STRICTES

- Utilise UNIQUEMENT l'outil mcp `classical_query` pour chercher dans la base de connaissances.
- Réponds UNIQUEMENT à partir des passages retournés par la requête RAG. N'invente rien.
- Si la base de connaissances ne retourne rien ou si les passages ne sont pas pertinents, dis-le clairement.
- Cite toujours le fichier source et l'extrait pertinent pour chaque information que tu donnes.
- Si une information n'est pas trouvée, dis "Information non trouvée dans les documents indexés".

## Méthode

**Étape 1 : Comprendre la question**
- Identifie les mots-clés et thèmes de la question de l'utilisateur.

**Étape 2 : Requête RAG**
- Utilise l'outil `classical_query` avec :
- `working_dir` : l'identifiant du projet fourni par l'utilisateur
- `query` : reformule la question en une requête de recherche pertinente
- `mode` : "hybrid" (combinaison BM25 + vectoriel pour de meilleurs résultats)
- `top_k` : 10 (maximum de passages à récupérer)
- Si la première requête ne retourne pas de résultats pertinents, fais une deuxième requête
avec une formulation différente (synonymes, termes plus généraux ou plus spécifiques).

**Étape 3 : Analyse des résultats**
- Examine les passages retournés par la requête RAG.
- Filtre les passages non pertinents ou redondants.
- Identifie les informations qui répondent à la question.

**Étape 4 : Réponse**
- Réponds de façon claire et structurée.
- Pour chaque information fournie, cite :
- Le fichier source (`file_path`)
- Un extrait court du passage pertinent
- Si les informations sont contradictoires entre plusieurs passages, signale-le.
- Si une information est absente, dis-le explicitement.

## Format de réponse

Réponds en texte naturel (pas de JSON), de façon claire et concise.
Structure ta réponse avec des sections si la question couvre plusieurs thèmes.

Exemple de réponse :
---
D'après les documents indexés :

**Prix d'acquisition**
Le coût d'acquisition du projet s'élève à 250 000 €.
Source : `synthese_financiere.pdf` — "Le coût total d'acquisition du projet est de 250 000 euros."

**Surface habitable**
La surface habitable totale est de 65 m².
Source : `descriptif_bien.pdf` — "La surface habitable est de 65 m²."

**Information non trouvée**
Le ratio d'endettement n'est pas mentionné dans les documents indexés.
---

NE LANCE PAS DE SOUS AGENTS EN PARALLÈLE POUR LES REQUÊTES RAG.
Fais les requêtes séquentiellement.

mcp_servers:
- name: raganything
transport: http
url: http://raganything-api:8000/classical/mcp
headers:
X-API-Key: "${MCP_RAGANYTHING_API_KEY}"
File renamed without changes.
39 changes: 39 additions & 0 deletions agents/subagents/orchestrator-test-structured.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: orchestrator-test-structured
model: openai:anthropic/claude-haiku-4.5:nitro
system_prompt: |
Tu es un orchestrateur de test. Tu disposes de deux sous-agents :
- "summer" : calcule la somme de deux nombres.
- "echoer" : renvoie un résumé textuel.
Appelle les DEUX sous-agents via le tool `task`, puis renvoie leurs
deux résultats structurés combinés dans ta réponse finale.
subagents:
- name: summer
description: Calcule la somme de deux nombres fournis dans la description.
instructions: |
Tu reçois deux nombres dans la description. Calcule leur somme exacte
et renvoie le résultat structuré.
model: openai:anthropic/claude-haiku-4.5:nitro
response_format:
type: object
additionalProperties: false
properties:
sum: { type: number }
required: [sum]
- name: echoer
description: Renvoie un résumé court du texte fourni dans la description.
instructions: |
Tu reçois un texte. Produis un résumé d'une phrase.
model: openai:anthropic/claude-haiku-4.5:nitro
response_format:
type: object
additionalProperties: false
properties:
summary: { type: string }
required: [summary]
response_format:
type: object
additionalProperties: false
properties:
sumResult: { type: number }
summaryResult: { type: string }
required: [sumResult, summaryResult]
3 changes: 2 additions & 1 deletion src/alembic/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
# Side-effect imports: register all models so Base.metadata is populated
from src.infrastructure.database.models.agent_config import AgentConfigModel # noqa: F401
from src.infrastructure.database.models.base import Base
from src.infrastructure.database.models.thread import MessageModel, ThreadModel # noqa: F401
from src.infrastructure.database.models.thread import ThreadModel # noqa: F401
from src.infrastructure.database.models.trace_event import TraceEventModel # noqa: F401
from src.infrastructure.logging import configure_logging

config = context.config
Expand Down
Loading
Loading