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