
1. 一个意外的发现全球开发者不约而同的架构选择前几天我在一篇关于用纯文本文件而非向量数据库来运行AI智能体系统的文章评论区里遇到了点不寻常的事。我原本以为会看到一些“挺有意思的思路”的客套话或者一两条关于可扩展性的质疑。结果八位来自不同国家、互不相识的开发者在评论区里不约而同地留言说“嘿我也造了个一模一样的轮子。”这八位开发者有的在欧洲有的在美洲有的在亚洲。他们之间没有任何事先的沟通或协作使用的技术栈细节也略有不同但最终搭建出的智能体系统核心架构却惊人地一致用Markdown文件存储记忆和状态用纯文本定义智能体身份用简单的文件系统或SQLite的FTS5进行搜索完全摒弃了向量数据库和复杂的编排框架。比如开发者kuro_agent分享道他的系统已经7x24小时不间断运行了数月核心就是Markdown记忆和一个名为SOUL.md的身份文件。sauloferreira6413则独立构建了几乎相同的架构只是把身份文件命名为SKILL.md。apex_stack用类似的方案协调着十多个定时运行的智能体靠一个中央CLAUDE.md文件充当系统的“灵魂”。max_quimby的团队也在用近乎相同的记忆架构每日的Markdown日志作为工作记忆一个精心维护的MEMORY.md文件记录长期事实。不同的命名不同的实现者但相同的骨架一个身份层、一个仅追加的日志作为工作记忆、一个经过提炼的摘要文件作为长期记忆以及零外部依赖。这让我开始思考当一群分布在全球的实干派开发者在互不知情的情况下用最朴素的工具解决了同一个复杂问题这到底意味着什么这或许不是一个关于“极简主义”的浪漫故事而是一个关于“什么在真实生产环境中真正有效”的强烈信号。2. 架构拆解为什么是纯文本栈2.1 核心组件一个自解释的文件夹结构这些不约而同的架构都可以归结为一个极其简单的目录树。我们不妨给它起个名字就叫“纯文本智能体栈”Plain-Text Agent Stack。它的典型结构是这样的my_agent_system/ ├── identity/ # 行为契约每次会话都会读取 │ └── SOUL.md (或 SKILL.md) # 定义“我是谁”、“我重视什么”、“我如何行事” ├── memory/ │ ├── 2023-10-27.md # 今天的经历日志仅追加不修改 │ ├── 2023-10-26.md # 昨天的日志 │ └── MEMORY.md # 长期记忆与核心事实需人工或智能体定期提炼 └── state/ └── agent_1_state.md # 单个智能体的临时工作状态身份层identity/这是智能体的“宪法”。SOUL.md或SKILL.md文件里写下的不是冰冷的配置参数而是这个智能体的核心原则、价值观、沟通风格和职责边界。例如一个负责代码审查的智能体它的身份文件里可能会明确写出“我重视代码的可读性和安全性高于极致的性能优化”、“我对模糊的命名会提出质疑”、“我以鼓励和建设性的口吻提供反馈”。每次智能体启动或执行任务前都会重新读取这个文件确保其行为的一致性。sauloferreira6413提出的SKILL.md命名在多智能体系统中更具优势因为它暗示了技能的模块化和可组合性你可以为“数据分析师”、“文案写手”、“调度员”分别定义不同的技能文件。记忆层memory/这里采用了经典的“双通道”记忆模型。YYYY-MM-DD.md这样的每日文件是“工作记忆”或“情景记忆”像一个只写不删的流水账日记忠实记录当天发生的所有交互、思考和临时数据。它的“仅追加”特性至关重要这保留了完整的上下文链条便于事后追溯。而MEMORY.md文件则是“长期记忆”它并非自动生成而是需要定期比如每天结束时从工作记忆日志中提炼、总结、去重写入那些需要持久化、跨会话使用的核心信息、学到的教训或重要结论。这个过程模拟了人类从短期记忆到长期记忆的转化充满了“判断”的介入。状态层state/存放智能体在单个任务执行周期内的临时工作状态。比如一个正在撰写报告的智能体可能会把当前的大纲、已收集的资料链接、下一步计划写在一个状态文件中。任务完成后这个文件可以被归档或清理。在多智能体协作场景中这个目录下的文件可以成为简单的“黑板”系统一个智能体将产出写入文件另一个智能体读取后继续处理。2.2 通信协议基于文件系统的“慢同步”你可能会问多个智能体之间如何协作在这个架构里没有消息队列也没有RPC调用。协作协议简单到令人惊讶文件即接口读写即通信。假设有智能体A分析员和智能体B报告员。A完成数据分析后会将结果摘要写入state/analysis_result.md。B被调度执行时它的第一件事就是扫描state/目录寻找与自己相关的输入文件比如通过文件名约定读取analysis_result.md的内容然后开始撰写报告。如果需要更复杂的协调可以引入一个简单的“协调员”智能体它的职责就是轮询状态目录根据文件内容决定下一步调度哪个智能体。注意这种基于文件的通信是异步且“慢”的它依赖于调度如cron或轮询。这听起来很原始但却带来了巨大的好处解耦和可调试性。每个智能体的输入和输出都是磁盘上一个实实在在的文本文件你可以在任何时候用任何文本编辑器查看精确知道协作在哪个环节卡住了数据是什么样子。没有需要解码的网络包没有晦涩的序列化格式。2.3 搜索从grep到SQLite FTS5没有向量数据库如何从数百个Markdown文件中找到需要的信息答案是多级搜索策略。初级文件系统操作与grep。对于小规模数据比如几十个文件直接使用grep -r “关键词” memory/就足够了。结合find命令按日期过滤文件可以快速定位近期相关记录。中级SQLite与FTS5。当文件数量增长到几百上千纯grep可能变慢。此时引入SQLite并启用其全文搜索扩展FTS5是一个“轻量级跃迁”。你可以写一个简单的脚本定期将Markdown文件中的内容索引到SQLite的FTS5虚拟表中。SQLite是一个单文件数据库无需服务端但提供了强大的关联查询和全文搜索能力性能远超遍历文件。这是kuro_agent等人采用的方法它在不引入大型外部依赖的前提下有效提升了搜索效率。高级按需引入语义搜索。只有当明确发现关键词搜索无法满足需求时例如用户查询“如何优化页面加载速度”但日志中只有“懒加载图片”、“减少HTTP请求”等具体技术描述才需要考虑嵌入模型和向量搜索。即使到这一步也可以从简单的句子嵌入模型如Sentence-BERT开始将向量存储在SQLite的BLOB字段中自制一个轻量级向量检索。核心原则是不为尚未出现的性能问题预付复杂度成本。3. 收敛背后的逻辑朴素工具的压倒性优势为什么素不相识的开发者们会走向同一条路这不是巧合而是因为这套架构击中了一些在工程实践中至关重要的属性这些属性往往被追求“技术先进性”的潮流所忽视。3.1 可审视性一切皆透明这是纯文本栈最核心的优势。你的智能体的全部状态、记忆和身份都是人类可读的文本。cat memory/MEMORY.md你就能立刻知道它记住了什么。tail -f memory/2023-10-27.md你可以实时观察它的思考过程。用git diff可以清晰地看到记忆随时间发生了哪些演变。当系统出现怪异行为时调试过程不再是黑盒猜谜。你可以直接检查它当时“脑”中的内容结合身份文件中的原则往往能立刻定位问题“哦原来它在MEMORY.md里错误地记录了一个矛盾的事实”或者“它的SOUL.md里关于这个场景的约束条件写得太模糊了”。这种透明性对于构建可靠、可信的AI系统至关重要因为它将控制权和理解权完全交还给了开发者。3.2 零抽象税与零运维负担eaglelucid的评论一针见血“零抽象税”。每一个你引入的抽象层——无论是ORM框架、消息中间件、还是向量数据库服务——都在向你征收“抽象税”。这份税单包括学习成本、依赖升级的维护成本、调试时的间接层、以及当抽象漏洞出现时它总会出现的那令人绝望的复杂性。一个文本文件就是操作系统提供的最原始、最稳定的抽象。它的API打开、读取、写入、关闭几十年不变。它不需要客户端驱动没有兼容性问题不会被云服务商涨价或关闭。kuro_agent能在2014年的MacBook Pro上24/7运行这套系统就是零运维负担的最佳证明。系统的复杂性没有消耗在基础设施的维护上而是全部聚焦于核心的智能逻辑本身。3.3 操作简单性的复利效应简单性带来的好处是复合增长的。启动一个新智能体复制一个文件夹改改SOUL.md就行。备份整个系统就是一个文件夹直接rsync或扔进Git仓库。迁移到新服务器SCP过去即可。版本控制直接用Git管理整个目录每一次记忆更新、身份调整都有完整的历史记录。这种简单性降低了实验和迭代的门槛。你可以快速尝试一个新的身份设定观察智能体行为的变化可以手动编辑记忆文件来纠正错误可以轻易地将整个系统打包分享给同事。apex_stack能协调10多个定时智能体正是因为每个智能体都是一个独立的、自包含的文件夹通过文件系统这个“万能胶水”松散耦合而不是被一个重型编排框架紧紧绑死。4. 实战构建从零搭建你的纯文本智能体理论说再多不如动手做。下面我将一步步拆解如何构建一个基础的纯文本智能体这里以一个“个人工作日志分析与建议助手”为例。4.1 第一步定义身份与技能首先创建项目根目录work_assistant/并在其下建立identity/文件夹创建SKILL.md文件。# 工作日志助手技能定义 ## 核心身份 我是用户的个人工作效率分析助手。我通过分析用户的每日工作日志帮助用户识别模式、发现问题、提升效率。 ## 核心价值观 1. **务实导向**提供的建议必须具体、可操作避免空泛的鼓励。 2. **积极建设性**指出问题时必须附带至少一个改进思路或解决方案。 3. **尊重隐私**所有分析仅基于用户提供的日志文本不猜测、不臆断用户未记录的事项。 ## 行为模式 1. **输入**我每天会读取 memory/ 目录下最新的工作日志文件如 memory/2023-10-27.md。 2. **分析**我会从以下几个维度分析日志 * **任务切换频率**计算提到不同任务或主题的段落数量。 * **阻塞时间**识别如“等待”、“受阻”、“卡住”等关键词及其上下文。 * **心流时段**寻找“顺利”、“深入”、“完成”等积极描述集中的时间段。 * **重复性事务**找出每天或每周都出现的类似任务。 3. **输出**我将生成一份简明的分析报告写入 state/daily_analysis.md。报告结构包括 * 今日概览积极点与潜在问题。 * 具体数据观察如任务切换次数。 * 1-2条最优先的改进建议。 * 1个可尝试的小实验如下午试行“专注块”。 4. **沟通风格**使用简洁的要点列表和友好的口吻像一位经验丰富的同事在分享观察。这个文件定义了智能体的“人格”和“工作流程”。它足够具体能指导AI的行为又全是明文你可以随时调整。4.2 第二步设计记忆系统创建memory/目录。用户或另一个自动化流程需要每天在此创建一个以日期命名的Markdown文件记录当天工作。例如memory/2023-10-27.md# 2023-10-27 工作日志 ## 上午 (9:00-12:00) * 9:00-9:30查看邮件回复了3封紧急邮件。 * 9:30-11:00开始编写项目X的API设计文档。中途被Slack上关于项目Y的讨论打断两次每次大约10分钟。 * 11:00-12:00继续写文档感觉思路比较连贯完成了核心部分。 ## 中午 (12:00-13:00) * 午餐散步20分钟。 ## 下午 (13:00-18:00) * 13:00-14:30参加项目Y的评审会。 * 14:30-15:30尝试修复一个棘手的Bug搜索了各种方案仍未解决有点卡住。 * 15:30-16:00喝了杯咖啡和同事简短讨论了另一个问题。 * 16:00-17:30换了个思路终于解决了那个Bug并提交了代码。 * 17:30-18:00写日报规划明天任务。同时我们还需要一个memory/MEMORY.md文件用于存储从每日日志中提炼的长期观察。初始可以是空的或者由你手动放入一些基础认知。4.3 第三步实现智能体逻辑现在我们需要一个脚本比如run_assistant.py来扮演这个智能体。这个脚本会读取身份、读取最新日志、调用大语言模型API进行分析、并输出结果。#!/usr/bin/env python3 import os import glob from datetime import datetime import openai # 或其他LLM API客户端 # 1. 读取身份定义 with open(identity/SKILL.md, r) as f: system_prompt f.read() # 2. 找到最新的日志文件 log_files glob.glob(memory/*.md) # 排除 MEMORY.md 本身 log_files [f for f in log_files if not f.endswith(MEMORY.md)] if not log_files: print(未找到日志文件。) exit() latest_log max(log_files, keyos.path.getctime) with open(latest_log, r) as f: today_log f.read() # 3. 构建给LLM的提示词 user_prompt f 请根据我的身份定义和行为模式分析以下今日工作日志 {today_log} 请生成分析报告直接写入state/daily_analysis.md文件。 # 4. 调用LLM (示例使用OpenAI格式) client openai.OpenAI(api_keyos.environ.get(OPENAI_API_KEY)) response client.chat.completions.create( modelgpt-4, # 或 gpt-3.5-turbo messages[ {role: system, content: system_prompt}, {role: user, content: user_prompt} ], temperature0.7, ) analysis response.choices[0].message.content # 5. 将结果写入状态文件 os.makedirs(state, exist_okTrue) with open(state/daily_analysis.md, w) as f: f.write(analysis) print(f分析完成结果已写入 state/daily_analysis.md) print(---) print(analysis[:500]) # 预览前500字符这个脚本就是一个最简单的智能体“大脑”。它没有状态每次运行都从文件中重新加载身份和记忆执行任务然后退出。你可以用系统的cron或计划任务让它每天下午6点自动运行。4.4 第四步引入记忆提炼与搜索上面的助手只分析单日日志。为了让它更有“智慧”我们需要让它能参考长期记忆MEMORY.md并定期提炼新知识进去。我们可以创建另一个脚本refine_memory.py每周运行一次#!/usr/bin/env python3 import glob import openai # 1. 读取现有的长期记忆 try: with open(memory/MEMORY.md, r) as f: existing_memory f.read() except FileNotFoundError: existing_memory # 2. 获取过去一周的日志 log_files sorted(glob.glob(memory/2023-10-*.md))[-7:] # 假设过去一周 weekly_logs for lf in log_files: with open(lf, r) as f: weekly_logs f\n--- {lf} ---\n f.read() # 3. 提示LLM提炼新知识并去重、整合到旧记忆中 system_prompt 你是一个记忆提炼专家。请仔细阅读过去一周的工作日志然后 1. 识别出新的、重要的模式、习惯、发现或教训例如“用户每周三下午会议较多效率较低”。 2. 将这些新知识与现有的长期记忆由[现有记忆]标记进行比较。 3. 输出一份更新后的、整合的长期记忆文档。保留旧记忆中有价值的部分合并重复项加入新提炼的知识。 请直接输出完整的更新后记忆文档内容。 user_prompt f [现有记忆开始] {existing_memory} [现有记忆结束] [过去一周日志开始] {weekly_logs} [过去一周日志结束] # ... 调用LLM获取新的memory_content ... # new_memory_content llm_call(system_prompt, user_prompt) # 4. 写回MEMORY.md with open(memory/MEMORY.md, w) as f: f.write(new_memory_content) print(长期记忆已更新。)现在你的助手在分析每日日志时可以同时读取MEMORY.md的内容作为上下文从而给出更贴合你长期工作模式的分析。对于搜索你可以创建一个简单的命令行工具search_memory.py使用grep或集成SQLite FTS5#!/usr/bin/env python3 import sys import subprocess query sys.argv[1] if len(sys.argv) 1 else if not query: print(请输入搜索词。) exit(1) # 方法1: 简单grep print(f在 memory/ 目录中搜索 {query}...) result subprocess.run([grep, -r, -i, -n, --colorauto, query, memory/], capture_outputTrue, textTrue) print(result.stdout) # 方法2: 如果集成了SQLite FTS5这里可以替换为SQL查询 # conn sqlite3.connect(memory_index.db) # cursor conn.execute(SELECT snippet(memory_fts, 1, b, /b, ..., 5) FROM memory_fts WHERE content MATCH ? ORDER BY rank, (query,)) # ...5. 边界、挑战与进阶思考没有任何架构是银弹。纯文本栈在带来巨大简洁性的同时也清晰地定义了它的能力边界。来自itskondrat的评论切中了要害“纯文本方案在需要对5000文档进行语义搜索之前都工作良好。真正的痛点不是数据库而是嵌入管道。你什么时候会碰到这堵墙”5.1 可扩展性边界与演进路径我目前管理的语料库在500个文档以下尚未触及这堵墙。但我们必须诚实这不是因为我解决了问题而是因为我还没成长到需要面对它。纯文本栈的扩展性并非无限。它的演进路径应该是渐进的纯文件grep阶段1000文件完全够用速度可以接受。SQLite FTS5索引阶段1000~10,000文件当grep变慢时引入一个后台进程将文本内容索引到SQLite的FTS5表中。查询速度会有数量级提升同时依然保持单文件、零外部服务的简洁性。这是大多数独立开发者或小团队项目的“甜蜜点”。按需引入轻量级向量搜索10,000文件或需要语义搜索只有当明确发现关键词匹配无法满足需求时例如“找一下关于优化用户体验的所有讨论”而日志里用的是“提升易用性”、“减少用户挫败感”等不同表述才需要考虑嵌入模型。即使这时也可以从本地运行的轻量级句子嵌入模型开始将向量存储在SQLite的BLOB字段中实现一个简单的KNN搜索。核心原则是不为尚未出现的痛点预付复杂的解决方案成本。5.2 并发与一致性问题多智能体同时读写文件怎么办这是一个经典的并发问题。解决方案取决于你对一致性的要求低要求最终一致依赖文件系统的原子写入操作如写入临时文件再重命名os.replace。偶尔的读写冲突可以接受或通过简单的文件锁fcntl或portalocker避免。高要求强一致将状态管理升级到SQLite数据库。即使这样SQLite依然是一个单文件、无服务的“重型文本文件”它提供了事务支持复杂度提升可控。你可以将核心的、需要强一致的状态如任务分配锁、流水号放在SQLite里而将大量的日志、记忆等仍放在纯文本文件中。5.3 记忆的“质”与“量”的权衡开发者mikeadolan提出了一个有趣的对比模型他通过六个钩子将每一次对话无损地记录到本地SQLite数据库从不手动记录任何东西。这与我的“高 curation策展、高 friction摩擦”模型形成了鲜明对比。我的模型策展式我或智能体决定什么值得记下来。这保证了长期记忆MEMORY.md的质量和相关性但可能会遗漏一些当时看似不重要、事后却有价值的背景信息。它依赖于良好的“判断力”。他的模型捕获式记录一切依赖强大的搜索来事后筛选。这保证了信息的完备性但会导致数据库急速膨胀并且搜索的负担更重。哪种更好答案取决于你的查询模式。如果你通常知道自己要找什么“我上个月关于项目X的会议结论是什么”策展式记忆配合关键词搜索效率更高。如果你需要重新发现那些你忘记自己知道的事情“我们以前有没有讨论过用某种方法解决这个错误”那么完备的捕获加上优秀的语义搜索可能更有价值。在实践中一个混合模式或许更佳无损捕获原始日志但定期由智能体或人工进行策展提炼出精华写入MEMORY.md。5.4 安全与隐私考量所有数据都以明文形式存储在本地这既是优势透明也是风险。必须考虑磁盘加密确保存储这些文件的磁盘或目录使用了全盘加密如macOS的FileVault或加密卷。敏感信息处理在日志中避免直接记录密码、密钥、个人身份信息等。可以考虑在记录前让智能体自动对敏感字段进行脱敏处理如替换为[REDACTED]或哈希值。访问控制如果系统部署在共享环境需设置好文件系统的权限确保只有授权进程和用户能访问这些目录。6. 从模式到哲学重新思考智能体基础设施这八位开发者的不约而同不仅仅是一个技术模式更像是一种正在浮现的工程哲学。它挑战了当前AI基础设施领域的一种主流叙事复杂性等于先进性。业界的声音常常暗示真正的、生产级的智能体系统必须建立在向量数据库、微服务编排框架、流处理管道等一系列复杂基础设施之上。然而这些来自一线的“战地报告”告诉我们那些真正在交付和运行生产系统的人用的却是文本文件、cron作业和SQLite。这并不是因为他们技术落后。恰恰相反这是因为他们深刻理解到智能体系统中的真正难题在于“判断”judgment——让AI在复杂情境下做出合理决策、管理自己的记忆、理解自身的身份和边界。而解决“判断”问题需要的是极致的上下文透明度和可调试性。每一个抽象层都在你和AI的“思考过程”之间增加了一层毛玻璃。当系统行为诡异时你最终需要穿透所有这些抽象去查看最原始的数据。既然如此为什么不从一开始就待在清晰可见的底层呢“零抽象税”意味着你可以将所有的工程精力投入到提升智能体本身的“智力”上而不是在基础设施的迷宫里打转。文件夹里的Markdown文件就是智能体可读、你可读、版本控制可跟踪的“思维白板”。这种 simplicity简单性不是简陋而是一种经过深思熟虑的、赋能性的简洁。它降低了启动项目的心理门槛和运维负担让开发者能快速验证想法、迭代模型并最终让系统能够持续运行下去——就像那台2014年的MacBook Pro一样稳定而可靠。所以如果你正在考虑构建自己的AI智能体无论是用于个人生产力、创意辅助还是业务流程自动化不妨先从这个“纯文本栈”开始。从一个SOUL.md文件和一个memory/文件夹起步。感受一下这种完全掌控、一目了然的体验。当你的需求增长当纯文本的边界真正被触及时你自然会知道该往哪个方向增加复杂度。而那时你增加复杂度的决策将基于真实遇到的具体痛点而非对“行业标准”的模糊追逐。这或许才是构建可持续、可理解、真正有用的AI系统的最短路径。