baa-conductor

git clone 

baa-conductor / plans
im_wower  ·  2026-03-28

ARTIFACT_STATIC_SERVICE.md

  1# Artifact 静态服务方案
  2
  3日期:`2026-03-28`
  4
  5## 状态
  6
  7- `设计中`
  8- 依赖当前主分支:`main`
  9- canonical local API:`http://100.71.210.78:4317`
 10- canonical public host:`https://conductor.makefile.so`
 11
 12## 关联文档
 13
 14- [`./STATUS_SUMMARY.md`](./STATUS_SUMMARY.md)
 15- [`./BAA_INSTRUCTION_SYSTEM.md`](./BAA_INSTRUCTION_SYSTEM.md)
 16
 17## 1. 背景与动机
 18
 19当前 BAA 指令闭环中,执行结果只能通过文本回送给 AI 对话。存在三个问题:
 20
 211. **大结果截断**:超长输出(如测试日志、文件内容)只能截断回送,AI 看不到完整结果
 222. **文件传输不可行**:通过浏览器扩展注入文件到 AI 对话,链路复杂且脆弱
 233. **跨会话断裂**:新对话无法获取之前的执行历史和消息记录
 24
 25## 2. 核心思路
 26
 27把所有消息和执行结果持久化到数据库,同时生成可通过 HTTP GET 直接访问的静态文件。AI 通过 fetch URL 获取完整内容,不需要浏览器扩展参与。
 28
 29```
 30AI 回复 → conductor 收到 browser.final_message
 31  → 全量写入 SQLite + D1(消息表)
 32  → 生成静态文件:/artifact/msg/{id}.txt + .json
 33  → 提取 baa 指令 → 执行
 34  → 全量写入 SQLite + D1(执行表)
 35  → 生成静态文件:/artifact/exec/{id}.txt + .json
 36  → 更新会话索引
 37  → 回送给 AI:
 38    结果 ≤ 2000 字符 → 内联全文
 39    结果 > 2000 字符 → 前 500 字符摘要 + exact URL
 40```
 41
 42## 3. 设计决策
 43
 44以下决策经过 ChatGPT 和 Gemini 实测验证。
 45
 46### 3.1 已确认的决策
 47
 48| 项 | 决策 | 原因 |
 49|---|---|---|
 50| 静态文件路由 | `/artifact/` | 通俗易懂,与内部 API `/v1/` 分离 |
 51| 文件格式 | 同时提供 `.txt``.json` | ChatGPT / Gemini 都建议两种格式并存 |
 52| `.txt` Content-Type | `text/plain; charset=utf-8` | AI 浏览工具最稳定的格式 |
 53| `.json` Content-Type | `application/json; charset=utf-8` | 需要字段提取时使用 |
 54| `.txt` 内容结构 | frontmatter 元数据 + 正文 | 两家 AI 都推荐此格式 |
 55| URL 中的 ID | hash/UUID 格式 | 不用递增数字,防止遍历猜解 |
 56| 文件过期 | 永不过期 | 所有内容永久保留 |
 57| 访问控制 | 当前公开访问,无 token | AI 无法带自定义 Header,只能 GET |
 58| 鉴权预留 | 后续用签名 URL `?sig=xxx&exp=xxx` | 比纯 token 更安全,AI 兼容性最高 |
 59| 回送 AI 时 | 直接给 exact URL | ChatGPT 强调不能让 AI 猜路径 |
 60| 截断阈值 | 2000 字符(可配置) | 超过此值开始截断 |
 61| 摘要长度 | 500 字符(可配置) | 截取前 500 字符作为内联摘要 |
 62| robots.txt | `Allow: /artifact/` | 允许 AI 爬虫访问 |
 63| 静态文件由谁 serve | conductor HTTP server | nginx 在 VPS 上不在本地,走 conductor |
 64
 65### 3.2 AI 平台访问约束(实测确认)
 66
 67| 约束 | 说明 |
 68|---|---|
 69| 只能 GET | AI 不能发 POST,不能带自定义 Header |
 70| 不能带 Cookie/Auth Header | 鉴权只能用 URL 参数 |
 71| 超时约 10-15 秒 | 必须返回预生成的静态文件,不能现算 |
 72| exact URL 最稳 | AI 访问明确给出的 URL 成功率最高,自己推导相邻 URL 可能被拒 |
 73| `/api` 路径不被拦截 | 实测确认,但给 AI 读的接口和内部 API 分开是好习惯 |
 74| JSON 和纯文本都能读 | 但纯文本/Markdown 更稳、更省 token |
 75
 76## 4. 数据库设计
 77
 78### 4.1 存储架构
 79
 80- **本地 SQLite**:先写,保证主链路不阻塞
 81- **Cloudflare D1**:异步同步,分布式备份和跨设备访问
 82- **新建数据库**:不复用旧版 D1 数据库
 83- **D1 适配器**:TypeScript async 重写,接口兼容旧版 `D1Client`
 84
 85### 4.2 表结构
 86
 87#### messages 表
 88
 89存储所有 AI 对话消息(`browser.final_message` 到达时写入)。
 90
 91```sql
 92CREATE TABLE IF NOT EXISTS messages (
 93  id              TEXT PRIMARY KEY,     -- assistant_message_id
 94  platform        TEXT NOT NULL,        -- 'claude' | 'chatgpt' | 'gemini'
 95  conversation_id TEXT,                 -- 平台对话 ID
 96  role            TEXT NOT NULL,        -- 'assistant' | 'human'
 97  raw_text        TEXT NOT NULL,        -- 完整消息文本(永不截断)
 98  summary         TEXT,                 -- 摘要(前 N 字符或 AI 生成)
 99  observed_at     INTEGER NOT NULL,     -- 毫秒时间戳
100  static_path     TEXT,                 -- 静态文件相对路径
101  page_url        TEXT,                 -- 来源页面 URL
102  page_title      TEXT,                 -- 来源页面标题
103  organization_id TEXT,                 -- Claude org ID
104  created_at      INTEGER NOT NULL      -- 入库时间戳
105);
106
107CREATE INDEX IF NOT EXISTS idx_messages_conversation
108  ON messages(conversation_id);
109CREATE INDEX IF NOT EXISTS idx_messages_platform
110  ON messages(platform, observed_at);
111```
112
113#### executions 表
114
115存储所有指令执行记录。
116
117```sql
118CREATE TABLE IF NOT EXISTS executions (
119  instruction_id  TEXT PRIMARY KEY,     -- inst_{hash前16位}
120  message_id      TEXT NOT NULL,        -- 关联 messages.id
121  target          TEXT NOT NULL,        -- 'conductor' | 'system' | 'browser.claude'
122  tool            TEXT NOT NULL,        -- 'exec' | 'files/read' | 'send' 等
123  params          TEXT,                 -- JSON,指令参数
124  params_kind     TEXT,                 -- 'none' | 'body' | 'inline_json' | 'inline_string'
125  result_ok       INTEGER NOT NULL,     -- 1=成功 0=失败
126  result_data     TEXT,                 -- JSON,完整执行结果(永不截断)
127  result_summary  TEXT,                 -- 摘要文本
128  result_error    TEXT,                 -- 错误信息
129  http_status     INTEGER,             -- HTTP 响应状态码
130  executed_at     INTEGER NOT NULL,     -- 执行完成时间戳
131  static_path     TEXT,                 -- 静态文件相对路径
132  created_at      INTEGER NOT NULL      -- 入库时间戳
133);
134
135CREATE INDEX IF NOT EXISTS idx_executions_message
136  ON executions(message_id);
137CREATE INDEX IF NOT EXISTS idx_executions_target_tool
138  ON executions(target, tool);
139```
140
141#### sessions 表
142
143会话级别索引,用于跨会话接续。
144
145```sql
146CREATE TABLE IF NOT EXISTS sessions (
147  id                TEXT PRIMARY KEY,   -- 自动生成 UUID
148  platform          TEXT NOT NULL,
149  conversation_id   TEXT,               -- 平台对话 ID
150  started_at        INTEGER NOT NULL,
151  last_activity_at  INTEGER NOT NULL,
152  message_count     INTEGER NOT NULL DEFAULT 0,
153  execution_count   INTEGER NOT NULL DEFAULT 0,
154  summary           TEXT,               -- 会话摘要
155  created_at        INTEGER NOT NULL
156);
157
158CREATE INDEX IF NOT EXISTS idx_sessions_platform
159  ON sessions(platform, last_activity_at);
160CREATE INDEX IF NOT EXISTS idx_sessions_conversation
161  ON sessions(conversation_id);
162```
163
164## 5. URL 结构
165
166### 5.1 静态文件(AI 直接 fetch)
167
168```
169/artifact/msg/{id}.txt            -- 消息全文(frontmatter + 正文)
170/artifact/msg/{id}.json           -- 消息 JSON
171/artifact/exec/{id}.txt           -- 执行结果(frontmatter + 正文)
172/artifact/exec/{id}.json          -- 执行结果 JSON
173/artifact/session/latest.txt      -- 最近活跃会话摘要
174/artifact/session/{id}.txt        -- 单个会话时间线
175```
176
177### 5.2 查询接口(内部/管理用)
178
179```
180GET /v1/messages                  -- 消息列表(分页、按 conversation 过滤)
181GET /v1/messages/{id}             -- 单条消息详情
182GET /v1/executions                -- 执行记录列表(分页、按 message 过滤)
183GET /v1/executions/{id}           -- 单条执行详情
184GET /v1/sessions                  -- 会话索引
185GET /v1/sessions/latest           -- 最近活跃会话
186```
187
188### 5.3 辅助路由
189
190```
191GET /robots.txt                   -- Allow: /artifact/
192```
193
194## 6. 静态文件格式
195
196### 6.1 消息 .txt 格式
197
198```
199kind: message
200id: m_a8b9c2d7
201platform: claude
202conversation_id: conv_d4e5f6a1
203role: assistant
204observed_at: 2026-03-28T16:09:00+08:00
205
206---
207
208(此处为消息完整原文)
209```
210
211### 6.2 执行结果 .txt 格式
212
213```
214kind: execution
215id: inst_a8b9c2d7
216target: conductor
217tool: exec
218status: ok
219executed_at: 2026-03-28T16:09:00+08:00
220message_id: m_d4e5f6a1
221message_url: https://conductor.makefile.so/artifact/msg/m_d4e5f6a1.txt
222
223---
224
225command: pnpm test
226exit_code: 0
227
228(此处为完整执行输出)
229```
230
231### 6.3 消息 .json 格式
232
233```json
234{
235  "kind": "message",
236  "id": "m_a8b9c2d7",
237  "platform": "claude",
238  "conversation_id": "conv_d4e5f6a1",
239  "role": "assistant",
240  "observed_at": "2026-03-28T16:09:00+08:00",
241  "raw_text": "(完整消息文本)",
242  "summary": "(前 500 字符)",
243  "artifact_url": "https://conductor.makefile.so/artifact/msg/m_a8b9c2d7.txt"
244}
245```
246
247### 6.4 执行结果 .json 格式
248
249```json
250{
251  "kind": "execution",
252  "id": "inst_a8b9c2d7",
253  "target": "conductor",
254  "tool": "exec",
255  "params": {"command": "pnpm test"},
256  "status": "ok",
257  "executed_at": "2026-03-28T16:09:00+08:00",
258  "message_id": "m_d4e5f6a1",
259  "message_url": "https://conductor.makefile.so/artifact/msg/m_d4e5f6a1.txt",
260  "result": {
261    "ok": true,
262    "data": {"exit_code": 0, "stdout": "..."},
263    "error": null
264  },
265  "summary": "pnpm test 完成,12 项通过",
266  "artifact_url": "https://conductor.makefile.so/artifact/exec/inst_a8b9c2d7.txt"
267}
268```
269
270### 6.5 会话索引 latest.txt 格式
271
272```
273kind: session_index
274generated_at: 2026-03-28T16:30:00+08:00
275count: 3
276
277---
278
279## [2026-03-28 16:09] Claude conv_abc123
280messages: 5, executions: 3, last_activity: 16:09
281latest_message: https://conductor.makefile.so/artifact/msg/m_001.txt
282session: https://conductor.makefile.so/artifact/session/s_abc.txt
283
284## [2026-03-28 15:30] ChatGPT conv_def456
285messages: 2, executions: 1, last_activity: 15:31
286latest_message: https://conductor.makefile.so/artifact/msg/m_002.txt
287session: https://conductor.makefile.so/artifact/session/s_def.txt
288```
289
290## 7. 回送策略
291
292### 7.1 内联回送(结果 ≤ 2000 字符)
293
294```
295执行完成。
296
297command: pnpm test
298exit_code: 0
299
300Tests: 12 passed, 0 failed
301Time: 3.2s
302
303记录:https://conductor.makefile.so/artifact/exec/inst_a8b9c2d7.txt
304```
305
306### 7.2 截断回送(结果 > 2000 字符)
307
308```
309执行完成(输出 8420 字符,已截断)。
310
311command: pnpm test
312exit_code: 1
313
314FAIL src/api.test.ts
315  ● should handle timeout
316    Expected: 408
317    Received: 500
318(...前 500 字符...)
319
320完整结果:https://conductor.makefile.so/artifact/exec/inst_a8b9c2d7.txt
321```
322
323### 7.3 配置项
324
325```typescript
326interface ArtifactServiceConfig {
327  /** 超过此字符数开始截断回送,默认 2000 */
328  inlineThreshold: number;
329  /** 截断时保留的前 N 字符,默认 500 */
330  summaryLength: number;
331  /** 静态文件存储目录 */
332  artifactDir: string;
333  /** 公开访问的 base URL */
334  publicBaseUrl: string;
335}
336```
337
338## 8. D1 同步策略
339
340```
341写入流程:
342  本地 SQLite 写入(同步,阻塞)
343    → 生成静态文件(同步,写入 artifactDir)
344    → D1 异步推送(后台,不阻塞主链路)
345    → 推送失败时记录重试队列
346
347读取流程:
348  静态文件:直接读磁盘,conductor HTTP serve
349  查询接口:读本地 SQLite
350  D1:跨设备/远程访问时使用
351```
352
353D1 同步细节:
354
355- 写入后触发异步推送,不等待结果
356- 失败时记录到本地重试队列(SQLite 表 `d1_sync_queue`357- 后台定时扫描重试队列,指数退避
358- D1 不可用时,本地功能完全不受影响
359
360## 9. 跨会话接续
361
362新对话的 AI 通过以下方式了解历史:
363
3641. AI 发送 `@conductor::describe` → conductor 回复中包含最近会话 URL
3652. AI fetch `https://conductor.makefile.so/artifact/session/latest.txt`
3663. 看到所有历史对话、消息、执行记录的 exact URL
3674. 按需 fetch 任意 URL 获取详情
368
369不需要人工复制粘贴历史,不需要浏览器扩展参与。
370
371## 10. 实施顺序
372
3731. **新建 SQLite 数据库 + 表结构**(messages / executions / sessions)
3742. **静态文件生成模块**(写入时生成 .txt + .json)
3753. **conductor HTTP 路由**(`/artifact/` serve 静态文件 + `/robots.txt`3764. **接入主链路**(browser.final_message → 写库 + 生成文件;executor → 写库 + 生成文件)
3775. **回送策略**(阈值截断 + URL 拼接)
3786. **查询路由**(`/v1/messages`、`/v1/executions`、`/v1/sessions`)
3797. **D1 异步适配器**(TypeScript async 重写)
3808. **D1 同步队列**(后台推送 + 重试)
3819. **会话索引自动更新**(事件触发)
38210. **stagit 集成**(后续)
383
384## 11. 不做的事
385
386- 不做文件上传/下载注入到浏览器
387- 不做 artifact 物化/manifest/upload receipt(v5 规范中的复杂方案)
388- 不做 TTL/自动过期
389- 不做多租户隔离(当前单用户)
390- 不做 AI 调用生成摘要(纯截断,简单可靠)