memory v2,结构化记忆memory md文件
This commit is contained in:
parent
18ab3cabd5
commit
4efc3d267c
@ -24,7 +24,8 @@
|
||||
|
||||
## Memory 与验证
|
||||
|
||||
- 当前项目支持最小 memory:使用 `/remember` 保存长期信息,使用 `/memory` 查看。
|
||||
- `AGENTS.md` 是项目规则文件,不用于保存长期项目知识或用户偏好。
|
||||
- 当前项目支持结构化 memory:使用 `/remember` 保存长期知识,使用 `/memory` 查看。
|
||||
- session 是原始对话历史,不直接拼进 system prompt;memory 才作为长期补充进入 prompt。
|
||||
- 默认语言:中文优先。
|
||||
- 默认验证:优先做最小可验证检查,不夸大成功状态。
|
||||
|
||||
@ -26,7 +26,8 @@
|
||||
- 需要创建或覆盖文件时,优先使用 `Write` 工具,而不是 `Bash`
|
||||
- 会话与 memory 管理优先使用 slash command,而不是自然语言或 `Bash` 探查对应文件
|
||||
- 优先通过 `/help` 查看当前命令,而不是猜测命令格式
|
||||
- 长期有价值的项目约束或偏好,优先使用 `/remember` 保存
|
||||
- 长期有效的项目知识或用户偏好,优先使用 `/remember` 保存
|
||||
- 项目规则和工作方式仍应写在 `AGENTS.md`
|
||||
- 高风险操作会触发确认,优先先读再改,减少无意义审批
|
||||
- 复杂任务可先切换到 `/mode plan` 进行规划,再切换回 `/mode build` 执行
|
||||
- workspace 是当前默认操作边界,先在 workspace 内定位和操作,避免无关路径探索
|
||||
|
||||
@ -216,7 +216,7 @@ class Agent:
|
||||
memory = MemoryStore(workspace).read()
|
||||
if not memory:
|
||||
return ""
|
||||
return f"# Memory\n\n{memory}"
|
||||
return memory
|
||||
|
||||
def _build_runtime_summary(self, workspace: Path) -> str:
|
||||
tool_names = ", ".join(self.tools.keys()) or "(none)"
|
||||
|
||||
@ -10,23 +10,67 @@ class MemoryStore:
|
||||
self.cwd = cwd.resolve()
|
||||
self.root = Path.home() / ".config" / "cc-slim" / "memory" / self._sanitize_cwd(self.cwd)
|
||||
self.root.mkdir(parents=True, exist_ok=True)
|
||||
self.ensure_structure()
|
||||
|
||||
def append(self, text: str) -> Path:
|
||||
path = self.path()
|
||||
existing = self.read()
|
||||
content = f"{existing}\n\n{text.strip()}" if existing else text.strip()
|
||||
path.write_text(content + "\n", encoding="utf-8")
|
||||
return path
|
||||
|
||||
def read(self) -> str:
|
||||
def ensure_structure(self) -> None:
|
||||
path = self.path()
|
||||
if not path.exists():
|
||||
return ""
|
||||
return path.read_text(encoding="utf-8").strip()
|
||||
path.write_text(self._default_template(), encoding="utf-8")
|
||||
return
|
||||
|
||||
existing = path.read_text(encoding="utf-8").strip()
|
||||
if self._is_structured(existing):
|
||||
return
|
||||
|
||||
migrated = self._default_template(existing)
|
||||
path.write_text(migrated, encoding="utf-8")
|
||||
|
||||
def append(self, text: str) -> str:
|
||||
content = self.read().rstrip()
|
||||
entry = self._format_scratch_entry(text)
|
||||
updated = f"{content}\n\n{entry}\n"
|
||||
self.path().write_text(updated, encoding="utf-8")
|
||||
return "Scratch Notes"
|
||||
|
||||
def read(self) -> str:
|
||||
self.ensure_structure()
|
||||
return self.path().read_text(encoding="utf-8").strip()
|
||||
|
||||
def path(self) -> Path:
|
||||
return self.root / "MEMORY.md"
|
||||
|
||||
def _default_template(self, scratch_notes: str = "") -> str:
|
||||
scratch = self._format_scratch_entry(scratch_notes) if scratch_notes.strip() else ""
|
||||
template = (
|
||||
"# Memory\n\n"
|
||||
"## User Memory\n\n"
|
||||
"## Project Memory\n\n"
|
||||
"## Constraints\n\n"
|
||||
"## Consolidated Facts\n\n"
|
||||
"## Scratch Notes\n"
|
||||
)
|
||||
return f"{template}\n{scratch}\n" if scratch else f"{template}\n"
|
||||
|
||||
def _format_scratch_entry(self, text: str) -> str:
|
||||
lines = [line.rstrip() for line in text.strip().splitlines() if line.strip()]
|
||||
if not lines:
|
||||
return ""
|
||||
if len(lines) == 1:
|
||||
return f"- {lines[0]}"
|
||||
tail = "\n".join(f" {line}" for line in lines[1:])
|
||||
return f"- {lines[0]}\n{tail}"
|
||||
|
||||
def _is_structured(self, text: str) -> bool:
|
||||
required_sections = [
|
||||
"# Memory",
|
||||
"## User Memory",
|
||||
"## Project Memory",
|
||||
"## Constraints",
|
||||
"## Consolidated Facts",
|
||||
"## Scratch Notes",
|
||||
]
|
||||
return all(section in text for section in required_sections)
|
||||
|
||||
def _sanitize_cwd(self, cwd: Path) -> str:
|
||||
text = re.sub(r"[^A-Za-z0-9._-]+", "_", str(cwd))
|
||||
digest = hashlib.sha1(str(cwd).encode("utf-8")).hexdigest()[:8]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user