memagent-local 0.3.4__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.
Files changed (47) hide show
  1. memagent/__init__.py +17 -0
  2. memagent/__main__.py +178 -0
  3. memagent/agent.py +3326 -0
  4. memagent/analogy.py +128 -0
  5. memagent/architecture.py +278 -0
  6. memagent/backup.py +135 -0
  7. memagent/checkers.py +62 -0
  8. memagent/cli.py +13 -0
  9. memagent/cognition.py +199 -0
  10. memagent/compat.py +40 -0
  11. memagent/compression.py +72 -0
  12. memagent/continuity.py +534 -0
  13. memagent/critique.py +491 -0
  14. memagent/curiosity.py +134 -0
  15. memagent/decay.py +72 -0
  16. memagent/diagnostics.py +93 -0
  17. memagent/embedders.py +157 -0
  18. memagent/embedding.py +147 -0
  19. memagent/emotion.py +263 -0
  20. memagent/graph.py +90 -0
  21. memagent/growth.py +349 -0
  22. memagent/human.py +996 -0
  23. memagent/innate.py +124 -0
  24. memagent/instructions.py +191 -0
  25. memagent/interactive.py +1773 -0
  26. memagent/interest.py +109 -0
  27. memagent/io_utils.py +178 -0
  28. memagent/literary.py +314 -0
  29. memagent/llm.py +363 -0
  30. memagent/mcp_server.py +401 -0
  31. memagent/memory.py +413 -0
  32. memagent/profiles.py +147 -0
  33. memagent/reader_postproc.py +123 -0
  34. memagent/release.py +323 -0
  35. memagent/responder.py +279 -0
  36. memagent/server.py +130 -0
  37. memagent/social.py +105 -0
  38. memagent/synonyms.py +93 -0
  39. memagent/visualize.py +1111 -0
  40. memagent/websearch.py +113 -0
  41. memagent/work_admin.py +380 -0
  42. memagent_local-0.3.4.dist-info/METADATA +1408 -0
  43. memagent_local-0.3.4.dist-info/RECORD +47 -0
  44. memagent_local-0.3.4.dist-info/WHEEL +5 -0
  45. memagent_local-0.3.4.dist-info/entry_points.txt +6 -0
  46. memagent_local-0.3.4.dist-info/licenses/LICENSE +21 -0
  47. memagent_local-0.3.4.dist-info/top_level.txt +1 -0
