cesjavi commited on
Commit
7ebd2cf
·
1 Parent(s): c38df78

Phase 7: Self-Optimizing Agents (Feedback analysis on rejection, lesson injection on retry)

Browse files
backend/routers/agent_runner.py CHANGED
@@ -363,8 +363,16 @@ async def approve_task(task_id: str, background_tasks: BackgroundTasks):
363
  return {"message": "Task approved", "task": task}
364
 
365
  @router.post("/{task_id}/reject")
366
- async def reject_task(task_id: str):
367
  task = update_task_status(task_id, "todo")
 
 
 
 
 
 
 
 
368
  await audit_service.log_action(
369
  user_id=None,
370
  action="task_rejected",
 
363
  return {"message": "Task approved", "task": task}
364
 
365
  @router.post("/{task_id}/reject")
366
+ async def reject_task(task_id: str, background_tasks: BackgroundTasks, feedback: str | None = None):
367
  task = update_task_status(task_id, "todo")
368
+
369
+ # Trigger Self-Optimization Loop
370
+ background_tasks.add_task(
371
+ memory_service.analyze_rejection,
372
+ task_id=task_id,
373
+ feedback=feedback
374
+ )
375
+
376
  await audit_service.log_action(
377
  user_id=None,
378
  action="task_rejected",
backend/services/agent_runner_service.py CHANGED
@@ -104,6 +104,19 @@ class AgentRunnerService:
104
  for m in memories:
105
  memory_blocks.append(f"- Memory: {m['content']}")
106
  extra_context += memory_header + "\n".join(memory_blocks)
 
 
 
 
 
 
 
 
 
 
 
 
 
107
 
108
  import time
109
  import hashlib
 
104
  for m in memories:
105
  memory_blocks.append(f"- Memory: {m['content']}")
106
  extra_context += memory_header + "\n".join(memory_blocks)
107
+
108
+ # Fetch Self-Optimization Lessons for this specific task
109
+ lessons_res = supabase.table("project_memory") \
110
+ .select("content") \
111
+ .eq("task_id", task_id) \
112
+ .eq("memory_type", "self_optimization_lesson") \
113
+ .order("created_at", desc=True) \
114
+ .limit(1) \
115
+ .execute()
116
+
117
+ if lessons_res.data:
118
+ lesson = lessons_res.data[0]["content"]
119
+ extra_context += f"\n\n### CRITICAL LESSON FROM PREVIOUS ATTEMPT\n{lesson}\n"
120
 
121
  import time
122
  import hashlib
backend/services/memory_service.py CHANGED
@@ -109,4 +109,66 @@ class MemoryService:
109
  }
110
  )
111
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  memory_service = MemoryService()
 
109
  }
110
  )
111
 
112
+ async def analyze_rejection(self, task_id: str, feedback: Optional[str] = None):
113
+ """
114
+ Analyzes a task rejection to generate a 'Self-Optimization Lesson'.
115
+ Triggered when a human rejects an agent's output.
116
+ """
117
+ try:
118
+ # 1. Fetch task and its failed output
119
+ task_res = supabase.table("tasks").select("*, projects(name, description)").eq("id", task_id).single().execute()
120
+ if not task_res.data:
121
+ return
122
+
123
+ task = task_res.data
124
+ output = task.get("output_data") or {}
125
+
126
+ # 2. Get an analyst agent
127
+ from agents.agent_factory import AgentFactory
128
+ from services.llm_config import getDefaultProvider, getDefaultModel
129
+
130
+ provider = getDefaultProvider()
131
+ model = getDefaultModel(provider)
132
+
133
+ analyst = AgentFactory.get_agent(
134
+ provider=provider,
135
+ name="Optimization Analyst",
136
+ role="Self-Optimization Expert",
137
+ model=model,
138
+ system_prompt="You analyze task rejections. Your goal is to produce a single, concise 'Lesson Learned' that the next agent should follow to avoid repeating the mistake. Focus on the core reason for rejection."
139
+ )
140
+
141
+ # 3. Construct prompt for analysis
142
+ analysis_prompt = f"""
143
+ TASK: {task.get('title')}
144
+ DESCRIPTION: {task.get('description')}
145
+
146
+ REJECTED OUTPUT:
147
+ {str(output)[:2000]}
148
+
149
+ HUMAN FEEDBACK: {feedback or "No explicit feedback provided, but the output did not meet quality standards."}
150
+
151
+ Provide a concise 'LESSON LEARNED' for the next agent. Start with 'Next time, you must...'
152
+ """
153
+
154
+ result = await analyst.run(analysis_prompt, [])
155
+ lesson_text = result.get("raw_output") or result.get("data")
156
+
157
+ if lesson_text:
158
+ await self.save_memory(
159
+ project_id=task.get("project_id"),
160
+ task_id=task_id,
161
+ content=f"Optimization Lesson for '{task.get('title')}': {lesson_text}",
162
+ memory_type="self_optimization_lesson",
163
+ metadata={
164
+ "original_task_id": task_id,
165
+ "was_rejected": True,
166
+ "feedback": feedback
167
+ }
168
+ )
169
+ logger.info(f"Saved self-optimization lesson for task {task_id}")
170
+
171
+ except Exception as e:
172
+ logger.error(f"Failed to analyze rejection for task {task_id}: {e}")
173
+
174
  memory_service = MemoryService()