自进化智能体记忆系统软件（sea）V1.0 源程序 第 1 页 / 共 6 页
============================================================
    1  # ===== 文件: pyproject.toml =====
    2  [project]
    3  name = "sea"
    4  version = "0.1.0"
    5  description = "self-evolving-agent CLI — local-first Markdown memory system (memory layering / distillation / skill management)"
    6  requires-python = ">=3.11"
    7  license = { text = "MIT" }
    8  dependencies = ["typer>=0.12", "rich>=13"]
    9  
   10  [project.scripts]
   11  sea = "sea.cli:app"
   12  
   13  [build-system]
   14  requires = ["hatchling"]
   15  build-backend = "hatchling.build"
   16  
   17  [tool.hatch.build.targets.wheel]
   18  packages = ["src/sea"]
   19  
   20  [tool.pytest.ini_options]
   21  testpaths = ["tests"]
   22  
   23  # ===== 文件: src/sea/__init__.py =====
   24  """sea — self-evolving agent memory CLI (P0 prototype).
   25  
   26  Local-first, Markdown-as-memory. Five-layer framework distilled into
   27  three runtime memory levels:
   28    L1 scratch (working) / L2 logs (episodic) / L3 kb (semantic).
   29  """
   30  
   31  __version__ = "0.1.0"
   32  
   33  # ===== 文件: src/sea/config.py =====
   34  """Configuration: paths for the sea memory home.
   35  
   36  Default: ~/.sea (override with $SEA_HOME).
   37  Memory is just files — this module owns where they live.
   38  """
   39  from __future__ import annotations
   40  
   41  import json
   42  import os
   43  from dataclasses import dataclass, field
   44  from pathlib import Path
   45  
   46  LEVELS: tuple[str, ...] = ("scratch", "logs", "kb")
   47  
   48  
   49  @dataclass
   50  class Config:

自进化智能体记忆系统软件（sea）V1.0 源程序 第 2 页 / 共 6 页
============================================================
   51      home: Path = field(default_factory=lambda: Path(os.environ.get("SEA_HOME", Path.home() / ".sea")))
   52  
   53      @property
   54      def memory_dir(self) -> Path:
   55          return self.home / "memory"
   56  
   57      @property
   58      def skills_dir(self) -> Path:
   59          return self.home / "skills"
   60  
   61      @property
   62      def task_log(self) -> Path:
   63          return self.home / "task-log.md"
   64  
   65      @property
   66      def config_file(self) -> Path:
   67          return self.home / "config.json"
   68  
   69      def level_dir(self, level: str) -> Path:
   70          if level not in LEVELS:
   71              raise ValueError(f"invalid level: {level!r} (expected one of {LEVELS})")
   72          return self.memory_dir / level
   73  
   74      def ensure_layout(self) -> None:
   75          """Create the full directory skeleton (idempotent)."""
   76          for level in LEVELS:
   77              self.level_dir(level).mkdir(parents=True, exist_ok=True)
   78          self.skills_dir.mkdir(parents=True, exist_ok=True)
   79          if not self.task_log.exists():
   80              self.task_log.write_text("# Task Log\n\n", encoding="utf-8")
   81          if not self.config_file.exists():
   82              self.config_file.write_text(
   83                  json.dumps({"levels": list(LEVELS), "version": 1}, ensure_ascii=False, indent=2),
   84                  encoding="utf-8",
   85              )
   86  
   87      def summary(self) -> dict[str, int]:
   88          """Count markdown files per level."""
   89          out: dict[str, int] = {}
   90          for level in LEVELS:
   91              d = self.level_dir(level)
   92              out[level] = sum(1 for p in d.glob("*.md")) if d.exists() else 0
   93          return out
   94  
   95  # ===== 文件: src/sea/memory/__init__.py =====
   96  
   97  # ===== 文件: src/sea/memory/layering.py =====
   98  """Layering heuristics: classify a memory entry into L1/L2/L3.
   99  
  100  P0 uses lightweight rules (keyword + structure hints). The boundary is

自进化智能体记忆系统软件（sea）V1.0 源程序 第 3 页 / 共 6 页
============================================================
  101  deliberately simple — a human or the main agent confirms before anything
  102  is promoted into kb (auditable distillation).
  103  """
  104  from __future__ import annotations
  105  
  106  import re
  107  from datetime import date
  108  
  109  _DATE_RE = re.compile(r"(19|20)\d{2}[-/.]\d{1,2}([-/.]\d{1,2})?")
  110  _RULE_HINTS = ("# ", "## ", "规则", "原则", "方法论", "铁律", "sop", "SOP", "必须", "禁止", "永远")
  111  
  112  
  113  def classify(text: str) -> str:
  114      """Return 'scratch' | 'logs' | 'kb' for a memory entry."""
  115      t = text.strip()
  116      if not t:
  117          return "scratch"
  118      # L3 kb: rule-like content (headings, normative words) and long enough
  119      if len(t) >= 30 and any(h in t for h in _RULE_HINTS):
  120          return "kb"
  121      # L2 logs: dated entries or task-like records
  122      if _DATE_RE.search(t) or "任务" in t or "记录" in t or "完成" in t:
  123          return "logs"
  124      # L1 scratch: everything else (low retention)
  125      return "scratch"
  126  
  127  
  128  def _safe_name(text: str) -> str:
  129      """First line → file name slug (max 40 chars, safe chars only)."""
  130      first = text.strip().splitlines()[0] if text.strip() else "entry"
  131      slug = re.sub(r"[^\w\u4e00-\u9fff-]", "", first)[:40]
  132      return slug or "entry"
  133  
  134  # ===== 文件: src/sea/memory/store.py =====
  135  """Memory store: append/read Markdown memory files per level."""
  136  from __future__ import annotations
  137  
  138  from datetime import datetime
  139  
  140  from sea.config import Config, LEVELS
  141  from sea.memory.layering import classify
  142  
  143  
  144  class LevelError(ValueError):
  145      """Raised when an unknown memory level is used."""
  146  
  147  
  148  def add(cfg: Config, text: str, level: str | None = None) -> str:
  149      """Append a memory entry. Auto-classifies when level is None.
  150  

自进化智能体记忆系统软件（sea）V1.0 源程序 第 4 页 / 共 6 页
============================================================
  151      Returns the absolute path of the written file.
  152      """
  153      text = text.strip()
  154      if not text:
  155          raise ValueError("empty memory entry")
  156      if level is None:
  157          level = classify(text)
  158      if level not in LEVELS:
  159          raise LevelError(f"invalid level: {level!r} (expected one of {LEVELS})")
  160  
  161      d = cfg.level_dir(level)
  162      d.mkdir(parents=True, exist_ok=True)
  163      stamp = datetime.now().strftime("%Y-%m-%d %H:%M")
  164      entry = f"\n## {stamp}\n\n{text}\n"
  165      fname = f"memory-{datetime.now().strftime('%Y-%m-%d')}.md"
  166      path = d / fname
  167      with open(path, "a", encoding="utf-8") as fh:
  168          fh.write(entry)
  169      return str(path)
  170  
  171  
  172  def read(cfg: Config, level: str) -> str:
  173      """Read all entries of a level, newest file first."""
  174      if level not in LEVELS:
  175          raise LevelError(f"invalid level: {level!r} (expected one of {LEVELS})")
  176      d = cfg.level_dir(level)
  177      if not d.exists():
  178          return ""
  179      parts: list[str] = []
  180      for p in sorted(d.glob("*.md"), reverse=True):
  181          parts.append(f"--- {p.name} ---\n{p.read_text(encoding='utf-8')}")
  182      return "\n".join(parts)
  183  
  184  # ===== 文件: src/sea/cli.py =====
  185  """sea CLI entry (Typer). Commands: init / add / status / read."""
  186  from __future__ import annotations
  187  
  188  from pathlib import Path
  189  
  190  import typer
  191  from rich.console import Console
  192  
  193  from sea import __version__
  194  from sea.config import Config, LEVELS
  195  from sea.memory import store
  196  
  197  app = typer.Typer(help="self-evolving-agent memory CLI — local-first Markdown memory.")
  198  console = Console()
  199  err = Console(stderr=True)
  200  

