从PDD、DDD、SDD到TDD:基于智能体的工程方法论在My-Notion项目中的完整开发实战攻略

2026-06-14阅读 0热度 0
其他

近期在构建 AI Agent、RAG、MCP、CLI 以及长期记忆相关模块时,不少开发者都会察觉到一个显著现象:

从 PDD、DDD、SDD 到 TDD:我是如何用一套 Agent 工程方法论推进 My-Notion 的

My-Notion (github.com/Ha veNiceDa/…) 是一个高度定制的个人版 Notion。它并非简单的克隆,而是将文档编辑、AI Chat、ReAct Agent、RAG、Memory、移动端、CLI、MCP 以及 Agent Skills 全部整合进同一个 monorepo 中。

项目规模扩大后,一个深刻的教训浮现:依靠“想到哪写到哪”的方式维护 AI 产品,几乎无法持久。AI 系统本身具有天然的复杂性,这种复杂性体现在多个维度:

  • 前端交互复杂:流式输出、工具调用、确认式写入、失败重试、继续生成……这些机制彼此交织。
  • 后端边界复杂:Convex、Clerk、Qdrant、DashScope、Next.js API Routes、Machine API……每个组件都有自己的行为模式。
  • Agent行为复杂:Agent 并非执行普通 CRUD 操作,它会规划、检索、调用工具、生成草稿、等待确认,每一步都可能出错。
  • 交付面复杂:Web、Mobile、CLI、MCP、Skills 这些交付成果必须遵守同一份契约。

因此,在 My-Notion 中,我逐步摸索出一套非常实用的工程闭环:

DDD -> SDD -> PDD -> TDD

通俗解释就是:

  • DDD:先把领域边界和核心概念聊透。
  • SDD:再将协议、契约和规格写清楚。
  • PDD:接着把 Agent 的执行路径、提示词约束、工作流限制都固定下来。
  • TDD:最后用测试、类型检查、构建和发布校验来兜底。

这篇文章不会照搬教科书概念,而是结合 My-Notion 真实项目中踩过的坑和总结出的经验,探讨这四种方法在 AI Native 项目中如何具体落地。


一、为什么 AI Native 项目更需要方法论?

传统 Web 项目中,大多数需求可以清晰拆解为页面、接口和数据库表。但 AI Native 项目多了一层挥之不去的不确定性:Agent 的输出不可预知,其工具调用路径也非固定。

举个例子,用户让 Agent“帮我总结并更新当前文档”,你猜背后会发生什么?

  • 读取当前文档上下文。
  • 检索相关历史文档。
  • 搜索 Memory。
  • 调用 LLM 生成总结。
  • 生成文档更新草稿。
  • 展示 dry-run 预览。
  • 等待用户确认。
  • 再提交真实写入。
  • 将 run 事件和 checkpoint 持久化,以便失败后恢复。

这完全不是一个简单的按钮,也不是一个普通 API 能搞定的。

如果没有方法论支撑,项目很容易滑向这几个坑:

  • 领域边界不清:Web Agent、MCP、CLI、Memory、RAG 全部混淆在一起,难以理清。
  • 协议不稳定:前端展示一种 tool result,后端返回另一种结构,双方无法对齐。
  • Agent 行为不可控:有时 dry-run,有时直接写入,完全凭运气。
  • 测试只覆盖 happy path:一旦遇到超时、断流、权限不足的场景,直接崩溃。
  • 文档过期:代码改动频繁,而 Skills、README、release checklist 却停留在旧版本。

因此,现阶段更倾向于将 AI 产品视为“协议驱动的复杂系统”来构建,而不是简单做成“多接几个 LLM API”的页面功能。


二、DDD:先把领域边界讲清楚

DDD 通常指 Domain-Driven Design,即领域驱动设计。在 My-Notion 中,我对 DDD 的理解不是一开始就画复杂的 UML 图,而是先理清三件事:

  • 这个系统里有哪些核心对象?
  • 哪些对象天然属于同一个业务边界?
  • 哪些核心词必须在后端、前端、Agent、CLI 的文档中保持一致?

1. My-Notion 的核心领域

