hy-memory 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hy_memory/__init__.py +146 -0
- hy_memory/agent/__init__.py +99 -0
- hy_memory/agent/abstractor.py +300 -0
- hy_memory/agent/emotion_analyzer.py +284 -0
- hy_memory/agent/extractor.py +558 -0
- hy_memory/agent/intention_detector.py +328 -0
- hy_memory/agent/llm_provider.py +606 -0
- hy_memory/agent/mem_agent.py +377 -0
- hy_memory/agent/prompts_zh.py +316 -0
- hy_memory/agent/reconciler.py +999 -0
- hy_memory/agent/reflector.py +480 -0
- hy_memory/agent/summarizer.py +169 -0
- hy_memory/agent/tools/__init__.py +43 -0
- hy_memory/agent/tools/base.py +237 -0
- hy_memory/agent/tools/basic_profile.py +388 -0
- hy_memory/client.py +2238 -0
- hy_memory/config.py +675 -0
- hy_memory/core/__init__.py +20 -0
- hy_memory/core/embed_service.py +395 -0
- hy_memory/core/merger.py +324 -0
- hy_memory/core/scorer.py +254 -0
- hy_memory/data/__init__.py +50 -0
- hy_memory/data/cache.py +77 -0
- hy_memory/data/cache_base.py +285 -0
- hy_memory/data/cache_disabled.py +259 -0
- hy_memory/data/cache_memory.py +413 -0
- hy_memory/data/cache_redis.py +836 -0
- hy_memory/data/cache_sqlite.py +892 -0
- hy_memory/data/graph_store.py +57 -0
- hy_memory/data/graph_store_base.py +336 -0
- hy_memory/data/graph_store_kuzu.py +1374 -0
- hy_memory/data/graph_store_neo4j.py +1185 -0
- hy_memory/data/history_store.py +362 -0
- hy_memory/data/kv_store.py +357 -0
- hy_memory/data/rdb.py +193 -0
- hy_memory/data/storage_interface.py +432 -0
- hy_memory/data/vector_store.py +63 -0
- hy_memory/data/vector_store_base.py +374 -0
- hy_memory/data/vector_store_chroma.py +529 -0
- hy_memory/data/vector_store_faiss.py +553 -0
- hy_memory/data/vector_store_qdrant.py +837 -0
- hy_memory/inspector.py +445 -0
- hy_memory/models/__init__.py +178 -0
- hy_memory/models/memory.py +1213 -0
- hy_memory/models/requests.py +745 -0
- hy_memory/pipelines/__init__.py +49 -0
- hy_memory/pipelines/_retrieval/__init__.py +15 -0
- hy_memory/pipelines/_retrieval/bm25.py +115 -0
- hy_memory/pipelines/_retrieval/config.py +133 -0
- hy_memory/pipelines/_retrieval/evolution.py +242 -0
- hy_memory/pipelines/_retrieval/intent.py +155 -0
- hy_memory/pipelines/_retrieval/rrf.py +94 -0
- hy_memory/pipelines/_retrieval/tag_index.py +199 -0
- hy_memory/pipelines/_retrieval/trace.py +639 -0
- hy_memory/pipelines/base.py +373 -0
- hy_memory/pipelines/cross_domain_sweeper.py +679 -0
- hy_memory/pipelines/lite/_retrieval/__init__.py +15 -0
- hy_memory/pipelines/lite/_retrieval/bm25.py +115 -0
- hy_memory/pipelines/lite/_retrieval/config.py +132 -0
- hy_memory/pipelines/lite/_retrieval/evolution.py +242 -0
- hy_memory/pipelines/lite/_retrieval/intent.py +155 -0
- hy_memory/pipelines/lite/_retrieval/rrf.py +94 -0
- hy_memory/pipelines/lite/_retrieval/tag_index.py +199 -0
- hy_memory/pipelines/lite/_retrieval/trace.py +484 -0
- hy_memory/pipelines/lite/reader_hybrid.py +574 -0
- hy_memory/pipelines/lite/reader_hybrid_tag.py +467 -0
- hy_memory/pipelines/lite/reader_legacy.py +498 -0
- hy_memory/pipelines/reader.py +112 -0
- hy_memory/pipelines/reader_exhaustive.py +620 -0
- hy_memory/pipelines/reader_hybrid.py +574 -0
- hy_memory/pipelines/reader_hybrid_tag.py +467 -0
- hy_memory/pipelines/reader_legacy.py +793 -0
- hy_memory/pipelines/registry.py +190 -0
- hy_memory/pipelines/system2_agent.py +801 -0
- hy_memory/pipelines/system2_tools.py +466 -0
- hy_memory/pipelines/system2_writer.py +697 -0
- hy_memory/pipelines/writer.py +1057 -0
- hy_memory/server.py +375 -0
- hy_memory/utils/lang_detect.py +165 -0
- hy_memory/utils/log_setup.py +170 -0
- hy_memory/utils/metrics.py +270 -0
- hy_memory/utils/time_window.py +169 -0
- hy_memory/utils/tracer.py +381 -0
- hy_memory-1.0.0.dist-info/METADATA +136 -0
- hy_memory-1.0.0.dist-info/RECORD +88 -0
- hy_memory-1.0.0.dist-info/WHEEL +5 -0
- hy_memory-1.0.0.dist-info/licenses/LICENSE +21 -0
- hy_memory-1.0.0.dist-info/top_level.txt +1 -0
hy_memory/__init__.py
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""
|
|
2
|
+
HY Memory - 核心 SDK
|
|
3
|
+
|
|
4
|
+
工业级智能体记忆系统核心框架。
|
|
5
|
+
|
|
6
|
+
快速开始:
|
|
7
|
+
export OPENAI_API_KEY="sk-your-key-here"
|
|
8
|
+
|
|
9
|
+
from hy_memory import HyMemoryClient
|
|
10
|
+
|
|
11
|
+
# 最简用法 — 只需设置 OPENAI_API_KEY 环境变量
|
|
12
|
+
client = HyMemoryClient(user_id="test_user")
|
|
13
|
+
client.add("用户喜欢科幻电影")
|
|
14
|
+
results = client.search("用户喜欢什么?")
|
|
15
|
+
client.close()
|
|
16
|
+
|
|
17
|
+
默认配置:
|
|
18
|
+
- embedder: OpenAI text-embedding-3-small (1536 维)
|
|
19
|
+
- llm: OpenAI gpt-4.1-nano
|
|
20
|
+
- vector_store: Chroma 本地嵌入式 (零外部依赖)
|
|
21
|
+
|
|
22
|
+
只需设置 OPENAI_API_KEY 环境变量即可运行。
|
|
23
|
+
|
|
24
|
+
# 自定义配置
|
|
25
|
+
client = HyMemoryClient.from_config({
|
|
26
|
+
"vector_store": {"provider": "qdrant"},
|
|
27
|
+
"graph_store": {
|
|
28
|
+
"provider": "neo4j",
|
|
29
|
+
"url": "bolt://localhost:7687",
|
|
30
|
+
"username": "neo4j",
|
|
31
|
+
"password": "password",
|
|
32
|
+
},
|
|
33
|
+
"enable_graph": True,
|
|
34
|
+
}, user_id="test_user")
|
|
35
|
+
|
|
36
|
+
数据隔离 (两级):
|
|
37
|
+
- user_id: 一级 key — 每个用户唯一的记忆库
|
|
38
|
+
- agent_id: 二级 key — 同一用户下不同 Agent 场景的隔离
|
|
39
|
+
|
|
40
|
+
安装方式:
|
|
41
|
+
pip install hy-memory # 核心依赖 (含 Chroma,开箱即用)
|
|
42
|
+
pip install hy-memory[qdrant] # + Qdrant 向量库
|
|
43
|
+
pip install hy-memory[faiss] # + FAISS 向量库
|
|
44
|
+
pip install hy-memory[all] # 包含所有可选依赖
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
from importlib.metadata import version as _get_version
|
|
49
|
+
__version__ = _get_version("hy-memory")
|
|
50
|
+
except Exception:
|
|
51
|
+
__version__ = "1.0.0"
|
|
52
|
+
|
|
53
|
+
# ====== 用户级 API(推荐) ======
|
|
54
|
+
from .client import HyMemoryClient
|
|
55
|
+
from .inspector import MemoryInspector
|
|
56
|
+
|
|
57
|
+
# ====== 配置 ======
|
|
58
|
+
from .config import MemoryConfig
|
|
59
|
+
|
|
60
|
+
# ====== 高级 API(按需使用) ======
|
|
61
|
+
from .pipelines import (
|
|
62
|
+
ComponentFactory,
|
|
63
|
+
PipelineConfig,
|
|
64
|
+
WritePipeline,
|
|
65
|
+
ReadPipeline,
|
|
66
|
+
ChatMessage,
|
|
67
|
+
WriteRequest,
|
|
68
|
+
WriteResponse,
|
|
69
|
+
ReadRequest,
|
|
70
|
+
ReadResponse,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# ====== 数据模型 ======
|
|
74
|
+
from .models import (
|
|
75
|
+
MemoryLayer,
|
|
76
|
+
MemoryNode,
|
|
77
|
+
MemoryEntry,
|
|
78
|
+
MemoryMetadata,
|
|
79
|
+
MemoryScore,
|
|
80
|
+
MemoryInputType,
|
|
81
|
+
AgentProcessMode,
|
|
82
|
+
TaskStatus,
|
|
83
|
+
DeleteScope,
|
|
84
|
+
QAPair,
|
|
85
|
+
AddRequest,
|
|
86
|
+
AddResponse,
|
|
87
|
+
AsyncAddResponse,
|
|
88
|
+
RecallRequest,
|
|
89
|
+
RecallResponse,
|
|
90
|
+
UpdateRequest,
|
|
91
|
+
UpdateResponse,
|
|
92
|
+
DeleteRequest,
|
|
93
|
+
DeleteResponse,
|
|
94
|
+
BatchDeleteRequest,
|
|
95
|
+
BatchDeleteResponse,
|
|
96
|
+
GetRequest,
|
|
97
|
+
GetResponse,
|
|
98
|
+
ListRequest,
|
|
99
|
+
ListResponse,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
__all__ = [
|
|
104
|
+
"__version__",
|
|
105
|
+
# 用户级 API
|
|
106
|
+
"HyMemoryClient",
|
|
107
|
+
"MemoryInspector",
|
|
108
|
+
# 配置
|
|
109
|
+
"MemoryConfig",
|
|
110
|
+
# 高级 API
|
|
111
|
+
"ComponentFactory",
|
|
112
|
+
"PipelineConfig",
|
|
113
|
+
"WritePipeline",
|
|
114
|
+
"ReadPipeline",
|
|
115
|
+
"ChatMessage",
|
|
116
|
+
"WriteRequest",
|
|
117
|
+
"WriteResponse",
|
|
118
|
+
"ReadRequest",
|
|
119
|
+
"ReadResponse",
|
|
120
|
+
# 数据模型
|
|
121
|
+
"MemoryLayer",
|
|
122
|
+
"MemoryNode",
|
|
123
|
+
"MemoryEntry",
|
|
124
|
+
"MemoryMetadata",
|
|
125
|
+
"MemoryScore",
|
|
126
|
+
"MemoryInputType",
|
|
127
|
+
"AgentProcessMode",
|
|
128
|
+
"TaskStatus",
|
|
129
|
+
"DeleteScope",
|
|
130
|
+
"QAPair",
|
|
131
|
+
"AddRequest",
|
|
132
|
+
"AddResponse",
|
|
133
|
+
"AsyncAddResponse",
|
|
134
|
+
"RecallRequest",
|
|
135
|
+
"RecallResponse",
|
|
136
|
+
"UpdateRequest",
|
|
137
|
+
"UpdateResponse",
|
|
138
|
+
"DeleteRequest",
|
|
139
|
+
"DeleteResponse",
|
|
140
|
+
"BatchDeleteRequest",
|
|
141
|
+
"BatchDeleteResponse",
|
|
142
|
+
"GetRequest",
|
|
143
|
+
"GetResponse",
|
|
144
|
+
"ListRequest",
|
|
145
|
+
"ListResponse",
|
|
146
|
+
]
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent Memory - 智能层 (Agent Layer)
|
|
3
|
+
|
|
4
|
+
基于 LLM 的智能处理能力,提供语义理解、信息提取、推理判断等功能。
|
|
5
|
+
该层是可选的,关闭后系统仍能正常工作。
|
|
6
|
+
|
|
7
|
+
包含模块:
|
|
8
|
+
- MemAgent: 智能体协调器,统一入口
|
|
9
|
+
- Summarizer: Lite+Agent pipeline 摘要生成器 (L3_SUMMARY)
|
|
10
|
+
- Abstractor: System 2 全量摘要智能体 (Session摘要/Schema归纳/Profile摘要)
|
|
11
|
+
- Extractor: 提取智能体,提取结构化信息 (V2: 轻量提取/深度提取双模式)
|
|
12
|
+
- Reflector: 反思智能体,检测冲突和更新 (V2: UpdateType分类/冲突检测/隐式推断)
|
|
13
|
+
- EmotionAnalyzer: 情绪分析智能体,valence + arousal 双维标注
|
|
14
|
+
- IntentionDetector: 意图检测智能体,前瞻性记忆 + 主动关怀
|
|
15
|
+
- LLMProvider: LLM 提供器,统一的模型调用接口
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
# Lite+Agent pipeline
|
|
19
|
+
from .mem_agent import MemAgent, AgentResult, ProcessMode
|
|
20
|
+
from .summarizer import Summarizer, SummaryResult
|
|
21
|
+
from .extractor import Extractor, ExtractResult
|
|
22
|
+
from .reflector import Reflector, ReflectResult, ConflictType, ConflictAction
|
|
23
|
+
from .llm_provider import LLMProvider, LLMResponse, LLMConfig, LLMBackend
|
|
24
|
+
|
|
25
|
+
# V2 Extractor
|
|
26
|
+
from .extractor import ExtractMode, V2ExtractResult
|
|
27
|
+
|
|
28
|
+
# System 2 / Pro Pipeline Abstractor
|
|
29
|
+
from .abstractor import Abstractor, SessionSummaryResult, SchemaAbstractResult, ProfileSummaryResult
|
|
30
|
+
|
|
31
|
+
# V2 Reflector
|
|
32
|
+
from .reflector import (
|
|
33
|
+
V2ConflictType,
|
|
34
|
+
UpdateTypeResult,
|
|
35
|
+
ConflictDetectionResult,
|
|
36
|
+
ImplicitSignal,
|
|
37
|
+
ImplicitInferenceResult,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# V2 Emotion Analyzer
|
|
41
|
+
from .emotion_analyzer import EmotionAnalyzer, EmotionAnalysisConfig, EmotionResult
|
|
42
|
+
|
|
43
|
+
# V2 Intention Detector
|
|
44
|
+
from .intention_detector import (
|
|
45
|
+
IntentionDetector,
|
|
46
|
+
IntentionDetectorConfig,
|
|
47
|
+
DetectedIntention,
|
|
48
|
+
IntentionDetectResult,
|
|
49
|
+
TriggeredIntentionItem,
|
|
50
|
+
IntentionTriggerResult,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
__all__ = [
|
|
54
|
+
# MemAgent
|
|
55
|
+
"MemAgent",
|
|
56
|
+
"AgentResult",
|
|
57
|
+
"ProcessMode",
|
|
58
|
+
# Summarizer (Lite+Agent)
|
|
59
|
+
"Summarizer",
|
|
60
|
+
"SummaryResult",
|
|
61
|
+
# Abstractor (System 2)
|
|
62
|
+
"Abstractor",
|
|
63
|
+
"SessionSummaryResult",
|
|
64
|
+
"SchemaAbstractResult",
|
|
65
|
+
"ProfileSummaryResult",
|
|
66
|
+
# Extractor (V1)
|
|
67
|
+
"Extractor",
|
|
68
|
+
"ExtractResult",
|
|
69
|
+
# Extractor (V2)
|
|
70
|
+
"ExtractMode",
|
|
71
|
+
"V2ExtractResult",
|
|
72
|
+
# Reflector (V1)
|
|
73
|
+
"Reflector",
|
|
74
|
+
"ReflectResult",
|
|
75
|
+
"ConflictType",
|
|
76
|
+
"ConflictAction",
|
|
77
|
+
# Reflector (V2)
|
|
78
|
+
"V2ConflictType",
|
|
79
|
+
"UpdateTypeResult",
|
|
80
|
+
"ConflictDetectionResult",
|
|
81
|
+
"ImplicitSignal",
|
|
82
|
+
"ImplicitInferenceResult",
|
|
83
|
+
# Emotion Analyzer (V2)
|
|
84
|
+
"EmotionAnalyzer",
|
|
85
|
+
"EmotionAnalysisConfig",
|
|
86
|
+
"EmotionResult",
|
|
87
|
+
# Intention Detector (V2)
|
|
88
|
+
"IntentionDetector",
|
|
89
|
+
"IntentionDetectorConfig",
|
|
90
|
+
"DetectedIntention",
|
|
91
|
+
"IntentionDetectResult",
|
|
92
|
+
"TriggeredIntentionItem",
|
|
93
|
+
"IntentionTriggerResult",
|
|
94
|
+
# LLMProvider
|
|
95
|
+
"LLMProvider",
|
|
96
|
+
"LLMResponse",
|
|
97
|
+
"LLMConfig",
|
|
98
|
+
"LLMBackend",
|
|
99
|
+
]
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent Memory V2 - Abstractor 摘要智能体 (System 2 Pro Pipeline)
|
|
3
|
+
|
|
4
|
+
负责 System 2 pro pipeline 的高阶摘要能力:
|
|
5
|
+
1. Session 摘要生成 (L3 层 — System 2 Sleep Replay 调用)
|
|
6
|
+
2. Schema 归纳 (L5 层 — System 2 Schema Miner 调用)
|
|
7
|
+
3. Profile 摘要生成 (Context Assembly 调用)
|
|
8
|
+
|
|
9
|
+
注意:lite+agent pipeline 的 L3_SUMMARY 生成由 summarizer.py 的 Summarizer 负责。
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import Dict, Any, Optional, List
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
import json
|
|
15
|
+
import logging
|
|
16
|
+
|
|
17
|
+
from .llm_provider import LLMProvider
|
|
18
|
+
from ..config import LLMConfig as GlobalLLMConfig
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# ================================================================
|
|
24
|
+
# 数据结构
|
|
25
|
+
# ================================================================@dataclass
|
|
26
|
+
class SessionSummaryResult:
|
|
27
|
+
"""Session 摘要结果 (V2)"""
|
|
28
|
+
success: bool
|
|
29
|
+
summary: str = ""
|
|
30
|
+
key_topics: List[str] = field(default_factory=list)
|
|
31
|
+
emotional_tone: str = ""
|
|
32
|
+
new_facts_count: int = 0
|
|
33
|
+
tokens_used: int = 0
|
|
34
|
+
error: Optional[str] = None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class SchemaAbstractResult:
|
|
39
|
+
"""Schema 归纳结果 (V2)"""
|
|
40
|
+
success: bool
|
|
41
|
+
central_proposition: str = ""
|
|
42
|
+
supporting_summary: str = ""
|
|
43
|
+
expected_inferences: List[str] = field(default_factory=list)
|
|
44
|
+
confidence: float = 0.0
|
|
45
|
+
tokens_used: int = 0
|
|
46
|
+
error: Optional[str] = None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class ProfileSummaryResult:
|
|
51
|
+
"""Profile 摘要结果 (V2)"""
|
|
52
|
+
success: bool
|
|
53
|
+
core: str = ""
|
|
54
|
+
personality: str = ""
|
|
55
|
+
active_schemas: List[str] = field(default_factory=list)
|
|
56
|
+
gotchas: List[str] = field(default_factory=list)
|
|
57
|
+
note: str = ""
|
|
58
|
+
tokens_used: int = 0
|
|
59
|
+
error: Optional[str] = None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# ================================================================
|
|
63
|
+
# Prompt 模板
|
|
64
|
+
# ================================================================
|
|
65
|
+
|
|
66
|
+
SESSION_SUMMARY_PROMPT = """Generate a summary of the following conversation session. The summary language MUST match the conversation language.
|
|
67
|
+
|
|
68
|
+
Session content:
|
|
69
|
+
---
|
|
70
|
+
{session_content}
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
Existing user profile:
|
|
74
|
+
{user_profile}
|
|
75
|
+
|
|
76
|
+
Current time: {current_time}
|
|
77
|
+
|
|
78
|
+
Output in JSON format:
|
|
79
|
+
{{
|
|
80
|
+
"summary": "A brief session summary (max 200 words, in the same language as the conversation)",
|
|
81
|
+
"key_topics": ["key topic 1", "topic 2"],
|
|
82
|
+
"emotional_tone": "overall emotional tone (e.g., positive/neutral/anxious/happy)",
|
|
83
|
+
"new_facts_count": 0
|
|
84
|
+
}}"""
|
|
85
|
+
|
|
86
|
+
SCHEMA_ABSTRACT_PROMPT = """Below is a set of related facts mentioned by the user across multiple sessions. Induce an abstract "mental model" (Schema).
|
|
87
|
+
|
|
88
|
+
Related facts:
|
|
89
|
+
{facts}
|
|
90
|
+
|
|
91
|
+
A good Schema should:
|
|
92
|
+
- Describe a recurring behavioral pattern or thinking style of the user
|
|
93
|
+
- Not be a simple enumeration of facts, but an abstract pattern
|
|
94
|
+
- Help predict how the user would react in similar situations
|
|
95
|
+
|
|
96
|
+
Output in JSON format:
|
|
97
|
+
{{
|
|
98
|
+
"central_proposition": "core proposition (one sentence describing this pattern)",
|
|
99
|
+
"supporting_summary": "evidence overview (why you believe this pattern exists)",
|
|
100
|
+
"expected_inferences": ["expected inference based on this pattern 1", "inference 2"],
|
|
101
|
+
"confidence": 0.0-1.0
|
|
102
|
+
}}"""
|
|
103
|
+
|
|
104
|
+
PROFILE_SUMMARY_PROMPT = """Generate a structured user profile summary based on the following data. The summary language MUST match the profile data language.
|
|
105
|
+
|
|
106
|
+
Profile data:
|
|
107
|
+
{profile_data}
|
|
108
|
+
|
|
109
|
+
Active mental models:
|
|
110
|
+
{active_schemas}
|
|
111
|
+
|
|
112
|
+
Known gotchas/notes:
|
|
113
|
+
{gotchas}
|
|
114
|
+
|
|
115
|
+
Output in JSON format:
|
|
116
|
+
{{
|
|
117
|
+
"core": "core description (e.g., name, age, location, occupation)",
|
|
118
|
+
"personality": "personality/style description (e.g., pragmatic, tech-oriented)",
|
|
119
|
+
"active_schemas": ["active behavioral patterns"],
|
|
120
|
+
"gotchas": ["gotchas/safety notes to be aware of"],
|
|
121
|
+
"note": "other notes"
|
|
122
|
+
}}"""
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
# ================================================================
|
|
126
|
+
# 核心实现
|
|
127
|
+
# ================================================================
|
|
128
|
+
|
|
129
|
+
class Abstractor:
|
|
130
|
+
"""
|
|
131
|
+
摘要智能体 (System 2 Pro Pipeline)
|
|
132
|
+
|
|
133
|
+
负责 System 2 的高阶摘要:Session 摘要 / Schema 归纳 / Profile 摘要。
|
|
134
|
+
lite+agent pipeline 的 L3_SUMMARY 由 Summarizer(summarizer.py)负责。
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
def __init__(
|
|
138
|
+
self,
|
|
139
|
+
llm_provider: LLMProvider,
|
|
140
|
+
llm_config: Optional[GlobalLLMConfig] = None,
|
|
141
|
+
):
|
|
142
|
+
self.llm = llm_provider
|
|
143
|
+
self._llm_config = llm_config or GlobalLLMConfig()
|
|
144
|
+
self._call_count = 0
|
|
145
|
+
self._total_tokens = 0
|
|
146
|
+
logger.debug("Abstractor initialized")
|
|
147
|
+
|
|
148
|
+
async def generate_session_summary(
|
|
149
|
+
self,
|
|
150
|
+
session_content: str,
|
|
151
|
+
user_profile: str = "",
|
|
152
|
+
current_time: str = "", # 由调用方传入,不传则为空字符串
|
|
153
|
+
) -> SessionSummaryResult:
|
|
154
|
+
"""
|
|
155
|
+
生成 Session 摘要 (L3 层)。
|
|
156
|
+
由 System 2 Sleep Replay Worker 调用。
|
|
157
|
+
"""
|
|
158
|
+
try:
|
|
159
|
+
prompt = SESSION_SUMMARY_PROMPT.format(
|
|
160
|
+
session_content=session_content,
|
|
161
|
+
user_profile=user_profile or "(暂无画像)",
|
|
162
|
+
current_time=current_time, # 由调用方决定,不传则为空字符串
|
|
163
|
+
)
|
|
164
|
+
response = await self.llm.complete(
|
|
165
|
+
prompt=prompt,
|
|
166
|
+
max_tokens=self._llm_config.agent_max_tokens,
|
|
167
|
+
temperature=0.3,
|
|
168
|
+
)
|
|
169
|
+
self._call_count += 1
|
|
170
|
+
self._total_tokens += response.tokens_used
|
|
171
|
+
|
|
172
|
+
data = self._parse_json(response.content)
|
|
173
|
+
|
|
174
|
+
return SessionSummaryResult(
|
|
175
|
+
success=True,
|
|
176
|
+
summary=data.get("summary", response.content.strip()),
|
|
177
|
+
key_topics=data.get("key_topics", []),
|
|
178
|
+
emotional_tone=data.get("emotional_tone", "中性"),
|
|
179
|
+
new_facts_count=data.get("new_facts_count", 0),
|
|
180
|
+
tokens_used=response.tokens_used,
|
|
181
|
+
)
|
|
182
|
+
except Exception as e:
|
|
183
|
+
logger.error(f"generate_session_summary failed: {e}")
|
|
184
|
+
return SessionSummaryResult(success=False, error=str(e))
|
|
185
|
+
|
|
186
|
+
# ================================================================
|
|
187
|
+
# V2 Schema 归纳 (L4.5)
|
|
188
|
+
# ================================================================
|
|
189
|
+
|
|
190
|
+
async def abstract_schema(
|
|
191
|
+
self,
|
|
192
|
+
facts: List[str],
|
|
193
|
+
) -> SchemaAbstractResult:
|
|
194
|
+
"""
|
|
195
|
+
从一组关联事实中归纳 Schema (心智模型)。
|
|
196
|
+
由 System 2 Schema Miner 调用。
|
|
197
|
+
"""
|
|
198
|
+
try:
|
|
199
|
+
facts_text = "\n".join(f" - {f}" for f in facts)
|
|
200
|
+
prompt = SCHEMA_ABSTRACT_PROMPT.format(facts=facts_text)
|
|
201
|
+
|
|
202
|
+
response = await self.llm.complete(
|
|
203
|
+
prompt=prompt,
|
|
204
|
+
max_tokens=self._llm_config.agent_max_tokens,
|
|
205
|
+
temperature=0.4,
|
|
206
|
+
)
|
|
207
|
+
self._call_count += 1
|
|
208
|
+
self._total_tokens += response.tokens_used
|
|
209
|
+
|
|
210
|
+
data = self._parse_json(response.content)
|
|
211
|
+
|
|
212
|
+
return SchemaAbstractResult(
|
|
213
|
+
success=True,
|
|
214
|
+
central_proposition=data.get("central_proposition", ""),
|
|
215
|
+
supporting_summary=data.get("supporting_summary", ""),
|
|
216
|
+
expected_inferences=data.get("expected_inferences", []),
|
|
217
|
+
confidence=float(data.get("confidence", 0.5)),
|
|
218
|
+
tokens_used=response.tokens_used,
|
|
219
|
+
)
|
|
220
|
+
except Exception as e:
|
|
221
|
+
logger.error(f"abstract_schema failed: {e}")
|
|
222
|
+
return SchemaAbstractResult(success=False, error=str(e))
|
|
223
|
+
|
|
224
|
+
# ================================================================
|
|
225
|
+
# V2 Profile 摘要 (L5)
|
|
226
|
+
# ================================================================
|
|
227
|
+
|
|
228
|
+
async def generate_profile_summary(
|
|
229
|
+
self,
|
|
230
|
+
profile_data: str,
|
|
231
|
+
active_schemas: str = "",
|
|
232
|
+
gotchas: str = "",
|
|
233
|
+
) -> ProfileSummaryResult:
|
|
234
|
+
"""
|
|
235
|
+
生成 Profile 摘要,用于 Memory Context Package 的 Layer 0。
|
|
236
|
+
由 Context Assembly 调用。
|
|
237
|
+
"""
|
|
238
|
+
try:
|
|
239
|
+
prompt = PROFILE_SUMMARY_PROMPT.format(
|
|
240
|
+
profile_data=profile_data or "(暂无画像数据)",
|
|
241
|
+
active_schemas=active_schemas or "(无)",
|
|
242
|
+
gotchas=gotchas or "(无)",
|
|
243
|
+
)
|
|
244
|
+
response = await self.llm.complete(
|
|
245
|
+
prompt=prompt,
|
|
246
|
+
max_tokens=self._llm_config.agent_max_tokens,
|
|
247
|
+
temperature=0.2,
|
|
248
|
+
)
|
|
249
|
+
self._call_count += 1
|
|
250
|
+
self._total_tokens += response.tokens_used
|
|
251
|
+
|
|
252
|
+
data = self._parse_json(response.content)
|
|
253
|
+
|
|
254
|
+
return ProfileSummaryResult(
|
|
255
|
+
success=True,
|
|
256
|
+
core=data.get("core", ""),
|
|
257
|
+
personality=data.get("personality", ""),
|
|
258
|
+
active_schemas=data.get("active_schemas", []),
|
|
259
|
+
gotchas=data.get("gotchas", []),
|
|
260
|
+
note=data.get("note", ""),
|
|
261
|
+
tokens_used=response.tokens_used,
|
|
262
|
+
)
|
|
263
|
+
except Exception as e:
|
|
264
|
+
logger.error(f"generate_profile_summary failed: {e}")
|
|
265
|
+
return ProfileSummaryResult(success=False, error=str(e))
|
|
266
|
+
|
|
267
|
+
# ================================================================
|
|
268
|
+
# 工具方法
|
|
269
|
+
# ================================================================
|
|
270
|
+
|
|
271
|
+
@staticmethod
|
|
272
|
+
def _parse_json(text: str) -> Dict[str, Any]:
|
|
273
|
+
"""从 LLM 输出中解析 JSON"""
|
|
274
|
+
text = text.strip()
|
|
275
|
+
if "```json" in text:
|
|
276
|
+
text = text.split("```json")[1].split("```")[0].strip()
|
|
277
|
+
elif "```" in text:
|
|
278
|
+
text = text.split("```")[1].split("```")[0].strip()
|
|
279
|
+
try:
|
|
280
|
+
return json.loads(text)
|
|
281
|
+
except json.JSONDecodeError:
|
|
282
|
+
import re
|
|
283
|
+
match = re.search(r'\{[\s\S]*\}', text)
|
|
284
|
+
if match:
|
|
285
|
+
try:
|
|
286
|
+
return json.loads(match.group())
|
|
287
|
+
except json.JSONDecodeError:
|
|
288
|
+
pass
|
|
289
|
+
return {}
|
|
290
|
+
|
|
291
|
+
def get_stats(self) -> Dict[str, Any]:
|
|
292
|
+
"""获取统计信息"""
|
|
293
|
+
return {
|
|
294
|
+
"call_count": self._call_count,
|
|
295
|
+
"total_tokens": self._total_tokens,
|
|
296
|
+
"avg_tokens_per_call": (
|
|
297
|
+
self._total_tokens / self._call_count
|
|
298
|
+
if self._call_count > 0 else 0
|
|
299
|
+
),
|
|
300
|
+
}
|