AI架构师自述:agent本身工作正常,但输出全存在聊天记录里——无结构、无视图、无查询接口,第三周就开始浪费时间翻历史消息找回想。
Agent 工作了。但架构不行。
这种失败模式不会在你的 eval 套件中暴露。
一位 AI 架构师为个人自动化流水线构建了一个 agent:agent 侦察趋势内容、把创意导入聊天线程、他做出反应、事情被记录下来。Agent 完成了它的本职工作——找到了趋势、写了下来、当他询问时做出了回应。
但他还是把整个系统 kill 了,而且不是因为幻觉或 token 成本。他 kill 掉它是因为所有东西都存在于"一条扁平的消息滚动里。没有结构、没有视图、无法区分脚本内容和调度内容。"他花了整整一小时的创作窗口在 Telegram 线程里往回滚动,寻找前一天写下的东西。
模型不是问题所在。底层架构才是,而如今几乎所有在做 agent 的人都面临同样的问题——因为默认的 agent UI 是聊天窗口,默认的系统记录是 transcript。
如果你的 agent 输出落在对话里,你构建的就是一个没有 schema、没有查询接口的仅追加日志。检索是 O(n) 的,且需要人眼扫描。在 demo 中这没问题,但在第三周左右就会崩溃。
把聊天日志分解到它的数据属性层面,问题就显而易见了:
CHAT TRANSCRIPT AS DATABASE
---------------------------
Schema none (freeform text)
Primary key timestamp (not semantic)
Indexes none
Query interface Ctrl+F, human eyes
Mutation impossible (append-only, no UPDATE)
Aggregation none
Views/filters none
State transitions untracked
关键的那一行是 Mutation:impossible。真实的工作有状态变化:创意变成草稿、草稿被排期、排期的项目发布。在聊天日志里这些转换都不作为数据存在。它们作为后续消息与早期消息的矛盾而存在,读者必须通过重放整个线程来重建当前真相。
这是没有投影的事件溯源。你得到了事件流,却没有物化视图。
这还会不断累积。每个 session 都从上下文重建开始:我决定了什么?我在哪里停下的?这四十条消息里哪些还是活跃的?人类每次都要付出这个成本,而且随着线程长度增长,成本也在增长,而线程的价值却保持平坦。
当你发现自己用 agent 去搜索 agent 自己的输出时,你就知道问题存在了。
流水线的可行版本把关注点分离到三个数据平面,每个平面有不同的写入模式和不同的 owner。
┌──────────────────────────────────────────┐
│ PLANE 1: IDEA / INTAKE │
│ Google Sheet · append-only │
│ Writer: agent (scheduled, unattended) │
│ Volume: high · Precision: low │
│ Never edited in place │
└───────────────────┬──────────────────────┘
│ human selects (the gate)
▼
┌──────────────────────────────────────────┐
│ PLANE 2: STATE / PIPELINE │
│ Notion DB · typed records │
│ Writer: agent on human instruction │
│ Volume: low · Precision: high │
│ status ∈ {Idea, Scripting, Filming, │
│ Scheduled, Published} │
└───────────────────┬──────────────────────┘
│ references by ID
▼
┌──────────────────────────────────────────┐
│ PLANE 3: ARTIFACT │
│ Google Docs · Drive · rendered media │
│ Writer: human (scripts) + agent (renders)│
│ Addressed by link from Plane 2 │
└──────────────────────────────────────────┘
每个平面都有一个其他两个不擅长的职责。
平面 1 优化的是召回率而非精确度。定时扫描每天写入 20+ 条创意,涵盖 TikTok、Reels、X 和 YouTube,每行包含趋势、一个 hook、角度、标题文案、话题标签、格式备注和建议发布时间。大部分行是垃圾,这没关系。这是一个 intake buffer,规则是永远不在原地做筛选。
这条规则有其价值。Agent 写入是非确定性的,所以如果你的 agent 在它写入的同一张表里修改行,你就会失去区分"agent 改变了主意"、"我改变了主意"、"agent 静默丢弃了某些东西"的能力。仅追加的 intake 免费给你提供了审计跟踪,而且它使得人工筛选成为从噪声到信号的唯一路径。
平面 2 优化的是可查询状态:每单位工作一条类型化记录,带一个状态枚举。一眼就能回答什么是正在脚本化的、什么是等待拍摄的、什么即将发布——这是聊天线程永远无法回答的问题。
平面 3 承载着有效载荷。文档和媒体通过 ID 从平面 2 引用,绝不内联到平面 2。这是普通的规范化,而 agent 系统经常破坏它——把整个草稿粘贴到聊天消息里。
状态机本身简单到可以直接复制:
Idea ──► Scripting ──► Filming ──► Scheduled ──► Published
│ │ │
└───────────┴─────────────┴──► Dropped
五个状态和一个逃生出口。这就是整个编排模型,人类和 agent 都能读取,这很关键:一个看到 status = "Scripting" 的 agent 可以直接对其采取行动。一个阅读聊天线程的 agent 必须从行文推断状态,而推断正是可靠性开始滑落的地方。
这篇文章中的第二个架构思路是把大多数人会混为一个"agent"的东西干净地拆分成三部分:
如果你做过数据基础设施,这是 Airflow 的 DAG-vs-worker-vs-scheduler 分离来到了 agent 世界,而它不断被独立重新发现这一事实本身就是一个不错的信号,说明它是正确的。
实际结果是每个维度独立变化。同一个 workflow,不同的 runner(dev vs prod 凭证)。同一个 runner,不同的 schedule(每日扫描 vs 按需)。同一个 schedule,换掉 workflow(从 v1 到 v2 的编辑流水线)而不触及触发器或身份。
单体替代方案是一个聊天 agent,其中 how、who 和 when 纠缠在一个 prompt 里,你每个 session 都要重新输入。你无法对它做版本控制,更不能把它交给别人。
ENTANGLED (chat agent)
"hey can you scan trends and put them in my sheet like
you did last time, you know the format" ──► ???
DECOMPOSED
Workflow: trend_scan.v3 (deterministic playbook)
Runner: content_bot (Drive+Notion scopes, memory: voice/topics)
Schedule: 0 6 * * * (daily 06:00)
分解后的版本是一个配置产物。你可以 diff 它、审查它、回滚它。
这是寿命最长的一个细节。视频编辑逻辑(裁剪到竖屏 1080×1920、词同步字幕、动态图形、品牌 logo、手机可辨缩略图)是一个包裹着 Remotion 的 Claude Skill。当流水线迁移到不同的编排器时,那套逻辑不是从零重建的。它被移植到一个保存的 Workflow 中并由 Runner 调用。
价值从来不在 runtime 里。
而是在编码的决策规则里:字幕风格、安全区域、字体大小、品牌颜色、渲染步骤。这些花功夫才调对,重新发现的代价会很昂贵。围绕它们的执行环境是大宗商品基础设施,大约每十八个月就会更迭一次。
┌─────────────────────────────────────┐
│ DURABLE (you author this once) │
│ • decision rules & checklists │
│ • constraints, safe zones, brand │
│ • step ordering & failure handling │
└──────────────┬──────────────────────┘
│ portable
┌──────────────▼──────────────────────┐
│ COMMODITY (swap every ~18 months) │
│ • agent runtime / orchestrator │
│ • model provider · tool bindings │
└─────────────────────────────────────┘
所以把 skills 写成指令产物而不是 runtime 代码。一个 skill 如果表达为包含明确步骤、约束和决策规则的 markdown playbook,就是一个你可以随身携带的文本文件。把它牢牢绑定到某个厂商的 SDK,你就预约了一次重写。
如果你已经有 Claude Skills,你就已经有了可移植资产。它们是耐久层,不是厂商锁定,每一个编排器(包括你今天正在用的那个)都是可替换的。
流水线有五个阶段。其中两个被特意不自动化:
1. Trend scan AGENT (scheduled, unattended)
2. Curation HUMAN → agent files the selection
3. Scripting HUMAN ← agent assists on request
4. Filming HUMAN (fully manual, by design)
5. Editing AGENT (workflow-invoked)
自动化位于两端——intake 和后期制作——而人类占据中间需要判断力的部分。保持脚本编写由人类完成的理由很犀利:"系统的目的不是把我从属于我的工作中移除。"
工程的框架是 Amdahl 定律。总时间等于关键路径加上串行开销。在创意或知识流水线中,关键路径是判断力,而且它是不可约的,因为你就是被生产出来的价值。串行开销是它周围的一切:注意到、记录、归档、链接、格式化、渲染。
加速开销,整个系统就变快。试图通过自动化判断力来加速关键路径,你得到的不是更快的产出,而是更多没人想要的东西。
文章明确命名了第二个约束,值得一个术语:摩擦预算。正如他所说:"任何不无缝的东西都不会被完成。如果一个创意需要我打开六个标签页、从一个地方复制内容到另一个地方、还记得我把草稿放在哪里,它就死了。"
把摩擦当作硬预算来对待。对于一个有一小时时间的兼职操作者,预算接近于零:六个标签页就超支了,无论 agent 有多好,流水线都会被放弃。这和那个准确但需要六次点击才能到达的内部工具输给一个在一个按钮背后的粗糙工具是同样的原因。采用遵循摩擦而非能力。大多数"失败"的 agent 项目通过了能力关卡却在摩擦预算上翻了车。
如果你正在搭建一个 agent 流水线,个人的或生产级的,移植是机械性的:
选择一个真正的系统记录:任何有 schema、有状态字段和有视图的东西。数据库、项目追踪器、甚至电子表格。不是聊天 transcript。
把 intake 和 state 分开。Agent 写入到一个高容量、仅追加的 intake 表。没有东西能不经过一道关卡就移动到 state 表——要么是人类审批,要么是确定性过滤器。永远不要让无人值守的 agent 就地修改你的真相来源。
把状态显式建模为一个简短的枚举,人类和 agent 都能读取。如果你的 agent 必须从行文推断状态,就加上那个字段。
把 how、who 和 when 分解为 workflow、runner 和 schedule,分别做版本控制。一个重新输入的 prompt 不是架构。
把 skills 写成可移植的指令产物。Playbook 用文本写,松散绑定到当前任何 runtime。假设 runtime 会更迭。
审计你的摩擦预算。计算从"我有一个创意"到"我正在做工作"之间的标签页和复制粘贴步骤。如果超过两个,流水线就会被放弃,而模型不会是原因。
聊天 transcript 是一个没有 schema、没有索引、没有 UPDATE 的仅追加日志,所以它无法作为有状态工作的系统记录,无论向它写入的 agent 有多好。
把创意平面、状态平面和产物平面分开。Intake 优化召回率并保持仅追加,state 优化可查询性并保持类型化,产物通过 ID 引用。
把 agent 拆分为 workflow(how)、runner(who)和 schedule(when),这样每个维度独立版本控制和回滚。Airflow 十年前就确立了同样的分离。
Skills 是耐久层,runtimes 是大宗商品。把决策规则编码为可移植的指令产物,这样编排器替换就是一次移植而不是重写。
自动化串行开销,而不是关键路径。判断力是 HUMAN 是产品的地方。注意到、归档和渲染是时间流失的地方。
采用遵循摩擦多于能力。超过用户摩擦预算的流水线会死,不管模型质量如何。
每一个主要的 agent 产品都在向同一个界面收敛:一个聊天框。但一旦 agent 的工作有了超越 session 的状态,聊天框就不再是 UI 而是一个糟糕的数据库。
那么它是哪一个?聊天是我们回头看待纯命令行数据库的方式——一种过渡性界面,agent 最终会推出真正的结构化前端?还是 transcript 其实没问题,解决方案只是在线程上嫁接更好的记忆和检索?
如果你 kill 过一个技术上能工作的 agent,是什么让你停止使用它的?我赌更多这类故事讲的是状态和摩擦,而不是模型。