为什么 AI 代理不断忘记你的项目(以及我如何修复它)

发布: (2026年2月23日 GMT+8 08:36)
9 分钟阅读
原文: Dev.to

Source: Dev.to

请提供您希望翻译的完整文本内容,我将按照要求将其翻译为简体中文并保留原始的格式、Markdown 语法以及代码块和 URL。谢谢!

PROGRESS.md 的问题

每次我启动与 AI 编码代理的新会话时,都会出现同样的情况:它根本不知道我昨天在做什么。

  • 它不知道哪些任务已经完成。
  • 它不知道我们决定使用 Redis 进行缓存。
  • 它不记得认证模块因依赖升级而被阻塞。

于是我像大多数人一样——维护一个 PROGRESS.md 文件。每次会话结束后,我让代理更新它;在下一次会话开始时,代理读取该文件并尝试从上次中断的地方继续。

这种做法大约维持了一周。

Markdown 的问题

随着项目的扩大,文件也变得更大。
50 行变成了 200 行。三周前的状态更新和当前的阻塞事项挤在一起。代理会读取整篇内容,在上下文中消耗 3,000+ token,但仍然会错过我标记为阻塞的任务,因为它被埋在两条旧的进度记录之间。

根本问题: 我把文本文件当作数据库使用。

  • 我需要查询(“有什么被阻塞?”),而不是读取整个文件。
  • 我需要结构(项目 → 史诗 → 任务),而不是平铺的项目符号。
  • 我需要一条不会膨胀上下文窗口的审计日志。

为 AI 代理构建项目跟踪器

于是我做了一个。Saga 是一个 MCP 服务器,为 AI 代理提供本地 SQLite 数据库用于项目跟踪——可以把它当作 Jira,但专为代理的实际工作方式设计。

如果你不熟悉 MCP(Model Context Protocol),它是 Anthropic 推出的标准,允许 AI 工具通过类型化的工具调用与外部服务交互。Claude Code、Claude Desktop、Cursor 和 Windsurf 都支持它。你的代理在启动时会发现可用的工具,并在对话过程中根据需要调用它们。

Saga 通过 MCP 暴露 23 个工具

  • 完整层级的 CRUD: 项目 → 史诗 → 任务 → 子任务
  • 笔记系统: 决策、上下文、会议记录、阻塞项——类型化且可搜索
  • 仪表盘: 一次调用返回整个项目状态(完成百分比、阻塞任务、最近活动)
  • 活动日志: 每一次更改都会自动记录旧值/新值
  • 会话差异: “显示自昨天以来的变更”——一次调用即可

所有数据都存放在单个 .tracker.db SQLite 文件中。无需服务器、API 密钥或账号。

实际工作原理

开始一个新项目

Me: "Set up tracking for the e-commerce API"

Agent calls:
  tracker_init
  epic_create (Auth)
  epic_create (Catalog)
  task_create (JWT auth)
  subtask_create ([setup lib, create endpoint, add middleware])

五次工具调用。项目现在已经按照史诗(epic)、任务(task)和子任务(subtask)进行结构化,并持久化到 SQLite 中。

次日继续

Me: "What's the status?"

Agent calls: tracker_dashboard

一次调用返回:

{
  "stats": {
    "total_tasks": 12,
    "tasks_done": 4,
    "tasks_blocked": 1,
    "completion_pct": 33.3
  },
  "blocked_tasks": [
    { "title": "Add rate limiting", "epic": "Authentication" }
  ],
  "recent_activity": [
    "Task 'JWT auth' status: in_progress → done",
    "... (other entries)"
  ]
}

代理立刻知道:完成度 33 %,有一个任务被阻塞,认证史诗(auth epic)进度领先于目录史诗(catalog)。它可以在无需额外说明的情况下进行优先级排序。

记录决策

Me: "We're going with Redis for caching. Mark the research tasks as done."

Agent calls:
  note_save (decision: Redis for caching, reasons, trade‑offs)
  task_batch_update (mark tasks 8, 9 as done)

该决策被存储为结构化笔记,并关联到相应的史诗。在下一次会话中,如果代理需要了解我们为何选择 Redis,它可以直接搜索这条笔记,而无需我重新解释。

令牌数学

  • Saga 的 23 个工具定义在系统提示中大约消耗 1,500 令牌。这是固定成本——不会随你的项目增长。
  • 一个 tracker_dashboard 调用返回约 800 令牌的结构化数据。
  • 类似 “show me blocked tasks” 的过滤查询返回约 200 令牌。

相比之下,中等规模项目的 PROGRESS.md 文件占用 3,000–5,000 令牌,每次会话都会完整加载,并随时间增长。

拐点: 大约 15–20 个任务。超过此数量,结构化方法的可扩展性更好,因为代理只检索它请求的内容,而不是全部。

并且不同于 markdown 文件,数据是可查询的。 “我们对缓存的决定是什么?” 是一次 note_search 调用,而不是完整文件扫描。

引擎内部

  • SQLite with WAL mode – 写入期间并发读取,锁争用时的忙等待超时为 5 秒
  • Foreign keys enforced – 删除 epic 时不会出现孤立任务(级联删除)
  • Append‑only activity log – 每一次创建、更新和删除都以字段粒度记录
  • Parameterized queries with column allowlists – 没有 SQL 注入风险
  • MCP safety annotations on every tool – 客户端可以知道每个工具是只读、破坏性还是幂等的

整个项目约有 1,400 行 TypeScript,只有两个依赖:MCP SDK 和 better-sqlite3

会话差异:我未计划的功能

发布后,Reddit 上有人问道:“它能显示会话之间的变化吗?”

好主意。活动日志已经捕获了所有内容——只需要一个聚合层。

我添加了 tracker_session_diff。你给它一个时间戳,它会返回:

{
  "total_changes": 14,
  "summary": {
    "created": 3,
    "status_changed": 4,
    "updated": 5,
    "deleted": 2
  },
  "highlights": [
    "Task 'Fix auth bug' status: in_progress → done",
    "Created epic 'API v2'",
    "Note 'Sprint retro' deleted"
  ]
}

代理可以调用它,以获得自上一次会话以来发生的事情的简明概览,从而保持对话的聚焦并提高 token 使用效率。

入门

将以下内容添加到项目的 .mcp.json 中:

{
  "mcpServers": {
    "saga": {
      "command": "npx",
      "args": ["-y", "saga-mcp"],
      "env": {
        "DB_PATH": "/absolute/path/to/your/project/.tracker.db"
      }
    }
  }
}

就这样。它可在 Claude Code、Claude Desktop 或任何兼容 MCP 的客户端上使用。数据库文件会在首次使用时自动创建。

我学到的

构建 Saga 让我了解到代理实际上是如何消费信息的:它们更擅长结构化信息而非散文。

Markdown 文件针对人类浏览文档进行了优化。返回过滤后 JSON 的类型化工具调用则针对大型语言模型(LLM)决定下一步操作进行了优化。代理不需要“阅读”你的项目状态——它需要查询它。

MCP 让这变得可行。该协议处理工具发现、类型化模式和传输。我所要做的只是把一个数据库放在它后面。

如果你正在构建多会话代理工作流,并且发现自己在维护不断增长的上下文文件,考虑一下这些上下文是否应该改为使用数据库。

Saga 是开源的(MIT 许可证),并可在 npm 上获取:

  • GitHub:
  • Install: npx -y saga-mcp
0 浏览
Back to Blog

相关文章

阅读更多 »