桌面架构与技术决策
状态:设计说明;模块边界、数据库与调度核心已随 M1–M4/M6 实施, 所有资源数字仍是验收目标,实测状态以验证记录为准。范围见 设计入口。
桌面方案
采用 Tauri 2 + Rust、Svelte + TypeScript + Vite;ECharts 按需加载图表/组件, 六类图表使用 SVG 渲染。Node 仅用于构建,不随成品分发;Python 原型不作为 sidecar。
Tauri 的 Windows/macOS/Linux 分别使用 WebView2/WKWebView/WebKitGTK。 这支持减少应用自带运行时的选择,但不证明总内存必然低于其他方案。 Windows 包体必须区分应用、WebView2 安装下载、已安装运行时和用户数据库。 依据:Tauri WebView、 Windows 安装。
| 选项 | 适合本任务的部分 | 取舍 |
|---|---|---|
| Tauri 2 + Rust | 系统 WebView、文件采集、SQLite、本地 IPC;原型图表思路可复用 | 采用;平台 WebView 差异和 Rust 维护需纳入验收 |
| Electron | Chromium/Node 与成熟桌面 Web 工作流;易迁移 JS 采集器 | 本项目没有既有 Node 采集实现,且希望减少自带运行时,因此不首选;不编造包体差值 |
| 原生 UI / egui / Flutter / Qt | 可实现桌面统计工具 | 当前无现成界面资产或团队约束支持承担第二套图表实现;仅在 Tauri 试验未达标时另做有界比较 |
| Python 原型 + 桌面壳 | 采集逻辑可复用 | 增加 Python 运行时和跨进程分发;保留为参考与迁移输入,不作为成品运行依赖 |
| 本地 HTTP 服务 + 浏览器 | 容易延续静态看板 | 不作为主交付,额外常驻服务和端口不符合当前简单桌面目标 |
Electron 的进程模型有 官方说明。 ECharts 的 按需导入有官方支持; 是否达到本项目资源上限仍需测量。M0 锁定实际依赖版本、许可证及兼容要求,不直接使用浮动 latest。
首发已确定 Windows 11 x64,GitHub CI 同时建立 macOS/Linux 作业, WSL 2 可作本地 Linux 构建试验。平台、制品和验证范围见 平台与 CI。
模块边界
本机 JSONL / JSON / SQLite / 可核验本机来源的导出文件 ↓源发现 → 版本/格式探测 → 有界读取 → 适配器 → 标准化/归属/去重 ↓ ↓ 逐源健康状态 SQLite 单写者事务与统计缓存 ↑↓可选本地 OTLP 接收器 → 白名单字段提取 有类型查询/刷新 IPC ↑↓ 本地静态前端、图表与设置后端按职责拆为 domain(统计语义)、ingest(读取与调度)、adapters(格式)、
storage(迁移与查询)、aggregates/query(聚合与查询)、app(IPC 与桌面生命周期)。
共享核心 crate 位于 desktop/src-tauri/crates/core,app 层位于 desktop/src-tauri/src
(目录化已随 M2/M6 实施)。
共享 Rust 核心还提供 headless 定时提取入口,启动该模式不创建窗口/WebView;
GUI、托盘和系统任务共享配置、跨进程所有权锁、采集队列与单写者规则,见 调度。
UI 不得直接读取任意文件、执行 SQL、访问 Agent 凭据或启动 shell。
导出文件只能由后端写到用户通过系统对话框选定的位置。
IPC 返回结构化 DTO:数值、已知字段数量、范围、来源状态、生成时间、数据修订号。 数值较大的 token 用十进制字符串传输,避免 JavaScript 超过安全整数后静默舍入; 图表可转换为缩放后的浮点展示,tooltip/导出保留精确整数。 分页明细、每图最大点数和允许的筛选字段由后端限制。
Agent 适配器目录与多版本组织
每个 Agent 的实现统一放在 desktop/src-tauri/crates/core/src/adapters/<agent_id>/,
即使只支持一个版本也使用独立目录。升级后需要兼容的历史版本仍在该 Agent 目录内实现。
codex.rs、claude.rs、pi.rs、omp.rs、gemini.rs、qwen.rs 的单文件迁移
已在 M2 完成(V30 结构检查 + 迁移前后回归见验证记录 m2d)。
此要求适用于 M2–M5 和后续 F1;同源衍生产品仍有自己的 Agent 目录及支持范围。
目标结构示意,agent_id 和 format_id 为占位标识,不代表已支持的产品或版本:
adapters/ mod.rs # 适配器导出与公共模块入口 framework.rs # 跨 Agent 的统一接口与采集流程 jsonl.rs # 跨 Agent 的通用读取器 usage_map.rs # 通用映射类型/辅助函数,产品特有映射下沉 <agent_id>/ mod.rs # 该 Agent 的稳定入口与统一接口实现 detect.rs # 产品/格式探测与版本分派 common.rs # 已证实可复用的 Agent 内部逻辑,按需建立 versions/ mod.rs # 已验证格式实现的注册与映射 <format_id>.rs # 各格式实现;复杂版本可再拆为同名子目录版本差异的解析、字段映射和生命周期逻辑放在 versions/ 的对应模块;
不要在适配器根目录新增 <agent>_v1.rs 等并列实现,也不要把全部历史差异堆回入口文件。
detect.rs 按来源文件/数据库的版本字段、记录类型或 schema 指纹选择实现,
不能用当前安装的 Agent 版本解释所有历史文件;同一源目录可含多个历史格式。
已知版本按映射选择实现;未知版本按下述兼容策略先尝试最新内置解析器。
Agent 发布版本、来源格式/schema 和本应用 parser_version 分别记录。 同一格式实现可覆盖多个发布版本,每个声明已验证支持的版本都须有核验依据与 fixture; 映射表明确版本到实现的关系,兼容尝试成功不自动升级为该版本已验证。 新增版本时保留已支持历史实现及回归样本,统一标准化输出和采集接口保持一致。
跨 Agent 的框架、读取器和经测试证明一致的辅助逻辑可共享;产品特有映射归各自目录。 目录迁移须保持公开入口、来源/记录身份和已有游标/解析状态兼容; 确需改变解析状态格式时另行版本化并验证恢复,不能因 Rust 文件移动生成新来源或重复计数。 fixtures 按 Agent 及版本/格式组织并注明确切来源版本;单元测试贴近版本模块, 集成测试可保留 Cargo 可发现的入口,必须经统一适配器入口覆盖探测、分派和历史回归。 迁移与验收步骤见 M2 和 V30。
未知版本的兼容尝试
本软件的新版本可能晚于 Agent 发布。版本号未收录或缺失时默认先尝试该 Agent 最新内置解析器, 无需等待软件更新或手动开启兼容模式。先确认 Agent 身份、输入类型和本机来源, 再选择该 Agent 对应输入类型的最新解析器;不把其他产品或任意未知文件交给通用猜测逻辑。 “最新”由 Agent 目录内的版本注册明确指定,指随应用发布的实现,不联网下载或执行解析器。
兼容尝试允许新增非必要字段;仍校验必需结构、字段类型/单位、token 包含关系、 记录身份及累计/逐次语义。缺失的可选字段保持 unknown;不能为了通过校验补零或猜测计量方式。 通过校验的数据正常入库并纳入统计,同时保留“使用最新解析器,版本兼容性未验证”标记; 可独立确认的部分数据可以保留,覆盖缺口随结果返回,不把整个未知版本一概排除。 仅忽略无法解释的记录不能据此报告完整成功;影响累计基线或调用关联时停止依赖它们的计算。 来源健康状态以实际解析结果判定:兼容解析且关键记录通过时保持正常;同一文件 出现坏记录(坏行/非法 token/缺关键字段/缺归属)时保留已确认调用,并提示需核对。 Codex 的累计快照差异(reconcile_mismatch/snapshot_regression)保留诊断与对账结果, 不单独降级读取健康;对照不相等仍显示 mismatch,不能宣称已证明用量完整。 新版独立逐次记录不依赖累计快照,旧版 total/last 识别调用所需的字段异常仍降级。 TokenCountEvent.info 缺失/null 是合法的无用量通知,不计调用、不补零。 此规则不自动推广至其他 Agent。已消费文件的增量扫描 沿用先前的版本选择依据,不能让“兼容”覆盖新出现的数据错误。
保存来源原始版本(可空)、所选格式与 parser_version、选择依据 known_version / latest_fallback、
兼容验证状态及有限失败原因,供诊断、查询、汇总和导出追溯;兼容状态与 token 字段质量分别记录。
通过结构校验只能说明当前数据可由该解析器处理,不能证明该版本所有字段和场景均兼容。
尝试后发现结构/语义不兼容、产品归属或格式匹配冲突时,明确标记失败或部分可用并保留旧结果。 失败批次不提交不可信事件、游标或聚合;不得返回“成功 0 条”掩盖失败。 解析器更新、来源变化或显式重扫后允许重新尝试,不永久封禁该版本。 后续取得逐版本样本后可增加专用实现;用稳定记录身份更正旧贡献并回归,不能再追加同一份用量。 此策略已在 M2 实施(探测/扫描共用版本注册表分派,兼容标记持久化于 usage_events.parse_basis 与 source_files.format_status); 各 Agent 的回退行为与标记见验证记录 m2d。
数据库选择
采用 SQLite,通过 Rust 的 rusqlite 使用随应用固定版本的 SQLite,使用显式 SQL 迁移。 最终 crate/SQLite 版本由 M0 核验。至少包含官方 WAL-reset 修复(3.51.3+ 或明确的已修复分支), 不仅凭系统 SQLite 名称判断安全版本。SQLite WAL 说明
| 方案 | 本任务考虑 | 结论 |
|---|---|---|
| SQLite | 事务、唯一键、局部更新、索引查询和本地文件;适合增量写入与交互查询 | 首选 |
| DuckDB | 擅长批量分析,官方明确大量小事务不是主要设计目标 | 暂不引入第二引擎;未来超大导出分析再评估 |
| KV 数据库 | 可存事件,但本项目仍需自行维护多维索引、迁移和聚合 | 增加这部分实现工作尚无充分依据 |
| JSON/CSV 单文件 | 便于交换数据 | 用于导出,不承担并发更新、索引和事务 |
| PostgreSQL 等服务型数据库 | 可扩展多用户并发 | 当前为单机桌面,无需用户维护数据库服务 |
DuckDB 的取舍来自 并发说明, 不声称 SQLite 在任何分析规模都更快或更小。
应用数据库放系统应用数据目录,与源码、源会话目录分开;不放网络共享或同步盘。
一个后台写者,少量只读连接;foreign_keys=ON、WAL、默认 synchronous=FULL。
通过批量事务减轻同步开销,不以降低持久性作为默认优化。
busy_timeout 和重试总时限有界;checkpoint 由写者调度,限制长读事务和 WAL 膨胀。
容量计算包含主库、WAL、备份和暂存文件;应用退出不能把清理成功作为依赖条件。
源 SQLite 与应用数据库使用不同读取规则:
- 对运行中的源库优先用 SQLite 只读连接和短事务,不改变 journal、schema 或做 checkpoint。
- 只读 WAL 需要满足对应文件/权限条件;如果无法保证不新建源端 sidecar,则不强行打开。
- 必要时从只读连接调用 Online Backup API 生成一致暂存副本,设置页/时间/空间上限并清理。
- 无法获得一致读取时标记 busy/unsupported,保留旧结果,提示导入工具官方导出或关闭后的副本。
- 不逐个复制活库
.db、-wal、-shm后宣称一致;不对活库使用immutable=1。
发现与今日刷新
初次启动只在已知产品候选目录做有界探测,展示发现项及能力;用户启用后才读取用量记录。 支持手动选择多个源根、显式环境覆盖和 IDE profile;不递归扫描整块磁盘。 同一路径的符号链接、Windows 大小写别名及重复配置先规范化,再确认文件/数据库身份。 仅本机 WSL/容器可显式添加并记录实例身份,不自动启动环境或 Agent。 远程目录、账号报表及云同步会话不作为本机用量;导入前检查来源归属,见 来源范围说明。
点击“采集并刷新”依次执行:
- 后端返回 job ID,并合并同源正在执行的刷新;UI 显示逐源状态。
- 扫描文件新增/变更及新的会话目录,读取增量和仍未终结的记录。
- 标准化并更新事件;事件、读取游标、解析上下文和受影响统计缓存原子提交。
- 发布新的数据修订号,UI 用同一修订的总计、图表和表格重新查询。
- 返回 added/updated/unchanged/skipped/error 及最近成功时间;失败不能伪装为零或切换到样例数据。
今日刷新允许更新历史日期:例如昨晚的请求今天才写出最终 usage。 刷新范围以“变化的数据源”为准,不只查今日文件名,也不每次重扫全部历史。 请求计数默认按来源提供的调用完成/用量事件时间,具体时间规则见 data-contract.md。
自动提取默认 1 小时,0 关闭全部自动触发;启用时启动校对全部启用来源。 逐源间隔/每日/每周规则覆盖全局节奏,手动仍可采集全部启用来源。 Windows 退出后采集默认关闭,启用后原生分钟任务按保存的意图和到期规则运行 headless;任务状态不等同于采集成功。GUI/headless 共用两个来源实例工作槽,数据库保持单写者。读取与解析时释放应用库锁, 文件归属、结果接纳和事务使用同一个 Storage 锁。 Windows 可选文件监听、节能暂停、逐源单调计时及协作式时间/重试限制已接入。 JSON/JSONL、SQLite 查询和备份分页均检查作用域取消;阻塞 OS 调用只能返回后检查, 不能保证立即强制终止。 要求及真实实现差异集中于 调度规则。 初始回填分页/分块提交,显示覆盖进度;不等待全部历史完成才显示今天。
各输入的增量策略
| 输入 | 游标及读取策略 | 关键失败处理 |
|---|---|---|
| JSONL | 文件身份 + generation + 完整行字节偏移 + 解析上下文 | 半行留待下次;截断/同大小替换/改名时重探测;不仅比较文件长度 |
| 整体重写 JSON | 稳定读取快照、内容指纹和内部记录 ID | 写入中变化则重试;记录以 ID 更新,不能拿文件偏移增量 |
| SQLite | schema 指纹 + 稳定键 + 更新序号;未完成记录重复检查 | 只有 created_at 无 updated_at 时用有界重扫与周期校对,不能承诺单一一小时窗口完整 |
| OTLP | 日志事件 ID/trace-span ID 或 metric series + start/end + temporality | 重传、累计值重置、采样和跨日区间独立处理 |
| CSV/JSON 导出 | 文件摘要 + 行语义身份 + 报表范围/修订号 | 覆盖同一报表分区,不能把重叠导出每次累加 |
默认单行上限 8 MiB、单块 4 MiB(允许跨块组装行)、单源每轮 30 秒, 这些是待测试数据校准的初值。超限不静默丢弃,状态显示位置与原因,允许受控重试。 JSONL 默认每文件读取窗 32 MiB,完整行续读及两来源实例槽的中断/事务规则见 调度规则;显式较大单行上限相应放大窗口。 常用日查询的可选派生结构、兼容与失效规则见 查询加速。 错误诊断只保存字段名、错误码及位置,不复制原始行内容。
仅本机的遥测与导入
M5 可选的 OTLP/HTTP 接收器支持 protobuf,按实际工具要求再启用 JSON; 只实现需要的 logs/traces/metrics 入口,不捆绑 Collector、Prometheus 或 Grafana。 默认关闭,不监听公网;启用时绑定 loopback,使用逐源随机令牌、体积/速率限制和无内容日志。 不能配置认证的发送端优先使用文件导出;不为了兼容开放无保护端口。 当前接收器已有 64 MiB 压缩/解压边界与明确允许的字段;接入全局每分钟 120 次、 最多 4 个同时处理连接的限制,超限返回 HTTP 429。此为本地接收边界的初始上限, 不能据此确认发送端版本、身份或完整性。Windows 已实施 Claude/Codex logs 和 CodeBuddy CLI 2.98.0 隔离 traces 逐源认证及当前用户系统凭据存储, 预览脱敏、失败回收、撤销及逐平台结果见 认证要求。 未核验的认证配置不开放无保护接入,其他平台原生存储及 V22/V25 剩余场景另验。 令牌绑定用户确认的本机 Agent 实例;loopback 地址本身不能证明数据在本机产生, 不接受 SSH 隧道、转发 Collector 或远端聚合导入。无法核验来源时标记排除,不能仅信任 host.name 字符串。
接收时只保留允许的数值及关联字段。CodeBuddy 的某些遥测模式会同时携带模型输入/输出, Qwen 的提示词日志开关也须明确关闭;不能照搬厂商完整调试配置。 应用生成可预览的最小配置,仅在用户明确操作后应用。 退出后无接收者的遥测不会自动补回,必须在数据源页说明采集窗口与丢失风险。 定时任务可以提取已保存的遥测;不能通过每隔几分钟短暂监听补齐流式遥测历史。 用量采集不请求远端用量、账户余额或企业分析接口。费用使用随包/手工价格快照, 可选在线刷新默认关闭,仅下载 models.dev 的公开 api.json,不携带本机数据; 缓存、失败回退和历史估算规则见 价格规则。
JetBrains 自家 AI Assistant/TRAE 的本地用量格式核验在 F1,当前不实施。 Junie CLI/Zed 内置已归 M8;JetBrains Copilot 已核验手工 OTel file 路线,见 接入矩阵,真实非空导出仍需独立验收。 官方企业 API/账号报表资料只说明范围差异,不进入实现计划;下载后的远端报表仍排除。 只允许用户选定且可核验本机来源的导出。没有可靠本地 usage 时显示受限,不增加账号登录回退。 本地接收令牌/项目 HMAC 密钥进系统密钥存储,不进 SQLite、前端、导出或日志。
数据与 UI 安全
只存允许保留的统计字段,不存提示词、回答、工具参数、完整诊断日志、token 密钥或账号邮箱。 项目维度启用后用本机密钥 HMAC 形成不可直接反推路径的标识;显示名称由用户设置。 UI 仅加载随应用发布的资源,CSP 与 Tauri capabilities 采取最小允许列表, 不加载远端网页或从采集内容生成 HTML。CSV 导出防公式注入,来源名称按纯文本渲染。 统计数据库默认不是加密数据库;依赖当前用户权限和系统磁盘保护,产品说明应如实表达。
资源与性能目标
基准环境暂定 Windows 11 x64、4 核 CPU、16 GiB 内存、本地 SSD、既有 WebView2, release 构建。记录实际机器配置并分别测冷/热缓存;开发机实测不能直接确认达到拟定基准。 最新测量与未达项见 最新验收。
| 项目 | 拟定目标 | 测量范围 |
|---|---|---|
| 压缩应用安装包 | ≤ 20 MiB | 不含可选 WebView2 离线包;同时报告后者下载与安装体积 |
| 应用安装目录 | ≤ 60 MiB | 不含用户数据;说明共享运行时的增量占用 |
| 前端 JS + CSS | gzip ≤ 1 MiB | 另报未压缩嵌入大小;无 CDN、远程字体、全量图标库 |
| 空闲内存 | 10 分钟均值 ≤ 350 MiB,采样峰值 ≤ 400 MiB | 全进程 private bytes:主程序及所有本应用 WebView 子进程;默认 GPU、完整可见总览,另报 working set |
| GUI 首次导入峰值内存 | 全进程 private bytes ≤ 512 MiB | 100 万条事件,从非空合成来源解析到落库、汇总及界面更新;不能一次载入全文件集合;headless 分开记录 |
| 空闲 CPU | 10 分钟平均 < 单个逻辑核的 1% | 页面静止、无源变更;另报后台轮询唤醒次数 |
| 首屏 | P95 ≤ 2 秒 | 已有 100 万条事件,测启动至可交互总览,不等全部源扫描 |
| 常用查询 | P95 ≤ 200 毫秒 | 366 天、50 模型、20 Agent 的日汇总筛选;复杂明细另报 |
| 今日增量刷新 | P95 ≤ 2 秒 | 本地已发现源合计新增 1,000 条,包含提交和 UI 更新;首次回填另测 |
| 本地观察新鲜度 | 默认任务启用时 ≤ 所配刷新间隔 + 65 秒 | 默认间隔 1 小时;更短间隔(如 60 秒)另测;自定义频率/系统任务与源本身延迟单独显示 |
2026-10-04 根据现有单窗口、SVG 图表与百万库的完整原生实测调整内存上限: 空闲均值/峰值 299.90/363.30 MiB,主程序均值 17.01 MiB,GPU 146.24 MiB。 原 180 MiB 不适合作为当前 Windows GUI 的强制上限;350/400 MiB 分别给本次 均值/峰值约 17%/10% 余量。这是项目工程取舍,不是 WebView2 官方最低内存保证。 首次导入增加到 512 MiB,避免原 300 MiB 导入目标低于已观测 GUI 空闲峰值; 导入验收仍须覆盖实际解析、事务和 UI,不从空闲值或无界面值推断通过。 微软性能指导 确认多进程与 GPU/驱动缓冲开销并建议保留硬件加速,未给统一 MiB 下限。 暖空页诊断只帮助定位,不能证明全新空页最低开销或代替完整产品。 空闲测量前停止采集/维护并等待页面稳定;固定窗口、DPI、运行时与驱动, 至少覆盖 600 秒、同时满足均值和峰值,观察是否持续增长。调整资源上限不免除泄漏排查、 拟定硬件复测、托盘后台与其他原生平台验收;本轮开发机结果按新资源上限单独标注。
100 万条及 1,000 万条分别报告主库、索引、WAL、吞吐和查询计划。 超大档不是首版自动承诺;超过目标先定位依赖、查询或解析问题,再提出可审阅调整。 无法达标不能通过省略 WebView 子进程、运行时或取消来源来美化结果。 另报托盘后台和 headless 单次执行的峰值/耗时/唤醒次数;无窗口提取不应启动 WebView 子进程。 macOS/Linux 记录各自平台等价指标,不把 Windows private bytes 直接作为跨系统可比数值。