# Telegram · 全文搜索

Canonical page: https://phishdestroy.github.io/TgWitness/zh/docs/reference/search/

根据词形、精确短语、域名和 @用户名 查找已保存的消息，按最匹配结果优先排序，索引与签名日志绑定。

[Project context and measured scale](https://phishdestroy.github.io/TgWitness/docs/reference/overview/)

[Documentation index](https://phishdestroy.github.io/TgWitness/zh/llms.txt)

根据词形、精确短语、域名和 @用户名 查找已保存的消息，按最匹配结果优先排序，索引与签名日志绑定。

[TgWitness](https://github.com/phishdestroy/TgWitness/blob/main/README.md) / [文档](https://github.com/phishdestroy/TgWitness/blob/main/docs/README.md) · [调查](https://phishdestroy.github.io/TgWitness/zh/docs/reference/investigation/)

![调查](https://phishdestroy.github.io/TgWitness/assets/chapter-investigate.svg)

[存档分析](https://phishdestroy.github.io/TgWitness/zh/docs/reference/analysis/) · [本地 OCR 和转录](https://phishdestroy.github.io/TgWitness/zh/docs/reference/media-analysis/) · [维基](https://github.com/phishdestroy/TgWitness/blob/main/docs/wiki/Archive-and-Integrations.md)

**预览。**在**“日志**”中，选择搜索框旁边的**“最佳匹配 · 词形变体**” 选项。结果按 BM25 算法排序，并匹配同一单词的其他形式： *搜索“перевод*”会找到*“переводом*”和*“переводы*”，*搜索“transfer*”会找到*“transfers*”，中文 短语在搜索时不保留空格。**选择“最新优先 · 精确文本**”可保留之前 对整个记录进行的子字符串搜索。

索引是衍生数据。它由签名日志重建而成，本身从未被签名， 也从未被导出。原始记录不会发生任何变化。

**本页内容**

- [搜索语法](https://phishdestroy.github.io/TgWitness/zh/docs/reference/search/#search-syntax)
- [语言](https://phishdestroy.github.io/TgWitness/zh/docs/reference/search/#languages)
- [索引的位置](https://phishdestroy.github.io/TgWitness/zh/docs/reference/search/#where-the-index-lives)
- [结果和已签名的日志](https://phishdestroy.github.io/TgWitness/zh/docs/reference/search/#results-and-the-signed-journal)
- [识别出的文本](https://phishdestroy.github.io/TgWitness/zh/docs/reference/search/#recognized-text)
- [访问](https://phishdestroy.github.io/TgWitness/zh/docs/reference/search/#access)
- [咨询存档和 MCP](https://phishdestroy.github.io/TgWitness/zh/docs/reference/search/#ask-the-archive-and-mcp)
- [依赖项](https://phishdestroy.github.io/TgWitness/zh/docs/reference/search/#dependencies)
- [基准测试](https://phishdestroy.github.io/TgWitness/zh/docs/reference/search/#benchmark)

## 搜索语法

| 撰写 | 查找 |
| --- | --- |
| `перевод карта` | 同时包含这两个词的记录（形式不限） |
| `"переводом на карту"` | 请按此顺序保留确切的短语 |
| `бонус OR подарок` | 任一词 |
| `перевод -казино` | 不含被排除词的记录 |
| `example.com` | 该域名及其子域名，包括隐藏链接和按钮 URL 中的域名 |
| `@username`, `t.me/username` | 该账号的提及和链接，以及其发送的消息 |
| `#tag` | 话题标签 |
| `+79991234567`，一个钱包地址，一个电子邮件地址 | 文本或链接中的相同值 |
| `from:@username`, `from:123456` | 来自该发件人的消息 |
| `chat:-1001234567890` | 一个聊天窗口；它只能限制您自己的访问权限 |
| `id:42` | 消息 42 的已保存版本 |
| `since:2026-01-01 until:2026-01-31` | 观察时段；仅含日期而不含时间的日期表示全天 |
| `kind:edited` | 仅编辑（`new`, `snapshot`, `edited`, `recovered`) |
| `source:ocr`, `source:transcription`, `source:all` | 识别出的文本，可替代消息或与消息并存 |

当存在其他单词时，介词和其他功能词将被忽略。 混入单词中的易混淆字母（例如西里尔字母单词中的拉丁字母 *a*）、全角 及装饰性 Unicode 字母以及不可见字符均将被规范化处理。

## 语言

系统会根据每条记录中的脚本和字母来检测语言：

| 脚本 | 匹配 |
| --- | --- |
| 西里尔字母 | 使用 [pymorphy3](https://github.com/no-plagiarism/pymorphy3) 处理俄语和乌克兰语词干。若未启用可选的乌克兰语词典，则使用轻量级乌克兰语词干提取器 |
| 拉丁文 | 英语 Snowball 词干提取器；忽略音标符号 |
| 中文、日语、韩语、泰语、老挝语、高棉语、缅甸语 | 重叠字符对：查找不包含空格的任何短语。中文查询将使用 jieba 拆分为单词 |
| 其他脚本 | 精确词句 |

乌克兰语词典（`pymorphy3-dicts-uk`）依据 GPL-3.0 协议发布，因此 默认情况下不会安装。请使用以下命令进行安装 `pip install "tgwitness[uk-dictionary]"`； 索引会自动识别新的分析器并重新构建自身。

## 索引的位置

| 模式 | 存储 | 用法 |
| --- | --- | --- |
| 磁盘存储（默认） | `search/fulltext.sqlite3` 在工作区目录中 | 支持大型存档；重启后数据仍可保留 |
| 仅限内存中 | 磁盘上不留任何数据；每次启动时都会重新构建 | 磁盘上不保留消息文本的任何衍生副本 |
| 关闭 | 不设索引；**按最新内容优先排序 ·** 保留**原文精确表述** | 最低配置 |

主管理员可在**“日志”→“过滤器”→“搜索索引**”中 为每个工作区选择模式。 `TGWITNESS_FULLTEXT=disk|memory|off` 为 未自行选择的 Workspace 设置默认值。切换至“内存”或“关闭”将删除该文件。 索引文件与日志具有相同的保护措施：它位于私有数据 目录中。该文件未加密，也不包含在备份或异地副本中。

新记录会在后台数秒内完成索引；搜索时也会优先索引 短暂的积压记录，因此刚保存的记录也能被检索到。现有日志会在 更新后完成索引，且不会阻塞面板；在此之前，搜索结果涵盖 已索引的记录，日志会显示进度。**重建索引时**将从 第一条记录开始重新进行。

```
python -m tgwitness index status --data-dir data/live
python -m tgwitness index rebuild --data-dir data/live --json
```

`rebuild` 需要停止该面板（否则退出代码为 3）。使用 `--json`， 标准输出仅显示结果；进度信息将输出到标准错误。

## 结果及签名日志

索引存储其所涵盖的最后一条日志记录的序列号和哈希值， 并在打开日志时将其与日志进行比对。若日志被替换、恢复或编辑， 则会触发索引重建。每次响应都会报告该位置，每次查询结果均 通过序列号从签名日志中读取：索引仅决定显示哪些 记录以及显示顺序，绝不涉及记录内容。聊天权限仅适用于 日志自身的聊天字段，而不适用于索引内容。

## 识别出的文本

OCR 结果和转录文本保留在其加密的原始文件中。其索引仅存在于 内存中，并在识别任务完成时重建。匹配结果将显示在其 `media.saved` 记录中显示，标注为“机器识别”，并附带引擎、模型及 摘录内容；该内容绝不会以 Telegram 中发送的文本形式呈现。同一索引还用于 **“搜索已识别文本**”功能，该功能仍可匹配纯文本子字符串。

## 访问

“聊天范围”访问仅查找来自其聊天的记录，包括 其附件中被识别的文本。 `chat:` 以及其他过滤条件仅能进一步缩小该数据集范围。向范围限定访问显示的索引状态 不包含日志总数。仅主管理员可以更改 模式或重建索引。

## 向存档和 MCP 咨询

当索引构建完成后，**Ask the Archive** 将从该索引中检索数据源：即 所选聊天记录中匹配度最高的条目，且每条已保存的文本仅计数一次。最多发送 24 个数据源；长文本将在匹配位置处截断，并标记为“截断”。 关于近期更改、单条消息的已保存版本以及在 索引仍在构建期间进行的搜索，均使用之前的按时间顺序检索方式。预览 界面会显示所使用的方法。

MCP 客户端可调用 `search_messages` 使用 `ranked=true` ，并使用 `next_offset`。范围限定连接仅接收其所属的聊天内容。

排序分页最多显示前1,000条结果，无论页面大小如何。请缩小查询范围、聊天范围或时间范围以查看更多匹配结果。按最新优先搜索会在选择每页之前，对消息、OCR 结果和转录结果进行排序。

## 依赖项

| 软件包 | 许可协议 | 为什么 |
| --- | --- | --- |
| pymorphy3、pymorphy3-dicts-ru、DAWG2-Python | MIT | 俄语和乌克兰语词干；词典在本地加载 |
| snowballstemmer | BSD-3-Clause | 英文词干，纯 Python 代码 |
| jieba | MIT | 中文查询分词，纯 Python 实现（仅提供源代码分发） |
| pymorphy3-dicts-uk（可选） | GPL-3.0 | 完整的乌克兰语词典 |

SQLite FTS5 需要 `unicode61` 分词器；该分词器是当前 Python 构建版本中 SQLite 的一部分。若缺少该分词器，最佳匹配搜索将报告其不可用。

## 基准测试

`python tools/bench_fulltext.py --messages 1000000 --work <empty directory>`生成一个确定性的签名日志，其中包含来自 200 个聊天群组中混合使用俄语、乌克兰语、英语、 中文和日语的消息，以及其中的链接和标识符；构建索引 并测量典型搜索的耗时（50 条结果，7 次运行的中位数）。

测试环境：Windows 11、Intel Core i9-13900K、128 GB 内存、Python 3.12.10、SQLite 3.49.1，数据存储于 exFAT 分区，分析器 `fulltext-v1+pymorphy3-ru+light-uk+snowball-en`:

| 度量 | 结果 |
| --- | --- |
| 签名日志 | 1,000,000 条消息，1.50 GiB，19 分钟内写入 |
| 基于日志构建索引 | 71 秒，约每秒 14,141 条消息 |
| 索引文件 | 58 MiB，占日志总量的 3.8% |
| 在新记录生成后立即进行搜索 | 中位数 8 毫秒，P95 值 9 毫秒 |
| 之前的子字符串搜索，同一日志 | `перевод` 3.2 秒， `银行转账` 2.6 秒， `verify-account.example` 2.6 秒 |

| 搜索 | 匹配记录 | 最佳匹配，中位数 | p95 | 最新优先，中位数排序 |
| --- | --- | --- | --- | --- |
| 常用词及词形： `перевод` | 134,403 | 82 毫秒 | 85 毫秒 | 7 毫秒 |
| 两个词： `оплата карта` | 28,996 | 43 毫秒 | 43 毫秒 | 14 毫秒 |
| 生僻词： `розыгрыш подарок` | 28,881 | 42 毫秒 | 47 毫秒 | 13 毫秒 |
| 精确短语： `"переказ коштів на картку"` | 不计入 | 35 毫秒 | 36 毫秒 | 6 毫秒 |
| 域名及子域名： `verify-account.example` | 12,435 | 17 毫秒 | 17 毫秒 | 3 毫秒 |
| @用户名： `@support_desk_bot` | 10,331 | 15 毫秒 | 16 毫秒 | 3 毫秒 |
| 中文无空格： `银行转账` | 17,691 | 23 毫秒 | 24 毫秒 | 3 毫秒 |
| 日语： `リンク` | 16,603 | 23 毫秒 | 25 毫秒 | 3 毫秒 |
| 英文词干： `withdrawal` | 27,775 | 26 毫秒 | 26 毫秒 | 2 毫秒 |
| 或与排除： `бонус OR подарок -казино` | 268,034 | 190 毫秒 | 193 毫秒 | 19 毫秒 |
| 聊天过滤器： `chat:-1000000000007 перевод` | 652 | 9 毫秒 | 9 毫秒 | 6 毫秒 |
| 时间范围筛选器： `since:2026-01-01 until:2026-01-31 выплата` | 115,732 | 155 毫秒 | 156 毫秒 | 81 毫秒 |

系统会按排名顺序逐一检查候选结果中是否存在完全匹配的短语，直到页面 填满为止，因此该项的总数不计入统计。耗时随匹配记录数量的增加而增长； 缩小聊天范围或时间范围可加快广泛搜索的速度。合成语料库中 词汇量有限，因此常见词匹配到的记录数量远多于真实聊天记录。
