resumen de artifacts

This commit is contained in:
Jordan
2026-04-02 00:34:06 +01:00
parent bfccb02373
commit 2997622b4d
3 changed files with 109 additions and 3 deletions

View File

@@ -84,11 +84,15 @@ class ContextEngine:
if kb_section: if kb_section:
sections.append(kb_section) sections.append(kb_section)
# 4. Task state # 4. Task history — compact summaries of past tasks in this session
if "task_state" in allowed and session.task_history:
sections.append(self._build_task_history(session))
# 5. Task state — current task
if "task_state" in allowed and session.current_task: if "task_state" in allowed and session.current_task:
sections.append(self._build_task_state(session.current_task)) sections.append(self._build_task_state(session.current_task))
# 5. Artifact memory — summarised, never raw # 6. Artifact memory — summarised, never raw (only current task's)
if "artifact_memory" in allowed and artifacts: if "artifact_memory" in allowed and artifacts:
sections.append(self._build_artifact_memory(artifacts)) sections.append(self._build_artifact_memory(artifacts))
@@ -379,6 +383,42 @@ class ContextEngine:
parts.extend(session.current_task.facts_extracted[-5:]) parts.extend(session.current_task.facts_extracted[-5:])
return " ".join(parts) return " ".join(parts)
def _build_task_history(self, session: SessionState) -> ContextSection:
"""Build a compact summary of past tasks in this session.
Each completed task is ~50 tokens instead of hundreds.
The agent retains awareness of what was done before.
"""
lines = [
"# Session History",
f"_{len(session.task_history)} previous task(s) in this session_",
"",
]
for i, entry in enumerate(session.task_history):
status = entry.get("status", "?")
objective = entry.get("objective", "")[:100]
summary = entry.get("summary", "")[:150]
facts = entry.get("facts", [])
lines.append(f"**Task {i + 1}** [{status}]: {objective}")
if summary:
lines.append(f" Result: {summary}")
if facts:
lines.append(f" Facts: {'; '.join(facts[:5])}")
review = entry.get("review", "")
if review:
lines.append(f" Review: {review[:100]}")
lines.append("")
content = "\n".join(lines)
return ContextSection(
section_type=ContextSectionType.TASK_STATE,
content=content,
priority=55, # Below knowledge (60), above artifacts (50)
token_estimate=estimate_tokens(content),
)
def _build_task_state(self, task: TaskState) -> ContextSection: def _build_task_state(self, task: TaskState) -> ContextSection:
lines = [ lines = [
"# Current Task", "# Current Task",

View File

@@ -85,6 +85,7 @@ class SessionState(BaseModel):
immutable_rules: list[str] = Field(default_factory=list) immutable_rules: list[str] = Field(default_factory=list)
current_task: TaskState | None = None current_task: TaskState | None = None
completed_tasks: list[str] = Field(default_factory=list) completed_tasks: list[str] = Field(default_factory=list)
task_history: list[dict[str, Any]] = Field(default_factory=list) # Compact summaries of past tasks
turn_count: int = 0 turn_count: int = 0
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))

View File

@@ -209,7 +209,10 @@ class OrchestratorEngine:
logger.error("Review failed: %s", e) logger.error("Review failed: %s", e)
review_result = {"content": f"Review skipped due to error: {e}"} review_result = {"content": f"Review skipped due to error: {e}"}
# 5. Complete — session ALWAYS returns to idle # 5. Compact task into history before completing
await self._compact_task_to_history(session, task, results, review_result)
# 6. Complete — session ALWAYS returns to idle
session.complete_task() session.complete_task()
final_content = self._assemble_response(results, review_result) final_content = self._assemble_response(results, review_result)
@@ -258,6 +261,68 @@ class OrchestratorEngine:
# Internals # Internals
# ------------------------------------------------------------------ # ------------------------------------------------------------------
async def _compact_task_to_history(
self,
session: SessionState,
task: TaskState,
results: list[dict[str, Any]],
review_result: dict[str, Any],
) -> None:
"""Compact a completed task into a minimal history entry.
This is critical for long sessions: instead of keeping all
artifacts and facts from every task, we compress each completed
task into a ~200 token summary that preserves:
- What was done (objective)
- What was produced (file changes, modules created)
- Key facts learned
- Issues found by reviewer
"""
# Collect all artifact summaries from this task
artifacts = await self.memory.list_artifacts(session.session_id)
task_artifacts = [a for a in artifacts if a.task_id == task.task_id]
# Build compact summary
step_summaries = []
for step in task.plan:
if step.result_summary:
step_summaries.append(f"{step.agent_role}: {step.result_summary[:100]}")
tools_used = set()
for step in task.plan:
tools_used.update(step.tools_used)
history_entry = {
"task_id": task.task_id,
"objective": task.objective,
"status": task.status.value,
"steps": len(task.plan),
"facts": task.facts_extracted[-10:],
"tools_used": list(tools_used)[:10],
"artifacts_count": len(task_artifacts),
"summary": "; ".join(step_summaries)[:300],
"review": (review_result.get("content", ""))[:200],
}
# Keep max 20 task histories (trim oldest)
session.task_history.append(history_entry)
if len(session.task_history) > 20:
session.task_history = session.task_history[-20:]
# Clean up old artifacts from Redis to free memory
# Keep only artifacts from the last 2 tasks
recent_task_ids = {t["task_id"] for t in session.task_history[-2:]}
for artifact in artifacts:
if artifact.task_id not in recent_task_ids:
# Remove old artifact from Redis hash
key = f"{self.memory._prefix}:session:{session.session_id}:artifacts"
await self.memory._r.hdel(key, artifact.artifact_id)
logger.info(
"Compacted task %s into history (%d facts, %d tools, %d artifacts → summary)",
task.task_id, len(task.facts_extracted), len(tools_used), len(task_artifacts),
)
def _create_agent(self, role: AgentRole) -> PlannerAgent | CoderAgent | CollectorAgent | ReviewerAgent: def _create_agent(self, role: AgentRole) -> PlannerAgent | CoderAgent | CollectorAgent | ReviewerAgent:
"""Instantiate a subagent for the given role.""" """Instantiate a subagent for the given role."""
profile = self._profiles[role] profile = self._profiles[role]