aetherforge-platform 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.
- aetherforge_platform-1.0.0.dist-info/METADATA +86 -0
- aetherforge_platform-1.0.0.dist-info/RECORD +55 -0
- aetherforge_platform-1.0.0.dist-info/WHEEL +5 -0
- aetherforge_platform-1.0.0.dist-info/top_level.txt +4 -0
- ai-life-assistant-copy/ai_agent.py +145 -0
- ai-life-assistant-copy/avatar_manager.py +231 -0
- ai-life-assistant-copy/avatar_packer.py +261 -0
- ai-life-assistant-copy/backup_all.py +262 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/ai_agent.py +145 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/avatar_manager.py +231 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/avatar_packer.py +261 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/backup_all.py +262 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/commands.py +210 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/config.py +30 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/daemon/__init__.py +3 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/daemon/daemon.py +174 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/database.py +292 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/graph.py +531 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/main.py +830 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/mcp_tools.py +449 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/memory.py +92 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/memory_v2.py +333 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/mock_shopping_data.py +172 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/personality.py +159 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/speech.py +41 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/test_simple.py +127 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/tools/__init__.py +15 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/tools/amazon_tool.py +103 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/tools/calendar_tool.py +92 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/tools/reminder_tool.py +92 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/tools/weather_tool.py +45 -0
- ai-life-assistant-copy/backups/backup_20260404_193836/tree_memory.py +340 -0
- ai-life-assistant-copy/commands.py +210 -0
- ai-life-assistant-copy/config.py +30 -0
- ai-life-assistant-copy/daemon/__init__.py +3 -0
- ai-life-assistant-copy/daemon/daemon.py +174 -0
- ai-life-assistant-copy/database.py +292 -0
- ai-life-assistant-copy/graph.py +531 -0
- ai-life-assistant-copy/main.py +830 -0
- ai-life-assistant-copy/mcp_tools.py +449 -0
- ai-life-assistant-copy/memory.py +92 -0
- ai-life-assistant-copy/memory_v2.py +333 -0
- ai-life-assistant-copy/mock_shopping_data.py +172 -0
- ai-life-assistant-copy/personality.py +159 -0
- ai-life-assistant-copy/speech.py +41 -0
- ai-life-assistant-copy/test_simple.py +127 -0
- ai-life-assistant-copy/tools/__init__.py +15 -0
- ai-life-assistant-copy/tools/amazon_tool.py +103 -0
- ai-life-assistant-copy/tools/calendar_tool.py +92 -0
- ai-life-assistant-copy/tools/reminder_tool.py +92 -0
- ai-life-assistant-copy/tools/weather_tool.py +45 -0
- ai-life-assistant-copy/tree_memory.py +340 -0
- ai_agent_runtime.py +447 -0
- main.py +6752 -0
- mcp_server.py +427 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
完整备份脚本 - 备份整个 AI Life Assistant 系统
|
|
4
|
+
包括所有代码、数据库、静态文件和配置
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import shutil
|
|
9
|
+
import sqlite3
|
|
10
|
+
import json
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
def create_backup():
|
|
15
|
+
"""创建完整备份"""
|
|
16
|
+
|
|
17
|
+
# 获取当前目录
|
|
18
|
+
base_dir = Path(__file__).parent
|
|
19
|
+
backup_dir = base_dir / "backups"
|
|
20
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
21
|
+
backup_folder = backup_dir / f"backup_{timestamp}"
|
|
22
|
+
|
|
23
|
+
print(f"🚀 开始创建备份...")
|
|
24
|
+
print(f"📂 备份目录:{backup_folder}")
|
|
25
|
+
|
|
26
|
+
# 创建备份文件夹
|
|
27
|
+
backup_folder.mkdir(parents=True, exist_ok=True)
|
|
28
|
+
|
|
29
|
+
# 1. 备份数据库
|
|
30
|
+
db_file = base_dir / "avatar_system.db"
|
|
31
|
+
if db_file.exists():
|
|
32
|
+
print("💾 备份数据库...")
|
|
33
|
+
shutil.copy2(db_file, backup_folder / "avatar_system.db")
|
|
34
|
+
|
|
35
|
+
# 导出数据库内容为 JSON(双重保险)
|
|
36
|
+
export_database_to_json(db_file, backup_folder / "database_export.json")
|
|
37
|
+
else:
|
|
38
|
+
print("⚠️ 数据库文件不存在")
|
|
39
|
+
|
|
40
|
+
# 2. 备份所有 Python 文件
|
|
41
|
+
print("📄 备份 Python 文件...")
|
|
42
|
+
py_files = list(base_dir.glob("*.py"))
|
|
43
|
+
for py_file in py_files:
|
|
44
|
+
shutil.copy2(py_file, backup_folder / py_file.name)
|
|
45
|
+
|
|
46
|
+
# 3. 备份静态文件
|
|
47
|
+
static_dir = base_dir / "static"
|
|
48
|
+
if static_dir.exists():
|
|
49
|
+
print("🎨 备份静态文件...")
|
|
50
|
+
backup_static = backup_folder / "static"
|
|
51
|
+
backup_static.mkdir(exist_ok=True)
|
|
52
|
+
shutil.copytree(static_dir, backup_static, dirs_exist_ok=True)
|
|
53
|
+
|
|
54
|
+
# 4. 备份工具文件
|
|
55
|
+
tools_dir = base_dir / "tools"
|
|
56
|
+
if tools_dir.exists():
|
|
57
|
+
print("🔧 备份工具文件...")
|
|
58
|
+
backup_tools = backup_folder / "tools"
|
|
59
|
+
backup_tools.mkdir(exist_ok=True)
|
|
60
|
+
shutil.copytree(tools_dir, backup_tools, dirs_exist_ok=True)
|
|
61
|
+
|
|
62
|
+
# 5. 备份守护进程
|
|
63
|
+
daemon_dir = base_dir / "daemon"
|
|
64
|
+
if daemon_dir.exists():
|
|
65
|
+
print("👻 备份守护进程...")
|
|
66
|
+
backup_daemon = backup_folder / "daemon"
|
|
67
|
+
backup_daemon.mkdir(exist_ok=True)
|
|
68
|
+
shutil.copytree(daemon_dir, backup_daemon, dirs_exist_ok=True)
|
|
69
|
+
|
|
70
|
+
# 6. 备份重要文档
|
|
71
|
+
print("📚 备份文档...")
|
|
72
|
+
doc_files = ["README.md", "SYSTEM_GUIDE.md", "requirements.txt", ".env.example"]
|
|
73
|
+
for doc_file in doc_files:
|
|
74
|
+
src = base_dir / doc_file
|
|
75
|
+
if src.exists():
|
|
76
|
+
shutil.copy2(src, backup_folder / doc_file)
|
|
77
|
+
|
|
78
|
+
# 7. 创建备份清单
|
|
79
|
+
create_backup_manifest(backup_folder, timestamp)
|
|
80
|
+
|
|
81
|
+
print(f"\n✅ 备份完成!")
|
|
82
|
+
print(f"📦 备份位置:{backup_folder}")
|
|
83
|
+
print(f"💡 提示:建议将备份文件夹压缩保存")
|
|
84
|
+
|
|
85
|
+
return backup_folder
|
|
86
|
+
|
|
87
|
+
def export_database_to_json(db_file, output_file):
|
|
88
|
+
"""导出数据库内容为 JSON"""
|
|
89
|
+
try:
|
|
90
|
+
conn = sqlite3.connect(db_file)
|
|
91
|
+
conn.row_factory = sqlite3.Row
|
|
92
|
+
cursor = conn.cursor()
|
|
93
|
+
|
|
94
|
+
data = {
|
|
95
|
+
"exported_at": datetime.now().isoformat(),
|
|
96
|
+
"tables": {}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# 获取所有表
|
|
100
|
+
cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
|
|
101
|
+
tables = [row[0] for row in cursor.fetchall()]
|
|
102
|
+
|
|
103
|
+
for table in tables:
|
|
104
|
+
cursor.execute(f"SELECT * FROM {table}")
|
|
105
|
+
rows = cursor.fetchall()
|
|
106
|
+
|
|
107
|
+
if rows:
|
|
108
|
+
# 转换为字典列表
|
|
109
|
+
columns = [description[0] for description in cursor.description]
|
|
110
|
+
data["tables"][table] = [dict(row) for row in rows]
|
|
111
|
+
|
|
112
|
+
conn.close()
|
|
113
|
+
|
|
114
|
+
with open(output_file, 'w', encoding='utf-8') as f:
|
|
115
|
+
json.dump(data, f, ensure_ascii=False, indent=2)
|
|
116
|
+
|
|
117
|
+
print(f" ✓ 数据库已导出为 JSON: {output_file}")
|
|
118
|
+
|
|
119
|
+
except Exception as e:
|
|
120
|
+
print(f" ✗ 数据库导出失败:{e}")
|
|
121
|
+
|
|
122
|
+
def create_backup_manifest(backup_folder, timestamp):
|
|
123
|
+
"""创建备份清单"""
|
|
124
|
+
manifest = {
|
|
125
|
+
"backup_date": timestamp,
|
|
126
|
+
"backup_type": "complete",
|
|
127
|
+
"version": "2.0",
|
|
128
|
+
"files": [],
|
|
129
|
+
"database": {
|
|
130
|
+
"exists": (backup_folder / "avatar_system.db").exists(),
|
|
131
|
+
"exported": (backup_folder / "database_export.json").exists()
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
# 列出所有文件
|
|
136
|
+
for file_path in backup_folder.rglob("*"):
|
|
137
|
+
if file_path.is_file():
|
|
138
|
+
relative_path = str(file_path.relative_to(backup_folder))
|
|
139
|
+
size = file_path.stat().st_size
|
|
140
|
+
manifest["files"].append({
|
|
141
|
+
"path": relative_path,
|
|
142
|
+
"size": size
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
# 保存清单
|
|
146
|
+
manifest_file = backup_folder / "backup_manifest.json"
|
|
147
|
+
with open(manifest_file, 'w', encoding='utf-8') as f:
|
|
148
|
+
json.dump(manifest, f, ensure_ascii=False, indent=2)
|
|
149
|
+
|
|
150
|
+
print(f" ✓ 已创建备份清单:{manifest_file}")
|
|
151
|
+
|
|
152
|
+
def restore_from_backup(backup_folder):
|
|
153
|
+
"""从备份恢复"""
|
|
154
|
+
backup_folder = Path(backup_folder)
|
|
155
|
+
|
|
156
|
+
if not backup_folder.exists():
|
|
157
|
+
print(f"❌ 备份文件夹不存在:{backup_folder}")
|
|
158
|
+
return
|
|
159
|
+
|
|
160
|
+
print(f"🔄 开始从备份恢复...")
|
|
161
|
+
print(f"📂 备份来源:{backup_folder}")
|
|
162
|
+
|
|
163
|
+
base_dir = Path(__file__).parent
|
|
164
|
+
|
|
165
|
+
# 1. 恢复数据库
|
|
166
|
+
db_backup = backup_folder / "avatar_system.db"
|
|
167
|
+
if db_backup.exists():
|
|
168
|
+
print("💾 恢复数据库...")
|
|
169
|
+
shutil.copy2(db_backup, base_dir / "avatar_system.db")
|
|
170
|
+
|
|
171
|
+
# 2. 恢复 Python 文件
|
|
172
|
+
print("📄 恢复 Python 文件...")
|
|
173
|
+
for py_file in backup_folder.glob("*.py"):
|
|
174
|
+
if py_file.name != "backup_all.py": # 不覆盖备份脚本本身
|
|
175
|
+
shutil.copy2(py_file, base_dir / py_file.name)
|
|
176
|
+
|
|
177
|
+
# 3. 恢复静态文件
|
|
178
|
+
static_backup = backup_folder / "static"
|
|
179
|
+
if static_backup.exists():
|
|
180
|
+
print("🎨 恢复静态文件...")
|
|
181
|
+
static_dir = base_dir / "static"
|
|
182
|
+
if static_dir.exists():
|
|
183
|
+
shutil.rmtree(static_dir)
|
|
184
|
+
shutil.copytree(static_backup, static_dir)
|
|
185
|
+
|
|
186
|
+
# 4. 恢复工具文件
|
|
187
|
+
tools_backup = backup_folder / "tools"
|
|
188
|
+
if tools_backup.exists():
|
|
189
|
+
print("🔧 恢复工具文件...")
|
|
190
|
+
tools_dir = base_dir / "tools"
|
|
191
|
+
if tools_dir.exists():
|
|
192
|
+
shutil.rmtree(tools_dir)
|
|
193
|
+
shutil.copytree(tools_backup, tools_dir)
|
|
194
|
+
|
|
195
|
+
# 5. 恢复守护进程
|
|
196
|
+
daemon_backup = backup_folder / "daemon"
|
|
197
|
+
if daemon_backup.exists():
|
|
198
|
+
print("👻 恢复守护进程...")
|
|
199
|
+
daemon_dir = base_dir / "daemon"
|
|
200
|
+
if daemon_dir.exists():
|
|
201
|
+
shutil.rmtree(daemon_dir)
|
|
202
|
+
shutil.copytree(daemon_backup, daemon_dir)
|
|
203
|
+
|
|
204
|
+
print("\n✅ 恢复完成!")
|
|
205
|
+
print("🚀 现在可以启动服务器:python main.py")
|
|
206
|
+
|
|
207
|
+
def list_backups():
|
|
208
|
+
"""列出所有备份"""
|
|
209
|
+
backup_dir = Path(__file__).parent / "backups"
|
|
210
|
+
|
|
211
|
+
if not backup_dir.exists():
|
|
212
|
+
print("❌ 没有找到备份文件夹")
|
|
213
|
+
return
|
|
214
|
+
|
|
215
|
+
print("📦 可用备份:")
|
|
216
|
+
print("-" * 60)
|
|
217
|
+
|
|
218
|
+
for backup_folder in sorted(backup_dir.iterdir()):
|
|
219
|
+
if backup_folder.is_dir():
|
|
220
|
+
manifest_file = backup_folder / "backup_manifest.json"
|
|
221
|
+
if manifest_file.exists():
|
|
222
|
+
with open(manifest_file, 'r', encoding='utf-8') as f:
|
|
223
|
+
manifest = json.load(f)
|
|
224
|
+
|
|
225
|
+
date = manifest.get("backup_date", "未知")
|
|
226
|
+
files = len(manifest.get("files", []))
|
|
227
|
+
db_status = "✓" if manifest.get("database", {}).get("exists") else "✗"
|
|
228
|
+
|
|
229
|
+
print(f"📁 {backup_folder.name}")
|
|
230
|
+
print(f" 日期:{date}")
|
|
231
|
+
print(f" 文件数:{files}")
|
|
232
|
+
print(f" 数据库:{db_status}")
|
|
233
|
+
print("-" * 60)
|
|
234
|
+
|
|
235
|
+
if __name__ == "__main__":
|
|
236
|
+
import sys
|
|
237
|
+
|
|
238
|
+
if len(sys.argv) > 1:
|
|
239
|
+
command = sys.argv[1]
|
|
240
|
+
|
|
241
|
+
if command == "backup":
|
|
242
|
+
create_backup()
|
|
243
|
+
|
|
244
|
+
elif command == "restore":
|
|
245
|
+
if len(sys.argv) > 2:
|
|
246
|
+
restore_from_backup(sys.argv[2])
|
|
247
|
+
else:
|
|
248
|
+
print("❌ 请指定备份文件夹路径")
|
|
249
|
+
print("用法:python backup_all.py restore <备份文件夹>")
|
|
250
|
+
|
|
251
|
+
elif command == "list":
|
|
252
|
+
list_backups()
|
|
253
|
+
|
|
254
|
+
else:
|
|
255
|
+
print("❌ 未知命令")
|
|
256
|
+
print("用法:")
|
|
257
|
+
print(" python backup_all.py backup - 创建备份")
|
|
258
|
+
print(" python backup_all.py restore <文件夹> - 从备份恢复")
|
|
259
|
+
print(" python backup_all.py list - 列出所有备份")
|
|
260
|
+
else:
|
|
261
|
+
# 默认创建备份
|
|
262
|
+
create_backup()
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""命令系统"""
|
|
2
|
+
|
|
3
|
+
from typing import Dict, Any, List
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
class CommandParser:
|
|
7
|
+
"""命令解析器"""
|
|
8
|
+
|
|
9
|
+
def __init__(self):
|
|
10
|
+
self.commands = {
|
|
11
|
+
"weather": self._parse_weather,
|
|
12
|
+
"w": self._parse_weather,
|
|
13
|
+
"calendar": self._parse_calendar,
|
|
14
|
+
"cal": self._parse_calendar,
|
|
15
|
+
"reminder": self._parse_reminder,
|
|
16
|
+
"rem": self._parse_reminder,
|
|
17
|
+
"shopping": self._parse_shopping,
|
|
18
|
+
"buy": self._parse_shopping,
|
|
19
|
+
"food": self._parse_food,
|
|
20
|
+
"taxi": self._parse_taxi,
|
|
21
|
+
"flight": self._parse_flight,
|
|
22
|
+
"help": self._parse_help
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
def parse(self, message: str) -> Dict[str, Any]:
|
|
26
|
+
"""解析命令
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
message: 用户输入
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
解析结果
|
|
33
|
+
"""
|
|
34
|
+
if not message.startswith("/"):
|
|
35
|
+
return {"type": "normal", "message": message}
|
|
36
|
+
|
|
37
|
+
# 提取命令和参数
|
|
38
|
+
parts = message[1:].split(" ", 1)
|
|
39
|
+
command = parts[0].lower()
|
|
40
|
+
args = parts[1] if len(parts) > 1 else ""
|
|
41
|
+
|
|
42
|
+
if command in self.commands:
|
|
43
|
+
return self.commands[command](args)
|
|
44
|
+
else:
|
|
45
|
+
return {"type": "normal", "message": message}
|
|
46
|
+
|
|
47
|
+
def _parse_weather(self, args: str) -> Dict[str, Any]:
|
|
48
|
+
"""解析天气命令"""
|
|
49
|
+
location = args.strip() or "北京"
|
|
50
|
+
return {
|
|
51
|
+
"type": "command",
|
|
52
|
+
"command": "weather",
|
|
53
|
+
"intent": "weather",
|
|
54
|
+
"requirements": {"location": location}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
def _parse_calendar(self, args: str) -> Dict[str, Any]:
|
|
58
|
+
"""解析日程命令"""
|
|
59
|
+
parts = args.split(" ")
|
|
60
|
+
if not parts:
|
|
61
|
+
return {
|
|
62
|
+
"type": "command",
|
|
63
|
+
"command": "calendar",
|
|
64
|
+
"intent": "calendar",
|
|
65
|
+
"requirements": {}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if parts[0] == "add":
|
|
69
|
+
# /calendar add 标题 时间 [日期]
|
|
70
|
+
if len(parts) >= 3:
|
|
71
|
+
title = " ".join(parts[1:-1])
|
|
72
|
+
time = parts[-1]
|
|
73
|
+
return {
|
|
74
|
+
"type": "command",
|
|
75
|
+
"command": "calendar",
|
|
76
|
+
"intent": "calendar",
|
|
77
|
+
"requirements": {
|
|
78
|
+
"action": "add",
|
|
79
|
+
"title": title,
|
|
80
|
+
"time": time
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# 默认获取日程
|
|
85
|
+
date = args.strip()
|
|
86
|
+
return {
|
|
87
|
+
"type": "command",
|
|
88
|
+
"command": "calendar",
|
|
89
|
+
"intent": "calendar",
|
|
90
|
+
"requirements": {"date": date}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
def _parse_reminder(self, args: str) -> Dict[str, Any]:
|
|
94
|
+
"""解析提醒命令"""
|
|
95
|
+
parts = args.split(" ")
|
|
96
|
+
if not parts:
|
|
97
|
+
return {
|
|
98
|
+
"type": "command",
|
|
99
|
+
"command": "reminder",
|
|
100
|
+
"intent": "reminder",
|
|
101
|
+
"requirements": {}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if parts[0] == "set":
|
|
105
|
+
# /reminder set 标题 时间 [日期] [重复]
|
|
106
|
+
if len(parts) >= 3:
|
|
107
|
+
title = " ".join(parts[1:-1])
|
|
108
|
+
time = parts[-1]
|
|
109
|
+
return {
|
|
110
|
+
"type": "command",
|
|
111
|
+
"command": "reminder",
|
|
112
|
+
"intent": "reminder",
|
|
113
|
+
"requirements": {
|
|
114
|
+
"action": "set",
|
|
115
|
+
"title": title,
|
|
116
|
+
"time": time
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
# 默认获取提醒
|
|
121
|
+
date = args.strip()
|
|
122
|
+
return {
|
|
123
|
+
"type": "command",
|
|
124
|
+
"command": "reminder",
|
|
125
|
+
"intent": "reminder",
|
|
126
|
+
"requirements": {"date": date}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
def _parse_shopping(self, args: str) -> Dict[str, Any]:
|
|
130
|
+
"""解析购物命令"""
|
|
131
|
+
query = args.strip()
|
|
132
|
+
return {
|
|
133
|
+
"type": "command",
|
|
134
|
+
"command": "shopping",
|
|
135
|
+
"intent": "shopping",
|
|
136
|
+
"requirements": {"query": query}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
def _parse_food(self, args: str) -> Dict[str, Any]:
|
|
140
|
+
"""解析外卖命令"""
|
|
141
|
+
query = args.strip()
|
|
142
|
+
return {
|
|
143
|
+
"type": "command",
|
|
144
|
+
"command": "food",
|
|
145
|
+
"intent": "food",
|
|
146
|
+
"requirements": {"query": query}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
def _parse_taxi(self, args: str) -> Dict[str, Any]:
|
|
150
|
+
"""解析打车命令"""
|
|
151
|
+
destination = args.strip()
|
|
152
|
+
return {
|
|
153
|
+
"type": "command",
|
|
154
|
+
"command": "taxi",
|
|
155
|
+
"intent": "taxi",
|
|
156
|
+
"requirements": {"destination": destination}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
def _parse_flight(self, args: str) -> Dict[str, Any]:
|
|
160
|
+
"""解析机票命令"""
|
|
161
|
+
parts = args.split(" ")
|
|
162
|
+
if len(parts) >= 2:
|
|
163
|
+
from_city = parts[0]
|
|
164
|
+
to_city = parts[1]
|
|
165
|
+
return {
|
|
166
|
+
"type": "command",
|
|
167
|
+
"command": "flight",
|
|
168
|
+
"intent": "flight",
|
|
169
|
+
"requirements": {
|
|
170
|
+
"from": from_city,
|
|
171
|
+
"to": to_city
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
"type": "command",
|
|
176
|
+
"command": "flight",
|
|
177
|
+
"intent": "flight",
|
|
178
|
+
"requirements": {}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
def _parse_help(self, args: str) -> Dict[str, Any]:
|
|
182
|
+
"""解析帮助命令"""
|
|
183
|
+
return {
|
|
184
|
+
"type": "command",
|
|
185
|
+
"command": "help",
|
|
186
|
+
"intent": "general",
|
|
187
|
+
"requirements": {}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
def get_help(self) -> str:
|
|
191
|
+
"""获取帮助信息"""
|
|
192
|
+
help_text = """可用命令:
|
|
193
|
+
|
|
194
|
+
/weather [城市] - 查看天气
|
|
195
|
+
/calendar [日期] - 查看日程
|
|
196
|
+
/calendar add [标题] [时间] - 添加日程
|
|
197
|
+
/reminder [日期] - 查看提醒
|
|
198
|
+
/reminder set [标题] [时间] - 设置提醒
|
|
199
|
+
/shopping [关键词] - 搜索商品
|
|
200
|
+
/food [关键词] - 点外卖
|
|
201
|
+
/taxi [目的地] - 打车
|
|
202
|
+
/flight [出发地] [目的地] - 查机票
|
|
203
|
+
/help - 查看帮助
|
|
204
|
+
|
|
205
|
+
示例:
|
|
206
|
+
/weather 上海
|
|
207
|
+
/reminder set 吃药 08:00
|
|
208
|
+
/shopping 手机
|
|
209
|
+
"""
|
|
210
|
+
return help_text
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from dotenv import load_dotenv
|
|
3
|
+
|
|
4
|
+
load_dotenv()
|
|
5
|
+
|
|
6
|
+
# API Keys
|
|
7
|
+
GROK_API_KEY = os.getenv("GROK_API_KEY", "")
|
|
8
|
+
SUPABASE_URL = os.getenv("SUPABASE_URL", "")
|
|
9
|
+
SUPABASE_KEY = os.getenv("SUPABASE_KEY", "")
|
|
10
|
+
|
|
11
|
+
# Amazon Affiliate
|
|
12
|
+
AMAZON_ASSOCIATE_TAG = os.getenv("AMAZON_ASSOCIATE_TAG", "")
|
|
13
|
+
AMAZON_ACCESS_KEY = os.getenv("AMAZON_ACCESS_KEY", "")
|
|
14
|
+
AMAZON_SECRET_KEY = os.getenv("AMAZON_SECRET_KEY", "")
|
|
15
|
+
|
|
16
|
+
# Uber Affiliate
|
|
17
|
+
UBER_CLIENT_ID = os.getenv("UBER_CLIENT_ID", "")
|
|
18
|
+
UBER_CLIENT_SECRET = os.getenv("UBER_CLIENT_SECRET", "")
|
|
19
|
+
|
|
20
|
+
# App Settings
|
|
21
|
+
APP_NAME = "AI Life Assistant"
|
|
22
|
+
APP_VERSION = "1.0.0"
|
|
23
|
+
DEBUG = os.getenv("DEBUG", "True").lower() == "true"
|
|
24
|
+
|
|
25
|
+
# API Settings
|
|
26
|
+
MAX_RETRIES = 3
|
|
27
|
+
REQUEST_TIMEOUT = 30
|
|
28
|
+
|
|
29
|
+
# Memory Settings
|
|
30
|
+
MEMORY_MAX_LENGTH = 50
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""生活助手守护进程"""
|
|
2
|
+
|
|
3
|
+
import threading
|
|
4
|
+
import time
|
|
5
|
+
import schedule
|
|
6
|
+
from typing import Dict, Any, List
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
|
|
11
|
+
class LifeAssistantDaemon:
|
|
12
|
+
"""生活助手守护进程"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, user_id: str = "default"):
|
|
15
|
+
self.user_id = user_id
|
|
16
|
+
self.running = False
|
|
17
|
+
self.thread = None
|
|
18
|
+
self.memory = self._load_memory()
|
|
19
|
+
self.triggers = {
|
|
20
|
+
"stock_check": self._check_stock,
|
|
21
|
+
"weather_alert": self._check_weather,
|
|
22
|
+
"reminder_check": self._check_reminders,
|
|
23
|
+
"habit_analysis": self._analyze_habits
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
def start(self):
|
|
27
|
+
"""启动守护进程"""
|
|
28
|
+
if self.running:
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
self.running = True
|
|
32
|
+
self.thread = threading.Thread(target=self._run, daemon=True)
|
|
33
|
+
self.thread.start()
|
|
34
|
+
print(f"[Daemon] 生活助手守护进程已启动")
|
|
35
|
+
|
|
36
|
+
def stop(self):
|
|
37
|
+
"""停止守护进程"""
|
|
38
|
+
self.running = False
|
|
39
|
+
if self.thread:
|
|
40
|
+
self.thread.join()
|
|
41
|
+
print(f"[Daemon] 生活助手守护进程已停止")
|
|
42
|
+
|
|
43
|
+
def _run(self):
|
|
44
|
+
"""运行守护进程"""
|
|
45
|
+
# 初始化定时任务
|
|
46
|
+
self._schedule_tasks()
|
|
47
|
+
|
|
48
|
+
while self.running:
|
|
49
|
+
schedule.run_pending()
|
|
50
|
+
time.sleep(1)
|
|
51
|
+
|
|
52
|
+
def _schedule_tasks(self):
|
|
53
|
+
"""调度任务"""
|
|
54
|
+
# 每天检查库存
|
|
55
|
+
schedule.every().day.at("08:00").do(self._check_stock)
|
|
56
|
+
# 每天检查天气
|
|
57
|
+
schedule.every().day.at("07:00").do(self._check_weather)
|
|
58
|
+
# 每分钟检查提醒
|
|
59
|
+
schedule.every().minute.do(self._check_reminders)
|
|
60
|
+
# 每周分析习惯
|
|
61
|
+
schedule.every().sunday.at("22:00").do(self._analyze_habits)
|
|
62
|
+
|
|
63
|
+
def _check_stock(self):
|
|
64
|
+
"""检查库存"""
|
|
65
|
+
print(f"[Daemon] 检查库存 - {datetime.now()}")
|
|
66
|
+
# 示例:检查常用商品库存
|
|
67
|
+
stocks = self.memory.get("stocks", {})
|
|
68
|
+
low_stock_items = []
|
|
69
|
+
|
|
70
|
+
for item, info in stocks.items():
|
|
71
|
+
if info.get("stock", 0) < info.get("threshold", 5):
|
|
72
|
+
low_stock_items.append(item)
|
|
73
|
+
|
|
74
|
+
if low_stock_items:
|
|
75
|
+
alert = f"您的以下商品库存不足:{', '.join(low_stock_items)}"
|
|
76
|
+
self._send_alert("stock", alert)
|
|
77
|
+
|
|
78
|
+
def _check_weather(self):
|
|
79
|
+
"""检查天气"""
|
|
80
|
+
print(f"[Daemon] 检查天气 - {datetime.now()}")
|
|
81
|
+
# 示例:检查天气并发送提醒
|
|
82
|
+
from tools.weather_tool import get_weather
|
|
83
|
+
weather = get_weather()
|
|
84
|
+
|
|
85
|
+
if weather.get("success"):
|
|
86
|
+
data = weather.get("data", {})
|
|
87
|
+
temp = data.get("temperature", "")
|
|
88
|
+
desc = data.get("description", "")
|
|
89
|
+
|
|
90
|
+
alert = f"今日天气:{temp},{desc}"
|
|
91
|
+
self._send_alert("weather", alert)
|
|
92
|
+
|
|
93
|
+
def _check_reminders(self):
|
|
94
|
+
"""检查提醒"""
|
|
95
|
+
now = datetime.now()
|
|
96
|
+
current_time = now.strftime("%H:%M")
|
|
97
|
+
current_date = now.strftime("%Y-%m-%d")
|
|
98
|
+
|
|
99
|
+
# 示例:检查提醒
|
|
100
|
+
from tools.reminder_tool import get_reminders
|
|
101
|
+
reminders = get_reminders(current_date)
|
|
102
|
+
|
|
103
|
+
if reminders.get("success"):
|
|
104
|
+
for reminder in reminders.get("reminders", []):
|
|
105
|
+
if reminder.get("time") == current_time:
|
|
106
|
+
alert = f"提醒:{reminder.get('title')}"
|
|
107
|
+
self._send_alert("reminder", alert)
|
|
108
|
+
|
|
109
|
+
def _analyze_habits(self):
|
|
110
|
+
"""分析习惯"""
|
|
111
|
+
print(f"[Daemon] 分析习惯 - {datetime.now()}")
|
|
112
|
+
# 示例:分析用户习惯
|
|
113
|
+
habits = self.memory.get("habits", {})
|
|
114
|
+
|
|
115
|
+
# 简单的习惯分析
|
|
116
|
+
if habits:
|
|
117
|
+
analysis = "习惯分析:\n"
|
|
118
|
+
for habit, data in habits.items():
|
|
119
|
+
frequency = data.get("frequency", 0)
|
|
120
|
+
analysis += f"- {habit}: 频率 {frequency}次/周\n"
|
|
121
|
+
|
|
122
|
+
self._send_alert("habit", analysis)
|
|
123
|
+
|
|
124
|
+
def _send_alert(self, type: str, message: str):
|
|
125
|
+
"""发送提醒"""
|
|
126
|
+
print(f"[Alert] {type}: {message}")
|
|
127
|
+
# 这里可以集成到前端通知系统
|
|
128
|
+
|
|
129
|
+
def _load_memory(self) -> Dict[str, Any]:
|
|
130
|
+
"""加载记忆"""
|
|
131
|
+
memory_file = f"memory_{self.user_id}.json"
|
|
132
|
+
if os.path.exists(memory_file):
|
|
133
|
+
try:
|
|
134
|
+
with open(memory_file, "r", encoding="utf-8") as f:
|
|
135
|
+
return json.load(f)
|
|
136
|
+
except:
|
|
137
|
+
pass
|
|
138
|
+
return {
|
|
139
|
+
"stocks": {
|
|
140
|
+
"纸巾": {"stock": 3, "threshold": 5},
|
|
141
|
+
"牙膏": {"stock": 2, "threshold": 3},
|
|
142
|
+
"洗发水": {"stock": 1, "threshold": 2}
|
|
143
|
+
},
|
|
144
|
+
"habits": {
|
|
145
|
+
"购物": {"frequency": 3},
|
|
146
|
+
"外卖": {"frequency": 2},
|
|
147
|
+
"打车": {"frequency": 1}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
def save_memory(self):
|
|
152
|
+
"""保存记忆"""
|
|
153
|
+
memory_file = f"memory_{self.user_id}.json"
|
|
154
|
+
with open(memory_file, "w", encoding="utf-8") as f:
|
|
155
|
+
json.dump(self.memory, f, ensure_ascii=False, indent=2)
|
|
156
|
+
|
|
157
|
+
def add_habit(self, habit: str):
|
|
158
|
+
"""添加习惯"""
|
|
159
|
+
if "habits" not in self.memory:
|
|
160
|
+
self.memory["habits"] = {}
|
|
161
|
+
|
|
162
|
+
if habit not in self.memory["habits"]:
|
|
163
|
+
self.memory["habits"][habit] = {"frequency": 0}
|
|
164
|
+
|
|
165
|
+
self.memory["habits"][habit]["frequency"] += 1
|
|
166
|
+
self.save_memory()
|
|
167
|
+
|
|
168
|
+
def update_stock(self, item: str, stock: int):
|
|
169
|
+
"""更新库存"""
|
|
170
|
+
if "stocks" not in self.memory:
|
|
171
|
+
self.memory["stocks"] = {}
|
|
172
|
+
|
|
173
|
+
self.memory["stocks"][item] = {"stock": stock, "threshold": 5}
|
|
174
|
+
self.save_memory()
|