用途与实际影响
这项功能怎样使用
为什么需要它
候选生成与用户选择之间,文件可能被移动、替换或改写;来源根也可能重新挂载到别的位置。若只在发现时记一个路径,之后直接打开,用户得到的可能已不是当时看到的那一项。
举个实际例子
我说“在刚选的合同里找到交付期限,告诉我在哪页或哪一段”。系统只核对并读取这一份原件,给出位置和相关片段;我再说“打开给我看”时才启动默认阅读器。若期间文件换了版本,就停止旧结果并说明变化。
最后我会得到什么
交回这份文件的标题、来源、版本和本次核验结果;要找某句话时给 PDF 页、Word 正文段落或表格位置、文本行及有限片段。扫描 PDF 没文字层、Word 某些区域未覆盖时会直说;桌面阅读器是否真的打开另行说明。
当前字节完全匹配时
确认选中原件的当前内容与来源后,只读交回文件或所需内容位置;明确要求桌面显示才启动阅读器。
定位、文件或身份发生变化时
文件不见了、版本改变、内容无法提取或应用启动失败时说清受影响部分;没有找到一句话不等于整份文件没有它。
写入或启动入口不可用时
来源暂不可读时停止验真;桌面启动失败不会抹掉已完成的只读文件核对。
从哪里开始
选中一份候选,请 AI 核对、找词句,或明确要求桌面打开。
需要准备什么
- 选定文件
- 待定位词句或桌面打开请求
从开始到拿到结果
- 1
系统核对并处理
完整读回选定文件并确认未在读取中变化;仅对可提取文字给出 PDF 页、Word 段落或文本行。
- 2
交付与接续
返回原件、位置和核验时间;无文字层、来源离线或版本漂移时报告缺口,旧缓存不填空。
技术实现与依据
这里保留实现、关键条件、精确入口、历史记录和验证结果。
status 返回 154 条精确定位;只读聚合显示其保存的 open_state 均为 verified,未逐件重算哈希或打开。inventory 因3个根缺失仅给路径下限,不能外推全部定位当前存在。复核、变化检测、身份冲突回滚和启动失败状态由 2026-09-07 基线 66 项合成回归与 Ruff 通过;不覆盖后来新增的 lookup-content,本轮未重跑 支持。关键规则与设计选择
候选排序不等于桌面打开请求;AI 可按可靠证据选中一项,只读验证后继续阅读,不要求用户搬运内部标识。
find 的 original_body_text_read=false 只证明查找阶段没有读取原件正文;命中可来自元数据或已有绑定文字,不能把文字索引覆盖冒充全库正文检索。
inspect-discovered 不登记候选;open-discovered --no-launch 仍会写索引,不能当成只读检查的替身。
locate-content 的 PDF 页码只对应可提取文字层;DOCX 段落不是固定打印页,TXT/MD 行号带着解码与截断范围回来。
登记候选每次打开都重算大小和 SHA-256,旧 verified 状态不会永久有效。
发现候选绑定来源根和相对位置,换盘、重挂载或根目录变化会要求刷新选择。
同一来源与相对路径出现新字节时保留稳定材料身份,刷新 version_id/version_role,并先清除旧 material_text,避免旧文字继续描述新内容。
身份冲突与读取中变化使用事务回滚,不让半条记录成为后续快速定位。
open 与 open-discovered 都在启动前再复验一次;启动器失败与字节验证失败分开,前者不抹掉已经证明的文件身份。
本模块用到的名词
- Root commitment(来源根承诺)
- 来源根当前解析结果的内部指纹;根目录换到另一位置时,旧选择必须失效。
- Base64 selection handle(Base64 选择句柄)
- 把发现时的来源与文件元数据编码成可解码 JSON 的执行层句柄;它不签名、不保密、也不授权打开,真正完整性仍来自随后重读来源根、stat 与 SHA-256。
- File signature(文件身份)
- 文件句柄与路径的设备、文件、大小和时间字段组合,用于检测读取前后是否换成另一项。
- Identity conflict(身份冲突)
- 稳定材料键或 source/native_id 已指向另一身份;事务必须回滚,而不是覆盖。
- verified / opened
- verified 只表示当前字节核验通过;opened 还表示系统已接受启动请求,两者都不等于用户已经阅读。
- open_state
- unverified、verified、not_openable、missing 或 hash_mismatch;每种状态对应不同恢复动作。
专业定义
登记候选与发现候选都先只读核验;选定原件可继续返回页、段落、单元格或行位置,桌面打开保留为用户明确要求的独立动作。
解决什么
解决候选到打开之间的时间差、来源根替换、路径逃逸、文件中途修改、登记身份冲突和默认应用启动失败。
当前怎样实现
- lookup-content 接收互斥的 --hashes-json / 可重复 --sha256,规范化并去重后最多 1000 项,以最多 900 个参数分批查询现有材料索引。只对精确命中的非媒体原件重新读取大小和 SHA-256;同内容的多个已验证位置都返回。缺失、不可读和内容漂移留在 unverified;not_indexed 只表示当前登记切片没有该哈希。命令不扫描目录、登记、复制或启动阅读器。
- inspect 与 inspect-discovered 使用只读数据库连接,按选中身份验证当前 locator(原件定位记录)、来源根、大小与 SHA-256;缺失或漂移只返回 gap。open / open-discovered 才保留状态写回和登记事务。
- locate-content 接收互斥的 --id / --token,在同一个已核验的原件上提取文字并匹配有限片段;读取结束再次验证大小、文件身份和 SHA-256,变化时拒绝旧片段。
- PDF 仅提取文字层;DOCX 遍历顶层正文段落和顶层表格单元格,明确排除嵌套表格、页眉页脚、文本框与嵌入对象;TXT/MD 返回行号和编码覆盖。整份 PDF 无可提取文字时返回单件 OCR(光学字符识别) 接续,命令本身不启动 OCR(光学字符识别)。
- 发现候选使用可解码的 discovery token(发现候选令牌),它是 URL-safe Base64(网址安全编码)选择句柄,内容是 schema(数据结构) 版本、来源键、相对位置、大小、修改时间和来源根承诺;它不是签名、秘密或动作授权。
- 解析相对位置时拒绝绝对路径与上级跳转,并复核最终路径仍位于登记来源根内。
- 以打开的文件句柄计算 SHA-256,在读取前、读取后和提交前比较文件与路径身份,防止中途替换。
- 原子事务检查稳定 material_key 与 source/native_id 唯一性;冲突或再次变化时回滚。
- 同一 source/native_id 的发现路径若内容 SHA-256 已变化,会先删除旧 material_text,再用当前 mtime/size 更新 version_id、按来源文件名更新 version_role,并写入当前来源时间、大小和哈希。
- open 对登记项保留同一 VerifiedFile 句柄,并在 os.startfile 前重新读取完整字节、比较原摘要和文件身份;open-discovered 在 SQLite 提交后重新打开并哈希当前文件,再与刚登记摘要比较。
- 两条打开路线复验通过后才调用 Windows os.startfile(path);启动失败记录 not_openable,但与 hash_mismatch、missing 分开。
执行流程
- 1
接收内部选中身份,而不是让用户复制路径、ID 或选择凭据。
- 2
确认材料仍是非媒体,并找到对应登记来源或已登记 locator(原件定位记录)。
- 3
对登记候选重算大小与 SHA-256;对发现候选先验证来源根承诺、相对位置与选择前 stat。
- 4
完整读取选中项并在文件句柄和当前路径上复核读取期间没有变化。
- 5
只有 open-discovered 明确写入路径才在事务中检查稳定身份、写入最小定位并在提交前复核;inspect-discovered 与 locate-content 无登记事务。
- 6
明确登记/打开时,同路径新内容才刷新版本并清除旧派生文字;只读检查发现变化只返回 gap,保留索引。
- 7
AI 读取默认在 inspect / inspect-discovered 的只读验证结果结束;需要查内容时用同一选定身份进入 locate-content,读取结束再核对原件。
- 8
用户要求桌面显示时才走前述登记/打开事务;open 与 open-discovered 都在 os.startfile 前最后复验,随后返回 opened、verified 或精确 gap。
边界
- inspect、inspect-discovered 与 locate-content 只读取用户或上层流程已经明确选中的一项,不批量为普通候选计算哈希;locate-content 最多返回 10 个位置、每段最多 280 字符并保留截断。独立的 lookup-content 接收已选内容哈希批次,只复核现有索引精确命中。
- 路径必须留在登记来源根内,不接受绝对路径、上级跳转或链接逃逸。
- 发现候选在成功提交前仍不是登记材料,也不能在搜索结果中冒充已核验。
- 默认应用启动不等于用户阅读完成,更不等于提交、签署或平台收到。
- 媒体即使持有旧材料记录或构造出的选择凭据,也会在读取字节前被拒绝。
- Windows os.startfile 消费的是路径而不是已验证文件句柄;即使启动前立即重哈希,最后一次复验与系统消费路径之间仍有极小竞争窗口。
- SQLite 提交与外部文件系统变化不能组成一个原子事务;发现路线用提交前身份检查、提交后启动前重哈希和后续每次 open 复核收窄风险,但不宣称数学上的零窗口。
失败与恢复
- 内容哈希没有已验证命中
- 精确索引行缺失、不可读或字节漂移时返回 unverified 与原因;没有登记行则为 not_indexed,不能据此判断其他磁盘或未登记来源不存在。
- 登记 locator(原件定位记录) 在可访问可信根中已不存在
- inspect 返回 locator_missing,不改索引、不猜相邻路径、不恢复文件;清单在日常 sync-current 中跟随删除。
- 大小或 SHA-256 漂移
- 只读检查返回 size_match/hash_match 与精确 gap,不改索引;明确打开动作才写回 hash_mismatch。旧版本不打开,重新发现后再选定。
- 选定原件没有可提取文字或只部分覆盖
- 返回格式、区域、编码和截断范围;整份 PDF 无文字层时指出单件 OCR(光学字符识别) 缺口,不把部分零命中当成整份原件不存在该内容。
- 来源根或候选 stat 改变
- 返回 refresh_required、root_changed 或 candidate_changed;旧内部选择凭据失效,重新 discover。
- 读取期间文件改变
- 事务回滚并返回 changed_during_hash;不留下新材料记录。
- 稳定身份冲突
- 回滚并返回 identity_conflict;保留既有记录,等待检查来源与相对位置。
- 同一路径内容已更新
- 保留稳定 source/native 身份,刷新 version_id/version_role、时间、大小与哈希,并删除旧 material_text;没有本次新文字时保持为空,不能沿用旧派生内容。
- SQLite 提交后、启动前文件又变化
- open-discovered 的第二次哈希或 open 的同句柄重哈希失败,写回 hash_mismatch 与 before_launch gap,不调用 os.startfile。
- 默认应用启动失败
- 记录 not_openable 和 launcher_failed;不把已通过字节核验的事实改写成哈希失败。
真实入口
README.md定义候选选择后才哈希、登记并打开,以及登记项每次打开前重新核对。
materials.py实现选择凭据、来源根承诺、路径约束、SHA-256、事务、状态写回和启动器调用。
schema.sql定义稳定材料身份、source/native_id 唯一性和五种 open_state。
tests/test_materials.py覆盖来源根变化、每候选独立来源根、读取中变化、身份冲突回滚、哈希漂移与媒体拒绝。
如何验证
- 验证登记文件缺失、大小变化和字节变化分别形成 missing 或 hash_mismatch,且不会启动应用。
- 验证同一相对位置在不同来源中使用各自来源根,不能借另一个候选的根通过。
- 在哈希读取期间模拟文件身份变化,确认事务回滚且没有新增材料记录。
- 预置冲突稳定身份,确认既有记录和候选都不被覆盖。
- 让同一来源相对路径出现新内容,确认 version_id/version_role 刷新、旧 material_text 清空且稳定材料身份不重绑。
- 分别在登记项启动前重哈希和发现项提交后重哈希时制造变化,确认 os.startfile 从未被调用并写回 before_launch gap。
- 将 Windows 路径消费的最后竞争窗口和 SQLite/文件系统非原子边界保留为已知限制,不用单元测试冒充已经消除。
- 真实端到端验收必须记录自然请求、候选选择、核验耗时、打开结果与用户实际看到的原件。
与其他模块的关系
它是登记查找和有界发现的共同收口:前两者只缩小候选,这里只读证明原件身份,并按请求定位内容。只有明确登记或桌面打开路径才把发现候选加入索引。