My-Notion 的核心并非“页面”,而是几个非常稳定的领域:

  • Document:文档本身,包含标题、正文、归档、导入导出、Markdown 表达。
  • Agent Run:一次完整的 AI 生成过程,包含 runId、conversationId、assistantMessageId、seq、checkpoint。
  • Tool Result:工具调用的结果,统一收口到 tool-result-v1 契约下。
  • Memory:长期记忆,包括提议、确认、检索和作用域。
  • RAG Source:知识检索的来源,无论是 document、web 还是 memory,都用强类型定义。
  • CLI Token:Device Flow、PAT、scope、token prefix、撤销与过期管理。
  • MCP Tool:供外部 Agent 通过 STDIO 调用的文档读写工具。

一旦这些概念稳定下来,后续 Web、Mobile、CLI、MCP、Skills 的工作才能围绕同一套语言展开协作。

2. DDD 在项目结构中的体现

My-Notion 是一个 pnpm monorepo:

apps/web             # Next.js + Convex + Clerk + BlockNote
apps/mobile          # Expo + React Native
packages/ai          # RAG、Embedding、Agent、AI 配置
packages/business    # Zustand、i18n、共享类型和业务逻辑
packages/convex      # Convex schema、文档、Chat、CLI Token 逻辑
packages/my-notion-cli
packages/my-notion-skills

这个结构本身就是 DDD 的成果:并非将所有代码塞进 Web,而是将领域能力拆分开来。例如,CLI 和 MCP 不是 Web UI 的附属品,而是 Agent 生态中的一等公民;Memory 也不是 Chat 组件中的一个状态,而是独立的领域能力。

3. DDD 的优缺点

做了什么

  • 将 My-Notion 拆解为文档、Agent、RAG、Memory、CLI/MCP、Skills 等清晰的边界。
  • runIdtool-result-v1sourcesdryRunconfirmationRequired 等概念成为团队的“通用语言”。
  • 确保 Web、Mobile、CLI、MCP 都围绕同一套业务词汇演进。

为什么这么做

  • AI 系统的复杂度本质上是跨边界协作的复杂度。
  • 如果核心概念不稳定,后续的协议、测试、文档都会随之漂移,后患无穷。
  • Agent 的工具调用尤其依赖清晰的领域对象,否则难以做好权限控制和失败恢复。

