Sozai Agent-Native · Design Review

素材工房「真 Agent 化」设计方案

把快速检索和反馈系统从"写死的流程 + LLM 打分"改造成两个真正会自己思考、能跟你对话的 Agent。这一页是完整设计稿,每一项都等你逐条确认后才开工。

2026-07-11 · 分支 feat/agent-native(基于 proto/st-guidance-2)· 现状核对:Codex 全量代码走查已完成

2
个 Agent(管家 + 检索工人)
2
张新表(聊天 + 行为事件)
5
个触发入口,全部收拢
5
期路线,每期可独立验收

一、现在的问题:为什么要改

现在两条路上 LLM 都只是"被动打分器",怎么找、怎么改,全是代码写死的:

哪里现在的样子问题
fast-search-fetch.ts 写死"搜一页 → LLM 挑一批 → 不够翻页,最多 5 轮"。LLM 只回答"这批留哪几条"。 不会换关键词、不会判断"这个平台没货该停了"、找不好也说不出为什么。
cron.ts 消化引擎 把所有待处理反馈拼成一个超大 prompt,一锤子输出 JSON。 反馈指向的素材/方案只有标题进 prompt——AI 看不到你点踩的东西长什么样,只能靠标题猜。
反馈页 单向提交,交完等后台慢慢消化。 没有对话。你说了话,Agent 不回应、不追问、不告诉你它打算怎么改。

另有两处 Codex 走查发现的暗伤,这次一并修:① 快搜/竞品的运行记录没盖当时的基因版本号(guidance_version 为空),"这批素材是哪一版检索文档找来的"答不上来;② 手动触发的快搜整个跑在 HTTP 请求结束后的 waitUntil 里,平台随时可能掐掉(7/10 消化引擎就是这么死的,§23.42)。

二、总架构:一个脑子 + 一个工人

WEBSOCKET 流式 TOOL-LOOP 下单入队 只 INSERT TICK 认领 USER ×N 浏览器标签页 多开 · 多人 DURABLE OBJECT 管家 Agent 每个业务 Agent 一实例 LLM GATEWAY ZenMux claude-sonnet-5 HTTP 触发入口 创建/按钮/每日 POSTGRES QUEUE agent_runs 队列 status=queued CRON TICK · 15MIN 检索执行 Agent 每轮转录写库 EXTERNAL 外部搜索源 TikHub · 竞品桥 NEON · 唯一事实源 Postgres 基因 · 聊天 · 反馈 · 行为 LEGEND ── 墨蓝=本次新建的两个 Agent · 灰=复用现有部件 · 深搜仍归 tobatsu,不在图内

要点在右下那条空隙:管家(实时、有人在)和检索工人(后台、慢慢跑)从不直接调用对方——中间永远隔一张 Postgres 队列表。这样任何一边挂了都不拖累另一边,而且两边的进展都落库,谁都能查。

为什么是"两个"而不是一个或三个

三、管家 Agent:完整规格

3.1 两种上班方式

对话模式批量模式
在哪跑DO 实例内,你发消息当场跑分钟 tick 认领队列后跑(现有 F5 通道改入队)
触发你在反馈页发一句话待消化反馈满 3 条,或最老一条超 3 天
响应流式,逐字回,点了就有反应无人值守,跑完在版本时间线留一条
改基因出 diff 卡片,你点确认才写库自主写入(乐观锁保护,和现在一致)
反馈记录聊天中识别出的反馈照样落反馈表并盖"已消化"回执消化后给每条反馈盖回执(现逻辑复用)

3.2 工具清单(逐个列)

工具做什么代码层硬约束(模型绕不过)
read_gene()读当前检索文档 + 创意规则 + 最近 5 个版本的变更摘要只读
read_feedback()读待消化反馈 + 上次消化以来的点赞点踩只读,上限 200 条(现有 SQL 原样复用)
view_object(type, id)新能力:读反馈指向对象的"认知层"——各源已有的视频理解字段归一化(见下方 3.5)只读;只能看本 Agent 名下的对象(product_key 校验)
read_performance()读行为统计:这一版基因找回的素材被采用了多少、点踩集中在哪只读(四期上线,之前返回"暂无数据")
propose_update({...})提议修改检索文档和/或创意规则,附一句话理由对话模式:只生成卡片,写库必须等人点确认;批量模式:直接走现有事务(乐观锁 + 版本铸造 + 回执,一字不改);新旧文本全等时自动降级为"本轮不改"
order_search(brief)应你要求下单一次快速检索("再帮我找一批更新的")只 INSERT 一条 queued run,不直接执行;每对话每分钟限 1 次
no_update(reason)判断本轮不需要改,给反馈盖回执批量模式专用
analyze_video(id)兜底:存量/漏网素材缺理解时当场看(聊天显示"我去看一下这条视频…")对话每轮 ≤2 次、批量每 run ≤5 次;触发即打日志

