From 7bdb943e7f325da25551b5fe46356905a2c42b83 Mon Sep 17 00:00:00 2001 From: Jordan Diaz Date: Fri, 3 Apr 2026 13:29:09 +0000 Subject: [PATCH] =?UTF-8?q?Fix=20problemas=20detectados=20en=20evaluaci?= =?UTF-8?q?=C3=B3n:=20historial,=20prompting,=20artifacts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Task history preserva key_data estructurado (recordNums, sectionIds, moduleIds, pages) extraído de las tool executions reales — el modelo retiene contexto entre tasks sin re-consultar. 2. Coder system prompt mejorado: instrucciones explícitas sobre qué tool usar para cada operación (create_module vs create_or_update_record), consultar knowledge base antes de actuar, y reutilizar key_data del historial. 3. Eliminado artifact_memory y working_context del coder context_sections — ya no son necesarios con conversación real. Reduce acumulación de artifacts en el context. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/context/engine.py | 14 +++++++ src/orchestrator/agents/coder.py | 18 +++++--- src/orchestrator/engine.py | 71 ++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 6 deletions(-) diff --git a/src/context/engine.py b/src/context/engine.py index d4d413b..435d6d1 100644 --- a/src/context/engine.py +++ b/src/context/engine.py @@ -398,6 +398,20 @@ class ContextEngine: lines.append(f" Result: {summary}") if facts: lines.append(f" Facts: {'; '.join(facts[:5])}") + # Key structured data (recordNums, sectionIds, etc.) + key_data = entry.get("key_data", {}) + if key_data: + kd_parts = [] + for table, nums in key_data.get("tables", {}).items(): + kd_parts.append(f"{table}: records {nums}") + for page, num in key_data.get("pages", {}).items(): + kd_parts.append(f"page '{page}' = record {num}") + if key_data.get("sections"): + kd_parts.append(f"sections: {key_data['sections']}") + if key_data.get("modules"): + kd_parts.append(f"modules: {key_data['modules']}") + if kd_parts: + lines.append(f" Key data: {'; '.join(kd_parts)}") review = entry.get("review", "") if review: lines.append(f" Review: {review[:100]}") diff --git a/src/orchestrator/agents/coder.py b/src/orchestrator/agents/coder.py index fac1dc9..1e41f06 100644 --- a/src/orchestrator/agents/coder.py +++ b/src/orchestrator/agents/coder.py @@ -5,7 +5,7 @@ from __future__ import annotations from ...models.agent import AgentProfile, AgentRole from .base import BaseAgent -CODER_SYSTEM_PROMPT = """Eres un Agente Programador. Tu rol es ejecutar tareas de implementación usando las herramientas disponibles. +CODER_SYSTEM_PROMPT = """Eres un Agente Programador de Acai CMS. Tu rol es ejecutar tareas de implementación usando las herramientas MCP disponibles. ## Instrucciones - Concéntrate en la descripción del paso actual. @@ -16,9 +16,17 @@ CODER_SYSTEM_PROMPT = """Eres un Agente Programador. Tu rol es ejecutar tareas d - Responde SIEMPRE en español. ## Uso de herramientas -- Usa herramientas cuando necesites leer archivos, escribir código o ejecutar comandos. -- Los resultados de herramientas se te presentarán resumidos — no verás la salida cruda. -- Si necesitas más detalle de un resultado, solicita rehidratación. +- CONSULTA la Knowledge Base ANTES de actuar — tiene la referencia completa de tools y flujos de trabajo. +- Para CREAR MÓDULOS usa `create_module` (crea archivos en filesystem), NUNCA `create_or_update_record` en tabla modulos. +- Para GESTIONAR REGISTROS de tablas (apartados, travesias, etc.) usa `create_or_update_record`. +- Para ESCRIBIR ARCHIVOS usa `acai_write` o `acai_line_replace`. +- Tras crear un módulo, SIEMPRE sigue el flujo: create_module → add_module_to_record → set_module_config_vars. +- tableName siempre SIN prefijo cms_ (ej: apartados, NO cms_apartados). +- La primary key es siempre `num`, nunca `id`. + +## Datos del historial +- Si el historial de sesión incluye Key Data con recordNums o sectionIds, ÚSALOS directamente sin re-consultar. +- Ejemplo: si el historial dice "pages: Inicio = record 2", usa recordNum=2 para la portada. """ @@ -35,8 +43,6 @@ def create_coder_profile() -> AgentProfile: "project_profile", "knowledge_base", "task_state", - "artifact_memory", - "working_context", ], ) diff --git a/src/orchestrator/engine.py b/src/orchestrator/engine.py index 508ed93..c5fb2ad 100644 --- a/src/orchestrator/engine.py +++ b/src/orchestrator/engine.py @@ -296,12 +296,16 @@ class OrchestratorEngine: for step in task.plan: tools_used.update(step.tools_used) + # Extract key structured data from tool executions + key_data = self._extract_key_data_from_results(results) + history_entry = { "task_id": task.task_id, "objective": task.objective, "status": task.status.value, "steps": len(task.plan), "facts": task.facts_extracted[-10:], + "key_data": key_data, "tools_used": list(tools_used)[:10], "artifacts_count": len(task_artifacts), "summary": "; ".join(step_summaries)[:300], @@ -327,6 +331,73 @@ class OrchestratorEngine: task.task_id, len(task.facts_extracted), len(tools_used), len(task_artifacts), ) + @staticmethod + def _extract_key_data_from_results(results: list[dict[str, Any]]) -> dict[str, Any]: + """Extract structured data from tool executions for task history. + + Preserves key identifiers (recordNum, sectionId, tableName, moduleId) + so the model retains context across tasks without re-querying. + """ + key_data: dict[str, Any] = {} + seen_tables: dict[str, list[int]] = {} # tableName -> recordNums + seen_sections: list[str] = [] + seen_modules: list[str] = [] + seen_pages: dict[str, int] = {} # page name/url -> recordNum + + for result in results: + for te in result.get("tool_executions", []): + args = te.arguments + name = te.tool_name + + # Track table + record relationships + table = args.get("tableName", "") + record = args.get("recordNum") + if table and record: + record_int = int(record) if str(record).isdigit() else None + if record_int and table not in seen_tables: + seen_tables[table] = [] + if record_int and record_int not in seen_tables.get(table, []): + seen_tables[table].append(record_int) + + # Track section IDs + section = args.get("sectionId", "") + if section and section not in seen_sections: + seen_sections.append(section) + + # Track modules + module = args.get("moduleId", "") or args.get("moduleName", "") + if module and module not in seen_modules: + seen_modules.append(module) + + # Extract page info from raw output (enlace, name) + if te.raw_output and "enlace" in te.raw_output: + try: + import json as _json + # Try to parse structured data from output + for line in te.raw_output.splitlines(): + line = line.strip() + if line.startswith("{"): + try: + data = _json.loads(line) + if "enlace" in data and "num" in data: + page_key = data.get("name", data["enlace"]) + seen_pages[page_key] = int(data["num"]) + except _json.JSONDecodeError: + pass + except Exception: + pass + + if seen_tables: + key_data["tables"] = {t: nums[:10] for t, nums in seen_tables.items()} + if seen_sections: + key_data["sections"] = seen_sections[:20] + if seen_modules: + key_data["modules"] = seen_modules[:20] + if seen_pages: + key_data["pages"] = dict(list(seen_pages.items())[:20]) + + return key_data + def _maybe_compact_previous_steps( self, task: TaskState, current_index: int ) -> None: