Fix problemas detectados en evaluación: historial, prompting, artifacts

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) <noreply@anthropic.com>
This commit is contained in:
Jordan Diaz
2026-04-03 13:29:09 +00:00
parent 3aa7a463d0
commit 7bdb943e7f
3 changed files with 97 additions and 6 deletions

View File

@@ -398,6 +398,20 @@ class ContextEngine:
lines.append(f" Result: {summary}") lines.append(f" Result: {summary}")
if facts: if facts:
lines.append(f" Facts: {'; '.join(facts[:5])}") 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", "") review = entry.get("review", "")
if review: if review:
lines.append(f" Review: {review[:100]}") lines.append(f" Review: {review[:100]}")

View File

@@ -5,7 +5,7 @@ from __future__ import annotations
from ...models.agent import AgentProfile, AgentRole from ...models.agent import AgentProfile, AgentRole
from .base import BaseAgent 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 ## Instrucciones
- Concéntrate en la descripción del paso actual. - 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. - Responde SIEMPRE en español.
## Uso de herramientas ## Uso de herramientas
- Usa herramientas cuando necesites leer archivos, escribir código o ejecutar comandos. - CONSULTA la Knowledge Base ANTES de actuar — tiene la referencia completa de tools y flujos de trabajo.
- Los resultados de herramientas se te presentarán resumidos — no verás la salida cruda. - Para CREAR MÓDULOS usa `create_module` (crea archivos en filesystem), NUNCA `create_or_update_record` en tabla modulos.
- Si necesitas más detalle de un resultado, solicita rehidratación. - 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", "project_profile",
"knowledge_base", "knowledge_base",
"task_state", "task_state",
"artifact_memory",
"working_context",
], ],
) )

View File

@@ -296,12 +296,16 @@ class OrchestratorEngine:
for step in task.plan: for step in task.plan:
tools_used.update(step.tools_used) tools_used.update(step.tools_used)
# Extract key structured data from tool executions
key_data = self._extract_key_data_from_results(results)
history_entry = { history_entry = {
"task_id": task.task_id, "task_id": task.task_id,
"objective": task.objective, "objective": task.objective,
"status": task.status.value, "status": task.status.value,
"steps": len(task.plan), "steps": len(task.plan),
"facts": task.facts_extracted[-10:], "facts": task.facts_extracted[-10:],
"key_data": key_data,
"tools_used": list(tools_used)[:10], "tools_used": list(tools_used)[:10],
"artifacts_count": len(task_artifacts), "artifacts_count": len(task_artifacts),
"summary": "; ".join(step_summaries)[:300], "summary": "; ".join(step_summaries)[:300],
@@ -327,6 +331,73 @@ class OrchestratorEngine:
task.task_id, len(task.facts_extracted), len(tools_used), len(task_artifacts), 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( def _maybe_compact_previous_steps(
self, task: TaskState, current_index: int self, task: TaskState, current_index: int
) -> None: ) -> None: