- 排除 .env / *.bak / 内部运维文档(README_INTERNAL.html) - config.py 默认密码已替换为占位符 CHANGE_ME_* - init_db.sql 移除生产数据库用户 GRANT 段 - README.html 数据库用户名已脱敏 - 保留:源码 + 公网 API 文档 + 建表 SQL(无授权语句)
93 lines
3.2 KiB
Python
93 lines
3.2 KiB
Python
"""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)
|