我的代理堆栈就是我狗食 [Neotoma](https://neotoma.io) 的方式。这也是我个人的操作系统。一个私人单一存储库，人工智能代理处理从电子邮件分类到比特币支付再到网站部署的所有事务，并以 Neotoma 作为底层的结构化内存。

我在 Neotoma 中发布的每个功能都首先在此处进行验证，主要是在 [Cursor](https://cursor.com) 中进行验证，其次是通过 [Claude Code](https://www.anthropic.com/claude-code)、[Codex](https://openai.com/codex) 和 [Cursor CLI](https://cursor.com/cli) 等终端代理进行验证。我在特工记忆中发现的每一个空白都首先在这里浮现出来。堆栈是我日常生活和工作的方式。除了用户反馈之外，我遇到的摩擦也是推动 Neotoma 路线图的因素。

我打算开源该堆栈。但该存储库积累了数月的个人数据、硬编码到我的帐户的脚本以及与我的设置相关的配置。在公开之前，我需要将这些数据完全推送到 Neotoma 中，并将工具重构为通用的。这项工作正在进行中。

这篇文章解释了堆栈是什么、我如何使用它，以及它揭示了结构化代理内存需要做什么。

## 栈是什么

该堆栈是一个包含十多个 [MCP](https://modelcontextprotocol.io) 服务器和 CLI 的单一存储库，每个服务器和 CLI 将 AI 代理连接到不同的服务：[Gmail](https://github.com/markmhendrickson/mcp-server-gmail)、[Google 日历](https://github.com/markmhendrickson/mcp-server-google-calendar)、[WhatsApp](https://www.whatsapp.com)、[比特币] [钱包](https://github.com/markmhendrickson/mcp-server-bitcoin)、[Instagram](https://github.com/markmhendrickson/mcp-server-instagram)、[Asana](https://github.com/markmhendrickson/mcp-server-asana)、[HomeKit](https://www.apple.com/home-app-accessories/)、 [DNSimple](https://github.com/markmhendrickson/mcp-server-dnsimple)、[Google Search Console](https://search.google.com/search-console)、[1Password](https://1password.com)、[网络抓取工具](https://github.com/markmhendrickson/mcp-server-web-scraper)等等。有些是代理称为工具的 MCP 服务器。其他是代理从终端调用的 CLI。两者都为代理提供了相同的东西：接触外部服务。

MCP 服务器之上是规则和技能。规则是存在于存储库中而不是 Neotoma 中的持久行为指令：在响应之前始终将联系人存储在 Neotoma 中，从不提交秘密，在标题中使用句子大小写，在代码更改后运行测试，在日志和配置方面更喜欢 CLI 而不是仪表板，链接第一次提到的产品名称。技能是多步骤的工作流程：分类我的收件箱、起草博客文章、部署网站、处理产品反馈、从电子邮件中提取亚马逊订单、用比特币向承包商付款。

在所有东西的下面都是 [Neotoma](https://neotoma.io) 作为结构化内存层。每个代理都会读取它并写入它。这就是使堆栈随着时间的推移而复合而不是在每个会话上重置的原因。

## 我如何使用它

我住在光标处。我的一天是一系列代理会议。我打开一个新代理，描述我想要完成的操作，然后代理使用工作区中的 MCP 服务器、规则和技能执行。有些会话很快：“回复这封电子邮件。”有些很长：“对我的收件箱进行分类，处理最新的测试人员反馈，然后起草一篇关于[Google的内存代理](/posts/always-on-memory-agents-and-the-truth-layer)的比较文章。”

它正在与代理商合作。我坐在办公桌前和他们一起工作一整天。个人任务：安排维修、向承包商付款、管理日历事件。专业的：撰写帖子、处理反馈、部署网站、管理域名。代理负责执行。我提供指导、审查输出并批准需要人员参与的行动。

每个代理会话都具有完整的工作区上下文：每个 MCP 服务器、每条规则、每项技能。该代理可以读取我的 Gmail、检查我的日历、查询 Neotoma 以获取先前的上下文、存储新数据、生成图像、推送代码并验证部署。我的角色越来越多地是描述意图和审查结果。

## Neotoma 如何适合

如果没有结构化内存，每个代理会话都从零开始。代理不知道您的联系人是谁、您有哪些未完成的任务、您昨天讨论了什么，或者您已经向某人支付了多少钱。您可以将上下文粘贴到每个提示中，但这不会扩展到几个会话之后。 [平台记忆](/posts/your-ai-remembers-your-vibe-but-not-your-work) 存储的是您的氛围，而不是您的工作。 [RAG](/posts/why-agent-memory-needs-more-than-rag) 对代码有帮助，但对驱动工作流程的结构化事实没有帮助：你欠谁的钱，你上周收到了什么反馈，哪些任务仍然悬而未决。

我的 Neotoma 实例存储了代理在遇到新类型信息时创建的 1,000 多个联系人、600 个任务、140 个对话、120 篇博客文章和 170 个实体类型：交易、常规规则、反馈说明、日历事件、争议、发票、技能、部署结果。当代理启动新会话时，它会检索所需的内容。完成后，它会存储所学到的内容。

以下是我今天的 Neotoma 实例中排名前 20 的实体类型，以及每个实体类型的示例：

|实体类型|示例|
|---|---|
|代理留言| “对我的收件箱进行分类并处理测试人员的反馈”|
|承诺|如果融资策略发生变化，通知投资者 |
|公司 | Acme 设计工作室，acme-studio.com |
|联系方式 | Sarah Kim，sarah@example.com，从 Gmail 导入，然后每次我与她互动时都会扩展 |
|对话 | “3 月 9 日电子邮件分类”|
|纠纷|退款纠纷，99 欧元，正在审核中 |
|电子邮件 | “回复：伙伴关系提案”，3 月 8 日 |
|活动 |学校排练，周六上午 10 点，带上服装 |
|反馈备注| “模式发现需要更好的文档”，开发人员发布 |
|发票 |发票 #2038，1,450 欧元，上门维修 |
|友情链接 | LinkedIn、linkedin.com/in/username、社交 |
|地点 |西班牙巴塞罗那（主页）|
|帖子 |为什么智能体记忆需要的不仅仅是 RAG，论文，已发表 |
|偏好 |切勿包含普拉提课程付款的备注字段 |
|常设规则 |始终在付款确认中包含交易链接 |
|技能 |修复功能错误：对错误进行分类，添加回归测试，运行套件；电子邮件分类：处理收件箱、草稿回复、存档 |
|任务 | “购买阻力带”，等待紧急级别，健康领域|
|时间线条目| 2007 年至 2009 年，TechCrunch 项目经理 |
|交易 | 0.0021 BTC（178 美元）支付给卡洛斯 3 月份的服务 |

这些模式都不是预先设计的。代理在遇到新类型的信息时[根据需要创建和扩展模式](https://neotoma.io)。该系统现在共有 170 个实体类型，其中大多数只有少量记录。长尾是代理记忆变得有趣的地方：具有完整谈判历史的单一争议实体、在策略发生变化时与某人跟进的单一承诺、关于如何处理特定付款的单一偏好。

实际的区别在于代理建立在先前的工作基础上。当我要求代理向某人发送电子邮件时，它会首先在 Neotoma 中搜索联系人。当我要求它处理反馈时，它会检索现有的反馈实体并链接新的实体。当我要求它以比特币向承包商付款时，它知道常规规则（始终以比特币向此人付款），并在确认中包含交易链接，因为另一条常规规则是这样规定的。

## 近距离存储和检索

Neotoma MCP 公开了代理直接调用的工具。这是实际存储和检索的样子。

当代理需要与提取的实体保持对话轮次时，它会使用单个有效负载调用“store”：

```json
商店（{
  “实体”：[
    {
      "entity_type": "对话",
      "title": "3 月 9 日电子邮件分类"
    },
    {
      "entity_type": "代理消息",
      “角色”：“用户”，
      "content": "分类我的收件箱",
      “turn_key”：“conv-42：1”
    },
    {
      "entity_type": "联系人",
      "full_name": "亚历克斯·陈",
      “电子邮件”：“alex@example.com”，
      "source": "测试人员反馈电话"
    },
    {
      "entity_type": "任务",
      "title": "跟进 Alex 的反馈",
      “状态”：“待处理”，
      “优先级”：“中”
    }
  ],
  “关系”：[
    {
      "relationship_type": "PART_OF",
      “来源索引”：1，
      “目标索引”：0
    },
    {
      "relationship_type": "REFERS_TO",
      “来源索引”：1，
      “目标索引”：2
    },
    {
      "relationship_type": "REFERS_TO",
      “来源索引”：1，
      “目标索引”：3
    }
  ],
  “idempotency_key”：“conv-42-turn-1-triage”
})
````

一次调用存储对话、消息、新联系人和任务，所有这些都通过类型化关系链接起来。代理事先不需要“contact”或“task”的模式定义。 Neotoma 接受任意字段并推断结构。

当代理在响应之前需要上下文时，它会通过标识符进行查询：

```json
通过标识符检索实体（{
  “标识符”：“亚历克斯·陈”
})
````

这将返回包含电子邮件、之前对话以及每个字段的出处的联系人记录。如果代理需要更广泛的上下文，它会按类型查询：

```json
检索实体（{
  "entity_type": "反馈说明",
  "search": "开发者版本",
  “限制”：10
})
````

这将返回有关开发人员版本的十个最相关的反馈注释，每个反馈注释都有其完整的快照和观察历史记录。

对于长期规则，代理会在工作流程开始时检索它们一次：

```json
检索实体（{
  “entity_type”：“stand_rule”
})
````

我的实例返回诸如“始终用比特币向卡洛斯付款”和“在降价块中提供起草的消息”之类的规则。代理会自动将这些内容应用于会话的其余部分。

当工作流程接触文件（收据、屏幕截图、文档）时，代理使用“file_path”将它们与同一调用中的实体一起存储：

```json
商店（{
  “实体”：[
    {
      "entity_type": "交易",
      “供应商”：“亚马逊”，
      “金额”：47.99，
      “货币”：“欧元”
    }
  ],
  "file_path": "/path/to/receipt.pdf",
  “idempotency_key”：“亚马逊订单三月 9 日”
})
````

Neotoma 的[非结构化存储路径](https://github.com/markmhendrickson/neotoma) 从那里处理文件。原始字节是内容寻址的 (SHA-256)，因此同一个文件永远不会存储两次。代理通过“file_path”（本地环境，如 Cursor）或“file_content”（base64，用于基于 Web 的环境）按原样传递文件；它在存储之前不会解释或提取数据。默认情况下，Neotoma 自动对存储的文件运行 AI 解释，提取结构化实体并通过 EMBEDS 关系将它们链接回源。使用“interpret: true”重新存储同一文件会触发重新解释，而不会创建重复文件。收据成为可查询的结构化数据，并保留原始 PDF 以供查证。还可以延迟解释（“interpret: false”）以进行批处理或配额管理，然后使用不同的配置运行。

## 实践中的工作流程

一些工作流程显示了这种模式。

**电子邮件分类。** 代理通过 Gmail MCP 读取未读电子邮件，检查 Neotoma 的现有联系人记录和每个发件人之前的上下文，使用我的通信风格规则起草回复，存储新联系人和任务，并存档已处理的消息。一次分类运行可能会存储五个新联系人、三个任务和十几个对话回合。

**博客文章写作。** 该技能本身作为一个结构化实体存在于 Neotoma 中。代理使用“retrieve_entity_snapshot”检索它。然后，它查询现有帖子以进行风格校准、编写草稿、存储包含所有元数据的帖子实体、生成英雄图像、为 Twitter 和 LinkedIn 创建共享副本、从 Neotoma 导出重新生成网站缓存并进行部署。这篇文章就是这样写的。

**比特币付款。** 我使用 [BTC 钱包 MCP 服务器](/posts/agentic-wallets-mcp-bitcoin) 以比特币向承包商付款。 Neotoma 存储常设规则、联系记录和交易历史。代理检索所有三项内容，执行付款，使用链上链接存储新交易，然后确认。

**反馈处理。** 当测试人员提供有关 Neotoma 开发人员版本的反馈时，代理会提取结构化反馈实体，将它们链接到测试人员的联系记录，按存储桶对反馈进行分类，并根据发布阶段限制对其进行评估。先前的反馈可由测试人员、按存储桶或按日期检索。

**网站部署。** 部署技能将本地 Markdown 编辑同步到 Neotoma，导出完整的网站数据集，重新生成缓存，推送[网站存储库](https://github.com/markmhendrickson/markmhendrickson)，并监视 [GitHub 操作](https://github.com/features/actions)，直到构建成功。如果构建失败，代理将读取日志、修复问题并重新运行。

## 对于没有我的情况下运行的代理

每个工作流程都从我打开 Cursor 代理并输入指令开始。我对每项任务都处于循环状态。对于当前阶段来说这很好，但这不是最终状态。

我正在设置自动化流程，以便代理可以在没有我直接参与的情况下处理工作流程。这些部分已经存在：技能定义了完整的工作流程步骤，Neotoma 存储上下文和规则，MCP 服务器提供覆盖范围。缺少的是按计划触发工作流程或响应事件的编排，以及轻量级审批界面，以便我无需坐在笔记本电脑前即可查看和授权操作。

Neotoma 的[分层架构](https://github.com/markmhendrickson/neotoma/blob/dev/docs/foundation/layered_architecture.md) 正是为此而设计的。它区分了三个问题：

1. **真相层 (Neotoma)。** 事件源、reducer 驱动、确定性。所有特工都从中读取。状态更新仅通过减速器处理的域事件流动。没有代理人会直接改变真相。
2. **策略层。** 从Neotoma读取当前的世界状态。评估优先级、限制、风险、承诺和时间。输出决策和命令。纯粹认知：陈述输入，决策输出。无副作用。
3. **执行层。** 从策略层获取命令。通过外部适配器（电子邮件 API、支付服务、日历、消息传递）执行副作用。发出描述所发生事件的域事件。这些事件通过减速器回流以更新状态。纯效果：命令输入，事件输出。

循环已闭合：

````
入站信号（电子邮件、WhatsApp、日历、财务数据）
  -> 标准化 -> Neotoma 状态（事件日志+减速器）
  -> 策略打勾（评估优先级，输出决策）
  -> 执行代理（执行副作用，发出事件）
  -> 减速器 -> 更新状态
  -> 下一个勾号
````

今天，我是战略层。我查看状态，决定要做什么，然后告诉代理执行。该架构使该角色可以由软件替代。策略引擎读取 Neotoma，根据现行规则和优先级评估需要注意的内容，并向执行代理发出命令。这些代理调用 MCP 服务器，存储结果，然后重复循环。

关键的不变量是没有层直接写入 Neotoma 的底层数据存储。更新仅流经域事件和化简器。这使得系统可审计且可逆。如果自主代理做出了错误的决定，我可以追踪导致该决定的事件，恢复状态更新，并纠正导致该决定的规则。

目标是减少我每天使用电脑的时间。不消灭之。从亲自执行转向审批。我想一觉醒来就看到我的客服人员一夜之间处理的事情的总结：电子邮件分类、帖子起草、部署验证、付款排队。我想批准通过我的 [Apple Watch](https://www.apple.com/watch) 进行比特币付款。我想边走路边查看手机上起草的电子邮件，然后点击发送。代理处理 80% 的可重复事务。我负责处理 20% 需要判断的事情。

这就是硬件问题变得有趣的地方。今天的手机和手表并不是为这种交互模式而设计的。您需要一款针对简短审阅和批准手势进行优化的设备，而不是针对打字或浏览进行优化。

在当今现有的设备中，[Apple Watch](https://www.apple.com/watch) 感觉最接近正确的外形尺寸：始终戴在手腕上、一目了然、能够进行简单的点击批准交互。但软件层还没有。无法以原生方式将代理摘要和批准请求传送到手表。

这可能是我在某个时候尝试的一个领域，构建一个轻量级配套应用程序，将 Neotoma 的状态桥接到手腕级界面。无论正确的表面最终是手表应用程序、专用人工智能设备还是尚不存在的东西，交互模型都是明确的：代理完成工作，结构化内存保存状态，而人类按照意图的速度而不是执行的速度提供方向。

## 开源堆栈

如今，该堆栈是私有的，因为它包含我的生活：联系人、财务、健康数据、个人通讯、有关我如何管理家庭的现行规则。在我开源它之前，我需要解决所有这些问题。

路径很简单。个人数据完全转移到 Neotoma，这已经是大部分数据的真相来源。引用我的特定帐户和路径的脚本被重构以从配置中读取。 MCP 服务器包装器变得通用。技能失去了硬编码的假设。

剩下的是一个可重用的代理堆栈：带有 MCP 服务器脚手架的 monorepo 模板、规则和技能框架、结构化内存的 Neotoma 集成以及任何人都可以适应的示例工作流程。架构是有趣的部分。我的个人数据不是。

我没有这方面的时间表。重构与日常使用同时进行。每次我接触一个脚本，我都会让它变得更加通用。每次我将数据移入 Neotoma 时，我都会将其从存储库中删除。每次会话时堆栈都会变得更加可移植。

## 这证明了 Neotoma 的什么

我在 Neotoma 出现之前就构建了这个堆栈。早期版本使用平面文件和 [Parquet](https://parquet.apache.org) 表。它一直有效，直到不起作用为止。

[失败模式](/posts/truth-layer-agent-memory) 是特定的：代理会在一个会话中将联系人存储为“Sarah Kim”，在另一个会话中将联系人存储为“S. Kim”，从而创建重复项，而无法合并它们。由于没有出处，所以我无法判断哪个代理在何时写了该字段。

查询仅限于单列上的精确匹配，因此询问“我上周得到了什么反馈？”意味着手动扫描每个文件。有时记录会被严重覆盖或完全删除，并且没有事件日志可供恢复。并且没有跨类型的链接，因此知道与交易相关的联系人相关的任务需要我在脑海中保留该图表。

Neotoma 取代了该层。它为代理提供了适用于每个工作流程的[结构化、可查询、关系感知记忆](/posts/agent-memory-truth-problem)。 Neotoma 中的堆栈现在有 170 个实体类型，不是因为我预先设计了 170 个模式，而是因为代理在遇到新类型的信息时创建实体类型。反馈说明不同于交易，也不同于常规规则，系统会处理所有这些。

正是这种内部测试让 Ne​​otoma 保持诚实。当检索缓慢时，我在每个代理会话中都会感觉到。当实体解析失败时，我会得到重复的联系人。当存储不可靠时，工作流程就会中断。每个错误和每个差距都会先出现在我的日常工作中，然后才会出现在其他人的工作中。

记忆问题是普遍存在的。每个构建代理工作流程的开发人员都会遇到同样的问题：代理无法记住，无法查询，并且无法在以前的工作基础上进行构建。 [仅检索是不够的](/posts/why-agent-memory-needs-more-than-rag);结构和来源使记忆变得可信。这个堆栈证明结构化内存改变了智能体可以做的事情。 Neotoma 就是我向每个人提供这种服务的方式。

[开发者版本](/posts/neotoma-developer-release) 已开放测试。如果您正在构建代理工作流程并想要底层的结构化内存，那么这就是开始的地方。