3.3 行为准则(写进 system prompt 的"宪法",逐条列)

这些全部是 PM 已拍板的原则,从现在散落的 prompt 里升格为一份独立的版本化文件:

3.5 view_object 的认知层:meta 从哪来(查库实证)

反馈引用的是视频,但管家"看"对象不需要现场跑视频理解——各源的理解产物早就落库了(2026-07-11 查共享测试库真实数据确认):

对象已有认知字段(真实 meta)产出方
热点素材analysis_excerpt · construction_beats(拆解节拍)· frames/frame_urls(抽帧)· description_md · review_scoretobatsu 深搜
竞品素材visual_summary(覆盖 82%,136/166)· content 基本只是广告标题 · impression/ads_days/region(投放数据)桥(18% 裸奔,走补齐)
方案summary · hook_plan · blocks(故事板)· refs构造自带
成片标题 + 关联方案故事板 + 创作载荷构造自带
快搜素材只有 author/plays/likes/platform——唯一缺口

3.6 对话里改基因的确认流(一步一步)

  1. 你说:"这批太老了,我只要最近一周的爆款。"
  2. 管家调 read_gene() 看当前文档,调 view_object() 翻你最近点踩的几条,确认问题属实。
  3. 管家调 propose_update() → 聊天里出一张 diff 卡片:左边旧文本、右边新文本、变化高亮,附一句理由。
  4. 卡片上两个按钮:应用 / 驳回。所有开着的标签页同时看到这张卡。
  5. 任何人点"应用"→ DO 串行执行写库(版本 +1,你的用户 ID 记进版本行)→ 所有标签页的卡片即刻变为"已应用 · V5";后点的人看到的是结果,不会重复写。点"驳回"→ 卡片作废,理由记进对话,管家会追问原因。

四、检索执行 Agent:完整规格

4.1 工具清单

工具做什么代码层硬约束
search_fast(platform, keyword, cursor?)TikHub 搜一页(tiktok / instagram / twitter),返回瘦身摘要:每条只有 140 字文案 + 播放/点赞数整个 run 最多 12 次调用;单次 10s 超时 + 1 次重试(现有)
search_competitor(query)竞品桥搜一轮(submit 后内部等结果,最多等 3 分钟)没等到就返回 task_id 让它先干别的,稍后用 check_competitor 回来取——不空转烧墙上时间
check_competitor(task_id)回头取竞品桥的结果只读;桥每轮 8 分钟超时沿用现有值
analyze_video(candidate_ids)前置分析(PM 拍板:选货之前先看货):对粗筛后的短名单并行调 mai 视频分析,带着真实内容理解做取舍每 run 分析数 ≤ 2× 目标 N;必须先粗筛出短名单才准调(防全量烧钱)
save_materials(ids, reason)把选中的候选写进素材库 + 选择理由 + 分析结果随行入 meta(入库即带认知,"回填"工序消失)去重、凑满即止(预算 ≤ 目标 N)、product_key 隔离、R2 转存——全在工具代码里,模型无权越界
finish(report)收工并写一段小结:找到多少、换过几次词、哪个方向没货强制收尾;不调它超预算时由框架代为收尾

4.2 它自己拿主意的事(现在做不到的)

4.3 硬预算(框架层,防跑飞)

闸门数值(首版)触发后
最大轮数40 轮 LLM 往返注入"预算将尽"消息,强制走 finish
累计 token200k(约 ¥3/run 上限)同上
墙上时间10 分钟/run框架收尾,已入库的素材保留
卡死兜底sweep:running 超 15 分钟按已写入的素材结算 done/failed(现有逻辑)

五、触发器全景(5 个入口,一个不漏)

入口触发什么路径和现在比的变化
① 创建 Agent 首搜检索 runPOST /api/crawl/slots → INSERT queued原来在 waitUntil 里整跑 ⚠ → 改为只入队
② 按钮"再找一批"检索 runPOST /api/runs → INSERT queued同上
③ 聊天里下单检索 run管家 order_search 工具 → INSERT queued新增
④ 每日自动(prod 北京 00:00)检索 rundaily planner → INSERT queued不变(本来就是入队)
⑤ 反馈攒够阈值批量消化 run反馈落库 → 满3条或超3天 → INSERT queued原来在 waitUntil 里直接跑 LLM ⚠ → 改为只入队