memagent/__init__.py ADDED
@@ -0,0 +1,17 @@
1
+ """memagent —— 模仿人脑分层遗忘机制的记忆系统原型。
2
+
3
+ 设计映射(详见 README.md):
4
+ - Hot 层 ≈ 工作记忆:近期高频使用的记忆,直接注入上下文
5
+ - Warm 层 ≈ 长时记忆:完整记忆,带遗忘曲线评分
6
+ - Cold 层 ≈ 海马体索引指向的深藏记忆:压缩摘要,命中才唤醒
7
+ - 衰减评分 ≈ Ebbinghaus 遗忘曲线
8
+ - 检索强化 ≈ 测试效应(retrieval practice)
9
+ - 睡眠巩固 ≈ 海马体重放:离线把低频记忆压缩进 Cold 层
10
+ """
11
+
12
+ from .agent import MemoryAgent
13
+ from .memory import Memory, Tier, MemoryStore
14
+ from .embedding import embed_text, cosine_similarity
15
+
16
+ __all__ = ["MemoryAgent", "Memory", "Tier", "MemoryStore", "embed_text", "cosine_similarity"]
17
+ __version__ = "0.3.4"
memagent/__main__.py ADDED
@@ -0,0 +1,178 @@
1
+ """Installed ``memagent`` and ``python -m memagent`` entry point."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import os
8
+ import platform
9
+ from pathlib import Path
10
+
11
+ from . import __version__
12
+ from .agent import AgentConfig, MemoryAgent
13
+ from .cli import enable_utf8
14
+ from .memory import MemoryStore, StoreCorruptionError
15
+
16
+
17
+ def _check(path: str) -> int:
18
+ target = Path(path).resolve()
19
+ try:
20
+ store = MemoryStore(path=str(target))
21
+ except StoreCorruptionError as exc:
22
+ print(f"[FAIL] persistence: {exc}")
23
+ return 1
24
+ print(f"[OK] memagent {__version__} / Python {platform.python_version()}")
25
+ print(f"[OK] persistence: {target} ({len(store)} memories)")
26
+ print(f"[OK] writable parent: {os.access(target.parent, os.W_OK)}")
27
+ backup = target.with_suffix(target.suffix + ".bak")
28
+ print(f"[INFO] backup: {backup if backup.exists() else 'created after first update'}")
29
+ return 0
30
+
31
+
32
+ def _one_shot_agent(args) -> MemoryAgent:
33
+ return MemoryAgent(persist_path=args.persist,
34
+ cfg=AgentConfig(evolve_on_sleep=False))
35
+
36
+
37
+ def _inject(args) -> int:
38
+ from .instructions import build_injection_md
39
+
40
+ agent = _one_shot_agent(args)
41
+ block = build_injection_md(agent, topic=args.inject or None, k=args.k)
42
+ print(block)
43
+ return 0
44
+
45
+
46
+ def _sleep_once(args) -> int:
47
+ import json as _json
48
+
49
+ agent = _one_shot_agent(args)
50
+ report = agent.sleep()
51
+ agent.save()
52
+ keep = ("replayed_count", "unreplayed_count", "cold_compressed",
53
+ "migrations", "triage_high")
54
+ print(_json.dumps({k: report.get(k) for k in keep}, ensure_ascii=False))
55
+ return 0
56
+
57
+
58
+ _DISTILL_PROMPT = (
59
+ "你是记忆沉淀助手。从下面的开发会话记录中提炼值得长期记住的关键结论:"
60
+ "技术决策、踩坑教训、用户偏好、项目约定。忽略闲聊与过程性内容。\n"
61
+ "只输出 JSON 数组,每项 {\"content\": \"一句话结论(≤80字)\", "
62
+ "\"importance\": 0.4~0.9},最多 8 条;没有值得记的就输出 []。\n\n会话记录:\n"
63
+ )
64
+
65
+
66
+ def _distill_session(args) -> int:
67
+ """把会话记录交给 LLM 提炼决策并入库;无 key 或失败时静默跳过。"""
68
+ import json as _json
69
+ import re as _re
70
+ import urllib.request
71
+
72
+ turns = _json.loads(Path(args.distill_session).read_text(encoding="utf-8"))
73
+ if not isinstance(turns, list) or not turns:
74
+ return 0
75
+ transcript = "\n".join(
76
+ f"{'用户' if t.get('role') == 'user' else '助手'}:{str(t.get('content', ''))[:400]}"
77
+ for t in turns[-40:]
78
+ )
79
+ base = (os.environ.get("OPENAI_BASE_URL") or "").rstrip("/")
80
+ key = os.environ.get("OPENAI_API_KEY") or ""
81
+ model = os.environ.get("OPENAI_MODEL") or ""
82
+ if not (base and key and model):
83
+ print("[]") # 无 LLM 配置:静默跳过(婴儿原则,不报错打扰)
84
+ return 0
85
+ payload = _json.dumps({
86
+ "model": model,
87
+ "messages": [{"role": "user",
88
+ "content": _DISTILL_PROMPT + transcript}],
89
+ "temperature": 0.2,
90
+ }).encode("utf-8")
91
+ req = urllib.request.Request(
92
+ f"{base}/chat/completions", data=payload,
93
+ headers={"Content-Type": "application/json",
94
+ "Authorization": f"Bearer {key}"}, method="POST")
95
+ try:
96
+ with urllib.request.urlopen(req, timeout=60) as resp:
97
+ reply = _json.loads(resp.read().decode("utf-8"))
98
+ text = reply["choices"][0]["message"]["content"]
99
+ except Exception as e:
100
+ print(f"[] # distill skipped: {type(e).__name__}")
101
+ return 0
102
+ m = _re.search(r"\[.*\]", text, _re.S)
103
+ if not m:
104
+ print("[]")
105
+ return 0
106
+ agent = _one_shot_agent(args)
107
+ added = 0
108
+ try:
109
+ for item in _json.loads(m.group(0))[:8]:
110
+ content = str(item.get("content", "")).strip()
111
+ if len(content) < 6:
112
+ continue
113
+ imp = min(0.9, max(0.4, float(item.get("importance", 0.5))))
114
+ mem = agent.remember(content, kind="fact", importance=imp)
115
+ added += 1
116
+ print(f"+ {mem.id[:8]} [{imp:.2f}] {content[:50]}")
117
+ except (ValueError, TypeError):
118
+ pass
119
+ if added:
120
+ agent.save()
121
+ print(f"# distilled {added}")
122
+ return 0
123
+
124
+
125
+ def main(argv: list[str] | None = None) -> int:
126
+ enable_utf8()
127
+ parser = argparse.ArgumentParser(description="Local-first layered memory agent")
128
+ parser.add_argument("--persist", default="memories.json", help="memory JSON path")
129
+ parser.add_argument("--persona", default=os.environ.get("OPENAI_PERSONA") or None)
130
+ parser.add_argument("--check", action="store_true", help="validate runtime and persistence")
131
+ parser.add_argument("--migrate-work", nargs=2, metavar=("OLD", "NEW"),
132
+ help="move pre-title work safely without overwriting chapters")
133
+ parser.add_argument("--works-dir", default="works", help="work root for --migrate-work")
134
+ # --- 一次性命令(opencode 插件 / 脚本调用,不进交互 REPL) ---
135
+ parser.add_argument("--inject", nargs="?", const="", default=None, metavar="TOPIC",
136
+ help="打印开工注入块(可选主题),配合 --k")
137
+ parser.add_argument("--k", type=int, default=5, help="注入条数(默认 5)")
138
+ parser.add_argument("--sleep-once", action="store_true",
139
+ help="执行一次睡眠巩固并落盘")
140
+ parser.add_argument("--distill-session", metavar="JSON_FILE",
141
+ help="会话记录 JSON 提炼决策入库(需 OPENAI_* 环境变量)")
142
+ parser.add_argument("--diagnostics", action="store_true",
143
+ help="打印脱敏诊断报告(零记忆内容,可自愿分享用于改进)")
144
+ parser.add_argument("--version", action="version", version=f"memagent {__version__}")
145
+ args = parser.parse_args(argv)
146
+ if args.inject is not None:
147
+ return _inject(args)
148
+ if args.sleep_once:
149
+ return _sleep_once(args)
150
+ if args.distill_session:
151
+ return _distill_session(args)
152
+ if args.diagnostics:
153
+ from .diagnostics import build_report
154
+
155
+ store = MemoryStore(path=str(Path(args.persist).resolve()))
156
+ print(json.dumps(build_report(store, version=__version__),
157
+ ensure_ascii=False, indent=2))
158
+ return 0
159
+ if args.check:
160
+ return _check(args.persist)
161
+ if args.migrate_work:
162
+ from .architecture import migrate_legacy_work
163
+
164
+ report = migrate_legacy_work(
165
+ Path(args.works_dir).resolve(), args.migrate_work[0], args.migrate_work[1]
166
+ )
167
+ print(json.dumps(report, ensure_ascii=False))
168
+ return 0
169
+ MemoryAgent(
170
+ persist_path=args.persist,
171
+ persona=args.persona,
172
+ cfg=AgentConfig(evolve_on_sleep=bool(args.persona)),
173
+ ).cli_loop()
174
+ return 0
175
+
176
+
177
+ if __name__ == "__main__":
178
+ raise SystemExit(main())