初始化:记忆系统源代码上传(已脱敏)

- 排除 .env / *.bak / 内部运维文档(README_INTERNAL.html)
- config.py 默认密码已替换为占位符 CHANGE_ME_*
- init_db.sql 移除生产数据库用户 GRANT 段
- README.html 数据库用户名已脱敏
- 保留:源码 + 公网 API 文档 + 建表 SQL(无授权语句)
This commit is contained in:
zqf
2026-08-03 04:53:48 +08:00
commit b1f93588f1
30 changed files with 5591 additions and 0 deletions
+101
View File
@@ -0,0 +1,101 @@
"""Fact Extractor - Extract structured atomic facts from working memory using LLM."""
import json
import logging
from typing import Optional, List
from lifecycle.llm_parse import parse_llm_json_array
logger = logging.getLogger(__name__)
FACT_SYSTEM_PROMPT = """你是JSON生成器。输入工作记忆,直接提取结构化事实。
严格规则:
1. 禁止输出分析、推理、思考、解释
2. 第一个字符必须是 [,最后一个字符必须是 ]
3. 每条事实:content50-200字)、importance0.0-1.0)、category(分类)
4. 最多20条,无值得提取的事实返回 []
输出格式(只输出JSON,不输出其他任何文字):
[{"content":"事实","importance":0.8,"category":"分类"}]"""
class FactExtractor:
"""Extract atomic facts from working memory using LLM."""
def __init__(self, mysql_store, redis_cache, llm_client, embedding_service):
self.mysql = mysql_store
self.redis = redis_cache
self.llm = llm_client
self.embedder = embedding_service
def extract(self, agent_id: str, team_id: str,
max_memories: int = 20,
delete_after: bool = False) -> dict:
"""Extract structured facts from working memory."""
if not self.llm.available:
return {"error": "LLM not configured. Set LLM_API_URL, LLM_API_KEY, LLM_MODEL environment variables."}
items = self.redis.get_working_memories(agent_id, limit=max_memories)
if not items:
return {"facts": [], "count": 0}
memory_lines = []
for i, item in enumerate(items):
if not isinstance(item, dict):
logger.warning('Skipping non-dict working memory item: %r', item)
continue
content = item.get("content", "")[:300]
memory_lines.append(f"[{i+1}] {content}")
user_prompt = "以下是需要提取事实的工作记忆:\n\n" + "\n".join(memory_lines)
try:
facts = parse_llm_json_array(
self.llm, FACT_SYSTEM_PROMPT, user_prompt,
temperature=0.2, context="extract-facts",
)
except ValueError as e:
return {"error": str(e)}
stored = []
for fact in facts:
if not isinstance(fact, dict):
logger.warning('Skipping non-dict fact from LLM: %r', fact)
continue
content = fact.get("content", "").strip()
if not content or len(content) < 10:
continue
importance = fact.get("importance", 0.6)
if not isinstance(importance, (int, float)) or importance < 0 or importance > 1:
importance = 0.6
category = fact.get("category", "")
embedding = self.embedder.embed(content)
from storage.vector_search import embedding_to_bytes
emb_bytes = embedding_to_bytes(embedding)
memory_id = self.mysql.add_personal_memory(
agent_id=agent_id,
team_id=team_id,
content=content,
embedding=emb_bytes,
importance=float(importance),
metadata={
"source": "fact_extraction",
"category": category,
"extracted_from": "working_memory",
},
)
stored.append({
"id": memory_id,
"content": content,
"importance": importance,
"category": category,
})
if delete_after and stored:
self.redis.clear_working_memory(agent_id)
logger.info(f"Deleted working memories for {agent_id} after extracting {len(stored)} facts")
return {"facts": stored, "count": len(stored)}