规矩一句话:HTTP 请求里的 waitUntil 只准做一次 INSERT,永远不准跑 LLM。所有重活由分钟 tick(scheduled handler,15 分钟墙上时间)认领后 await 整跑。对话模式例外——它在请求存续期间流式跑,客户端连着就没有时限,本来就不走 waitUntil。⑤ 带来的唯一代价:反馈提交到消化开始多 0–60 秒,后台事,无感。

六、运行时:为什么管家住进 Durable Object

6.1 多开网页这个场景,无状态方案兜不住

没有 DO 会怎样

  • 两个标签页同时发言 → 各自读旧上下文并发跑 → 对话串成一锅粥
  • A 页点了"应用",B 页还挂着同一张过期卡片,再点就重复写
  • 标签页之间互相看不见,全靠各自轮询

DO 给什么

  • 每个业务 Agent 一个实例(idFromName(agentId)),单线程排队处理,天然不打架
  • 所有标签页 WebSocket 连同一实例,谁说话、卡片状态怎么变,所有人同帧看到
  • WebSocket Hibernation:没人说话时实例休眠,连接保持,基本不花钱

6.2 责任划分(谁存什么)

东西存哪为什么
聊天记录、反馈、基因、版本、行为事件Postgres(Neon)唯一事实源。DO 实例被平台迁移/重启,什么都不丢
热对话上下文、diff 卡片的待确认状态、在线标签页名单DO 内存 + storage串行化和广播用的热态,可随时从库里重建
检索 run 的排队和转录agent_runs(jsonb)检索工人不进 DO;DO 只是轮询转录把进度贴进聊天

6.3 连接协议

6.4 为什么是 WebSocket 而不是 SSE(定论)

对比点WebSocketSSE
DO 休眠计费(决定性)Hibernation API 只支持 WS:页面整天开着,DO 睡着、连接保持、基本不计费SSE 长连把 DO 钉在内存里持续计费——每个开着页的 Agent 实例整天活着
双向一条通道收发发言另走 POST,自己的话从流里回来还要去重对时序,两通道全是边角 bug
多标签广播原生:遍历 sockets 推能做,但叠上第一条已无意义

SSE 唯一优势是纯 HTTP 好穿企业代理;内部工具走 443 的 wss 不存在此问题。

6.5 各种"多个 session",分四层各有答案

哪种"多"怎么办
同一人开 N 个标签页全连同一 DO,每个 socket 只是订阅者,广播同帧刷新(单实例连接上限 3 万+,随便开)
多个人同时在线同一实例;WS 握手时从 mb_auth cookie 取 user_id 打在 socket 上——发言带署名、头部在线名单。两人同时发言 → mailbox 排队,后一条回答时已看得到前一条往返
同时开多个不同 Agent 的页各连各的 DO 实例(idFromName 按 agentId 分),天然隔离
对话历史越攒越长首版不设 session 概念:每 Agent 一条永续线程。每轮只装载"宪法 + 当前基因 + 最近 N 条",老历史留库里滚动懒加载。将来要分段,chat 表加一列 conversation_id 即可

附带定一个细节:管家正在回答时又进来几条消息,不逐条跑 LLM——下一轮合并一起读,省钱且回答更连贯。

6.6 引入 DO 的代价(如实列)

七、数据结构:每张表逐项过

7.1 新表 ① agent_chat_messages(聊天记录)

