33 KiB
33 KiB
MiroFish — Fork Context (Private Impact Feature)
Branche de travail
feature/private-impact
Remotes
origin→ https://github.com/CyrilDEVIA/MiroResult.git (fork perso)upstream→ https://github.com/666ghj/MiroFish.git (repo original)
Historique des sessions
2026-04-16 — Session 1
Étapes terminées
- Prompt N°01 — Lecture complète du code source (audit, zéro modification)
- Prompt N°02 — Setup Git : fork + remote + branche
feature/private-impact - Prompt N°03 — Création
backend/scripts/run_private_simulation.py - Prompt N°04 — Création
backend/app/services/private_impact_profile_generator.py - Prompt N°05 — Création
backend/app/services/private_impact_config_generator.py - Prompt N°06 — Création
backend/app/services/private_impact_runner.py - Prompt N°07 — Blueprint
backend/app/api/private.py+ enregistrement (api/__init__.py,app/__init__.py) - Prompt N°08 — Modification
backend/app/services/simulation_runner.py(7 zones : champs private_*, start, monitor, read_log, check_completed, get_actions, cleanup) - Prompt N°09 — Frontend :
api/private.js+ModeSelector.vue+PrivateImpactView.vue+ route/private/:projectId - Prompt N°10 —
action_logger.py: ajoutget_private_logger()+ suppression fallbackrun_private_simulation.py+ intégrationModeSelectordansHome.vue
Fichiers créés / modifiés
| Fichier | Action |
|---|---|
backend/scripts/run_private_simulation.py |
Créé — moteur de simulation privé |
backend/scripts/private/ |
Créé — répertoire de sortie actions.jsonl |
backend/app/services/private_impact_profile_generator.py |
Créé — générateur de profils relationnels |
backend/app/services/private_impact_config_generator.py |
Créé — générateur de paramètres comportementaux |
backend/app/services/private_impact_runner.py |
Créé — orchestrateur subprocess + monitoring |
CONTEXT.md |
Créé — ce fichier |
backend/app/api/private.py |
Créé — blueprint Flask /api/private-impact (7 routes) |
backend/app/api/__init__.py |
Modifié — ajout private_bp + import private |
backend/app/__init__.py |
Modifié — enregistrement private_bp |
backend/app/services/simulation_runner.py |
Modifié — 7 zones private (champs, start, monitor, read_log, check, get_actions, cleanup) |
frontend/src/api/private.js |
Créé — client API private impact (7 fonctions) |
frontend/src/components/ModeSelector.vue |
Créé — sélecteur Public / Private Impact (2 cartes) |
frontend/src/views/PrivateImpactView.vue |
Créé — wizard 5 étapes (form → prepare → run → report → chat) |
frontend/src/router/index.js |
Modifié — route /private/:projectId ajoutée |
backend/scripts/action_logger.py |
Modifié — ajout get_private_logger() à SimulationLogManager |
backend/scripts/run_private_simulation.py |
Modifié — suppression fallback hasattr, appel direct log_manager.get_private_logger() |
frontend/src/views/Home.vue |
Modifié — intégration ModeSelector (right panel) + handleModeSelected + sessionStorage pendingSimMode |
Décisions d'architecture prises
- Pas d'env OASIS (pas de Twitter/Reddit/PlatformConfig)
- Appels LLM directs via
camel-ai ChatAgent+asyncio.to_thread() - Graphe relationnel construit depuis
cascade_influencedans agent_configs REACT_PRIVATELY= invisible → ne propage pas l'exposition- Tous les autres actions (sauf
DO_NOTHING) cascade verscascade_influencetargets zep_graph_memory_updater.pyréutilisé sans modification (platform="private")- IPC
PrivateIPCHandler: interviews via LLM direct (pas de SQLite) - Output :
private/actions.jsonl(même format JSONL que twitter/reddit) RelationalAgentProfilehérite deOasisAgentProfile— 8 champs relationnels ajoutés- Encodage des dimensions relationnelles dans le champ
persona(texte naturel) - Fallback rule-based par type : Employee, Manager, Client, Competitor, Partner, FamilyMember
to_private_format()retourne le dict lu parrun_private_simulation.pyPrivateImpactConfigGenerator.generate_config(): entrée = liste de dicts agents (issue de profile_generator), pas d'EntityNode directPrivateTimeConfig: jours + rounds/jour (matin/midi/soir) — pas d'heures ni timezonePrivateEventConfig: injection pardecision_statement— pas de posts sociauxRelationalActivityConfig.exposure_round: round 0 = exposition directe (distance 1)- Fallback rule-based : table
RELATIONAL_FALLBACKSdans le générateur (6 types) PrivateImpactRunner: même pattern classmethods queSimulationRunner(états en mémoire de classe)- Config lue depuis
private_simulation_config.json(≠simulation_config.jsonOASIS) - Log unique :
{sim_dir}/private/actions.jsonl(une seule plateforme) PrivateRunnerStatus: enum séparé — pas de réutilisation deRunnerStatusprivate_simulated_dayslu depuis le champsimulated_dayduround_endevent- Frontend : CSS plain (pas Tailwind — non présent dans package.json) — même style que les vues existantes
ModeSelector.vue: composant standalone, émet@mode-selectedavec"public"ou"private", à intégrer manuellement dansHome.vueouProcess.vuePrivateImpactView.vue: route/private/:projectId— charge le projet viagetProject()pour récupérergraph_id- Step 3 : polling
/api/private-impact/status/{simId}toutes les 3s + affichagerecent_actionsdepuisto_detail_dict() - Step 4 : report via
generatePrivateReport()→ task_id → pollinggetReportStatus(reportId)→getReport(reportId)(réutilise le ReportManager existant) - Step 5 :
chatAgentsreconstruit depuis la liste d'actions (agent_id + agent_name) ; chat viainterviewAgents()(réutilise simulation.js) SimulationRunState: 5 champsprivate_*ajoutés (current_round, simulated_days, running, actions_count, completed)add_action(): elif platform=="private" → private_actions_count (évite comptage dans reddit)to_dict(): private_* inclus dans total_actions_countstart_simulation(): elif platform=="private" → run_private_simulation.py + private_running=True_monitor_simulation(): lectureprivate/actions.jsonldans la boucle ET en final ; private_running=False à la fin_read_action_log(): simulation_end → private_completed=True ; round_end → private_current_round + private_simulated_days (depuis simulated_day)_check_all_platforms_completed(): private_log + private_enabled + check private_completedget_all_actions(): bloc private après reddit (même pattern)cleanup_simulation_logs(): private_simulation.db + dirs_to_clean inclut "private"- Blueprint
private_bpenregistré sans url_prefix (les routes déclarent/api/private-impact/...en entier) /preparestocke les métadonnées (graph_id, sim_requirement, agent_count…) dansprivate_meta.jsondans le sim_dir/prepareappelleZepEntityReader.get_entities_by_type()en boucle sur les types relationnels puisPrivateImpactProfileGenerator.generate_profiles_from_entities()/startlitprivate_simulation_config.jsonviaPrivateImpactRunner.start_simulation()/statusretourneto_detail_dict()(inclutrecent_actions)/reportréutiliseReportAgentavecsimulation_id=sim_idetgraph_idlu depuisprivate_meta.json/cleanupdélègue entièrement àPrivateImpactRunner.cleanup()get_private_logger()ajouté àSimulationLogManager— même pattern queget_twitter_logger()/get_reddit_logger(); fallback supprimé dansrun_private_simulation.pyModeSelectorintégré dansHome.vue(right panel, au-dessus de.console-box) ; mode stocké danssessionStorage(pendingSimMode) —MainView.vue(N°11) doit lire ce flag et rediriger vers/private/:projectIdaprès création du projet
2026-04-16 — Session 2 (PrivateImpactView — UX + bug fix)
Étapes terminées
- Prompt N°XX — Step 3 : graphe de propagation D3 force-directed (nœuds/liens temps réel)
- Prompt N°05 — Step 1 : bouton "Import config" + parser
private_impact_requirement.txt - Prompt N°06 — Tooltip natif sur le bouton Import
- Prompt N°07 — Légende fixe sous le bouton Import (
.import-hint) - Prompt N°08 — Drop zone drag & drop (remplace bouton + légende)
- Prompt N°09 — Parser : extraction du bloc
#CONFIG…#END_CONFIGen priorité - Prompt N°10 — Bug fix :
graph_idtoujoursnullà l'arrivée sur PrivateImpactView
Fichiers modifiés
| Fichier | Action |
|---|---|
frontend/src/views/PrivateImpactView.vue |
Graphe D3, drop zone import, parser config, polling graph_id |
frontend/src/views/MainView.vue |
Ajout await startBuildGraph() avant redirect private |
Décisions d'architecture — Session 2
- D3 force-directed :
forceManyBody(-120)+forceLink(distance 80)+forceCenter - Couleur nœud = action dominante (CONFRONT→rouge, COALITION_BUILD→orange, VOCAL_SUPPORT→vert…)
- Feed réduit à 10 dernières actions, hauteur 200px fixe sous le graphe
- Import config :
FileReadernatif + drag & drop, parser#CONFIG…#END_CONFIGou fallback ligne par ligne - Bug N°10 :
MainView.vueredirigait vers/private/:projectIdAVANT d'appelerstartBuildGraph()→graph_idjamais set- Fix :
await startBuildGraph()ajouté avantrouter.push()dans le blocpendingMode === 'private' - Robustesse :
PrivateImpactViewpollegetProject()toutes les 3s sigraph_idabsent au mount - UX : notice jaune + bouton "Prepare" désactivé tant que
graph_idest null
- Fix :
2026-04-16 — Session 3 (Prompt N°12 — Audit + bug fix)
Étapes terminées
- Audit — Lecture complète
private_impact_runner.py,PrivateImpactView.vue,MainView.vue,ModeSelector.vue - Bug fix —
roundProgresscorrigé : utilisaittotal_rounds(inexistant) au lieu deprivate_total_rounds→ barre de progression toujours à 0%
Fichiers modifiés
| Fichier | Action |
|---|---|
frontend/src/views/PrivateImpactView.vue |
Bug fix roundProgress : fallback sur progress_percent (backend) puis private_total_rounds |
Décisions d'architecture — Session 3
roundProgressprioritiseprogress_percentretourné parto_dict()(calculé côté backend) — évite la double-computationModeSelector.vue: aucune modification nécessaire — complet et fonctionnelMainView.vue: redirection private correcte (await startBuildGraph()avantrouter.push()) — aucune modification- RELATIONAL_TYPES frontend (
ouvrier_production,technicien, etc.) : cohérent avec le parseur import — pas de désynchronisation backend (les types backendemployee/managersont utilisés dans le profil interne, non exposés au frontend)
2026-04-16 — Session 4 (Prompt N°13 — Audit e2e + corrections bugs bloquants)
Bugs identifiés et corrigés
| # | Sévérité | Fichier | Bug | Fix |
|---|---|---|---|---|
| 1 | BLOQUANT | backend/app/__init__.py:71 |
private_bp enregistré sans url_prefix → toutes les routes /api/private-impact/* retournent 404 |
register_blueprint(private_bp, url_prefix='/api') |
| 2 | BLOQUANT | backend/scripts/run_private_simulation.py |
Script lit event_config.initial_posts (vide) et time_config.total_simulation_hours (absent) → 0 agents exposés, contexte générique, mauvais nombre de rounds |
Support dual-format : decision_statement + initial_exposed_agent_ids + fallback expose-all ; total_simulation_days/rounds_per_day |
| 3 | BLOQUANT | frontend/src/views/PrivateImpactView.vue + frontend/src/api/private.js |
getReportStatus fait GET avec report_id en query param ; backend attend POST avec task_id en body → 405 Method Not Allowed |
Ajout getPrivateReportStatus (POST task_id) dans private.js ; réécriture runReport/pollReport pour extraire task_id, gérer already_generated |
| 4 | Cosmétique | backend/scripts/action_logger.py + run_private_simulation.py |
log_round_end n'incluait pas simulated_day → compteur de jours toujours 0 dans le panneau status |
Ajout param optionnel simulated_day à PlatformActionLogger.log_round_end ; passage du champ depuis les 3 call sites |
Fichiers modifiés
| Fichier | Action |
|---|---|
backend/app/__init__.py |
Fix : url_prefix='/api' ajouté sur private_bp |
backend/scripts/run_private_simulation.py |
Fix : get_decision_context + get_initial_exposed_agents dual-format ; total_rounds dual-mode ; simulated_day dans log_round_end |
backend/scripts/action_logger.py |
Fix : log_round_end accepte param optionnel simulated_day |
frontend/src/api/private.js |
Fix : ajout getPrivateReportStatus(taskId) (POST /api/report/generate/status) |
frontend/src/views/PrivateImpactView.vue |
Fix : runReport extrait task_id + gère already_generated ; pollReport utilise getPrivateReportStatus ; import nettoyé (getReportStatus supprimé) |
Décisions d'architecture — Session 4
run_private_simulation.py: détecte le format de config par présence detotal_simulation_days(PrivateImpactConfigGenerator) vstotal_simulation_hours(OASIS) — pas de breaking changeget_initial_exposed_agents: fallback ultime "expose tous les agents" — évite une simulation silencieuse à 0 activitégetPrivateReportStatusdansprivate.jsplutôt que modification dereport.js— préserve le flux Public Opinion existantpollReportrécupèrereport_idfinal depuisres.data.result.report_id(retourné partask.to_dict()quand completed)
Prochaines étapes
| Prompt | Fichier cible | Action |
|---|---|---|
| N°04 | backend/app/services/private_impact_profile_generator.py |
✅ Terminé |
| N°05 | backend/app/services/private_impact_config_generator.py |
✅ Terminé |
| N°06 | backend/app/services/private_impact_runner.py |
✅ Terminé |
| N°07 | backend/app/api/private.py |
✅ Terminé |
| N°07 | backend/app/api/__init__.py |
✅ Terminé |
| N°07 | backend/app/__init__.py |
✅ Terminé |
| N°08 | backend/app/services/simulation_runner.py |
✅ Terminé |
| N°09 | frontend/src/views/PrivateImpactView.vue |
✅ Terminé |
| N°09 | frontend/src/components/ModeSelector.vue |
✅ Terminé |
| N°10 | backend/scripts/action_logger.py |
✅ Terminé — get_private_logger() ajouté |
| N°10 | backend/scripts/run_private_simulation.py |
✅ Terminé — fallback supprimé |
| N°10 | frontend/src/views/Home.vue |
✅ Terminé — ModeSelector intégré |
| N°11 | backend/app/services/private_impact_config_generator.py |
✅ Terminé — vérifié Prompt N°11 (déjà présent, complet) |
| N°11 | backend/app/services/private_impact_runner.py |
✅ Terminé — vérifié Prompt N°11 (déjà présent, complet) |
| N°11 | backend/app/api/private.py |
✅ Terminé — vérifié Prompt N°11 (7 routes : prepare, start, status, stop, actions, report, cleanup) |
| N°11 | backend/app/api/__init__.py |
✅ Terminé — private_bp défini + import private |
| N°11 | backend/app/__init__.py |
✅ Terminé — private_bp enregistré sans url_prefix |
| N°12 | frontend/src/views/PrivateImpactView.vue |
✅ Terminé — bug fix roundProgress (total_rounds → private_total_rounds + fallback progress_percent) |
| N°12 | frontend/src/components/ModeSelector.vue |
✅ Terminé — vérifié, aucune modification nécessaire |
| N°13 | backend/app/__init__.py |
✅ Terminé — Bug 1 : url_prefix='/api' sur private_bp |
| N°13 | backend/scripts/run_private_simulation.py |
✅ Terminé — Bug 2 : dual-format event_config + time_config + simulated_day |
| N°13 | backend/scripts/action_logger.py |
✅ Terminé — Bug 4 : simulated_day dans log_round_end |
| N°13 | frontend/src/api/private.js |
✅ Terminé — Bug 3 : getPrivateReportStatus (POST task_id) |
| N°13 | frontend/src/views/PrivateImpactView.vue |
✅ Terminé — Bug 3 : polling report corrigé (task_id, already_generated, import nettoyé) |
| N°14 | backend/app/services/zep_tools.py |
✅ Terminé — Bug 5 : matching case-insensitif get_entities_by_type |
| N°14 | backend/app/api/private.py |
✅ Terminé — Bug 6 : get_json(silent=True) sur 3 endpoints |
| N°15 | frontend/src/views/PrivateImpactView.vue |
✅ Terminé — Fix 1 : rapport affiche outline.sections + fallback markdown_content, CSS ajouté |
| N°15 | backend/scripts/run_private_simulation.py |
✅ Terminé — Fix 3 : expose tous les agents dès round 1 |
| N°15 | PR | ✅ Terminé — Description PR complète dans CONTEXT.md |
| N°16 | backend/app/api/private.py |
✅ Terminé — Bug 7 : prepare utilise les types de l'ontologie du projet en priorité sur _RELATIONAL_ENTITY_TYPES |
| N°17 | backend/app/services/zep_entity_reader.py |
✅ Terminé — Bug 8 : matching case-insensitif dans filter_defined_entities ligne 264 |
| N°17 | backend/app/api/private.py |
✅ Terminé — Bug 9 : fallback synthétique quand 0 entités Zep + appel Zep optimisé (1 appel global vs N par type) |
2026-04-16 — Session 5 (Prompt N°14 — Test d'intégration réel)
Simulation exécutée
- Projet :
proj_00e87b997a03(seed : scénario PDG + Rolls-Royce DurandTech) - Graph ID :
mirofish_cea02d9a257e44a0 - Sim ID :
private_ff2f2200b746 - Agents générés : 4 (Sophie Martin/manager, Karim Benali/employee, Claire Rousseau/employee, Bertrand Lemaire/client)
- Rounds : 90 (
total_simulation_days: 30 × rounds_per_day: 3— valeur générée par LLM, override de la config envoyée) - Actions : 31 (COALITION_BUILD: 19, CONFRONT: 12)
- Agent actif : Sophie Martin uniquement (
initial_exposed_agent_ids: [0]généré par le LLM — les 3 autres font DO_NOTHING) - Rapport : généré (
report_2e3e9e073cc3),markdown_contentcohérent avec le scénario
Observations du flux complet
| Étape | Résultat | Notes |
|---|---|---|
| Ontologie | ✅ OK | Types : Ceo, Manager, Employee, Client, Company |
| Graph build | ✅ OK | mirofish_cea02d9a257e44a0 en ~40s |
| Prepare | ✅ OK (après fix Bug 5) | 4 agents, statut prepared |
| Start | ✅ OK | runner_status: running |
| Status polling | ✅ OK | Champs : runner_status, progress_percent, private_current_round, private_total_rounds, private_simulated_days, private_actions_count |
| Actions | ✅ Verbes relationnels | COALITION_BUILD + CONFRONT — aucun verbe Twitter/Reddit |
| Rapport | ✅ OK | markdown_content en ~3min, contenu pertinent et ancré dans le scénario |
| Cleanup | ✅ OK | cleaned_files: ["run_state.json"] |
Bugs identifiés et corrigés — Session 5
| # | Sévérité | Fichier | Bug | Fix |
|---|---|---|---|---|
| 5 | BLOQUANT | backend/app/services/zep_tools.py:802 |
get_entities_by_type compare en case-sensitive → "manager" ne match pas "Manager" dans les labels Zep → "No relational entities found" |
any(lbl.lower() == entity_type.lower() for lbl in node.labels) |
| 6 | Mineur | backend/app/api/private.py:95,243,417 |
request.get_json() or {} lève 400 si body vide avec Content-Type: application/json |
request.get_json(silent=True) or {} sur les 3 endpoints |
Observations non-bloquantes
| # | Description |
|---|---|
| A | round=None dans /actions et recent_actions de /status — JSONL contient le champ, mais _read_action_log ne le remonte pas |
| B | PrivateImpactConfigGenerator override la config event envoyée dans /prepare — initial_exposed_agent_ids et total_simulation_days sont régénérés par le LLM |
| C | report.content est vide dans la réponse /api/report/<id> — le contenu est dans markdown_content (non dans content) — frontend doit lire le bon champ |
| D | Un seul agent actif sur 4 : comportement attendu si initial_exposed_agent_ids: [0] (distance 1 = Sophie uniquement) — les agents 1-3 ne reçoivent pas le contexte de décision |
Fichiers modifiés — Session 5
| Fichier | Action |
|---|---|
backend/app/services/zep_tools.py |
Bug 5 : matching case-insensitif dans get_entities_by_type |
backend/app/api/private.py |
Bug 6 : get_json(silent=True) sur 3 endpoints (prepare, start, report) |
2026-04-16 — Session 6 (Prompt N°15 — Corrections finales + PR)
Corrections appliquées
| # | Fichier | Correction |
|---|---|---|
| Fix 1 | frontend/src/views/PrivateImpactView.vue |
Rapport : remplace reportResult.title/.summary/.sections (inexistants) par reportResult.outline?.sections + fallback <pre class="report-markdown">{{ reportResult.markdown_content }}</pre> |
| Fix 2 | (non nécessaire) | round=None dans mes scripts de test était une erreur de clé (round vs round_num) — to_dict() et le template utilisent bien round_num |
| Fix 3 | backend/scripts/run_private_simulation.py |
get_initial_exposed_agents simplifié : expose TOUS les agents dès le round 1 — le LLM ne filtre plus via initial_exposed_agent_ids (paramètre structurel, pas LLM) |
| Fix 4 | backend/app/api/private.py |
Vérifié : 3/3 endpoints déjà en get_json(silent=True) depuis N°14 |
Fichiers modifiés — Session 6
| Fichier | Action |
|---|---|
frontend/src/views/PrivateImpactView.vue |
Fix 1 : affichage rapport corrigé (outline.sections + fallback markdown_content), CSS .report-markdown ajouté, .report-title/.report-summary supprimés |
backend/scripts/run_private_simulation.py |
Fix 3 : get_initial_exposed_agents expose tous les agents — suppression du filtre LLM |
2026-04-16 — Session 7 (Prompt N°16 — Bug 7 : entity types domaine-spécifiques)
Diagnostic
/api/private-impact/prepare→ 404 (en fait 400) sur le projetfromagerie-auriac- Diagnostic : les routes sont correctement enregistrées (
flask routesconfirme/api/private-impact/prepare POST) - Cause réelle : l'ontologie LLM génère des types domaine-spécifiques (
ProductionWorker,CheeseTechnician,FoodRetailer,FamilyMember, etc.) — aucun ne matche_RELATIONAL_ENTITY_TYPES(même avec fix case-insensitif du Bug 5)
Bug corrigé
| # | Sévérité | Fichier | Bug | Fix |
|---|---|---|---|---|
| 7 | BLOQUANT | backend/app/api/private.py:136 |
entity_types hardcodé (employee, manager, etc.) — l'ontologie LLM génère des types domaine-spécifiques jamais en liste → 0 entités, "No relational entities found" |
Résolution en 3 niveaux : 1) entity_types explicite dans la requête 2) types de l'ontologie du projet (avec filtre _is_structural_type) 3) _RELATIONAL_ENTITY_TYPES en dernier recours |
Résultat
proj_b420d07dfb38(Fromagerie Auriac, 3 fichiers seed) : 27 agents générés, statutprepared✅- Types utilisés :
ProductionWorker,CheeseTechnician,SalesRepresentative,ExecutiveTeam,FoodRetailer,FamilyMember
Fichiers modifiés — Session 7
| Fichier | Action |
|---|---|
backend/app/api/private.py |
Bug 7 : _is_structural_type() + résolution entity types en 3 niveaux (request → ontologie projet → défaut) |
2026-04-16 — Session 8 (Prompt N°17 — Bugs 8 & 9 : 0 entités Zep → fallback synthétique)
Diagnostic
筛选完成: 总节点 14, 符合条件 0, entité_types: set()— graphemirofish_80371e75ec2844b3a 14 nœuds mais 0 match- Cause 1 :
filter_defined_entitiesligne 264 faitl in defined_entity_types(case-sensitive) — les labels Zep peuvent différer en casse - Cause 2 : même avec case-insensitif, les 14 nœuds ont uniquement les labels
["Entity", "Node"](pas de labels typés) → 0 entités retournées - Le fallback Bug 7 ne s'activait pas car le 404 était retourné par le code précédent avant le fallback synthétique
- Cause 3 : la boucle
for etype in entity_types: reader.get_entities_by_type(...)faisait N appels Zep (N = nombre de types) — chacun lit tous les nœuds + toutes les arêtes
Bugs corrigés
| # | Sévérité | Fichier | Bug | Fix |
|---|---|---|---|---|
| 8 | MINEUR | backend/app/services/zep_entity_reader.py:264 |
l in defined_entity_types case-sensitive → labels Zep ProductionWorker vs type productionworker ne matchent pas |
defined_lower = {t.lower() for t in defined_entity_types}; matching_labels = [l for l in custom_labels if l.lower() in defined_lower] |
| 9 | BLOQUANT | backend/app/api/private.py:180 |
Quand 0 entités matchent, retourne 404 immédiatement sans alternative | Remplacé par : _build_synthetic_entities() — crée des EntityNode synthétiques par type d'ontologie (LLM enrichit les profils sans ancrage Zep) ; ajout helper _build_synthetic_entities() ; appel Zep optimisé (filter_defined_entities 1 fois pour tous les types au lieu de N appels) |
Résultat
- Test unitaire Python confirmé : 7 agents synthétiques créés pour
proj_d86ee68acfa3(types :ProductionWorker,CheeseMaster,SalesRepresentative,ExecutiveTeam,RetailClient,FamilyMember,UnionRepresentative) EntityNodeimport ajouté dansprivate.py- La génération HTTP complète prend plusieurs minutes (3 LLM call groups dans
PrivateImpactConfigGenerator.generate_config()— comportement attendu)
Fichiers modifiés — Session 8
| Fichier | Action |
|---|---|
backend/app/services/zep_entity_reader.py |
Bug 8 : case-insensitive dans filter_defined_entities |
backend/app/api/private.py |
Bug 9 : _build_synthetic_entities() helper + fallback + import EntityNode + appel Zep optimisé (1 appel) |
Pull Request — feat: Private Impact simulation mode
Titre
feat: Private Impact simulation mode
Description
Ce PR ajoute un second mode de simulation à MiroFish : Private Impact.
Contrairement au mode Opinion Publique (Twitter/Reddit), Private Impact simule l'impact d'une décision privée (ex. achat d'un bien de luxe, licenciement, choix stratégique) dans un réseau relationnel fermé : employés, managers, clients, partenaires, famille.
Ce que ça fait
- Pipeline complet : Prepare → Start → Status polling → Actions → Rapport → Cleanup
- Agents relationnels : profils enrichis avec
relational_link_type,trust_level,seniority_years, encodés dans le persona LLM - Verbes relationnels :
REACT_PRIVATELY,CONFRONT,COALITION_BUILD,SILENT_LEAVE,VOCAL_SUPPORT,DO_NOTHING(≠ verbes Twitter/Reddit) - Rapport : réutilise
ReportAgentavecmarkdown_contentstructuré - Frontend : wizard 5 étapes, graphe D3 force-directed temps réel, import config
#CONFIG…#END_CONFIG
Comment tester
git checkout feature/private-impactnpm run setup:all && npm run dev- Sur l'interface Home → sélectionner mode "Private Impact"
- Créer un projet avec un fichier seed décrivant un décideur + son réseau (voir
/tmp/mirofish_private_test_seed.txtcomme exemple) - Attendre la génération de l'ontologie + du graphe
- Accéder à
/private/:projectId→ remplir le formulaire → Prepare → Start - Observer le graphe D3 et le feed d'actions en temps réel
- Générer le rapport → vérifier
markdown_content
Fichiers créés
| Fichier | Description |
|---|---|
backend/scripts/run_private_simulation.py |
Moteur subprocess — 6 verbes relationnels, LLM direct via camel-ai |
backend/app/services/private_impact_profile_generator.py |
Générateur de profils relationnels (8 dimensions) |
backend/app/services/private_impact_config_generator.py |
Générateur de paramètres comportementaux via LLM |
backend/app/services/private_impact_runner.py |
Orchestrateur : subprocess + monitoring + état en mémoire |
backend/app/api/private.py |
Blueprint Flask — 7 routes /api/private-impact/* |
frontend/src/api/private.js |
Client API Private Impact (7 fonctions) |
frontend/src/components/ModeSelector.vue |
Sélecteur Public / Private Impact |
frontend/src/views/PrivateImpactView.vue |
Wizard 5 étapes + graphe D3 |
Fichiers modifiés
| Fichier | Modification |
|---|---|
backend/app/__init__.py |
Enregistrement private_bp avec url_prefix='/api' |
backend/app/api/__init__.py |
Import + export private_bp |
backend/app/services/simulation_runner.py |
7 zones private (champs, start, monitor, log, check, actions, cleanup) |
backend/app/services/zep_tools.py |
get_entities_by_type — matching case-insensitif |
backend/scripts/action_logger.py |
get_private_logger() + simulated_day dans log_round_end |
frontend/src/router/index.js |
Route /private/:projectId |
frontend/src/views/Home.vue |
Intégration ModeSelector |
frontend/src/views/MainView.vue |
await startBuildGraph() avant redirect private |
Bugs connus résiduels (non bloquants)
PrivateImpactConfigGeneratorpeut générer untotal_simulation_daysdifférent de celui envoyé dans la requête (comportement LLM — override délibéré du générateur)- Le rapport affiché dans l'accordéon utilise
outline.sectionssans titres de sections — les sections s'affichent "Section 01, 02…" si pas de titre ; le contenu complet est toujours accessible via le fallbackmarkdown_content
Checklist
- Test d'intégration end-to-end réalisé (scénario PDG + Rolls-Royce, 4 agents, 90 rounds, 31 actions)
- Verbes relationnels vérifiés (COALITION_BUILD, CONFRONT — aucune fuite Twitter/Reddit)
- Rapport généré et lisible (
markdown_contentnon vide, contenu pertinent) - Cleanup propre (
run_state.jsonsupprimé) - 6 bugs bloquants corrigés (url_prefix, config mismatch, report polling, simulated_day, case-sensitive, silent json)
- CONTEXT.md à jour
Point d'attention — MainView.vue (N°11)
Status : ✅ RÉSOLU (Prompt N°10 Session 2)
Bug : redirection vers /private/:projectId se faisait après l'ontologie, avant startBuildGraph().
Fix : await startBuildGraph() ajouté avant router.push() dans le bloc pendingMode === 'private'.
Robustesse : PrivateImpactView polle getProject() jusqu'à ce que graph_id soit disponible.
2026-04-17 — Session 3
Prompt N°18 — Correction de 5 bugs + 1 fragilité
Bugs corrigés
| # | Bug | Correctif | Fichier(s) |
|---|---|---|---|
| 1 | Rapport section 01 en chinois | Règle de langue centralisée dans get_language_instruction() : override forçant l'alignement sur simulation_requirement, fallback français |
backend/app/utils/locale.py |
| 2 | Graphe D3 sans arêtes | Endpoint /status augmenté avec agents + relational_edges issus de cascade_influence. Frontend : nœuds statiques + merge arêtes cascade (grises pointillées si pas d'action, pleines si activées) |
backend/app/api/private.py, frontend/src/views/PrivateImpactView.vue |
| 3 | Bouton export rapport absent | Bouton Export .md ajouté dans Step 4 ; sérialisation outline.sections → markdown via Blob + download |
frontend/src/views/PrivateImpactView.vue |
| 5 | Mode Public/Private via sessionStorage | Remplacé par query param ?mode=private sur /process/:projectId ; MainView lit route.query.mode |
frontend/src/views/Home.vue, frontend/src/views/MainView.vue |
Bug partiellement corrigé
| # | Bug | État | Raison |
|---|---|---|---|
| 4 | Chat agents 400 | Corrigé côté frontend seulement (body aligné sur interviews: [{agent_id, prompt}] + parsing réponse result.results) |
La route chat pour Private Impact n'existe pas dans private.py. Le frontend tape /api/simulation/interview/batch qui exige SimulationRunner.check_env_alive() — incompatible avec PrivateImpactRunner. Le 400 "require interviews" est corrigé, mais env not running reste à traiter par une nouvelle route dédiée côté backend (hors périmètre : pas de nouveaux fichiers autorisés) |
Fichiers modifiés
backend/app/utils/locale.py— directive de langue universellebackend/app/api/private.py— status endpoint renvoieagents+relational_edgesfrontend/src/views/PrivateImpactView.vue— graphe cascade, export .md, chat body corrigéfrontend/src/views/Home.vue— suppression sessionStorage, passage query paramfrontend/src/views/MainView.vue— lectureroute.query.modeCONTEXT.md— mise à jour
Prochaine étape
- PR vers
main:feature/private-impact— regrouper cette session avec les sessions 1–2. - À anticiper post-PR : créer une route
/api/private-impact/chat/<sim_id>dédiée pour finaliser le bug 4 (le runner privé n'a pas d'IPC de typesend_batch_interview, donc un chat direct viaReportAgent.chatou un LLM call sur le profil d'agent est sans doute plus pertinent).