> For the complete documentation index, see [llms.txt](https://tinyhumans.gitbook.io/openhuman/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tinyhumans.gitbook.io/openhuman/zh/gong-neng/obsidian-wiki/scoring.md).

# 评分与排序

并非每个块都值得在 Memory Tree 中占有一席之地。像“thanks!”回复、邮件页脚或日历自动通知几乎不带任何信号，把它们纳入摘要树只会稀释结果并消耗 LLM token。评分就是门槛：一个按块执行的流程，在 **分块之后、将块追加到 L0 缓冲区之前**运行，决定该块是否值得保留，并用提取出的实体丰富它，同时为检索建立索引。

入口点是 `score_chunk` ，位于 [`src/openhuman/memory/tree/score/mod.rs`](https://github.com/tinyhumansai/tinymemory/blob/1d6b997874a06600ba0c4922708b5613497c9ffe/crates/tinymemory-core/src/tree/score/mod.rs)。它是一个纯函数——会计算结果但不会触碰存储；调用方根据 `ScoreResult::kept`.

***

## 为什么需要评分

有两个目标，都服务于一个紧凑且相关性高的树：

* **让树保持高信号密度。** 树负责压缩 *和* 导航。引入噪声会让这两者都变差——摘要会更含糊，检索会暴露垃圾内容。
* **控制成本。** 深度路径可能会调用 LLM 进行实体提取和重要性评级。评分的结构设计确保明显应保留和明显应丢弃的内容都不会付出这笔成本——只有真正处于边界上的块才会查询模型。

***

## 这些信号

`score_chunk` 计算一组相互独立的信号，每个都归一化到 `[0.0, 1.0]`，定义在 [`score/signals/`](https://github.com/tinyhumansai/tinymemory/tree/1d6b997874a06600ba0c4922708b5613497c9ffe/crates/tinymemory-core/src/tree/score/signals)中。它们会合并为一个加权总分，并与之一起存储在 `mem_tree_score` 中，因此每次接收/丢弃决策都可审计。

| 信号                | 衡量什么                                                                                      | 默认权重 |
| ----------------- | ----------------------------------------------------------------------------------------- | ---- |
| `token_count`     | 在块大小上形成平台期：低于 `TOKEN_MIN` (10) 时为 0，到 `TOKEN_RAMP_LOW` (30) 时升至 1，随后向 `TOKEN_MAX` (8000). | 1.0  |
| `unique_words`    | 词元比噪声检测器；词汇多样性低则得分低，极短消息返回中性的 0.5。                                                        | 1.0  |
| `metadata_weight` | 每个 `SourceKind` 的基础权重（Email > Document > Chat）。                                           | 1.5  |
| `source_weight`   | 按`DataSource` 推断出的每个 `provider:<name>` 标签的权重，采用 `SourceKind` 默认值。                         | 1.5  |
| `interaction`     | 参与度标签加成（`sent`, `reply`, `dm`, `mention`）；缺失标签则返回 0.5，因此静默内容不会被惩罚。                        | 3.0  |
| `entity_density`  | 每个 token 的不同实体数，最多约 1 个实体 / 100 个 token。实体越多 → 内容越实在。                                     | 1.0  |
| `llm_importance`  | 在 `[0.0, 1.0]`中由 LLM 生成的重要性评分。 `2.0` 默认关闭；一旦接入 LLM 提取器，权重                                 | 0.0  |

`interaction` 就会 `SignalWeights` ([`signals/types.rs`](https://github.com/tinyhumansai/openhuman/tree/main/src/openhuman/memory/tree/score/signals/types.rs)); `combine` / `combine_cheap_only` ，位于 [`signals/ops.rs`](https://github.com/tinyhumansai/openhuman/tree/main/src/openhuman/memory/tree/score/signals/ops.rs) 生成归一化后的总分（廉价版本不包含 `llm_importance` 项）。

***

## 接收门槛

```
chunk --> regex extract --> cheap signals --> combine_cheap_only
                                                     |
              +--------------------------------------+--------------------------------------+
              |                                      |                                      |
     total >= DEFINITE_KEEP (0.85)         DEFINITE_DROP < total < KEEP            total <= DEFINITE_DROP (0.15)
        接收，跳过 LLM                  边界情况 -> LLM 提取，                   丢弃，跳过 LLM
                                          合并，重新计算，重新组合
              |                                      |                                      |
              +--------------------------------------+--------------------------------------+
                                                     v
                              final total >= DROP_THRESHOLD (0.3) ?  --> 接收 / 丢弃
                                                     v
                              提取实体 --> 规范化 --> 索引 + 嵌入
```

这三个分段常量定义在 `mod.rs`:

* `DEFAULT_DEFINITE_KEEP = 0.85` - 低于或等于此廉价总分的内容会在不调用 LLM 的情况下直接接收。
* `DEFAULT_DEFINITE_DROP = 0.15` - 高于或等于此廉价总分的内容会在不调用 LLM 的情况下直接丢弃。
* `DEFAULT_DROP_THRESHOLD = 0.3` - 在任何 LLM 增强之后应用的最终接收阈值。

只有那些廉价总分落在 **严格介于** 两个明确区间之间的块才会为一次 LLM 调用付费——这正是重要性信号最有信息量的地方。任一侧的短路都会跳过它。

上面还有两个细化规则。标记为 `priority_high` 并在摄取时带入的内容（GitHub 提交信息、已关闭/已合并的问题和 PR）会获得 `PRIORITY_BOOST` ，数值为 `+0.25` （会钳制到 1.0），因此高信号来源内容能够通过门槛并获得更高排名。另有一个守卫会丢弃“体量很小、且没有实体”的闲聊——少于 `TOKEN_MIN` 个 token 且没有提取到实体的内容——因此它不能仅凭元数据先验侥幸通过（带优先级标签的块会绕过这个守卫）。

被丢弃的块仍会写入一行分数用于诊断，并带有 `drop_reason`；它们的 chunk 行会保留用于溯源，但不会被任何缓冲区或摘要引用。

***

## 实体提取

提取会丰富一个块，并同时为 `entity_density` / `llm_importance` 信号和索引提供输入。它可通过 `EntityExtractor` trait 在 [`score/extract/`](https://github.com/tinyhumansai/tinymemory/tree/1d6b997874a06600ba0c4922708b5613497c9ffe/crates/tinymemory-core/src/tree/score/extract)中插拔，并分两阶段运行：

* **`RegexEntityExtractor`** - 始终开启、确定性强、成本低。一次编译的模式会提取机械化标识符：邮箱、URL、账号（`@alice` 以及 Discord 风格的 `alice#1234`），还有 hashtag。UTF-8 安全（跨度使用字符偏移）。
* **`LlmEntityExtractor`** - 只在边界上的块上调用。一次结构化 JSON 调用会向模型请求语义实体识别（Person / Organization / Location / Topic / …）以及重要性评级，并在传输失败时进行跨度恢复和温和的告警后置空回退。

这两者通过 **`CompositeExtractor`**&#x4E32;联，它会运行一系列提取器并容忍每个提取器的失败。输出会合并（`ExtractedEntities::merge` 会去重实体并取最大重要性），然后由 **规范化** 由 [`resolver.rs`](https://github.com/tinyhumansai/openhuman/tree/main/src/openhuman/memory/tree/score/resolver.rs) 完成——包括将邮箱小写、去除前导 `@`/`#`，并分配稳定的 `canonical_id` 字符串——这样同一个人或主题在不同块中都会解析为同一身份。

***

## 实体索引与图

每个保留块的规范化实体都会写入 **`mem_tree_entity_index`**，这是一个倒排索引，将 `entity_id → node_id` ([`store.rs`](https://github.com/tinyhumansai/tinymemory/blob/1d6b997874a06600ba0c4922708b5613497c9ffe/crates/tinymemory-core/src/tree/score/store.rs)）。这是 Memory Tree 其余部分读取的连接组织：

* **检索** 会将查询实体与索引进行匹配以找到候选节点。
* **主题路由** 使用实体热度来决定哪些实体值得拥有自己的主题树。
* **Memory 图** （Intelligence 选项卡中的力导向可视化）由共现边生成——同一块中提到的两个实体会获得一条无向边（`graph::pairs_from_entities`），并与索引在同一个事务中写入，因此二者永远不会分叉。

***

## 用于语义回忆的嵌入

评分也会生成向量。位于 [`score/embed/`](https://github.com/tinyhumansai/tinymemory/tree/1d6b997874a06600ba0c4922708b5613497c9ffe/crates/tinymemory-core/src/tree/score/embed) 中的嵌入器会把每个块（以及之后的摘要）转成固定的 `EMBEDDING_DIM = 1024`-float `Vec<f32>`，并打包进 SQLite BLOB，因此检索可以按余弦相似度重新排序候选，而不必只依赖实体索引。

当前启用的嵌入器由 `build_embedder_from_config` ([`embed/factory.rs`](https://github.com/tinyhumansai/tinymemory/blob/1d6b997874a06600ba0c4922708b5613497c9ffe/crates/tinymemory-core/src/tree/score/embed/factory.rs)）选择，读写路径使用相同的解析阶梯：

1. **显式 Ollama 覆盖** (`memory_tree.embedding_endpoint` + `embedding_model`）——适用于高级用户 / 端到端环境。
2. **本地 Ollama** 通过统一的 `embeddings` 工作负载设置——即 [Local AI](/openhuman/zh/gong-neng/model-routing/local-ai.md) 设置中的“Memory embeddings”复选框。
3. **用户配置的 OpenAI 兼容** 端点（`OpenAiCompatEmbedder`，例如 LM Studio）。
4. **托管云端** (`CloudEmbedder`，OpenHuman 后端 / Voyage）——登录后默认使用。
5. **没有提供方** - 读取路径会回退到 `InertEmbedder` （零向量），因此检索仍可运行；写入路径返回 `None`，跳过嵌入，并将 `semantic_recall` 标记为降级，以便之后可以重新嵌入该块。

嵌入运行在后台工作线程上，而不是摄取热路径上，因此新来源的突发流量永远不会阻塞 UI。树提供压缩和导航；嵌入则让底层的相似度搜索持续可用。

***

## 另见

* [Memory Trees](/openhuman/zh/gong-neng/obsidian-wiki/memory-tree.md) - 评分所在的管线内部。
* [检索](/openhuman/zh/gong-neng/obsidian-wiki/retrieval.md) - 索引和嵌入的查询方式。
* [Obsidian Wiki](/openhuman/zh/gong-neng/obsidian-wiki.md) - Markdown 仓库，评分后的块会落在这里。
* [Token Compression](/openhuman/zh/gong-neng/token-compression.md) - 为什么保持树的高密度很重要。
