Raindrop Workshop 架构拆解:raindrop CLI、本地守护进程与实时 Trace 流内幕
Raindrop Workshop 架构拆解:raindrop CLI、本地守护进程与实时 Trace 流内幕
Raindrop Workshop 是一个专为 AI Agent 打造的本地 Trace 调试器:它通过 raindrop CLI 拉起一个本地守护进程,把 Agent 运行时的每个 token、每次工具调用、每个 span 通过 WebSocket 实时推送到浏览器,无需轮询、无需刷新。本文带你拆解它的三层架构——raindrop CLI、本地守护进程(daemon)、实时 Trace 流,看看"让 Claude Code 读自己的 trace 并自动修复 bug"这套本地闭环是如何运转的。
三层架构总览:一条 Trace 的完整旅程
Workshop 的核心链路可以概括为四个角色:
- Agent 应用:你的 AI 服务(TypeScript / Python / Go / Rust),由 Raindrop SDK 在模型调用与工具执行的边界上采集 telemetry;
- raindrop CLI:单一二进制,负责安装、启动、停止、升级本地守护进程,并桥接 MCP 协议;
- Workshop 守护进程:内置 HTTP + WebSocket 服务 + SQLite 数据库,是唯一的"控制中心";
- 浏览器 UI:React + Vite 前端,订阅 WS 事件实时渲染 trace 树与 payload。
Trace 的流向是:SDK 上报 OTLP → 守护进程解析入库 → broadcast() 广播给所有 WS 客户端 → UI 即时刷新。整个过程只走回环地址,数据不出本机。
raindrop CLI 内幕:一个二进制如何管好整个生命周期
CLI 入口在 src/index.ts,raindrop 是一个"伞形"命令,当前伞下只有 workshop 一个产品,未来可平级扩展。
幂等启动与状态文件
所有守护进程状态都落在 ~/.raindrop/ 目录:
| 文件 | 作用 |
|---|---|
raindrop_workshop.pid |
守护进程 PID,用于 stop/status |
raindrop_workshop.port |
实际监听端口(可能被自动上移) |
raindrop_workshop.log |
后台进程日志 |
raindrop_workshop.db |
SQLite 数据库(可用 RAINDROP_WORKSHOP_DB_PATH 覆盖) |
ensureDaemonRunning()(src/index.ts)的启动逻辑非常讲究:
- 健康探测优先:先请求
/health,如果已有实例存活就直接复用,避免重复拉起; - 端口自动上移:5899 被占用就向上找空闲端口,而不是报错退出;
- 守护化:用
detached子进程自执行workshop serve,日志重定向到文件,随后轮询健康检查最多 30 秒确认就绪; - 僵尸清理:检测到过期 PID 文件会自动删除,防止状态漂移。
💡 一个小细节:raindrop workshop mcp 命令会在 MCP 桥接启动前自动确保守护进程在线(src/index.ts),这样即使机器重启后直接打开 Claude Code,Workshop 也能自愈式地跑起来。
本地守护进程:绑定 127.0.0.1 的"控制中心"
服务端实现集中在 src/server.ts,技术栈是 Express + ws + Drizzle ORM(SQLite)。
回环访问守卫:本地工具的安全底线
Workshop 默认只绑定 127.0.0.1,并且在socket 层校验远端地址(src/server.ts),这样即使误配成 0.0.0.0 监听,伪造的 Host/Origin 头也无法把它变成局域网服务。CORS 也只放行采集路由(src/server.ts),且白名单可通过 RAINDROP_WORKSHOP_ALLOWED_HOSTS 为 Docker 等场景定向打开。
OTLP 采集入口:一套数据,多条路径
为了兼容不同 SDK 的导出器,守护进程在多个路径上挂载了同一个 ingestTraces 处理器(src/server.ts):
POST /v1/traces— Raindrop 直发POST /v1/otel/v1/traces、POST /otel/v1/traces— 标准 OTLP 导出器POST /v1/live— 轻量级实时事件流(token、tool_start、tool_result 等)
请求体同时支持 JSON 和 protobuf(express.raw 挂载,src/server.ts),protobuf 由 src/otlp-protobuf.ts 解码,保证 Traceloop 等以二进制格式上报的 SDK 不会被静默丢弃。
入库与 SDK 无关化
每条 trace 按 traceId 分组后 upsert 进 runs 表,每个 span 写入 spans 表(含 model、token 数、parent 关系等字段),表结构定义在 src/db/schema.ts,迁移文件位于 drizzle/。
真正巧妙的是读取时的归一化层:src/spans/normalize.ts 维护一条 Adapter 链(Vercel AI SDK → LiveKit → Traceloop → Claude Agent SDK → 工具调用),按"更具体的适配器优先"的顺序匹配,把各 SDK 风格迥异的原始属性统一成结构化视图。新增一个 SDK 支持 = 在 adapters/ 下加一个文件 + 注册一行,这就是它能宣称"兼容一切"的底气。
实时 Trace 流内幕:没有轮询的"所见即所得"
这是 Workshop 最有意思的部分——UI 如何做到零延迟刷新。
服务端:一条 /ws 通道 + broadcast
守护进程在 /ws 路径上挂了一个 WebSocketServer,并在握手时做回环校验(src/server.ts)。核心是极简的 broadcast()(src/server.ts):把 { event, data } 序列化成 JSON 后发给所有在线客户端。采集到 spans 就广播 "spans",收到 live 事件就广播 "live"(src/server.ts)——每个正在看的浏览器页面会立刻增量刷新。
另一个细节:视图注册表。UI 会把自己的"当前正在看哪条 run、选中哪个 span"通过 WS 上报(src/server.ts),这让后续的 MCP 工具能理解"用户此刻聚焦的 trace",而不是让用户去复制一串 run id。
前端:单例 Broker + 断线自愈
浏览器端所有组件共享一个 WebSocket 单例(app/src/hooks/use-workshop-ws.ts):
- 连接
ws(s)://host/ws,按event名称分发给各 React hook; - 断线 2 秒自动重连,重连成功后自动补发关键状态(如
ui_view,latest-wins 策略),保证重连即恢复上下文; - 暴露
useWorkshopEvent/useWorkshopConnected等 hook,组件只需订阅事件名,完全无感知底层重连。
你在聊天面板里看到的那个像素雨滴小助手,背后就是这条 WS 通道在驱动:loadout 变更、Agent 提问(ask-user-question)、注解更新,都走同一套广播机制。
MCP 桥:让 Claude Code 直接"读 trace、写 eval、修 bug"
守护进程之外,Workshop 还提供了一个 stdio MCP Server(src/mcp/index.ts),这是"自修复 eval 循环"的关键。Claude Code 通过 src/mcp/tools.ts 暴露的 10 个工具操作本机数据:
get_current_run:读取 UI 当前聚焦的 run 与选中的 span(依托前文的视图注册表);query_traces:对本地 SQLite 执行只读 SELECT,做聚合与发现;get_span_payload:按 JSONPath/字符区间分页读取完整 prompt/工具结果,不爆上下文;search_run/get_run_outline/get_span_context:定位与结构概览;annotate:把"问题/亮点/备注"作为持久化注解存回 Workshop,UI 可见;replay_run/import_cloud_trace/show_in_ui:触发回放、拉取云端 trace、跳转 UI 定位。
工具说明里甚至规定了输出礼仪:"不要向用户展示原始 span id"(src/mcp/index.ts)——工程细节体现了产品品味。
两个 Skill:把架构闭环成工作流
架构之上,Workshop 用两个 Skill 把工作流"焊死":
/instrument-agent(skills/instrument-agent/SKILL.md):指导 AI 编码助手完成埋点——先查文档、尊重已有 OTel 归属、以"一条有信息量的 run 进 Workshop"为验收标准,避免一上来就满世界埋点;/setup-agent-replay(app/replay-plan.md):为项目生成 HTTP 回放端点并注册到~/.raindrop本地注册表,之后 Workshop 可以拿一条生产 trace 打回你的真实 Agent 代码,形成"写 eval → 跑 Agent → 看失败 → 修代码 → 重跑"的自愈循环。
结语:小而精的本地可观测性设计
回头看 Raindrop Workshop 的架构,没有微服务、没有消息队列,只有一个二进制、一个 SQLite 文件、一条 WebSocket,却完整覆盖了采集 → 归一化 → 存储 → 实时推送 → AI 反查的闭环。它的几个设计决策尤其值得借鉴:
- 伞形 CLI + 状态文件 + 健康探测,让"守护进程"对普通用户透明;
- socket 层回环校验,把"本地工具"的安全边界做实;
- 读取时 Adapter 归一化,而非写入时强转,换取对 SDK 生态的开放兼容;
- 单例 WS Broker + 视图注册表,让"UI 在看什么"成为 AI 可查询的一等数据。
如果你想在自己的 Agent 项目里体验这套本地 Trace 调试流程,跑一条安装命令、在 Agent 里执行 /instrument-agent,trace 就会在 http://localhost:5899 上开始流动——这正是这个架构存在的意义。

