agentprof serve(M2.3)拉起一个 localhost-only 的 HTTP 看板 —— 5 个视图(sessions / session detail / aggregate / mcp-waste list / mcp-waste detail),5 秒轮询自动刷新,零 JS 框架。整套方案 reuse M2.2 已经在用的 axum + askama 栈,workspace top-level 零新增依赖。ADR-0024 把它当作 7 个独立决策(D-1..D-7)逐条钉死,本课带着真实代码把这 7 个决策摸一遍。
cargo doc --open- cargo doc:把当前 crate 的文档渲染成静态 HTML、起一个 localhost server、浏览器一拉就看。
- agentprof serve:把当前 store DB 的 session 数据渲染成 HTML、起一个 localhost server、浏览器一拉就看。
- 共同点:单进程、不需要 docker / nginx / 数据库 / 任何 ops;杀掉进程数据就没人能访问;不留 cookie / 不挂登录。
这种「ephemeral local dashboard」模式比「企业级 BI 看板」轻 100x —— 个人用户和小团队的 95% 需求都被覆盖,剩下 5% 的场景留给 grafana + OTLP collector + prometheus。agentprof 不想做 grafana 的活,做「本地 ROI 速查」就够了。
ADR-0024 全 7 决策对比(D-1..D-7)
| 决策编号 | 选什么 | 为什么 / 关键 trade-off |
|---|---|---|
| D-1 后端栈 | axum 0.7 + tokio + askama 0.16 | 复用 M2.2 OTLP HTTP receiver 同款;workspace 已 depend,top-level 零新增依赖 |
| D-2 前端框架 | 无(vanilla JS poller, ~80 LOC) | 不引 React/Vue/Svelte = 不引 npm/webpack/vite 整个 build chain;本地看板规模 ≤ 千行 HTML,原生 DOM 完全够 |
| D-3 数据传输 | HTML 切片(chunk-endpoint pattern) | JS 拉 /api/<view>.html → innerHTML 替换;不需要 JSON → client-side 模板 → 渲染。少一层 = 少一次 bug |
| D-4 缓存策略 | 无(每请求一次 SQLite read) | 5 秒轮询 × 单用户 ≈ 0.2 QPS,SQLite 完全顶得住;缓存层带 invalidation bug 多于性能收益 |
| D-5 数据源 | store mode(不 fallback adapter scan) | serve 要长期跑、要全量 trend;adapter scan 每次 walk filesystem 太重,必须有 SQLite store 撑 |
| D-6 网络绑定 | 默认 127.0.0.1:4329,loopback only,无 auth | 本地工具,认证靠 OS user;显式绑非 loopback 时 warn 用户(与 ADR-0022 同款 capacity-cap 风格防御) |
| D-7 发版策略 | v0.3.3(patch bump,不是 0.4.0) | feature gated 在 serve feature 下;不破坏 v0.3.x 用户的 cli 兼容性;零 breaking change |
⚠️ Recon 校正:chunk endpoint 真实路径形如 /api/sessions.html / /api/session/:id.html(.html 后缀是 D-3 的 signal —— 这是HTML不是 JSON);handler 名是 *_chunk(5 个 chunk)+ *_page(5 个 page shell)共 10 个 + 1 个 healthz。Detail 页 reuse format::html::render_body_only —— 浏览器看到的 Cache 段、Tool rank 表和 analyze --export html cli 完全一致。
👇 三张卡片:① 7 决策 (D-1..D-7) 逐条摘要 · ② chunk-endpoint pattern 真实 router 表 · ③ 5 视图源码 walk-through。
1 ADR-0024 7 决策 (D-1..D-7) 摘要 点击展开
static_assets.rs 里、build 时编进 binary(include_str!)—— 用户连 npm 都不知道在哪。取舍:失去 SPA 路由 / 状态管理 / vd-DOM 加速 —— 但本地 5 视图根本用不上。<html> / <head>),前端拉到后 el.innerHTML = chunk 直接换。少一层 = 少写一份模板 + 少一份「服务端 JSON schema 和前端期望对不上」的故障源。用户感知:每 5 秒页面区域 flash 一下 —— 加 CSS transition 就柔和了。SELECT * FROM sessions ORDER BY started_at LIMIT 100 < 10ms 内即返。D-5 必须 store mode:serve 跑数小时 / 天,adapter scan 每次 walk filesystem ≈ 几秒,根本不行;启动时检查 storage_mode==Store,否则 cli 报错退码 1。D-6 loopback-only + 无 auth:本地工具,谁能 connect 127.0.0.1 谁就是本机用户 —— OS 已经做了认证。绑 0.0.0.0 / 公网 IP 时显式 warn,提醒用户加反代或 ssh tunnel。--features serve),不动现有 cli/cmd/* 任何子命令,不改 schema,不改 wire format。SemVer 规则下,additive feature = patch bump。v0.4.0 留给真正的 breaking change(比如 ADR-0019 决策反转之类)。这条决策也借鉴 ADR-0022 的 v0.3.2 hardening 经验 —— 硬化和 additive feature 都走 patch。2 chunk-endpoint pattern 真实 router 表 点击展开
crates/agentprof-cli/src/cmd/serve/router.rs:29)pub fn build_router(state: AppState) -> Router {
Router::new()
// page shells(完整 HTML,浏览器首次进入)
.route("/", get(|| async { Redirect::permanent("/sessions") }))
.route("/sessions", get(handlers::sessions_page))
.route("/session/:id", get(handlers::session_page))
.route("/aggregate", get(handlers::aggregate_page))
.route("/mcp-waste", get(handlers::mcp_waste_list_page))
.route("/mcp-waste/:tool", get(handlers::mcp_waste_detail_page))
// chunk endpoints(HTML 片段,JS 5 秒轮询)
.route("/api/sessions.html", get(handlers::sessions_chunk))
.route("/api/session/:id.html", get(handlers::session_chunk))
.route("/api/aggregate.html", get(handlers::aggregate_chunk))
.route("/api/mcp-waste.html", get(handlers::mcp_waste_list_chunk))
.route("/api/mcp-waste/:tool.html",get(handlers::mcp_waste_detail_chunk))
// 健康检查 + 静态资产
.route("/healthz", get(handlers::healthz))
.route("/static/:name", get(handlers::static_asset))
.with_state(state)
}
- 浏览器 GET
/sessions→sessions_page返完整 HTML(含 askama base 模板 + nav + 一个空<div id="chunk">)。 - 页面 JS(include 在 base template 里,~80 LOC)启动
setInterval(refresh, 5000)。 - 每 5 秒
fetch('/api/sessions.html')→sessions_chunk返 HTML 片段(不含<html>)。 - JS
document.getElementById('chunk').innerHTML = htmlText。 - 浏览器原生 parse + render;表格、链接、CSS hover 全部按浏览器规则工作。
等价心智模型:把每个视图当成「会自动 reload 的静态页」—— 用户连刷新都不用按。如果未来要换 framework(HTMX / Turbo / Hotwire),切换的也只是「fetch + innerHTML」这一小段 JS,handler 一行都不动。
{% include %} chunk 模板,同一份 HTML 渲染逻辑被两个端点 reuse —— 改一个地方两处生效。3 5 视图源码 walk-through 点击展开
sessions_chunk)SELECT id, agent, dominant_model, started_at, duration_ms, total_input_tokens, total_output_tokens FROM sessions ORDER BY started_at DESC LIMIT 100。askama 模板:渲染表格,每行 <a href="/session/{id}"> 跳详情。用户体验:dev 一边跑 agent 一边开浏览器,每 5 秒新 session 自动浮上来 —— 不用手动 list --since 1h。session_chunk) —— 关键 reuse 点// crates/agentprof-cli/src/cmd/format/html.rs:175
pub fn render_body_only(
report: &AnalysisReport,
cache_metrics: Option<&CacheMetrics>,
/* ... */
) -> askama::Result<String>;
analyze --export html 和 web detail共用这个 fn —— 浏览器里看到的 Turn Summary / Tool Rank / Cache 段,和 agentprof analyze --export html > out.html 输出完全一致。这是 ADR-0024 显式提到的「与 ADR-0023 cache metrics 一致」的实现机制。意义:CLI 和 web 用户看到的「真相」是同一份;不会出现「CLI 显示 80% hit rate,web 显示 78%」这种诡异 bug。
aggregate_chunk)agentprof-core::aggregate(M2.1.1 加 episodes_json 后开放)。SQL 拉 sessions + episodes_json,分组维度(model / tool / day)由 URL query 决定(?by=model)。展示:表 + 简单 SVG bar chart(也是手写 SVG,不引 chart.js)。aggregate_waste(per_session)(见 wiki 4),按 tool 名展示「累计 wasted tokens」+「loaded session count」+「actually-called session count」。Detail 视图点进单 tool,列出哪些 session 加载了它但从没调用 —— 用户能直接定位「哪个 server 该从 mcp.json 删了」。这是 agentprof 的核心 ROI 价值在 web 里的直接呈现。下一步
本课讲清了 agentprof serve 的 7 决策、chunk-endpoint pattern 真实路由、5 视图各自的数据源。下一课「贡献指南」是 Wiki 章节的收官 —— 怎么给 agentprof 提 PR、9 阶段 pipeline 怎么走、Conventional Commits 怎么写、加 ADR / 通过 CI 的实操清单。
📂 相关源码:
agentprof-cli/cmd/serve/router.rs
build_router
📂 相关源码:
agentprof-cli/cmd/serve/handlers.rs
sessions_chunk
📂 相关源码:
agentprof-cli/cmd/format/html.rs
render_body_only