自进化智能体记忆系统软件（sea）V1.0 源程序 第 5 页 / 共 6 页
============================================================
  201  
  202  def _cfg() -> Config:
  203      return Config()
  204  
  205  
  206  @app.command()
  207  def init() -> None:
  208      """Create the memory directory layout under ~/.sea."""
  209      cfg = _cfg()
  210      cfg.ensure_layout()
  211      console.print(f"[green]✓[/] sea home ready at [bold]{cfg.home}[/]")
  212      console.print("  memory/scratch  L1 working memory")
  213      console.print("  memory/logs     L2 episodic logs")
  214      console.print("  memory/kb       L3 semantic knowledge")
  215      console.print("  skills/         skill files")
  216      console.print("  task-log.md     distillation source")
  217  
  218  
  219  @app.command()
  220  def add(text: str = typer.Argument(..., help="Memory entry to store"),
  221          level: str = typer.Option(None, "--level", "-l", help="Force level: scratch|logs|kb"),
  222          show: bool = typer.Option(False, "--show", help="Print the detected level")) -> None:
  223      """Append a memory entry (auto-classified into L1/L2/L3)."""
  224      try:
  225          path = store.add(_cfg(), text, level)
  226      except ValueError as e:
  227          err.print(f"[red]✗ {e}[/]")
  228          raise typer.Exit(code=2)
  229      detected = Path(path).parent.name
  230      if show:
  231          console.print(f"[blue]level[/] {detected}")
  232      console.print(f"[green]✓[/] saved → [bold]{path}[/]")
  233  
  234  
  235  @app.command()
  236  def status() -> None:
  237      """Show memory statistics per level."""
  238      cfg = _cfg()
  239      if not cfg.home.exists():
  240          err.print("[yellow]not initialized — run: sea init[/]")
  241          raise typer.Exit(code=1)
  242      summary = cfg.summary()
  243      console.print(f"[bold]{cfg.home}[/]")
  244      for level in LEVELS:
  245          n = summary[level]
  246          label = {"scratch": "L1 scratch", "logs": "L2 logs", "kb": "L3 kb"}[level]
  247          console.print(f"  {label:<12} {n} file(s)")
  248  
  249  
  250  @app.command()

自进化智能体记忆系统软件（sea）V1.0 源程序 第 6 页 / 共 6 页
============================================================
  251  def read(level: str = typer.Argument(..., help="Level to read: scratch|logs|kb")) -> None:
  252      """Print all entries of a memory level."""
  253      try:
  254          content = store.read(_cfg(), level)
  255      except ValueError as e:
  256          err.print(f"[red]✗ {e}[/]")
  257          raise typer.Exit(code=2)
  258      if not content:
  259          console.print(f"[yellow](empty)[/] {level}")
  260      else:
  261          console.print(content)
  262  
  263  
  264  @app.command()
  265  def version() -> None:
  266      """Print sea version."""
  267      console.print(f"sea {__version__}")
  268  
  269  
  270  if __name__ == "__main__":
  271      app()
  272  
