483 lines
16 KiB
Markdown
483 lines
16 KiB
Markdown
# Spec F2-A+B: Agents Configurables i Paràmetres de Simulació
|
|
|
|
**Data:** 2026-05-03
|
|
**Fases:** F2-A i F2-B del roadmap enterprise (2026-04-26-enterprise-roadmap.md)
|
|
**Estat:** Aprovat per disseny
|
|
|
|
---
|
|
|
|
## Context
|
|
|
|
La Fase 2 del roadmap enterprise té com a objectiu un flux iteratiu:
|
|
construir graf → ajustar agents → simular → ajustar → re-simular.
|
|
|
|
Fins ara el Step 2 és un pas únic i automàtic: genera perfils d'agents i
|
|
config de simulació sense pauses ni possibilitat d'edició. L'usuari ha de
|
|
reconstruir tot el projecte si vol ajustar qualsevol cosa.
|
|
|
|
Aquesta spec cobreix:
|
|
|
|
1. **Redisseny del flux Step 2** en tres fases amb confirmació explícita
|
|
2. **Selector de nombre d'agents** — top-N per connectivitat
|
|
3. **Edició, regeneració, creació i eliminació d'agents individuals** (Fase A)
|
|
4. **Edició de paràmetres de comportament i simulació** (Fase B)
|
|
5. **Clonació de simulació** — reutilitzar agents i config d'una simulació anterior
|
|
6. **Aïllament de grafs per simulació** — cada simulació té el seu propi `group_id` Neo4j
|
|
|
|
---
|
|
|
|
## Redisseny del flux Step 2
|
|
|
|
El Step 2 passa de ser un pas únic a tenir tres fases seqüencials:
|
|
|
|
```
|
|
FASE A — Generació i edició de personalitats
|
|
El sistema genera els perfils d'agents (nom, bio, persona, mbti, etc.)
|
|
L'usuari pot retocar, regenerar, crear o eliminar agents.
|
|
→ L'usuari prem "Continuar" quan està satisfet
|
|
|
|
FASE B — Generació i edició de config de comportament
|
|
El sistema genera la config de comportament dels agents restants
|
|
(posts_per_hour, active_hours, etc.) i els paràmetres globals de simulació
|
|
(total_simulation_hours, pesos de plataforma, etc.).
|
|
L'usuari pot editar qualsevol d'aquests paràmetres.
|
|
→ L'usuari prem "Iniciar simulació" per passar al Step 3
|
|
|
|
FASE C — Step 3: Llançament
|
|
(sense canvis respecte a l'actual)
|
|
```
|
|
|
|
**Camps editables per fase:**
|
|
|
|
- **Fase A** (per agent): `name`, `bio`, `persona`, `age`, `gender`, `mbti`,
|
|
`country`, `profession`, `interested_topics`, `stance`, `sentiment_bias`
|
|
- **Fase B** (per agent): `posts_per_hour`, `comments_per_hour`, `active_hours`,
|
|
`response_delay_min`, `response_delay_max`, `activity_level`, `influence_weight`
|
|
- **Fase B** (globals simulació): `total_simulation_hours`, `minutes_per_round`,
|
|
`agents_per_hour_min`, `agents_per_hour_max`, `following_probability`, `recsys_type`,
|
|
`recency_weight`, `popularity_weight`, `relevance_weight`, `viral_threshold`,
|
|
`echo_chamber_strength`
|
|
|
|
---
|
|
|
|
## Model de dades
|
|
|
|
### Nou estat de simulació: `profiles_ready`
|
|
|
|
El cicle d'estats s'amplia:
|
|
|
|
```
|
|
created → preparing → profiles_ready → configuring → prepared → running → completed
|
|
→ stopped
|
|
→ failed
|
|
```
|
|
|
|
- `profiles_ready`: perfils d'agents generats, esperant confirmació de l'usuari per generar config
|
|
- `configuring`: generant config de comportament i simulació (async)
|
|
- `prepared`: config generada, llest per llançar (estat actual)
|
|
|
|
### Canvi a la BD: `parent_simulation_id`
|
|
|
|
```sql
|
|
ALTER TABLE simulations
|
|
ADD COLUMN parent_simulation_id VARCHAR(64) REFERENCES simulations(simulation_id);
|
|
```
|
|
|
|
- `NULL` → simulació nova
|
|
- Valor → simulació clonada d'una existent
|
|
|
|
### Canvi a la BD: `graph_id_simulation`
|
|
|
|
```sql
|
|
ALTER TABLE simulations
|
|
ADD COLUMN graph_id_simulation VARCHAR(128);
|
|
```
|
|
|
|
Conté el `group_id` Neo4j exclusiu d'aquesta simulació. `NULL` fins que s'inicia.
|
|
|
|
### Immutabilitat
|
|
|
|
- `status IN ('completed', 'running')` → no es pot editar
|
|
- `status IN ('created', 'preparing', 'profiles_ready', 'configuring', 'prepared', 'stopped', 'failed')` → editable
|
|
|
|
### Flag de protecció contra sobreescriptura
|
|
|
|
Cada agent porta `manually_edited: bool` (default `False`) a `reddit_profiles.json`
|
|
i `simulation_config.json`. El generador en batch salta els agents amb
|
|
`manually_edited: True`.
|
|
|
|
---
|
|
|
|
## Subsistema 1: Selector de nombre d'agents (pre-generació)
|
|
|
|
### Comportament
|
|
|
|
Abans d'iniciar la Fase A, el sistema consulta quantes entitats hi ha disponibles
|
|
al graf i mostra el total com a suggeriment. L'usuari pot reduir el número.
|
|
Si no toca res, el comportament és idèntic a l'actual (es generen tots).
|
|
|
|
Si l'usuari redueix a N, el backend selecciona les **top-N entitats per grau de
|
|
connectivitat** (edges entrants + sortints). Les entitats més connectades generen
|
|
simulacions socialment més riques.
|
|
|
|
**Mínim recomanat:** 15 agents. El frontend mostra un avís si el valor és inferior.
|
|
|
|
### Canvis al backend
|
|
|
|
`POST /api/simulation/prepare` — nou paràmetre opcional `max_agents: int`.
|
|
|
|
Nova funció: `ZepEntityReader.get_entities_by_connectivity(graph_id, max_n)` —
|
|
obté totes les entitats, les ordena per nombre d'edges i retorna les top-N.
|
|
|
|
### Canvis al frontend (Step 2)
|
|
|
|
Just abans d'iniciar la generació:
|
|
|
|
- `GET /api/simulation/entities/{graph_id}` per obtenir el total disponible
|
|
- Camp numèric amb valor default = total disponible
|
|
- Avís visual si valor < 15
|
|
- Desplegable "Nova simulació / Des de simulació anterior" (vegeu Subsistema 5)
|
|
- El valor es passa a `POST /api/simulation/prepare` com a `max_agents`
|
|
|
|
---
|
|
|
|
## Subsistema 2: Fase A — Edició de personalitats d'agents
|
|
|
|
### Comportament
|
|
|
|
La Fase A comença quan el primer agent és generat. L'usuari veu les cards
|
|
d'agents aparèixer progressivament. En completar-se la generació, apareix el
|
|
botó "Continuar →" que activa la Fase B.
|
|
|
|
Mentre la generació és en curs o un cop completada, l'usuari pot:
|
|
|
|
- **Editar** qualsevol agent ja generat
|
|
- **Regenerar** un agent (un cop la generació completa, `status: profiles_ready`)
|
|
- **Crear** un agent nou basat en una entitat del graf
|
|
- **Eliminar** un agent
|
|
|
|
### 2a. Edició d'un agent
|
|
|
|
**Backend:** `PATCH /api/simulation/{sim_id}/agent/{user_id}`
|
|
|
|
```json
|
|
{ "bio": "...", "stance": "opposing" }
|
|
```
|
|
|
|
Acció:
|
|
|
|
1. Valida `status NOT IN ('running', 'completed')`
|
|
2. Carrega `reddit_profiles.json`
|
|
3. Localitza l'agent per `user_id`, aplica canvis + `manually_edited: True`
|
|
4. Desa atòmicament (backup → escriptura → elimina backup si OK, restaura si falla)
|
|
5. Retorna el perfil actualitzat
|
|
|
|
**Frontend:** modal d'agent existent amb botó "Editar" que activa mode edició.
|
|
Camps editables de Fase A com `<input>` / `<textarea>` / `<select>`.
|
|
Botó "Desa" / "Cancel·la". Indicador "Editat manualment" a la card si `manually_edited: True`.
|
|
|
|
### 2b. Regeneració d'un agent existent
|
|
|
|
Disponible només quan `status: profiles_ready`.
|
|
|
|
**Backend:** `POST /api/simulation/{sim_id}/agent/{user_id}/regenerate`
|
|
|
|
```json
|
|
{ "extra_instructions": "..." }
|
|
```
|
|
|
|
Acció:
|
|
|
|
1. Valida `status: profiles_ready`
|
|
2. Llegeix `source_entity_uuid` de l'agent
|
|
3. Consulta Zep per obtenir el context de l'entitat original
|
|
4. Crida `OasisProfileGenerator.generate_profile_from_entity()` amb `extra_instructions`
|
|
5. Actualitza `reddit_profiles.json`
|
|
6. Retorna `task_id` per polling
|
|
|
|
**Frontend:** botó "Regenera" al modal (visible si `status: profiles_ready`),
|
|
camp opcional d'instruccions, spinner durant el task, refresc en completar.
|
|
|
|
### 2c. Creació d'un agent nou
|
|
|
|
Permet afegir un agent basat en una entitat existent al graf sense agent assignat.
|
|
|
|
**Backend:** `POST /api/simulation/{sim_id}/agent`
|
|
|
|
```json
|
|
{ "source_entity_uuid": "...", "extra_instructions": "..." }
|
|
```
|
|
|
|
Acció:
|
|
|
|
1. Valida `status IN ('profiles_ready', 'created')`
|
|
2. Valida que l'entitat no té ja un agent assignat
|
|
3. Assigna el proper `user_id` disponible (max existent + 1)
|
|
4. Consulta Zep per obtenir el context de l'entitat
|
|
5. Crida `OasisProfileGenerator.generate_profile_from_entity()` amb `extra_instructions`
|
|
6. Afegeix el nou agent a `reddit_profiles.json`
|
|
7. Retorna `task_id` per polling
|
|
|
|
**Frontend:** botó "Afegeix agent" al panell. Flux:
|
|
|
|
1. Desplegable de tipus d'entitat (basats en l'ontologia del projecte)
|
|
2. Llista d'entitats disponibles sense agent assignat
|
|
3. Camp d'instruccions opcional
|
|
4. Confirmar → spinner → nou agent apareix a les cards en completar
|
|
|
|
### 2d. Eliminació d'un agent
|
|
|
|
**Backend:** `DELETE /api/simulation/{sim_id}/agent/{user_id}`
|
|
|
|
Valida `status NOT IN ('running', 'completed')`. Elimina l'agent de
|
|
`reddit_profiles.json`. No reassigna `user_id` dels restants.
|
|
|
|
**Frontend:** botó "Elimina" al modal amb confirmació explícita.
|
|
|
|
### 2e. Confirmació de Fase A → Fase B
|
|
|
|
**Backend:** `POST /api/simulation/{sim_id}/generate-config`
|
|
|
|
Acció:
|
|
|
|
1. Valida `status: profiles_ready`
|
|
2. Canvia `status → configuring`
|
|
3. Llança async la generació de config de comportament i simulació
|
|
(crida `SimulationConfigGenerator` amb els agents que han quedat)
|
|
4. En completar, canvia `status → prepared`
|
|
5. Retorna `task_id` per polling
|
|
|
|
**Frontend:** botó "Continuar →" (visible quan `status: profiles_ready`).
|
|
En clicar, inicia polling del `task_id`. En `completed`, passa a mostrar la Fase B.
|
|
|
|
---
|
|
|
|
## Subsistema 3: Fase B — Edició de paràmetres de comportament i simulació
|
|
|
|
### Comportament
|
|
|
|
Un cop la config de comportament és generada (`status: prepared`), el Step 2
|
|
mostra una nova secció editable amb:
|
|
|
|
- Paràmetres globals de simulació
|
|
- Paràmetres de comportament per a cada agent
|
|
|
|
L'usuari pot editar qualsevol valor. En clicar "Iniciar simulació" es passa al Step 3.
|
|
|
|
### 3a. Edició de paràmetres globals
|
|
|
|
**Backend:** `PATCH /api/simulation/{sim_id}/config`
|
|
|
|
```json
|
|
{
|
|
"total_simulation_hours": 48,
|
|
"minutes_per_round": 60,
|
|
"agents_per_hour_min": 5,
|
|
"agents_per_hour_max": 20,
|
|
"following_probability": 0.05,
|
|
"recsys_type": "random",
|
|
"twitter_config": {
|
|
"recency_weight": 0.4,
|
|
"popularity_weight": 0.3,
|
|
"relevance_weight": 0.3,
|
|
"viral_threshold": 10,
|
|
"echo_chamber_strength": 0.5
|
|
}
|
|
}
|
|
```
|
|
|
|
Acció: valida `status: prepared`, actualitza `simulation_config.json` atòmicament,
|
|
retorna la config actualitzada.
|
|
|
|
**Frontend:** formulari de paràmetres globals amb inputs numèrics i selectors.
|
|
Valors actuals carregats de `GET /api/simulation/{sim_id}/config`.
|
|
|
|
### 3b. Edició de comportament per agent
|
|
|
|
**Backend:** `PATCH /api/simulation/{sim_id}/agent/{user_id}` (el mateix endpoint
|
|
de Subsistema 2a) — ara accepta també els camps de comportament:
|
|
`posts_per_hour`, `comments_per_hour`, `active_hours`, `response_delay_min`,
|
|
`response_delay_max`, `activity_level`, `influence_weight`.
|
|
|
|
**Frontend:** cada agent card a la Fase B mostra els seus paràmetres de comportament
|
|
editables inline (sense modal), amb inputs numèrics i selector d'hores actives.
|
|
|
|
---
|
|
|
|
## Subsistema 4: Clonació de simulació
|
|
|
|
### Comportament
|
|
|
|
Al desplegable de pre-generació (Subsistema 1):
|
|
|
|
- **"Nova simulació"** (default) → genera des de zero
|
|
- **"Des de [nom/data sim anterior]"** → clona agents de sim anterior
|
|
|
|
Simulacions clonables: qualsevol del mateix projecte amb
|
|
`status NOT IN ('created')`.
|
|
|
|
En triar una simulació font, el sistema:
|
|
|
|
1. Crida `POST /clone` → crea nova simulació amb `status: profiles_ready`
|
|
2. Carrega directament la Fase A amb els agents clonats (salta la generació)
|
|
3. L'usuari pot editar agents i continuar a Fase B
|
|
|
|
### Canvis al backend
|
|
|
|
`POST /api/simulation/{sim_id}/clone`
|
|
|
|
```json
|
|
{ "project_id": "proj_xxxx" }
|
|
```
|
|
|
|
Acció:
|
|
|
|
1. Valida `status != 'created'`
|
|
2. Crea nova simulació amb `status: profiles_ready`
|
|
3. Guarda `parent_simulation_id = sim_id`
|
|
4. Copia `reddit_profiles.json`, `twitter_profiles.csv`, `agent_profiles.json`
|
|
5. **No copia** `simulation_config.json` (es regenerarà a la Fase B)
|
|
6. Retorna `new_simulation_id`
|
|
|
|
---
|
|
|
|
## Subsistema 5: Aïllament de grafs per simulació
|
|
|
|
### Problema
|
|
|
|
Ara `enable_graph_memory_update` és `true` hardcodejat al frontend. Totes les
|
|
simulacions escriuen converses al mateix `graph_id` del projecte.
|
|
El report de sim2 inclou converses de sim1.
|
|
|
|
### Solució
|
|
|
|
Cada simulació té el seu propi `group_id` Neo4j (`graph_id_simulation`), creat
|
|
per clonatge APOC de `graph_id_document` just abans de llançar. `graph_id_simulation`
|
|
conté una còpia exacta del document original i és on els agents escriuen les
|
|
converses. `graph_id_document` queda immutable. El ReportAgent consulta
|
|
**únicament** `graph_id_simulation`.
|
|
|
|
### Flux
|
|
|
|
**Clonatge APOC** just abans de `POST /api/simulation/start`:
|
|
|
|
```python
|
|
# Clona nodes
|
|
await session.run("""
|
|
MATCH (n) WHERE n.group_id = $src
|
|
WITH n, properties(n) AS props
|
|
CREATE (m) SET m = props SET m.group_id = $dst
|
|
""", src=graph_id_document, dst=graph_id_simulation)
|
|
|
|
# Clona relacions
|
|
await session.run("""
|
|
MATCH (n)-[r]->(m)
|
|
WHERE n.group_id = $src AND m.group_id = $src
|
|
MATCH (n2 {uuid: n.uuid, group_id: $dst})
|
|
MATCH (m2 {uuid: m.uuid, group_id: $dst})
|
|
CALL apoc.create.relationship(n2, type(r), properties(r), m2) YIELD rel
|
|
SET rel.group_id = $dst
|
|
RETURN rel
|
|
""", src=graph_id_document, dst=graph_id_simulation)
|
|
```
|
|
|
|
Neo4j Aura Cloud inclou APOC de sèrie.
|
|
|
|
### Canvis al backend
|
|
|
|
**`graphiti_backend.py`** — nova funció `clone_graph(src_group_id, dst_group_id)`
|
|
via `execute_query()`.
|
|
|
|
**`POST /api/simulation/start`** — si `enable_graph_memory_update: true`:
|
|
|
|
1. Genera `graph_id_simulation = f"mirofish_{sim_id}_sim"`
|
|
2. Crida `clone_graph(graph_id_document, graph_id_simulation)`
|
|
3. Desa `graph_id_simulation` a la BD
|
|
4. Llança la simulació usant `graph_id_simulation`
|
|
|
|
**`POST /api/report/generate`** — passa `graph_id_simulation` al `ReportAgent`
|
|
si existeix; si no, passa `graph_id_document`.
|
|
|
|
**`DELETE /api/simulation/{sim_id}`** — si existeix `graph_id_simulation`,
|
|
esborrar el graf Neo4j associat:
|
|
|
|
```python
|
|
await session.run("""
|
|
MATCH (n) WHERE n.group_id = $gid DETACH DELETE n
|
|
""", gid=graph_id_simulation)
|
|
```
|
|
|
|
### Canvis al frontend (Step 3)
|
|
|
|
Convertir `enable_graph_memory_update` de hardcodejat a checkbox configurable
|
|
a la UI del Step 3 (default `true`).
|
|
|
|
---
|
|
|
|
## Ordre d'implementació recomanat
|
|
|
|
1. Nou estat `profiles_ready` a la BD i backend
|
|
2. `PATCH /agent/{user_id}` (camps Fase A) + edició modal
|
|
3. Botó "Continuar →" + `POST /generate-config` + polling Fase B
|
|
4. Selector de N agents pre-generació
|
|
5. `DELETE /agent/{user_id}` + botó eliminar modal
|
|
6. `POST /agent` (creació) + UI selector d'entitat
|
|
7. `POST /agent/{user_id}/regenerate` + UI polling
|
|
8. `PATCH /simulation/{sim_id}/config` + formulari Fase B globals
|
|
9. `PATCH /agent/{user_id}` (camps comportament Fase B) + edició inline
|
|
10. `POST /clone` + desplegable pre-generació + `parent_simulation_id` a la BD
|
|
11. Subsistema 5: `clone_graph`, `graph_id_simulation`, `enable_graph_memory_update` configurable
|
|
|
|
---
|
|
|
|
## Verificació end-to-end
|
|
|
|
### Test 1: Flux complet Fase A → B → simulació
|
|
|
|
- Generar agents → editar bio d'un → clicar "Continuar →"
|
|
- Verificar que la config de comportament es genera amb els agents actuals
|
|
- Editar `total_simulation_hours` i `posts_per_hour` d'un agent
|
|
- Iniciar simulació → verificar que els valors editats s'apliquen
|
|
|
|
### Test 2: Edició protegida durant generació
|
|
|
|
- Editar un agent mentre altres es generen
|
|
- Verificar que l'agent editat NO es sobreescriu al finalitzar la generació
|
|
|
|
### Test 3: Regeneració individual
|
|
|
|
- `status: profiles_ready` → regenerar agent amb instrucció "Fes-lo més escèptic"
|
|
- Verificar spinner → perfil canviat coherentment
|
|
|
|
### Test 4: Creació i eliminació d'agents
|
|
|
|
- Afegir agent nou (entitat existent sense agent) → verificar a cards
|
|
- Eliminar un agent → verificar que desapareix i la config és consistent
|
|
|
|
### Test 5: Selector N agents
|
|
|
|
- 50 entitats disponibles → reduir a 30 → verificar 30 agents generats
|
|
- Verificar que corresponen a les entitats més connectades
|
|
- No tocar → verificar 50 agents (comportament actual)
|
|
|
|
### Test 6: Clonació
|
|
|
|
- Completar sim1 → Nova simulació "Des de sim1" al mateix projecte
|
|
- Verificar Fase A amb mateixos agents que sim1
|
|
- Editar un agent → Continuar → editar config → llançar
|
|
- Verificar que sim1 queda intacta
|
|
|
|
### Test 7: Aïllament de grafs
|
|
|
|
- Llançar sim1 i sim2 amb `enable_graph_memory_update: true`
|
|
- Verificar que el report de sim1 NO inclou converses de sim2 i viceversa
|
|
- Esborrar sim2 → verificar que el seu `graph_id_simulation` s'esborra de Neo4j
|
|
|
|
---
|
|
|
|
## Dependències i notes futures
|
|
|
|
- **F3** (UI de llistat de simulacions): `parent_simulation_id` prepara l'arbre de versions
|
|
- **Subsistema 5** depèn de Graphiti/Neo4j com a backend actiu (no Zep Cloud);
|
|
verificar configuració de l'entorn abans d'implementar
|