CREATE TABLE agent_chat_messages (
  id            uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  agent_id      uuid NOT NULL,            -- crawl_slots.id,一个 Agent 一条共享线程
  role          text NOT NULL CHECK (role IN ('user','assistant','system')),
  user_id       uuid,                      -- role=user 时记谁说的(团队共聊,区分发言人)
  content       jsonb NOT NULL,           -- 文本块 / 工具调用摘要块 / 卡片引用块
  gene_card_id  uuid,                      -- 这条消息挂的 diff 卡片(如有)
  created_at    timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX idx_chat_agent_time ON agent_chat_messages (agent_id, created_at DESC);

7.2 新表 ② agent_gene_cards(基因修改卡片,确认制的载体)

CREATE TABLE agent_gene_cards (
  id              uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  agent_id        uuid NOT NULL,
  proposed_search text,                    -- 提议的新检索文档(不改则 NULL)
  proposed_steer  text,                    -- 提议的新创意规则(不改则 NULL)
  base_version    int  NOT NULL,          -- 基于哪一版提的(应用时乐观锁校验)
  reason          text NOT NULL,          -- 管家的一句话理由
  status          text NOT NULL DEFAULT 'pending',  -- pending/applied/rejected/expired
  resolved_by     uuid, resolved_at timestamptz,
  applied_version int,                     -- 应用后铸出的版本号
  created_at      timestamptz NOT NULL DEFAULT now()
);

7.3 新表 ③ agent_behavior_events(行为事件,四期)

CREATE TABLE agent_behavior_events (
  id            bigserial PRIMARY KEY,
  agent_id      uuid NOT NULL,
  user_id       uuid,
  event_type    text NOT NULL,   -- material_viewed / material_ignored / delivery_downloaded
  object_type   text, object_id uuid,
  source_run_id uuid,             -- 顺着它能查到当时的基因版本
  meta          jsonb DEFAULT '{}'::jsonb,
  created_at    timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX idx_behavior_agent_time ON agent_behavior_events (agent_id, created_at DESC);

只埋 viewed / ignored / downloaded 三个前端事件。"素材入方案""方案被派发"不用埋——从 creative_boards、deliveries 的现有引用关系就能算出来。

7.4 现有表的改动(只字段级,不动结构的红线)

改什么迁移?
agent_runs.inputs.agentjsonb 里加 transcript[]:每轮 {role, content, tool, tokens, at},每轮落盘一次不用(jsonb)
agent_runs.guidance_version快搜/竞品的 INSERT 补上这一列(列本来就有,是代码没写)——断了的血统接上不用(改代码)
agent_guidance_versions.origin新增来源值 'chat'(聊天确认制产生的版本,区别于 digest/manual/seed)看约束——若有 CHECK 需一条小迁移
agent_stage_feedback聊天识别出的反馈照常落行 + 盖回执,meta 里标 from_chat不用
产品表(hot_materials / creative_boards / deliveries)一个字段都不加——血统照旧走 source_run_id,遵守项目红线

八、上下文工程与缓存(PM 重点关注项,P1 施工图按此逐条验收)

8.1 上下文分两层,职责不同

存哪活多久
一次 run / 一轮对话的工作记忆内存跑,每轮快照进库(chat 表 / transcript)run 结束封存,只用于回放和审计
Agent 的长期记忆就是创意基因本身:检索文档 + 创意规则 + 版本史 + 行为事件Agent 一生

刻意的设计:run 和 run 之间不传对话——"上次学到的"必须沉淀成基因文档的修订才算数。基因是用户可见、可编辑、可回滚的,Agent 的记忆不藏在任何黑盒里。

8.2 Prompt 三层结构(缓存友好的不变前缀)

内容变化频率cache 断点
L0工具定义(按名排序序列化)+ 宪法随代码版本断点①
L1基因文档快照 + 本 run 任务参数每 run 开跑时快照,中途不刷新(新版本下个 run 生效——与乐观锁语义一致)断点②
L2对话/工具轮次每轮增长断点③滚动打在最新一条

铁律:前缀禁止时间戳、随机 ID、未排序 JSON——一个字节漂移,断点之后全部白给。agent-loop.ts 必须内建"断点装配器",且每轮把 cache_read / cache_creation / input / output 四个 usage 数记进转录——成本可审计是 P1 验收项。

8.3 缓存经济学(为什么这是成本命门)

8.4 新大户治理:视频拆解报告的分级注入

8.5 修剪与缓存的张力(折叠必须批量)

8.6 各存储职责边界(不越位)

存储干什么不干什么
Anthropic prompt cache轮间前缀复用(TTL 5min)不是持久层
KV SEARCH_CACHE幂等小调用结果(提词,1 天 TTL)不放对话/转录(最终一致,不做真源);不做并发锁
DO 内存/storage热对话上下文、卡片状态、频控偏好不做真源——随时可从 PG 重建
Postgres一切真源(聊天/转录/基因/分析全文/行为)

分析幂等不靠 KV:靠 meta 已有检查 + DB ON CONFLICT。对话模式每轮装配 = L0 + L1(当前基因)+ 最近 N 条聊天(按 ≈8k tok 预算倒推);老历史只在 UI 懒加载展示、不进上下文;基因被他人手动改掉时 DO 收到版本事件 → 下一轮 L1 刷新、接受一次冷写——正确性优先于缓存命中

九、UI 设计(PM 拍板:直接替代现有反馈中心的入口和位置)

不另开新页——管家对话页顶替当前反馈中心的入口和位置,细节 PM 后续迭代。旧反馈中心的内容去处:版本时间线 → 右栏常驻;历史反馈与版本推送 → 以时间线卡片出现在对话流开头(反馈本来就成为对话的一部分)。

🤖 中世纪解压素材 · Agent 基因 V4 · ● 2人在线 对话流(WEBSOCKET 实时广播) 这批素材太老了,我只要最近一周的爆款 ▸ 查看了基因 V4 · 翻看了你点踩的 3 条素材 确实,最近两批里 60% 是三个月前的老梗。我建议把检索 文档加一条时效约束,你看这样改行不行:▊ 基因修改提议 · 检索文档 · 基于 V4 − 旧 搜集解压向素材…… + 新 …仅限 7 天内发布的爆款 应用 驳回 ⟳ 检索中 · 第3轮 · 换词"oddly satisfying" · 已收 8/10 条 跟你的 Agent 说点什么…(回车发送,中文输入法防误触已内建) 基因面板(常驻右栏) 检索文档 · V4 搜集解压向、中世纪题材的 高互动短视频素材…… ✎ 手动编辑 创意规则 成片节奏快切、前3秒出爆点… 版本时间线(带效果标注) V4 · 消化 · 昨天 采用率 14% ↑(V3 是 6%) V3 · 手动 · 7/09 采用率 6% V2 · 消化 · 7/08 LEGEND ── 墨蓝卡片=需要你点头的基因修改提议 · 灰折叠条=Agent 的动作过程,点开可看细节

整页只有一处要你做决定:那张墨蓝卡片。其它一切(它翻了什么、搜到哪一轮)都是可看可不看的过程信息,默认折叠,不抢注意力。

逐组件说明

组件行为细节
流式回复气泡逐字出现;工具调用期间显示动词短语("正在翻你点踩的素材…")不留白屏
动作折叠条Agent 每次调工具收起为一行灰条,点开看参数和返回摘要——透明但不聒噪
基因 diff 卡片旧/新对照 + 理由 + 应用/驳回。应用后原位变为"已应用 · V5 · 由谁确认";其它标签页同帧更新;过期卡(底版本被超)自动置灰标"已过期"
检索进度条聊天下单后进度直播在对话流里(轮次/换词/已收条数),来源是转录轮询,完成后变成结果卡链去素材库
右栏基因面板检索文档 + 创意规则常驻可见、可手动编辑(走现有 F2 润色);版本时间线每版标采用率(四期数据接入后)
在线标记头部显示几人在线——既然是共享线程,谁在场应当可见
输入框沿用 7/10 修的 isComposing 防护,中文输入法组词回车不误发

十、行为日志:让 Agent 越用越准

没有行为记录,管家只能听你"说"了什么;有了它,管家还能看你"做"了什么——两边对照才知道怎么改。

事件怎么来信号强度
material_viewed前端埋点:点开素材详情弱正
material_ignored前端埋点:展示多次从未点开弱负
material_adopted不埋点,从"素材被选进方案"的现有引用推导强正——检索质量的真实成绩
plan_dispatched / revised从派发/修改记录推导方案质量
delivery_downloaded前端埋点:成片被下载最强正——最终被拿去用了

血统接通后的完整追溯(一条线捋到底)

检索文档 V4 ──> 当时的检索 run(guidance_version=4,本次补上)
        ──> 这批素材 ──> 采用了几条 / 点踩几条
        ──> V4 比 V3 是变好还是变差 ──> 写进 V5 版本行的效果快照
        ──> 管家 read_performance() 读到 ──> 下次消化拿证据说话

每次铸新版时把上一版的成绩快照进版本行——版本时间线自动变成"每次改动有没有用"的成绩单,你看得到,管家也读得到。

十一、技术选型定论(每项一句话理由)

选择定论理由
LLM 调用@anthropic-ai/sdk 官方 SDK纯 fetch 实现 Workers 原生跑,baseURL 指 ZenMux,白得重试 + 类型(现在的裸 fetch 连重试都没有)
Agent 框架不用 LangChain / LangGraph两个 Agent 各 4-7 个工具,循环本体 ~150 行;框架的编排能力用不上,bundle 和兼容坑倒是白背
结构化输出全走 tool call 参数schema 由 API 层校验,现在的"正则抠 JSON"整段删除
实时通道WebSocket(DO Hibernation)多标签广播是硬需求,SSE 做不了双向
后台执行面Postgres 队列 + 分钟 tick(现有模式)已被快搜/竞品验证;不引入 CF Queues/Workflows 新部件
模型分层沿用 env 配置判断活 anthropic/claude-sonnet-5,提词起名 gemini-3.5-flash,随时可换不改代码
thinkingtick 上下文里放开,对话模式看延迟实测§23.42 关 thinking 是为了挤 30 秒窗口;tick 里 15 分钟,约束消失
视频理解mai video-analysis-worker(同账号 binding)已实测 200/29s、输出即广告创意拆解格式;零 prompt 改造、零新 key

十二、动工前必须过的验证(第 0 步)

ZenMux 网关能力冒烟——4 项全绿才动工

  • [ ]tools 定义 + tool_use / tool_result 多轮往返是否透传
  • [ ]stream:true SSE 流式是否透传(对话模式的命脉)
  • [ ]流式 + 工具组合是否正常
  • [ ]cache_control 断点是否被认、usage.cache_read_input_tokens 是否非零(成本模型的前提)

执行方式:不需要本地 key——往 sozai-st 部一个临时冒烟路由(新 secret SMOKE_TOKEN 门禁,跑完即删),用环境自己的 ANTHROPIC_API_KEY 打 ZenMux。你手头有 key 直接给的话本地调试更快(可选)。ZenMux 此前在 thinking 字段上坑过(§23.37/23.42),兼容性只信实测。

其它风险,如实列

十三、改动清单:后端动哪些文件、前端动哪些

13.1 后端 · 新增文件

文件装什么谁写
src/lib/agent-loop.ts通用 tool-loop:SDK 调用、每轮转录落盘、双预算闸、滑动窗口修剪
src/lib/agents/prompts/*.ts全部 system prompt:管家宪法、检索工人指令、各工具描述文案。版本化、带注释说明每条规则来自哪个 PM 决定只能我
src/lib/agents/butler.ts管家工具实现(read_gene / read_feedback / view_object / propose_update / order_search / no_update)+ 对话/批量两个入口Codex(按施工图)
src/lib/agents/search-runner.ts检索工人工具实现(search_fast / save_materials / finish)+ 入队/认领接线Codex(按施工图)
src/do/agent-butler-do.tsDO class:WS Hibernation、消息排队、广播、diff 卡状态、断线补发骨架我,管道 Codex
src/routes/agent-chat.ts/api/agents/:id/ws 升级路由 + 聊天历史 REST + 埋点接收(P4)Codex
migrations/0035~0037_*.sqlchat 表、gene_cards 表、behavior_events 表 + origin 加 'chat'Codex 初稿,我审,你过目后才执行
scripts/e2e-agent-native-*.mjs四期各一套端到端脚本(见第十四节)用例我定,脚本 Codex 写,验收我跑

13.2 后端 · 改动的现有文件

文件改什么
src/lib/cron.tsF5 从"waitUntil 里跑 LLM"改为"阈值命中只 INSERT 队列";tick 加认领消化 run;flag 开时走管家批量模式,关时走旧大 prompt(旧代码保留)
src/routes/guidance.ts反馈 POST 的 waitUntil 只做入队判断;聊天来源反馈盖回执带 from_chat 标记
src/routes/v7.tsrecordRevisionFeedback 同上改入队
src/lib/fast-search-fetch.tsflag 开时 runFastSearchAgent 换成检索工人;INSERT 补 guidance_version
src/routes/api.ts · runs.ts创建首搜 / "再找一批"从 waitUntil 整跑改为只入队
src/lib/competitor-fetch.tsP3 起被检索 Agent 收编:现有"每 tick 推进一轮 + LLM 选 search|stop"的控制器换成 runner 的工具(flag 控制,旧路径保留);INSERT 补 guidance_version
wrangler.jsonc5 个环境加 DO binding + migration 声明;新增 flag 环境变量
package.json · src/lib/env.ts引入 @anthropic-ai/sdk;Bindings 类型加新 env

13.3 前端 · 改动清单(SSR JSX + 客户端脚本)

位置改什么谁写
反馈页(pm-loop.tsx 一带)整页改造成第九节的样子:对话流 + 右栏基因面板 + 版本时间线。这是体验大头
聊天客户端 JSWS 连接/重连(带 last_msg_id 补发)、流式气泡渲染、折叠条、diff 卡交互、多标签状态同步、在线标记,Codex 辅助
素材卡(hot 页)曝光/点开两个埋点(P4);卡片上显示"来自检索文档 V4"角标Codex
成片卡下载按钮埋点(P4)Codex
检索进度素材库页和聊天里共用的 run 进度组件(轮次/换词/已收)——数据来自转录轮询
styles.css(tailwind)聊天气泡、卡片、时间线样式

十四、测试策略:部署到测试环境的端到端,本地不作数

验收只认一种证据:部署到 sozai-st(sozai-st-test.funplus-marketing.ai)之后,从真实入口打进去、走完整条路、在库里和页面上看到正确结果。typecheck 和单测只是提交门槛,不算验收。

14.1 用例目录:五期 62 例 + 每期回归 4 例(编号制,报告按号对账)

执行模型:Codex 全量跑(token 便宜,用例宁多勿少),每例出证据(SQL 输出 / 转录摘录 / 截图);我抽验 ★ 号关键例亲手复跑后签收。完整编号目录在 docs/rfc-agent-native.md §14,这里列每期覆盖面:

例数覆盖面(正向 + 边界/负向)
P010tools 单轮/多轮链/并行、SSE、流式+工具、cache 命中计费★、thinking+tools(§23.42 回归)、max_tokens 边界、错误路径、图片块透传(为看抽帧留路)
P114三 stage 路由★、view_object 证据★、回执/时间线推送、纯点击不触发、超3天路径、乐观锁中途手改★、全等降级、LLM 失败留 pending、flag=off 回归★、无 unit 分支、200 条上限、总闸、并发认领互斥
P217流式对话★、折叠条、diff 卡应用★、双标签同帧★、多人署名在线、聊天反馈落表、order_search、断线补发★、卡片过期★、两页同点互斥★、驳回流、消息合并、休眠唤醒、未登录 401、实例隔离、输入法回归、长历史懒加载
P315三个入口端到端★、换词重试★、跨源互补★、guidance_version 血统★、去重、R2 转存、源故障收尾(§23.25 回归)、预算闸、sweep、桥失败三路径、产品隔离★(§23.32 回归)、并发认领、waitUntil 无 LLM 静态断言
P47三埋点落表、推导事件、read_performance 数字核对★、版本快照、时间线 UI、消化引用行为证据★、埋点鉴权
R组4/期现有 smoke 主流程、typecheck、反馈中心原功能、红线静态检查(无绕过端点 / bearer 不可触发 manual)

14.2 测试基建(复用现成的)

十五、分期路线图(每期可独立验收、可随时叫停)

进度看板(2026-07-11 更新)

  • P0 ✅ 已验收:10 探针 9 PASS(0.10 图片透传为可选项,不阻塞)。关键 0.6★ 缓存计费透传实证:首发 cache_creation=18482 → 二发 cache_read=18482、增量 input 仅 16,且跨进程命中——7-8 倍成本模型成立,agent 循环走 ZenMux 定案,无需直连备案。
  • P1 ✅ 已验收(commits 0efea15 / bf13fff / 3246cba):agent-loop(三断点缓存/批量折叠/双预算/逐轮 usage+工具结果转录)+ 管家批量消化(flag AGENT_NATIVE_DIGEST,旧路径零语义变化)+ 视频认知(mai service binding、400-500 tok 五节摘要、亮点评级、幂等回填口)。17+4 例 E2E 全部部署态执行:Codex 全量 22 PASS,我亲手重跑全部 ★ 例(含翻 flag 两次部署验 1.10 回归)。1.17★ 实测:消化 run 第 1 轮起即命中缓存、增量 input 仅个位数 token。
  • 施工中六项裁决(run 行形状 / 回填 URL 直传 / 乐观锁锁点=认领时 / outcome 存储 / 转录含工具结果 / 回填定点复验)沉淀于 RFC §16 决议 12;宪法与全部提示词 = butler-batch-v1-20260711,我手写。
  • 施工中抓获修复 1 个真 bug(入队 started_at NULL 使"最老优先"认领守卫失效);已知测试 flake:mai worker 偶发瞬时失败,fail-open + 可重入回填,生产无害。
  • P2a ✅ 已验收(commits 7748bf1 / 880f180 / 7d154fc / d3f1706):管家对话运行时全部落地——DO(Hibernation WS / mailbox 多消息合并 / 断线 seq 补发 / presence)、流式 agent-loop(非破坏,P1 回归绿)、确认卡三态(应用=乐观锁铸 origin='chat' 版本、驳回理由进下轮语境追问、底版过期自动作废)、聊天反馈即时落档、试搜下单频控 + gene-verify。21 例 WS 级 E2E 部署态全绿:Codex 全量 + 我亲手全量重跑各 21/21。实测彩蛋:管家把生成中并发到达的 3 条消息合并成一条有条理的回复(含复述暗号、主动提过期卡重出)。施工中抓获并修正 ZenMux 真实行为(空参工具流式发 partial_json="")。对话宪法 butler-chat-v1-20260711 与施工图逐字 diff=0。
  • P2b 前端已上线 sozai-st(commit 2551fa4,我亲手写):反馈中心抽屉被「创意管家」实时对话接管——WS 流式气泡、工具折叠条、基因确认卡(应用/驳回+理由追问/过期三态)、试搜进度条、断线 seq 补发、多标签同步;flag AGENT_NATIVE_CHAT 一键回退旧反馈中心。浏览器实测真流式往返 ✓。
  • 复验中抓获 P2a 逃逸 bug 并已修复(commit e83a052):DO 休眠驱逐后内存里的 agentId 丢失,所有帧被拒"连接身份已失效"——E2E 没踩中是因为用例都连上即聊无闲置窗口,浏览器闲置 3 分钟就复现。修法:连接 attachment 随身携带 agentId + DO storage 二级兜底;新增 E2E 2.18(同 socket 静置 120s 再发言)双轮验证通过。

分工铁律(先立规矩再开工)

  • :业务理解、全部设计决定、全部 system prompt / 宪法 / 工具描述文案、UI 设计与关键前端实现、每期施工图、E2E 用例设计、验收亲手复跑、handoff 记录。
  • Codex:严格按施工图写代码(后端管道、SQL、E2E 脚本实现),写完跑自证清单。不写任何提示词、不做任何设计决定——图纸没写清的地方回来问我,不许自由发挥。
  • 签收流程:Codex 自证 → 我读 diff 对图纸 → 我在部署后的 sozai-st 亲手复跑 ★ 关键例 → 全绿才算完。
内容分工验收标准
P0 ZenMux 4 项冒烟 + agent-loop.ts 核心(循环/转录落盘/预算闸/修剪) 冒烟=Codex 执行;loop=我写 P0 冒烟 4 项断言全绿(14.1)
P1 批量消化换管家批量模式(flag 控制),F5 改入队;view_object 上线 施工图+prompt=我;实现=Codex 部署 sozai-st 后 P1 E2E 全绿(14.1),含 flag=off 回归旧路径
P2 DO + WebSocket + 对话模式 + 聊天 UI(含 diff 卡确认流、多标签同步) UI+DO 骨架+prompt=我;管道与表=Codex 部署 sozai-st 后 P2 E2E 全绿:双标签同帧、确认互斥、断线补发,agent-browser 截图存证
P3 检索执行 Agent(双源:TikHub + 竞品桥)替换两套手写循环;5 个触发口全部入队化;guidance_version 补链 施工图+prompt=我;实现=Codex 部署 sozai-st 后 P3 E2E 全绿:实搜换词/停手可见于转录、guidance_version 非空、无 waitUntil 整跑
P4 行为事件表 + 3 个埋点 + read_performance + 版本效果快照 + 时间线标注 埋点与 UI=我;统计 SQL=Codex 部署 sozai-st 后 P4 E2E 全绿:事件落表、时间线显示真实采用率、消化转录引用行为证据

全程规矩:每期完成记 handoff §23.x;typecheck 必过;部署目标只有 sozai-st 测试环境(随便部),生产不碰;migration SQL 先给你过目(共用测试库,建表全环境可见)。Codex 走施工图制——我给图纸和自证清单,它实现并跑完自证,我再亲手复跑一遍才签收。

十六、等你逐项确认的清单

已拍板聊天里改基因走确认制
Agent 提议出卡,人点"应用"才写库。你已确认。
已拍板管家进 Durable Object,协议用 WebSocket
多开网页并发反馈的场景由你提出、已论证。
已拍板分工铁律:Codex 只写代码,所有设计与 system prompt 由我出
见第十五节铁律卡。图纸没写清的地方 Codex 必须回来问,不许自由发挥。
已拍板验收只认部署到测试环境后的端到端测试
每期 E2E 用例见第十四节;本地 typecheck/单测只是提交门槛,不算验收。
已拍板聊天线程共享;权限 = 能看到这个 Agent 就能聊
PM 2026-07-11 确认。访问控制沿用 Agent 本身的可见性判断。
已拍板三张新表 + origin 'chat' 迁移;test-login 授权重置;测试库 = pm-vibe 的 DB_URL(已实测连通);ZenMux key 用 sozai-st secret(已确认存在)
PM 2026-07-11 逐条答复。所有基础设施障碍清零。
已拍板UI 入口:对话页直接替代当前反馈中心的入口和位置
PM 原话。细节 PM 后续迭代。
已拍板检索 Agent 是多源工人:快搜(TikHub)+ 竞品(桥)都归它
按查找目标自己决定用哪个源、怎么配比;深搜照旧派 tobatsu。竞品引擎现有的半自动控制器被它收编(P3,flag 可回退)。
已拍板视频理解全套(2026-07-11 晚 PM 系列拍板)
走 mai video-analysis-worker(Gemini 只是工具);检索 Agent 前置分析——选货前先看内容;库级不变式——入库即已分析、绝不跑第二遍;亮点提前发现;两个 Agent 都挂 analyze_video;auth key 非敏感进 .dev.vars;契约已实测打通(200/29s)。"快手"系误听(实为快搜),平台不动。
已执行分期顺序:P1 批量消化先行
P0、P1 均已验收完毕(见第十五节进度看板);P2 聊天体验接续开工,如需调整请喊停。
已拍板测试环境 = sozai-st(sozai-st-test.funplus-marketing.ai),当前分支随便部署直接测
测试库为三环境共用同一 Neon;生产不碰。P0 冒烟改走临时路由方案,不再需要本地 key。

设计 · Claude(sozai-cc)· 现状核对 · Codex(sozai-codex)· 2026-07-11 · 确认后按 P0→P4 开工