resumen de artifacts
This commit is contained in:
@@ -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",
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
Reference in New Issue
Block a user