add memory v1,只支持手动记忆

This commit is contained in:
hc 2026-04-11 16:00:39 +08:00
parent 192e6a56da
commit e9cf2e3533
5 changed files with 59 additions and 1 deletions

View File

@ -29,6 +29,8 @@ REPL 内的会话管理命令优先使用 slash command。
`/history`、`/resume`、`/new` 由程序直接处理,不进入 agent loop。
当前项目支持最小 memory使用 `/remember` 保存长期信息,使用 `/memory` 查看。
未明确说明时,使用以下默认值:
- 工作目录:当前进程目录

View File

@ -24,6 +24,7 @@
- 修改已有文件时,优先使用 `Edit`,而不是 `Bash``Write`
- 需要创建或覆盖文件时,优先使用 `Write` 工具,而不是 `Bash`
- 会话管理优先使用 slash command而不是自然语言或 `Bash` 探查 session 文件
- 长期有价值的项目约束或偏好,优先使用 `/remember` 保存
- Windows 下先验证 shell 兼容性,再选择命令写法
## 沟通风格

View File

@ -13,6 +13,7 @@ from typing import Any, Iterator
from anthropic import Anthropic
from openai import OpenAI
from cc_slim.memory import MemoryStore
from cc_slim.session import SessionStore
from cc_slim.tools import Tool
@ -148,6 +149,10 @@ class Agent:
for path in sorted(skills_dir.glob("*.md"), key=lambda p: p.name):
parts.append(path.read_text(encoding="utf-8"))
memory = MemoryStore(workspace).read()
if memory:
parts.append(f"# Memory\n\n{memory}")
return "\n\n".join(part.strip() for part in parts if part.strip())
def _build_runtime_summary(self, workspace: Path) -> str:

View File

@ -8,6 +8,7 @@ from rich.console import Console
from rich.table import Table
from cc_slim.engine import Agent, resolve_config
from cc_slim.memory import MemoryStore
from cc_slim.session import SessionStore
from cc_slim.tools import build_default_tools
@ -81,6 +82,7 @@ def build_agent(
def handle_repl_command(
user_input: str,
store: SessionStore,
memory: MemoryStore,
config: object,
root: Path,
agent: Agent,
@ -95,6 +97,20 @@ def handle_repl_command(
console.print(f"已创建新 session: {session_meta.get('session_id')}")
return build_agent(root, config, store, session_meta, [])
if command == "/memory":
content = memory.read()
console.print(content or "当前项目还没有 memory。")
return agent
if command.startswith("/remember "):
text = user_input.split(" ", 1)[1].strip()
if not text:
console.print("[red]error:[/red] 缺少需要保存的 memory 内容")
return agent
path = memory.append(text)
console.print(f"已写入 memory: {path}")
return agent
if command.startswith("--resume ") or command.startswith("/resume "):
target = command.split(maxsplit=1)[1].strip()
if not target:
@ -123,6 +139,7 @@ def run(
) -> None:
root = cwd.resolve()
store = SessionStore(root)
memory = MemoryStore(root)
if history:
render_history(store)
return
@ -168,7 +185,7 @@ def run(
if user_input.strip().startswith("/") or user_input.strip().startswith("--history") or user_input.strip().startswith("--resume"):
try:
agent = handle_repl_command(user_input, store, config, root, agent)
agent = handle_repl_command(user_input, store, memory, config, root, agent)
except Exception as exc: # pragma: no cover
console.print(f"[red]error:[/red] {exc}")
continue

33
src/cc_slim/memory.py Normal file
View File

@ -0,0 +1,33 @@
from __future__ import annotations
import hashlib
import re
from pathlib import Path
class MemoryStore:
def __init__(self, cwd: Path) -> None:
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)
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:
path = self.path()
if not path.exists():
return ""
return path.read_text(encoding="utf-8").strip()
def path(self) -> Path:
return self.root / "MEMORY.md"
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]
return f"{text[:48]}-{digest}"