"""Scenario Aggregator - Group related memories into scenario blocks.""" import json import logging from typing import Optional, List from lifecycle.llm_parse import parse_llm_json_array logger = logging.getLogger(__name__) SCENE_SYSTEM_PROMPT = """你是JSON生成器。直接输出JSON数组,禁止分析、思考、解释、讨论、markdown。 任务:将记忆按主题聚合成场景块。 规则: 1. 将相关记忆归入同一场景 2. 每个场景:name(名称)、summary(100字以内摘要)、memory_ids(ID列表) 3. 最多10个场景 4. 第一个字符必须是[,最后一个字符必须是] 输出格式: [{"name":"场景名","summary":"摘要","memory_ids":["id1","id2"]}] 只输出JSON,不要有任何其他文字。""" class ScenarioAggregator: """Aggregate personal memories into scenario blocks.""" def __init__(self, mysql_store, llm_client): self.mysql = mysql_store self.llm = llm_client def aggregate(self, agent_id: str, team_id: str, max_memories: int = 50) -> dict: """Aggregate memories into scenarios using LLM.""" if not self.llm.available: return {"error": "LLM not configured. Set LLM_API_URL, LLM_API_KEY, LLM_MODEL environment variables."} memories = self.mysql.get_personal_memories_by_agent(agent_id, limit=max_memories) if not memories: return {"scenarios": [], "count": 0} memory_lines = [] for m in memories: mid = m["id"] content = m.get("content", "")[:100] memory_lines.append(f"[{mid}] {content}") user_prompt = "请将以下记忆聚合为JSON场景数组:\n\n" + "\n".join(memory_lines) try: scenarios = parse_llm_json_array( self.llm, SCENE_SYSTEM_PROMPT, user_prompt, temperature=0.3, context="aggregate-scenes", max_tokens=2000, ) except ValueError as e: return {"error": str(e)} stored = [] for scene in scenarios: if not isinstance(scene, dict): logger.warning("Skipping non-dict item in scenarios: %r", scene) continue name = scene.get("name", "unnamed") summary = scene.get("summary", "") memory_ids = scene.get("memory_ids", []) valid_ids = [mid for mid in memory_ids if isinstance(mid, str) and len(mid) > 0] scene_id = self.mysql.add_scenario( team_id=team_id, agent_id=agent_id, name=name, summary=summary, memory_ids=valid_ids, ) stored.append({ "id": scene_id, "name": name, "summary": summary, "memory_ids": valid_ids, }) return {"scenarios": stored, "count": len(stored)} def get_scenarios(self, agent_id: str) -> List[dict]: return self.mysql.get_scenarios_by_agent(agent_id) def delete_scenario(self, scenario_id: str, team_id: str) -> bool: scenario = self.mysql.get_scenario(scenario_id) if not scenario or scenario["team_id"] != team_id: return False return self.mysql.delete_scenario(scenario_id)