HOMECORE-RECORDER 全解析:RuView 中用 HA 兼容 SQLite Schema 与 ruvector HNSW 构建的状态历史与语义检索
导读
homecore-recorder 是 RuView 项目 HOMECORE 生态(ADR-126 主导的原生 Rust Home Assistant 移植计划)中负责持久化状态历史与语义检索的 Rust crate。它以 SQLite 为存储引擎,完整镜像 Home Assistant Recorder schema v48 的四张表结构与去重键约定,使 HOMECORE 可以直接读取用户已有的 HA home-assistant_v2.db;同时通过 feature-gated 的 ruvector 语义索引,把「查询历史状态」从传统的 SQL BETWEEN 升级为 HNSW 上的 k-NN 向量近邻检索。本文以 ADR-132 为骨架,结合 crate 源码逐层拆解其表结构、捕获链路、去重原理、双写架构、文本/语义双查询路径与安全加固结论,并给出可直接运行的构造与查询示例。
读完本文,你将掌握:HA Recorder schema v48 的关键表结构及其在 schema.rs 中的落地方式、RecorderListener 如何挂接 HOMECORE 事件总线、DedupEngine/FNV-1a 哈希去重的实现细节、P2 哈希嵌入语义检索的边界与 P3 演进方向,以及该 crate 经安全评审确认的各项加固(有界查询、事务化 purge、fail-closed 写入)。
1. 为什么 HOMECORE 需要一个 Recorder(决策上下文)
1.1 迁移与共存:复用 HA 的 on-disk Schema
ADR-126 决定用 Rust 原生重实现 Home Assistant 的数据模型与 API 契约。而 HA 本身把每次状态变更都写入一个 SQLite recorder 数据库,下游的历史曲线、logbook、长期统计、引用历史状态的自动化条件全部读取这份存储。HOMECORE 因此需要一条持久的"状态历史主干"。
决策由两股力量驱动:
- 迁移/共存成本。采用 HA 的用户手上已经有一份 HA 的
recorder数据库。与其另起炉灶发明新 schema,不如直接复用 HA 的 on-disk schema——这样 HOMECORE 既能直接读取既有的 HAhome-assistant_v2.db,HA 生态的工具也能直接读取 HOMECORE 生成的历史库。这与 ADR-165 中homecore-migrate处理.storage/*.json时遵循的是同一条信任边界思路。 - 语义化查询的缺口。HA 的历史检索本质是 SQL
BETWEEN/WHERE行扫描,不支持自然语言语义查询。而 HOMECORE 平台已携带 ruvector(ADR-124)向量检索能力,所以 recorder 可以额外把状态变更向量化,用 k-NN 回答类似「下午 3 点哪些厨房设备是热的?」这类问题——这是 HA 本身不具备的能力。
1.2 Recorder 的承重定位
Recorder 是 HOMECORE 的 durable-state surface(持久化状态面):如果它出错,历史、logbook、基于历史条件的自动化会全部出错。ADR-164(ADR 语料缺口分析)曾将该 crate 标为 CRITICAL 覆盖缺口(Gap G3)——一个如此承重的 crate 却缺少治理性 ADR。本 ADR 属于 retroactive(追溯性)文档:crate 的 Cargo.toml、README.md、各源码文件都引用 "ADR-132",但它以 ADR-126 系列映射中规划的条目形式随 crate 一并发布;本文档记录的是已构建、已测试代码已蕴含的决策,不引入新设计。
说明:本 ADR 的「Deciders = ruv」,正式批准状态为 Accepted,日期对齐 crate intake 时期(2026-05-25),真实实现落盘提交
7c8071145在 2026-06-11。
2. 决策总览:三阶段交付
ADR-132 的决策是把 homecore-recorder 做成一个带 HA 兼容 schema 的 SQLite 状态历史记录器,外加可选的 ruvector 语义索引,分三个阶段落地:
| 阶段 | 内容 | 状态 | 验证方式 |
|---|---|---|---|
| P1 | SQLite 结构化持久化(HA schema v48 兼容) | 已构建、已测试 | cargo test -p homecore-recorder --no-default-features(14 个测试) |
| P2 | ruvector HNSW 语义索引(feature-gated,哈希嵌入) | 已构建、已测试 | cargo test -p homecore-recorder --features ruvector(20 个测试) |
| P3 | ruvector-attention 真句向量嵌入(dim → 384) | 计划中(未实现) | README 与 Cargo.toml 中显式标注 |
lib.rs 的模块注释给出了 P1 数据流骨架:
StateMachine ──broadcast──► RecorderListener ──► Recorder
│
┌───────┴──────────┐
states state_attributes
events recorder_runs
而 Cargo.toml 头部注释则记录了 feature 矩阵与测试命令。默认构建不开启任何额外 feature(default = []),ruvector feature 只在显式指定 --features ruvector 时引入 ruvector-core 与 sha2。
3. P1 存储层:SQLite + HA Recorder Schema v48
3.1 依赖选型:只带 SQLite 后端的 sqlx
Cargo.toml 中的依赖取舍值得注意:
- 通过
sqlx-core+sqlx-sqlite(均锁定=0.8.6)而非 umbrellasqlx包接入,避免把未使用的 MySQL 后端(及其易受攻击的rsa依赖)解析进Cargo.lock。这也是一个供应链安全层面的刻意决策。 - 时间处理用
chrono,序列化用serde/serde_json,错误处理用thiserror,结构化日志用tracing,异步 trait 用async-trait。 - SQLite 驱动开启
bundled(自带 SQLite),连接池上限为 4(见db.rs的open_with_index)。
3.2 四张核心表:与 HA 1:1 对齐
schema.rs 用常量字符串形式给出了完整 DDL,镜像 HA 2025.1 引入的 schema v48:
state_attributes —— 共享属性 JSON 块(按哈希去重)
CREATE TABLE IF NOT EXISTS state_attributes (
attributes_id INTEGER PRIMARY KEY NOT NULL,
shared_attrs TEXT NOT NULL,
hash INTEGER NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS ix_state_attributes_hash
ON state_attributes (hash);
shared_attrs 存 TEXT(JSON 块);hash 是对该 JSON 串做 FNV-1a 64-bit 后按有符号 i64 存储——这与 HA 的去重键完全一致。多个状态共享同一份属性时只保留一行,显著减少高频轮询传感器的写入 I/O。
states —— 每次状态写入一行
CREATE TABLE IF NOT EXISTS states (
state_id INTEGER PRIMARY KEY NOT NULL,
entity_id TEXT NOT NULL,
state TEXT,
attributes_id INTEGER,
last_changed_ts REAL,
last_updated_ts REAL NOT NULL,
context_id TEXT
);
CREATE INDEX IF NOT EXISTS ix_states_entity_id_last_updated_ts
ON states (entity_id, last_updated_ts);
CREATE INDEX IF NOT EXISTS ix_states_last_updated_ts
ON states (last_updated_ts);
entity_id 是校验过的 domain.name 字符串;state 是 "on"/"off"/"20.5" 之类的状态值;attributes_id 外键指向 state_attributes(为 HA 兼容允许 NULL);last_changed_ts/last_updated_ts 为 REAL Unix 秒;context_id 为 UUID 文本,用于串联因果链。
events —— 领域事件
CREATE TABLE IF NOT EXISTS events (
event_id INTEGER PRIMARY KEY NOT NULL,
event_type TEXT NOT NULL,
event_data TEXT,
time_fired_ts REAL NOT NULL,
context_id TEXT
);
CREATE INDEX IF NOT EXISTS ix_events_event_type_time_fired_ts
ON events (event_type, time_fired_ts);
event_type 形如 "state_changed"、"call_service";event_data 为 JSON 块。
recorder_runs —— 启动/关停的边界记录
CREATE TABLE IF NOT EXISTS recorder_runs (
run_id INTEGER PRIMARY KEY NOT NULL,
start_ts REAL NOT NULL,
end_ts REAL
);
记录每次 start/stop 对,供历史 API 标注数据缺口。
四张表的 DDL 全部使用 CREATE TABLE IF NOT EXISTS,因此 apply_schema 是幂等的——每次启动时重复应用都安全。实现细节见 db.rs:sqlx::query 不支持多语句字符串,代码先把每个 DDL 块按 ; 切分、逐条执行。
3.3 FNV-1a 去重哈希:与 HA 逐字节对齐
dedup.rs 实现了 HA db_schema.py 中 fnv64a 的精确副本,用于指纹化共享属性块:
- Offset basis:
0xcbf29ce484222325;Prime:0x100000001b3;每字节hash = (hash XOR byte) * prime。 - 以位重解释(bit-reinterpret)方式转成有符号
i64(而非数值转换),与 HA 完全一致。
源码注释里给出三个基准值,且均有单测锁定(见 dedup.rs):
| 输入 | 有符号 i64 结果 |
|---|---|
空串 "" |
-3750763034362895579 |
"a" |
-5808556873153909620 |
{"state": "on"} |
3947789143477681127 |
在写入路径(db.rs record_state)中,该哈希承担属性去重职责:序列化 new_state.attributes → 计算 fnv64a_hash → 先按 hash 查已有 attributes_id 并复用,否则插入新 state_attributes 行。测试 same_attrs_dedup_to_one_row 验证了「两个实体共享同一份属性时只生成一行 state_attributes、却各自拥有独立 states 行」。
3.4 持久化位置与直接可查性
默认持久化路径为 .homecore/home.db(可通过构造函数入参配置),在 README.md 中有明示。Recorder::open 接收任意路径字符串,传 "sqlite::memory:" 即可获得内存库(测试广泛使用),传其余路径时以 create_if_missing(true) 自动建库建表。
选择标准 SQLite 意味着没有任何私有导出格式:任何能读 SQLite 的工具都能直接查询历史,无需经过 HOMECORE API。
4. P1 捕获层:监听事件总线,落库状态变更
4.1 RecorderListener 的订阅模型
listener.rs 实现了事件总线监听者:
RecorderListener::new(state_machine, recorder)在构造时就调用state_machine.subscribe()完成订阅,而不是在spawn()之后——这保证new()到spawn()之间触发的事件被缓冲在 receiver 队列中,不会被漏掉(源码注释明确说明了这一顺序性设计)。spawn()返回JoinHandle,在优雅关停时调用handle.abort()即可。- 运行循环
run()中三种退出/降级分支:- 正常收到
Ok(event)→ 调recorder.record_state(&event); Lagged(n)(订阅者落后超过 4,096 个事件)→ 记录 warning 后从下一个可用事件继续,而非崩溃退出,因为「丢弃监听者」等同于静默停止持久化;Closed(StateMachine 关停)→ 记录 debug 日志后正常退出。
- 正常收到
Recorder 本身用 Arc 承载 SqlitePool 与语义索引,因此 Clone 极廉价,可以把副本同时交给监听者与 API 历史处理器。
4.2 谁把事件推进总线
在 ADR-126 的架构图里,homecore-recorder 订阅的是 ADR-127 定义的 HOMECORE 状态机 + Tokio broadcast 事件总线。crate 依赖 homecore(path = "../homecore"),并从 homecore::state::StateMachine 订阅 StateChangedEvent。listener.rs 的集成测试展示了最小闭环:建内存 recorder → new + spawn → sm.set(...) 触发两次状态变更 → 从 get_state_history 读回两行且顺序为 on/off。
5. P2 语义检索:ruvector HNSW(feature-gated)
5.1 SemanticIndex trait 与 NullSemanticIndex
db.rs 定义了可插拔的语义索引 trait:
insert_state(state_id, state):在 SQLite 插入成功之后被调用(state_id即行 id,用于把向量结果映射回 SQLite 行);不得把错误回传给 recorder——失败只记日志,不阻断持久化(fail-closed 语义的另一面)。search(query, k):嵌入自由文本查询,返回(state_id, score)升序距离列表。NullSemanticIndex:在ruvectorfeature 关闭时提供零分配的 no-op 实现,满足 trait bound,使结构化的存储主干不依赖 ruvector 即可独立发布。语义索引是可加的、feature 门控的——这是 ADR 列出的关键正面收益之一。
Recorder 内部用 Arc<RwLock<dyn SemanticIndex>> 持有索引,因此 trait 的 &mut self 方法无需调用方持有 &mut Recorder。
5.2 P2 嵌入策略:SHA-256 哈希向量(诚实的占位)
semantic.rs 中,P2 嵌入刻意采用确定性哈希程序,避免在 P2 引入 ML 模型依赖:
- 把状态规范化为字符串
"{entity_id}={state}|{attributes_json}"; - SHA-256 → 32 字节;
- 每 4 字节解释为一个大端
i32,转成f32,共 8 维; - L2 归一化得到单位向量(
norm > 1e-10守卫)。
EMBEDDING_DIM = 8。搜索用余弦距离(匹配单位归一化向量)。crate 自述明确:哈希嵌入不捕捉语义相似度——两个状态值相同但实体 ID 不同的状态向量会不同。ADR 与 README 均反复强调:这是诚实的占位实现,提供了可工作的 HNSW 表面,但不得被引用为语义质量已验证。
5.3 HNSW 配置与端到端行为
RuvectorSemanticIndex::new(max_elements) 使用 ruvector-core 的 VectorDB,配置如下(见 semantic.rs):
dimensions = 8,distance_metric = Cosine;HnswConfig { m: 16, ef_construction: 100, ef_search: 50, max_elements };- 索引完全驻留进程内存——重启即清空,P3 才引入持久化。
文本兜底路径:search_semantic(query, k)(db.rs)先从向量索引取 top-k,若命中为空(默认 NullSemanticIndex 或尚无向量可查),则透明回退到真实 SQL 文本检索 search_states_by_text——调用方永远得到真实匹配行,而不是静默的空 Vec。该行为由测试 search_semantic_falls_back_to_text_with_null_index 锁定。
5.4 SQL 文本检索与 LIKE 元字符转义
search_states_by_text 是与 feature 无关的查询路径:大小写不敏感的 LIKE 在 entity_id、state 值或属性 JSON 上做匹配,按 last_updated_ts DESC 取最近 k 条;空查询返回"最新活动"视图。所有参数均以 ? 绑定,其中唯一用 format! 构造的是 LIKE 的 pattern 串——该串本身作为绑定参数传入,并带 ESCAPE '\' 与 % _ \ 转义。语义见测试 like_metacharacters_in_query_are_literal_not_wildcards:搜索文本中的 % 只会字面匹配 100%,而不是变成通配符。
5.5 语义查询示例(P2 形态)
在 --features ruvector 下,可构造最小可运行端到端流程:
use std::sync::Arc;
use tokio::sync::RwLock;
use homecore_recorder::{Recorder, semantic::RuvectorSemanticIndex, db::SemanticIndex};
let idx = Arc::new(RwLock::new(RuvectorSemanticIndex::new(1000)?));
let semantic: Arc<RwLock<dyn SemanticIndex>> = idx;
let recorder = Recorder::open_with_index("sqlite::memory:", semantic).await?;
// record_state(...) 之后,insert_state(state_id, state) 由 Recorder 内部自动调用。
// 用与嵌入相同的规范串查询,即可经 HNSW 找回对应 state_id:
let rows = recorder.search_semantic(&query, 5).await?;
注意:如上所述,P2 嵌入是哈希而非句子级语义,README 以注释形式保留了 P3 语义索引的调用设想(
index.search("find all warm rooms at 3pm", 5)),并明确标注"尚未实现"。
6. 查询、恢复与清理:Recorder 的完整操作面
crate README 的能力表与 db.rs 的方法一一对应:
| 能力 | 类型 | 方法 | 说明 |
|---|---|---|---|
| 记录状态变更 | 监听 | RecorderListener |
订阅 HOMECORE 事件总线,写 SQLite |
| 查询状态历史 | SQL | get_state_history / get_state_history_limited |
标准 SQLite,处处可查 |
| 清理旧状态 | 维护 | Recorder::purge(older_than) |
事务化、exclusive 截止、GC 孤儿属性块 |
| 恢复最新状态 | 启动 | Recorder::restore_latest(states, limit) |
按实体 ID 排序、有界、隔离坏行 |
| 写入去重 | 去重 | fnv64a_hash |
属性哈希不变则复用行 |
| 语义索引 | 索引 | SemanticIndex::insert_state(P2) |
哈希嵌入;P3 换真句向量 |
| 语义搜索 | 搜索 | search_semantic / search_states_by_text |
向量优先、SQL 文本兜底 |
6.1 直接 SQL 查询示例(可复制运行)
读写直接经由 SQLite,README 给出了两条开箱即用的 SQL:
-- 近一小时内 light.kitchen 的全部状态变化
SELECT state, attributes, last_changed
FROM states
WHERE entity_id = 'light.kitchen'
AND last_changed > datetime('now', '-1 hour')
ORDER BY last_changed DESC;
-- 按小时聚合亮度均值(JSON_EXTRACT)
SELECT
strftime('%Y-%m-%d %H:00:00', last_changed) AS hour,
JSON_EXTRACT(attributes, '$.brightness') AS brightness
FROM states
WHERE entity_id = 'light.kitchen'
GROUP BY hour;
6.2 启动恢复:latest_states 与 restore_latest
latest_states(limit)(db.rs)的核心是窗口函数查询:用 ROW_NUMBER() OVER (PARTITION BY entity_id ORDER BY last_updated_ts DESC, state_id DESC) 选取每个实体最新的一行,按实体 ID 升序输出,上限 100,000 行(MAX_RESTORE_STATES);坏行(非法 entity_id、缺 state、JSON 属性解析失败、非法时间戳、非法 context UUID)被逐个跳过并生成类型化 warning,单条损坏不影响其余实体恢复。restore_latest 用 Context::restoration(parent_id) 标记恢复上下文,states.restore(state) 装回状态机且不产生新 recorder 事件,随后才启动 recorder 监听者与自动化引擎(见 ADR-132 §3a)。
6.3 事务化 purge:有界磁盘增长的答案
ADR 记载了一段真实教训:README 曾宣传 Recorder::purge,但长期缺失 retention 路径导致磁盘无界增长(Disk-DoS)。补齐后的 purge(older_than)(db.rs)具备三重保障:
- Exclusive(排他)cutoff:只删
last_updated_ts < cutoff的行,等于 cutoff 那一刻的行被保留——幂等、无 off-by-one,杜绝「保留窗口刚打开瞬间的行被误删」。 - 单事务原子性:
states、events删除与state_attributes孤儿清理(NOT IN (SELECT attributes_id FROM states ...))在同一事务内,中途失败整体回滚,绝不出现半清理状态。 - 共享块延迟 GC:被去重共享的属性块只有在最后一个引用它的 states 行消失后才会被回收。
测试 purge_gcs_orphaned_attributes_but_keeps_shared 精确验证:删除 sensor.a 时共享块仍被 sensor.b 引用 → 保留;再删除 sensor.b → 块孤儿 → 被 GC。purging 释放的是逻辑行;SQLite 会复用已释放页面,因此周期性 purge 下即便不做 VACUUM,磁盘增长也是有界的。
7. 有界查询:内存-DoS 防线(2026-06 安全评审修复)
ADR-132 §3a 记载了 2026-06 在 ADR-154–159 之后的一轮专项安全评审,homecore-recorder 被确认清理项与修复项如下。
7.1 修复项 1:get_state_history 无界 → 现在封顶
原实现无 LIMIT:宽时间窗 × 高频实体(如每秒轮询的功率传感器,约 86k 行/天)会把数百万行一次性载入内存 Vec。修复后 get_state_history 在 SQL 层硬性封顶 MAX_HISTORY_ROWS = 1_000_000(db.rs),get_state_history_limited 允许调用方自选上限但再次取 min 封顶;需要更宽跨度的调用方必须收窄窗口分页读取。测试 history_query_carries_a_limit_clause 与 history_caller_limit_is_applied_before_materialization 锁定「LIMIT 在物化前生效」。兄弟路径 search_states_by_text/search_semantic 本身已按 k 有界。
7.2 修复项 2:文档化但缺失的 purge
即上文 6.3 的事务化 purge 补齐(README 广告与实现之间的缺口被闭合,测试 19 → 25 / 25 → 31 全绿)。
7.3 确认无风险的项
- SQL 注入 —— 干净:
db.rs每个查询使用绑定?参数,无任何用户或实体可影响的字符串经format!/拼接进入 SQL;唯一format!只构造 LIKE pattern 且自身作为绑定参数 +ESCAPE '\'。测试malicious_entity_id_is_stored_literally_not_executed用一个状态值'; DROP TABLE states; --证明 states 表完好且内容逐字往返。 - NaN 索引投毒 —— 结构上不可能:嵌入为 SHA-256 →
i32→f32,i32→f32转换恒有限(绝不 NaN/Inf),全零摘要由norm > 1e-10守卫;空索引搜索、空串查询、k=0均返回Ok(0)不 panic。语义索引路径无任何原始传感器 float 进入索引(与 calibration/vitals/geo 路径不同)。 - Fail-closed 写入:移除事件返回
Ok(None);语义索引失败只记日志不阻断 SQLite 持久化;EntityId解析失败回退到哨兵值(unknown.unknown)而非 panic。 - 启动状态恢复:
latest_states(limit)按(last_updated_ts, state_id)决胜取每实体最新行、按实体 ID 排序、请求封顶 100,000 行、坏行带类型化 warning 跳过;restore_latest保留原始时间戳并以homecore.restore上下文安装快照。
7.4 评审后的测试规模
homecore-recorder 测试规模从 19 → 25(--no-default-features)、25 → 31(--features ruvector),0 失败;Python 确定性证明不变(recorder 不在信号证明路径上)。
8. 后果与诚实边界(ADR-132 原文)
正面后果:
- HA-schema 兼容令迁移与共存变便宜:HOMECORE 可直接读既有 HA
recorder.db,任何 SQLite 工具也可读 HOMECORE 的历史库。 - 语义索引是增量、feature-gated 的:承重的结构化 recorder 对 ruvector 无硬依赖,存储主干先行发布。
- 标准 SQLite:无专有导出格式,历史直接可查。
负面 / 诚实限制:
- P2 语义检索用哈希嵌入而非真句向量——查询质量在 P3 前有限;已在 crate 文档与本 ADR 双重披露,不得被引用为语义质量已验证。
- 尚无 per-crate 基准:README 中的延迟数字(状态写入 p50 < 2 ms、1 M 记录下语义搜索 < 10 ms)是设计目标/估算,需以 criterion 基线验证(
cargo bench -p homecore-recorder --features ruvector)。 - 钉在 HA schema v48 上,把 HOMECORE 与特定 HA recorder schema 代际耦合;未来 HA schema 升级需要显式迁移步骤。
中性:
性能参考口径(README 性能节,均标注"尚无 per-crate 基准,后续 issue 跟踪基线测量"):
- 状态写入 p50 < 2 ms(SQLite WAL 追加)、p99 < 15 ms(磁盘 fsync);
- 索引实体 ID 查询 < 1 ms;全表范围扫描 < 50 ms;
- P2 语义搜索:1 M 状态记录下 k-NN < 10 ms(ruvector HNSW);
- 每百万条记录内存开销约 ~10 MB(SQLite 索引开销);每条状态约 2–4 KB 磁盘(entity_id + 属性 + 时间戳)。
9. 快速上手:构建、测试与最小接线
在仓库 v2/ 工作区中:
# P1(无 feature):14 个测试
cargo test -p homecore-recorder --no-default-features
# P2(ruvector 语义索引):20 个测试
cargo test -p homecore-recorder --features ruvector
# 构建 P2
cargo build -p homecore-recorder --features ruvector
# criterion 基准(基线尚待建立)
cargo bench -p homecore-recorder --features ruvector
最小接线(P1,取自 crate README 的 Usage,整理为可直接阅读形态):
use homecore_recorder::{Recorder, RecorderListener};
use homecore::HomeCore;
#[tokio::main]
async fn main() {
let homecore = HomeCore::new();
// 创建 recorder(默认写 .homecore/home.db)
let recorder = Recorder::new(".homecore/home.db").await.expect("init recorder");
// 创建并 spawn 监听者(订阅在 new() 时立即生效)
let listener = RecorderListener::new(homecore.state_machine(), recorder);
listener.spawn();
// 此后所有 StateChanged 事件都会持久化到 SQLite
}
注意:
RecorderListener::new在 crate 源码中接收&StateMachine并在内部subscribe();上面homecore.state_machine()为示意调用形态,实际以homecorecrate(ADR-127)暴露的 API 为准。
10. 相关资源索引
- ADR 决策文档:ADR-132(本文主体)、ADR-126 HOMECORE 主计划(系列映射:ADR-132 = HOMECORE-RECORDER)、ADR-127 状态机、ADR-130 REST/WebSocket 查询面、ADR-165 HOMECORE-MIGRATE、ADR-164 缺口分析(Gap G3)、ADR-124 ruvector/SENSE-BRIDGE。
- crate 源码:Cargo.toml(feature 矩阵)、lib.rs(P1/P2 架构注释与公共 API 再导出)、schema.rs(四张表 DDL)、db.rs(写入/查询/purge/恢复/安全加固)、dedup.rs(FNV-1a)、listener.rs(总线监听)、semantic.rs(P2 哈希嵌入 + HNSW)。
- HA 上游概念参照:Home Assistant Recorder integration(SQLite 默认存储;schema 迁移与 v48 概念)。本文不转载外部链接正文,读者可在仓库 README 中溯源。
综合来看,homecore-recorder 的价值在于双轨历史:一条 SQLite 结构化轨道承担「可迁移、可审计、可直接 SQL 查询」的持久事实;一条可选的 HNSW 向量轨道(P2 诚实占位、P3 演进到真句向量)为未来「用自然语言问历史」的能力铺路。若你正在 RuView 仓库中研究 HOMECORE 的历史子系统或规划 HA 兼容的本地智能家居枢纽,本文所述的 schema、去重、双写与加固结论都可直接作为源码级参考。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00