实现优缺点

  • 优点是边界清晰,长期维护成本更低。
  • 缺点是需要前期投入时间来命名、拆包、写文档,短期内比直接写功能慢一些。
  • 对于小型 Demo 可能显得“重”,但面向需要持续演进的 AI 产品,这是必要的投入。

    一句话总结:DDD 不是为了“显得架构很高级”,而是为了确保项目中每个人、每个 Agent、每份文档,说的都是同一种语言。


    三、SDD:把契约写在代码前面

    SDD 可理解为 Spec-Driven Development,即规格驱动开发。在 AI Agent 项目中,SDD 的重要性尤为突出,因为 AI 系统最怕的不是“没有功能”,而是“功能看起来能用,但契约不稳定”。

    1. My-Notion 中的 SDD 实践

    My-Notion 中维护了几类至关重要的规格文档和契约:

    • Agent Stream Resume Protocol:明确定义了 runIdseq、checkpoint、resume state。
    • tool-result-v1:统一了所有 Web Agent 工具的结果结构。
    • MCP dry-run 契约:所有写工具默认 dryRun: true,必须返回确认提示。
    • CLI release checklist:发布前必须执行的步骤,如 test、typecheck、build、e2e、skills sync。
    • Device Flow 规则:授权 URL 只能包含 user_code,绝不能泄漏 device_code
    • Machine API 规则:连接 Convex Machine API 使用 .site URL,而 runtime client 则使用 .cloud URL。

    这些东西如果只写在代码里,很容易在迭代中被无意打破。一旦写成明确的规格,它们就能成为 Agent、开发者和测试共同遵守的边界。

    2. 一个具体例子:流式续跑协议

    Web Agent 进行流式输出时,如果中间网络断开,绝不能简单地重新请求。因为前端可能已经展示了部分文本或工具状态,直接重试会导致:

    • 重复展示 token。
    • 重复执行 tool call。
    • 重复生成写入预览。
    • 更糟糕的是,重复提交有副作用的操作。

    因此,My-Notion 定义了一个流式续跑协议:

    interface AgentStreamEnvelope<T> {
      runId: string;
      seq: number;
      event: T;
      createdAt: number;
    }
    

    客户端只应用 seq > lastAppliedSeq 的事件。服务端通过 AgentRunRecorder 记录所有事件和 checkpoint。这样一来,系统不是靠“猜测当前状态”来恢复,而是依赖显式定义的协议。

    3. 另一个例子:写入必须确认

    在 My-Notion 中,Agent 想要写入文档或记忆,不允许直接操作数据库。它必须遵循一个明确的流程:

    Dry-run -> Preview -> User Confirmation -> Commit
    

    这个规则绝不是 UI 层的一个小细节,而是系统级的规格。它同时影响了:

    • Web Agent 上的工具确认按钮。
    • MCP 写工具默认 dryRun: true 的实现。
    • CLI 和 Skills 的安全说明。
    • 相关的测试和 release checklist。

    4. SDD 的优缺点

    做了什么

    • 将 Agent Stream、MCP dry-run、CLI 发布、安全写入等关键行为,都写成了明确的规格。
    • 用协议来约束代码实现,而不是每次有新需求都重新讨论。
    • 让外部 Agent 也能理解和遵守 My-Notion 的安全边界。

    为什么这么做

    • AI Agent 的行为不完全可预测,因此系统的边界必须高度可预测。
    • 协议比口头约定更容易被测试、被复查、被迁移。
    • 当 Web、Mobile、CLI、MCP 同时存在时,规格是唯一能让各方稳定协作的基础层。

    实现优缺点

    • 优点是可维护、可复盘、可测试。
    • 缺点是规格文档需要持续更新,否则就会变成“历史遗留的文档僵尸”。
    • 对团队来说,SDD 要求开发前多做一些设计,但这能有效减少后期返工。

      核心观点很明确:SDD 的价值不在于写文档本身,而在于让系统从“靠实现碰巧能工作”,变成“按契约稳定工作”。


      四、PDD:让 Agent 也能按工程规范工作

      PDD 在这里更愿意理解为 Prompt-Driven Development。这不是让 Prompt 去替代代码,而是把 Prompt、Agent Rules、Skills、Checklist 这些东西,都当作正规的工程资产来管理。

      在 AI Native 项目中,Agent 的角色远不止一个聊天窗口。它可以参与:

      • 读代码
      • 改代码
      • 生成测试
      • 同步文档
      • 发布 npm 包
      • 调用 MCP 工具
      • 写入项目进度

      如果没有 PDD,Agent 很容易做出一些“看起来合理,但完全不符合项目规则”的事情。

      1. My-Notion 中的 PDD 载体

      My-Notion 中存放了几类 Prompt/规则资产:

      • AGENTS.md:项目入口规则,告诉 Agent 当前的架构、目录、验证命令和安全约束。
      • .trae/rules/project-workflow.md:工作流、项目结构和技术约束。
      • packages/my-notion-skills:面向外部 Agent 的可复用 Skills。
      • progress/:阶段性的过程记录,避免 Agent 重复做无意义的探索。
      • milestones/:里程碑索引,帮助 Agent 快速理解项目当前状态。
      • release checklist:发布前必须执行的一系列固定动作。

      这些文件的核心作用不是“给人看的 README”,而是“给 Agent 的工程上下文”。

      2. 一个真实案例:CLI 从 beta 推到 latest

      最近 My-Notion 将 @mynotion/cli 从 beta 版本推送到 npm 的 latest 标签。这个流程如果靠临场发挥,很容易漏掉步骤:

      • 忘记更新 package version。
      • 忘记同步 Skills。
      • 忘记检查 tarball 中是否还残留 beta 时期的文案。
      • 忘记验证 npm dist-tag 是否正确。
      • 将 npm token 打入日志。
      • 忘记清理 .npmrc.publish

      但有了 PDD,就可以把 Agent 的执行路径固定下来,变成一个清晰的流程:

      读 release checklist
      确认 package version
      同步 Skills
      运行 typecheck/test/build
      生成 tarball
      检查 tarball 内容
      使用本地忽略的 token 文件发布
      验证 npm latest 是否更新成功
      清理本地 token 和 tarball
      更新 progress 记录
      

      在这个过程中,Prompt 和规则不是一句“帮我发布一下”的模糊指令,而是一组可执行、可验证的工程约束。

      3. 另一个真实案例:下线废弃能力

      My-Notion 曾经拥有绘图能力,后来因产品路线调整和后端热路径风险,最终决定下架。这类任务绝不是简单删除一个按钮就能解决。你需要问自己:

      • Web 前端入口移除干净了吗?
      • 历史文档中的绘图块还能安全渲染吗?
      • CLI 和 MCP 是否还暴露着旧的绘图工具?
      • Machine API 是否返回了明确的错误?
      • 相关的 scope 是否已移除?
      • 文档中是否还存在旧能力的字段?
      • 生产环境的 Convex 部署完成了吗?

      这些问题非常适合写进 Agent 的执行 Prompt 中。因为它不是单点代码修改,而是一个需要跨 Web、CLI、MCP、后端和文档统一执行的收口任务。

      4. PDD 的优缺点

      做了什么

      • 将项目规则、执行顺序、安全边界、验证命令都写成了 Agent 可读的上下文。
      • 让 Agent 不仅仅是“写代码”,而是能按照项目的整体工作流推进任务。
      • 通过 progress 和 milestones 降低 Agent 的重复探索成本。

      为什么这样做

      • AI Agent 的能力很强,但它无法凭空知道项目的长期约束。
      • 如果 Prompt 只存在于零散的聊天记录中,既难复用,也难审计。
      • 复杂项目需要让 Agent 按“项目的方法论”工作,而不是按“通用的建议”工作。

      实现优缺点

      • 优点是 Agent 行为更稳定,跨会话接续能力更强。
      • 缺点是需要维护规则文件,而且规则过多时可能增加 Agent 的上下文负担。
      • 最佳实践不是堆砌规则,而是把关键的安全边界和验证路径写清楚。

        说到底,PDD 不是什么“提示词玄学”,而是要把 Agent 的工作方式产品化、流程化、可审计化。


        五、TDD:让不确定的 AI 系统有确定的验收线

        TDD 通常指 Test-Driven Development,即测试驱动开发。在 My-Notion 中,我不会机械地要求所有功能都必须先写测试,但针对关键链路坚持一个原则:

        1. My-Notion 的验证分层

        项目中常见的验证命令包括:

        # Web
        pnpm --filter @notion/web typecheck
        pnpm --filter @notion/web lint
        pnpm --filter @notion/web build
        pnpm ci:ai-smoke# CLI / MCP
        pnpm --filter @mynotion/cli test
        pnpm --filter @mynotion/cli typecheck
        pnpm --filter @mynotion/cli build
        pnpm e2e:cli
        pnpm e2e:cli:errors
        pnpm e2e:mcp
        pnpm e2e:mcp:client# Skills
        pnpm sync:skills
        pnpm sync:skills:package
        pnpm sync:skills:check
        

        维护这些命令不是为了“跑起来好看”,而是因为 My-Notion 的交付面实在太多了:

        • Web 改动要保证 Next.js 和 Convex 的类型正确。
        • CLI 改动要保证命令、输出格式和错误契约的稳定。
        • MCP 改动要保证外部 Agent 能正确调用工具。
        • Skills 改动要保证源码、.trae/skills 中的拷贝,以及随包发布的 skills,这三份内容完全一致。

        2. 最近一次发布的 TDD 实践

        @mynotion/cli@0.1.0 发布到 npm latest 前后,实际跑了这些验证步骤:

        • pnpm --filter @mynotion/cli typecheck
        • pnpm --filter @mynotion/cli test
        • pnpm --filter @mynotion/cli build
        • pnpm sync:skills
        • pnpm sync:skills:package
        • pnpm sync:skills:check
        • npm publish --tag latest --access public
        • npm view @mynotion/cli dist-tags --json
        • npx @mynotion/cli@latest --help
        • npx @mynotion/cli@latest install --check --format json

        最后确认:

        {
          "latest": "0.1.0",
          "beta": "0.1.0-beta.1"
        }
        

        以及安装检查的返回:

        {
          "ok": true,
          "version": "0.1.0",
          "skillsBundled": true
        }
        

        这就是 TDD 在发布链路中的真正价值:不是拍胸脯说“我改完了”,而是让一行行命令去证明“它真的能工作”。

        3. TDD 的优缺点

        做了什么

        • 用单测、类型检查、构建、E2E,甚至发布后的 npm viewnpx smoke test 来做最终验收。
        • 对 CLI、MCP、Skills 这些 Agent 入口,保持更严格的回归检查。
        • 将每次验证的结果写入 progress,方便后续复盘。

        为什么这样做

        • Agent 产品的失败模式实在太多,不能只靠人肉点点点来验证。
        • 发布包一旦推到 npm,回滚成本比本地修 bug 高得多。
        • 外部 Agent 依赖 CLI 和 MCP 的契约,破坏契约会直接影响到下游自动化。

        实现优缺点

        • 优点是确定性极强,能及时发现协议漂移和发布问题。
        • 缺点是验证链路会变长,尤其是 E2E 测试涉及真实服务依赖时。
        • 实践中需要根据改动范围选择最小必要的验证集,避免每次都跑全量。

          对于 AI 项目,TDD 不是什么“保守”的做法。恰恰相反,它是用一个工程的确定性,去兜住 AI 那天然的不确定性。


          六、把四种方法串起来:一个完整任务怎么跑?

          如果用 My-Notion 的方式去做一个新功能,通常会这样拆解和执行:

          1. DDD:先问领域问题

          这个能力属于哪个领域?
          它是否会引入新的核心对象?
          它和 Document、Agent Run、Memory、Tool Result 的关系是什么?
          它是否需要新的权限或 scope?
          

          例如,在实现“Agent 写入文档”功能时,领域对象并非“一个按钮”,而是一整套事物:

          • 文档草稿
          • 写入预览
          • 用户确认
          • 提交动作
          • 审计与恢复

          2. SDD:再写协议和契约

          API 的输入输出是什么样?
          tool result 的结构是什么?
          错误码如何定义?
          是否支持 dry-run?
          是否需要 checkpoint?
          

          如果该功能还涉及外部 Agent,则需进一步明确:

          • MCP structuredContent
          • text fallback
          • isError
          • requestId
          • confirmationRequired

          3. PDD:把 Agent 执行路径写清楚

          Agent 允许做什么?
          它禁止做什么?
          它是应该先 dry-run,还是可以直接 commit?
          修改完成后,需要同步哪些文档?
          需要在本地跑哪些验证命令?
          

          这部分内容会沉淀到 AGENTS.md、Skills、progress 或 release checklist 中。

          4. TDD:最后用验证收口

          类型检查能通过吗?
          单测覆盖了关键契约吗?
          E2E 覆盖了真实链路吗?
          发布后做了 smoke test 吗?
          文档和代码是否对齐?
          

          这样一来,AI 项目就不再是“拼凑一段 Prompt + 调用几个 API”那么简单了,它变成了一套可持续演进、有章可循的工程系统。


          七、这套方法在 My-Notion 中解决了什么问题?

          1. 解决跨端割裂

          Mobile 端早期通过 /api/chat/api/rag 接口接入 Web 的能力。为了堵住未鉴权的入口,后续补上了 Clerk Bearer token,并在 Web 端增加了兼容路由。

          这个问题的闭环是这样的:

          • DDD:Mobile 的 AI 能力属于同一个 Agent/AI 领域,不能独立地随便接一个接口。
          • SDD/api/chat/api/rag 的请求和 SSE 输出必须保持稳定。
          • PDD:规则中明确 Mobile 必须复用 Web 的 API 鉴权和 Agent Stream 方向。
          • TDD:至少跑通 Web 的 typecheck 和影响范围验证。

          2. 解决 Agent 写入安全

          Agent 能写文档是很有价值的,但如果没有确认链路,就非常危险。My-Notion 选择了这样一条路:

          Dry-run -> Preview -> User Confirmation -> Commit
          

          这种模式让 Agent 从“直接执行者”的角色,转变为“生成方案并等待确认的协作者”。

          3. 解决发布不可控

          CLI、MCP 和 Skills 都是供外部 Agent 使用的。一旦发布到 npm,任何契约上的变化都会影响使用方。因此发布绝不仅仅是执行一句 npm publish,而是一个完整流程:

          • version bump
          • changelog 更新
          • Skills sync
          • typecheck/test/build
          • tarball 内容检查
          • npm dist-tag 验证
          • npx smoke test
          • token 清理
          • progress 记录

          4. 解决历史功能下线

          下线一个废弃的能力,比新增一个功能更能考验工程的边界。My-Notion 的做法是:前端入口移除,历史 block 保留只读降级处理,CLI 和 MCP 不再暴露相关工具,Machine API 返回明确的错误,相关 scope 移除,清理文档,最后部署生产。

          这类任务如果没有 DDD、SDD、PDD、TDD 这套方法论支撑,很容易只记得删掉前端按钮,而忘了后端入口或外部 Agent 的工具。


          八、给 AI Agent 项目的几个建议

          如果你也在做 AI Native 产品,建议不要一开始就追求复杂的框架,而是先建立这几个习惯。

          1. 不要先写 Prompt,先定义领域词汇

          如果团队对“run”、“tool result”、“memory”、“source”、“confirmation”这些词的理解都不一致,那么 Prompt 写得再漂亮,最终落地时也会走样。

          2. 不要只写接口,要写协议

          接口回答的是“某次请求返回什么”,而协议回答的是“系统长期如何协作”。Agent Stream、MCP、CLI、Memory 这类能力,都应该用协议的思维去设计。

          3. 不要让 Agent 直接拥有副作用权限

          写文档、写记忆、删除、归档、发布……这些操作都应该有明确的确认链路或权限边界。尤其是 MCP 和 CLI 场景,将 dry-run 设为默认,是一个非常实用的安全策略。

          4. 不要把 Prompt 当临时聊天记录

          真正有价值的 Prompt 应该被沉淀成:

          • AGENTS.md
          • Skills
          • Checklist
          • progress
          • milestone
          • release docs

          只有这样,Agent 才能做到跨会话、跨任务保持一致的行为。

          5. 不要跳过发布后验证

          发布成功不代表真的能用。至少需要做这两步验证:

          npm view <package> dist-tags --json
          npx <package>@latest --help
          

          如果是带 Skills 的包,还需要验证随包发布的资源是否存在。


          九、总结

          My-Notion 不是一次性的 demo,而是一个需要持续演进的 AI Native 知识工作台。在不断打磨的过程中,我对 PDD、DDD、SDD、TDD 的理解也越来越具体:

          • DDD 解决的是“我们到底在做什么领域”的问题。
          • SDD 解决的是“系统之间如何稳定协作”的问题。
          • PDD 解决的是“Agent 如何按项目规则工作”的问题。
          • TDD 解决的是“我们如何证明它没有坏”的问题。

          它们不是互相替代的关系,而是一条完整的链路:

          先用 DDD 统一语言
          再用 SDD 固化契约
          再用 PDD 约束 Agent 执行
          最后用 TDD 验证交付结果
          

          如果只做 PDD,Agent 可能很会写代码,但方向不稳定。如果只做 DDD,架构概念很漂亮,但可能无法落地。如果只做 SDD,协议很清楚,但执行过程可能失控。如果只做 TDD,测试很多,但测试的可能是错误的抽象。

          而只有把这四者串起来,才更接近理想中的 Agent 工程方式:

          这也是 My-Notion 继续演进时,会长期坚持的一套方法论。

免责声明

本网站新闻资讯均来自公开渠道,力求准确但不保证绝对无误,内容观点仅代表作者本人,与本站无关。若涉及侵权,请联系我们处理。本站保留对声明的修改权,最终解释权归本站所有。

相关阅读

更多
欢迎回来 登录或注册后,可保存提示词和历史记录
登录后可同步收藏、历史记录和常用模板
注册即表示同意服务条款与隐私政策