背景
当前 ui/ dashboard 的日志数据来源是本地文件系统(agents/*/logs/run-*/),server 直接 fs.readdirSync 读取。这意味着:
- dashboard 只能看到当前部署主机上跑出的 agent 日志
- 如果多位成员各自在本地跑
blueprint-absorber,其他人的运行记录对 dashboard 完全不可见
- 无法实现跨主机的统一进度监控
目标
不引入重量级外部数据库,以最小改动实现:任何成员在本地跑 agent,日志实时汇聚到部署在服务器上的 dashboard,所有人都能在同一个界面监控。
方案设计
总体思路
成员 A 本地 run.sh 成员 B 本地 run.sh
│ │
│ 每条 JSONL event │
▼ ▼
POST /api/ingest ────────▶ dashboard server(服务器)
│
写入本地文件(保持现有目录结构)
│
WebSocket 推送给前端
▼
浏览器实时看到
原有文件结构完全不变,server 侧"读文件"逻辑无需修改,ingest 端点只是另一条写入路径。
改动一:agent 侧——run.sh 加上报 hook
在现有的 JSONL 解析 pipeline 末尾,每 emit 一行到本地 .jsonl 文件时,同时 POST 到远程 server:
# run.sh 新增环境变量(可选,不设则纯本地模式)
INGEST_URL="${KIP_INGEST_URL:-}" # e.g. http://kip.opensii.ai:8081/api/ingest
INGEST_TOKEN="${KIP_INGEST_TOKEN:-}"
上报逻辑插入到现有 python 日志解析脚本中(约 10 行):
import urllib.request, os
INGEST_URL = os.getenv("KIP_INGEST_URL", "")
INGEST_TOKEN = os.getenv("KIP_INGEST_TOKEN", "")
def ingest(row: dict):
if not INGEST_URL:
return
payload = json.dumps({
"agent": meta["agent"],
"runId": RUN_TS,
"hintFile": meta["hint_file"],
"row": row,
}).encode()
req = urllib.request.Request(
INGEST_URL,
data=payload,
headers={"Content-Type": "application/json",
"Authorization": f"Bearer {INGEST_TOKEN}"},
method="POST",
)
try:
urllib.request.urlopen(req, timeout=3)
except Exception:
pass # 上报失败不影响本地运行
每次 emit(...) 后调用 ingest(row),fire-and-forget,超时 3s,失败静默,不阻塞 agent。
改动二:server 侧——新增 /api/ingest 端点
// ui/server/src/routes/ingest.ts
POST /api/ingest
Body: { agent: string, runId: string, hintFile: string, row: object }
Auth: Bearer token(读环境变量 KIP_INGEST_TOKEN,为空则不校验)
逻辑:
- 校验 token(如果配置了)
- 确保
agents/<agent>/logs/<runId>/ 目录存在(mkdir -p)
- 追加
row 到 agents/<agent>/logs/<runId>/absorber.jsonl
- 如果是第一条 event,写
meta.json(包含 agent/hintFile/startedAt)
- 如果
row.event === "session_end",更新 meta.json 的 completedAt/status
- 通过现有 WebSocket 机制广播这条 event 给正在查看该日志的前端连接
改动三:start.sh / 部署文档
新增可选环境变量说明:
KIP_INGEST_TOKEN=<随机字符串> # server 侧设置,用于校验上报来源
KIP_INGEST_URL=http://<server>:8081/api/ingest # 成员本地设置
两者都是可选的:不设 KIP_INGEST_URL 则纯本地模式,行为与现在完全一致。
改动范围
| 文件 |
变更 |
agents/blueprint-absorber/run.sh(python 内联脚本) |
加 ingest() 调用,约 +15 行 |
ui/server/src/routes/ingest.ts |
新文件,约 +60 行 |
ui/server/src/index.ts |
注册新路由,+2 行 |
ui/start.sh |
新增环境变量说明注释 |
无数据库依赖,无第三方服务,不破坏现有本地工作流。
后续扩展(out of scope)
- 支持
blueprint-prover 等未来 agent(run.sh 结构一致,同样适用)
- ingest 端点加速率限制(防止异常打满磁盘)
- dashboard 上显示"来源主机"字段,区分本地运行与远程上报
背景
当前
ui/dashboard 的日志数据来源是本地文件系统(agents/*/logs/run-*/),server 直接fs.readdirSync读取。这意味着:blueprint-absorber,其他人的运行记录对 dashboard 完全不可见目标
不引入重量级外部数据库,以最小改动实现:任何成员在本地跑 agent,日志实时汇聚到部署在服务器上的 dashboard,所有人都能在同一个界面监控。
方案设计
总体思路
原有文件结构完全不变,server 侧"读文件"逻辑无需修改,ingest 端点只是另一条写入路径。
改动一:agent 侧——
run.sh加上报 hook在现有的 JSONL 解析 pipeline 末尾,每 emit 一行到本地
.jsonl文件时,同时 POST 到远程 server:上报逻辑插入到现有 python 日志解析脚本中(约 10 行):
每次
emit(...)后调用ingest(row),fire-and-forget,超时 3s,失败静默,不阻塞 agent。改动二:server 侧——新增
/api/ingest端点逻辑:
agents/<agent>/logs/<runId>/目录存在(mkdir -p)row到agents/<agent>/logs/<runId>/absorber.jsonlmeta.json(包含 agent/hintFile/startedAt)row.event === "session_end",更新meta.json的 completedAt/status改动三:
start.sh/ 部署文档新增可选环境变量说明:
两者都是可选的:不设
KIP_INGEST_URL则纯本地模式,行为与现在完全一致。改动范围
agents/blueprint-absorber/run.sh(python 内联脚本)ingest()调用,约 +15 行ui/server/src/routes/ingest.tsui/server/src/index.tsui/start.sh无数据库依赖,无第三方服务,不破坏现有本地工作流。
后续扩展(out of scope)
blueprint-prover等未来 agent(run.sh 结构一致,同样适用)