russalo/file-observer

GitHub: russalo/file-observer

一款确定性文件观察工具,对目录进行一次只读扫描即可生成包含类型、元数据、结构及溯源信息的可复现 JSON 清单,供数据管道在摄取前了解文件内容。

Stars: 2 | Forks: 0

file-observer

# File Observer [![PyPI](https://img.shields.io/pypi/v/file-observer)](https://pypi.org/project/file-observer/) [![Downloads](https://img.shields.io/pypi/dm/file-observer)](https://pypi.org/project/file-observer/) ![Python](https://img.shields.io/pypi/pyversions/file-observer) [![tests](https://static.pigsec.cn/wp-content/uploads/repos/cas/6b/6b52945adbf8d9e421fe243515ae54cfbd3da263f16b1eabda37cdc0b797b8eb.svg)](https://github.com/russalo/file-observer/actions/workflows/tests.yml) [![Container](https://img.shields.io/badge/ghcr.io-file--observer-2496ED?logo=docker&logoColor=white)](https://github.com/russalo/file-observer/pkgs/container/file-observer) [![License: AGPL-3.0 + commercial](https://img.shields.io/badge/license-AGPL--3.0%20%2B%20commercial-blue)](https://github.com/russalo/file-observer/blob/main/LICENSE) **在打开文件之前,先了解里面有什么。** *一次性的、只读的观察过程——而不是文件监视器。将它指向一个目录,在摄取之前获取一份包含其内容的确定性 JSON 清单。* File Observer 对目录进行一次只读扫描,并确切地告诉你里面有什么——文件类型、元数据、对话模式、作者指纹、结构信号——全部集中在一个确定性的 JSON 清单中。你按需运行它;它不会驻留后台或监视更改。它读取所有内容。它不进行任何更改。 *(如果你已经了解 Apache Tika:可以将其想象为一个为 pipeline 构建的确定性 Tika。)*

Many messy files in → one deterministic, checksum-sealed JSON manifest out → many consumers downstream

``` pip install file-observer fo ./your-project --specialists ``` ``` Scanned 4,366 files (3,526 text, 840 binary) in 31 directories. 1,163 supported (336 with specialist metadata). 3,203 unsupported extensions. Quality: 676 clean, 3,690 degraded. 4 safety flags, 2 polyglots. Vectors: author_aggregate found 64 distinct authors across 114 files. chatlog matched 22 files. reference_tokens ran on 806 files (2,164 URLs, 382 paths, 262 @mentions). filename_patterns matched 84 of 4366 files. Largest directories: tika-parsers (2,037), tika-pipes (459), tika-core (440). ``` 这是人类可读的摘要。完整的清单是结构化的 JSON——这里是一个截断的文件对象,以便你在安装前查看数据契约: ``` { "schema_version": "1.23", "context": { "scanner_version": "1.42.0", "logic_version": "1.22.0", "...": "…" }, "files": [ { "path": "docs/report.pdf", "mime_type": "application/pdf", "checksum_sha256": "9f86d081884c7d65…", "is_binary": true, "requires_specialist_tool": true, "specialist_tool": "pdf_extraction", "safety_flags": ["has_javascript"], "signal_provenance": { "requires_specialist_tool": { "layer": "derived", "method": "specialist_tools_registry", "trigger": "extension_match" } }, "...": "…" } ], "vectors_collected": [ { "vector_id": "chatlog", "method_version": 9, "identity_digest": "a3f1c2…", "...": "…" } ], "manifest_checksum": "7d2bafef…", "manifest_signature": { "algorithm": "hmac-sha256", "key_id": "default", "value": "…" } } ``` 每个派生字段都带有一个 `signal_provenance` 条目;每个向量带有一个 `identity_digest`;整个清单带有校验和及可选的 HMAC 签名。 | | | |---|---| | **Package** | `file-observer` | | **CLI** | `file-observer` 或 `fo` (简写) | | **Version** | `1.42.0` | | **Schema** | `1.23` | | **Python** | `>= 3.12` (在 Linux、macOS、Windows 上测试) | | **License** | [AGPL-3.0](https://github.com/russalo/file-observer/blob/main/LICENSE) (提供商业许可) | | **Tests** | 1000+ (运行 `pytest` 获取确切数量) + 包含 49,879 个文件 / 13 棵树的全面测试——运行正常 (零致命错误),请参阅下文的“规模化验证” | ## 为什么选择 File Observer? **你的 pipeline 在处理之前需要知道它正在处理什么。** File Observer 是位于任何文档 pipeline 最前端的观察层——摄取、分类、OCR、embedding、审计。它在不触碰文件的情况下告诉 pipeline 即将到来的是什么。*(需要对发生的文件系统更改做出反应?那应该使用像 watchdog 或 watchfiles 这样的 watcher——这是另一种工具。)* - **确定性。** 相同的文件 + 相同的配置 = 每次都得到相同的清单。跨环境的差异会被解释,而不会被隐藏。 - **可审计。** 每个派生字段都有溯源记录——使用了哪种方法、哪个触发器、哪些输入。没有任何东西是黑盒。 - **诚实。** `null` 表示“在限制范围内未观察到”,而不是“不存在”。安全标志是观察结果,而不是评估结论。扫描器负责记录;消费者负责解读。 - **可验证。** 每个向量都有加密身份摘要。HMAC 签名的清单。支持跨增量扫描的监管链。 ## 它观察什么 ### 26 种文件类型,4 个能力层级 | 层级 | 运行于 | 提取内容 | |---|---|---| | **通用层** | 每个文件 | 标识、校验和、MIME、文件签名、polyglot 检测、路由标志 | | **基线层** | 文本文件 | 编码、预览、标签、frontmatter、chatlog 检测、reference token、文件名模式 | | **结构层** | 文本文件 | 标题、标题层次、CSV 表头、JSON/YAML/XML/TOML 键、技术提示 | | **专家层** | 支持的格式 (选择性开启) | PDF 页面、图像尺寸 + 拍摄 EXIF、视频容器/拍摄元数据、音频标签 + 属性、电子邮件信封、电子表格 / 文档 / 演示文稿结构 | 支持的专家格式: - **文档** — `.pdf`, `.docx`, `.doc`, `.odt`, `.rtf` - **电子表格** — `.xlsx`, `.xls`, `.ods` - **演示文稿** — `.pptx`, `.ppt`, `.odp` - **图像** — `.png`, `.jpg`/`.jpeg`, `.heic`/`.heif`/`.avif`, `.tiff`/`.tif`, `.jp2` (尺寸;JPEG/HEIC/HEIF/AVIF/TIFF 额外包含拍摄 EXIF 元数据 —— `.png`/`.jp2` 仅提取尺寸) - **视频** — `.mp4`, `.mov`, `.m4v` (编解码器/时长/尺寸 + QuickTime 拍摄设备及 GPS 存在状态) - **音频** — `.mp3` (ID3 标签 + 格式/比特率/时长) - **电子邮件** — `.msg`, `.eml` - **聊天记录** — `.jsonl` (基于内容检测) ### 带有加密身份的观察向量 | 向量 | 发现内容 | |---|---| | **chatlog** | 对话模式——轮次、发言人(每个发言人的计数/交替)、段落标记。检测散文形式的记录和各种常见 schema 下的对话 JSON/JSONL(role/from/speaker + text/value/content)。适用于 `.txt`、`.md`、`.jsonl`、`.json`。 | | **reference_tokens** | @提及、wiki 链接、代码块、URL、电子邮件、文件路径、工单号 | | **author_aggregate** | 跨格式作者标准化。识别模板默认值与真实人类的区别。(谁创作的。) | | **provenance** | 生成来源——标准化的 `toolchains`(通过封闭表得出 producer/creator)、`production_years` 以及 `digitization`(`born_digital` / `scanned` / `ocr_detected` / `unknown`)。跨格式:PDF + OOXML `app.xml`。(什么工具 / 何时 / 数字化。) | | **filename_patterns** | 日期前缀、版本标记、带编号的修订、模板名称、UUID、副本后缀 | | **lexicon** *(提供词典时)* | **消费者提供**的术语词典 (`--lexicon`) 按类别的计数——这是一种观察,绝不是判决。带有内容哈希 `dictionary_id`;术语本身永远不会被输出。(v1.38) | 此外还有 `preservation`、`fact_block` 和 `ai_session`——运行 `file-observer --schema` 获取始终准确的完整向量列表。每个向量都带有一个身份摘要 (SHA-256)。相同的摘要 = 相同的规则 + 相同的调优 = 相同的输出。永远如此。*(这些是观察向量——命名的、带指纹的观察结果——而不是用于向量数据库的 embedding 向量。)* ### 安全与完整性 - **安全标志** — 检测 PDF 中的 JavaScript、DOCX 中的宏、RTF 中的 OLE 对象、XML 中的外部实体 - **清单校验和** — 对规范清单计算 SHA-256 - **HMAC 签名** — 可选的签名清单,用于审计链 - **增量扫描** — 对比两次独立运行的清单,查看添加/修改/删除的文件。这是快照之间的对比,而不是实时的更改事件。 - **目录级摘要** — 一目了然地查看语料库的形态 - **重复检测** — 根据相同的内容校验和将文件分组 (`quality.duplicate_clusters`);为迁移/去重呈现出冗余副本 - **专家级统计** — 每个专家工具的尝试/成功/失败次数,因此提取质量是可见的,而不是推测出来的 ## 快速开始 ### 安装 ``` pip install file-observer # 可选:specialist format 支持 pip install "file-observer[all]" # every optional specialist (one line — recommended) pip install "file-observer[msg]" # .msg/.doc/.xls/.ppt (OLE2 formats) pip install "file-observer[security]" # Hardened XML parsing pip install "file-observer[mcp]" # MCP server (use it from an AI agent — see below) pip install "file-observer[dev]" # Full dev environment ``` **无需安装** — 直接使用 [uv](https://docs.astral.sh/uv/) 或 [pipx](https://pipx.pypa.io/) 从 PyPI 运行: ``` uvx file-observer ./project --stdout | jq '.quality' # uv: zero-install, cached pipx run file-observer ./project --stdout # pipx: same idea ``` **Docker** — 无需 Python;扫描挂载的目录,将清单输出到 stdout: ``` # 将要扫描的目录以只读方式 mount 到 /data;在其外部捕获 manifest docker run --rm -v "/path/to/scan:/data:ro" ghcr.io/russalo/file-observer > manifest.json # 传入你自己的 args(默认为 `--stdout .`): docker run --rm -v "/path/to/scan:/data:ro" ghcr.io/russalo/file-observer /data --specialists --stdout ``` 该镜像捆绑了 `libmagic` + 所有可选的专家工具。(基于 [`Dockerfile`](Dockerfile) 构建;每次发布时推送到 GHCR。)以**只读**方式挂载你的数据,并将输出文件放在扫描树之外,这样你重定向到同一文件夹的清单就不会被后续的扫描拾取。 **GitHub Action** — 在 CI 中扫描仓库并将清单捕获为 artifact: ``` - uses: russalo/file-observer@v1.28.1 # pin a release tag id: scan with: path: . # directory to scan (default ".") args: --specialists # extra CLI args (default "--specialists") output: file-observer-manifest.json # where to write the manifest - uses: actions/upload-artifact@v4 with: name: file-observer-manifest path: ${{ steps.scan.outputs.manifest-path }} ``` 该 action 将 `file-observer[all]` 安装到一个隔离的 venv 中(它不会触碰你工作流的 Python),并通过 `--stdout` 写入清单。输出为:`manifest-path`。可以将它与基线进行对比,使用 `jq` 根据 `quality`/`safety_flags` 控制作业,或者只是将其存档以供审计。 ### 从 AI 代理中使用 (MCP) `file-observer[mcp]` 附带了一个 **[MCP](https://modelcontextprotocol.io/) 服务器**,以便 AI 代理可以将扫描文件树作为一种工具使用——这是一种对未知或不受信任的文件进行安全的 **“先查看,再触碰”** 的过程。由于 File Observer 是只读的,从不执行文件内容,保持在目录树内,并且永远不会崩溃,因此可以安全地将其指向你不信任的文件:代理在打开或摄取任何内容之前,就能获得一份关于*里面有什么*的确定性清单。该扫描器没有模型,也从不读取含义,因此它无法被 prompt 注入。 运行 stdio 服务器(或 `uvx --from "file-observer[mcp]" file-observer-mcp`),然后将其添加到你的 MCP 客户端 (Claude Desktop / Claude Code) 配置中: ``` { "mcpServers": { "file-observer": { "command": "file-observer-mcp", "args": [] } } } ``` 四个为代理的上下文预算设计的只读工具(渐进式展开): | 工具 | 作用 | |---|---| | `scan_summary(path)` | **从这里开始** — 一个紧凑的概览:计数、类型和值得注意的观察结果(chatlog、MIME 不匹配、polyglot、宏/JS、地理标记、风险格式)。对于大型文件夹约消耗 300 token。 | | `scan_file(path)` | 单个文件的完整观察记录——在摘要标记出某些内容后进行深入挖掘。 | | `scan_directory(path, max_files)` | 完整的清单(与使用相同专家设置的 CLI 扫描具有相同的校验和);**受保护的** — 如果目录树超过 `max_files`,将在扫描*之前*拒绝,从而限制上下文大小和工作量(巨大的目录树不会被读取)。 | | `describe_surface()` | 完整的输出 schema——编写消费者程序时的参考。 | 它**负责观察和报告**——它从不判断文件是否安全;由代理进行解读。`--root ` 将扫描限制在子树内(深度防御)。请参阅 [`examples/08-mcp-server/`](examples/08-mcp-server/)。 **将返回的清单视为不受信任的数据。** 扫描器无法被 prompt 注入,但清单会逐字*报告*攻击者可控的字符串——文件名、`content_preview`、嵌入的元数据——因此消费者必须将这些源自文件的字段视为数据,而不是指令。优先使用 fo 派生的信号(计数、`safety_flags`)而不是逐字字符串。这些标志是路由信号,而不是判决——*缺失的*标志意味着“未观察到任何情况”,绝不意味着“安全”,因此未标记的文件仍保持不受信任状态。你仍然可以基于信号进行路由:将*已标记的*文件发送到更严格(阻塞性)的审核,将其余文件通过较轻量(建议性)的路径处理,而不是采用全有或全无的闸门控制。敏感配置(`--lexicon` 术语)在启动时传递给服务器,绝不会作为工具参数传递,因此它永远不会进入代理的上下文([原因](SECURITY.md#mcp-never-pass-secrets-as-tool-arguments))。有关信任边界的完整详细信息,请参见 [SECURITY.md](SECURITY.md#the-manifest-is-untrusted-data)。 **可选:** `libmagic` 可增强基于内容的 MIME 检测。从 v1.3 开始,它不再是必需的——如果没有它(Windows、最小容器),File Observer 会回退到内置的 **纯 Python 内容嗅探**来处理常见的二进制格式(存档、图像、数据、媒体),然后进行基于扩展名的推断。安装它以获得最广泛的覆盖范围: ``` sudo apt install libmagic1 # Debian/Ubuntu brew install libmagic # macOS pip install python-magic-bin # Windows (or rely on the pure-Python fallback) ``` ### 扫描 ``` # 快速扫描 fo ./project # manifest 直接输出到 stdout — 适配 pipe(不写入文件) fo ./project --stdout | jq '.quality' # 使用 specialist metadata 进行深度扫描 fo ./project --specialists # 带有 JSONL 输出的 Named profile fo ./project --profile deep_extract --format jsonl # 针对之前 manifest 的 Delta scan,已签名 fo ./project --previous-manifest ./last.json --signing-key-file ./key ``` ### 安全模式 — 将不受信任的文件提供给模型 清单是一份*关于*不受信任文件的报告,因此它会回显攻击者可控的字符串(文件名、`content_preview`、提取的元数据)——将清单粘贴到 LLM 的上下文中是一种 prompt 注入向量。`--trusted-only` 将清单投影为**仅包含 File Observer 计算的内容**——计数、类型、哈希、`safety_flags`——将所有源自文件的字符串置空,并添加每个文件的 `path_id` 关联句柄。在构造上是安全的,可直接提供给模型: ``` fo ./untrusted-uploads --trusted-only --stdout | your-llm-pipeline ``` 在 `fo --schema` 中,每个字段都被标记为 `fo_derived`(受信任)或 `file_derived`(攻击者可控),因此你也可以构建自己的投影。请参阅 [`examples/09-trusted-only/`](examples/09-trusted-only/) 和[教程](docs/TUTORIAL.md#11-safe-mode-feeding-untrusted-files-to-a-model)。 ### 在代码中使用 ``` from pathlib import Path from file_observer import scan, scan_to_json, manifest_to_json manifest = scan("./documents") # one call, sane defaults manifest = scan("./documents", specialists=True) # opt-in format extraction json_str = scan_to_json("./documents") # straight to a JSON string # (显式的 Scanner(...)/ScannerConfig(...) 路径仍然可用于完全控制) # 人类可读的摘要 print(manifest.summary) # 查找对话日志 for f in manifest.files: if f.is_chatlog and f.specialist_metadata: chat = f.specialist_metadata["chatlog"] print(f"{f.path}: {chat['turn_count']} turns, {chat['speaker_labels']}") # 通过 quality block 进行 Triage q = manifest.quality print(f"{q.clean_files}/{q.total_files} clean, {q.safety_flags} safety flags") # 写入 manifest Path("manifest.json").write_text(manifest_to_json(manifest)) ``` 每次扫描还会生成一份独立的 Markdown 报告 (`report_v{version}_{timestamp}.md`)——可在任何浏览器中阅读,易于分享,无需 JSON 解析。 ## 用例 ### 文档 pipeline 预处理 在你的摄取器触碰传入的文档文件夹之前,将 File Observer 指向它。在处理开始之前,就能了解哪些文件需要 OCR,哪些具有专家元数据,哪些被错误标记,以及哪些带有安全标志。 ### AI 训练数据筛选 正在扫描 AI 对话记录、知识库和文档语料库?File Observer 可以检测 `.txt`、`.md` 和 `.jsonl` 文件中的 chatlog 模式,统计轮次和发言人,并在数千个文件中突出显示 reference token(URL、@提及、代码块)。专为训练和评估语言模型的数据集而构建。 ### 审计与合规 每个字段都有溯源记录。每个向量都有加密身份摘要。可以使用跨增量扫描监管链的 HMAC 对清单进行签名。当审计员问“你怎么知道这个文件包含 X?”时——清单会给出答案。 ### 知识管理与知识库分析 针对 Obsidian 知识库、Confluence 导出或共享驱动器运行 File Observer。每个目录的摘要可以立即显示语料库的形态。reference token 揭示了链接密度、交叉引用和结构模式。作者聚合可以识别模板默认值与真实贡献者。 ### 迁移与去重 要在系统之间移动文件?File Observer 为每个文件提供校验和、MIME 分析、格式签名和 polyglot 检测。增量扫描可跟踪运行之间的变化。文件名模式可捕获副本后缀、带编号的修订以及以 UUID 命名的文件。 ### 安全分诊 安全标志可揭示 PDF 中的 JavaScript、DOCX 文件中的宏、RTF 中的 OLE 对象以及 XML 中的外部实体——而无需打开或执行任何内容。将它们呈送到你的安全 pipeline 中,由*你自己的策略*决定隔离还是分诊。这些标志是结构性观察,而不是判决——预期会出现误报和漏报,并调整你自己的阈值。 ## 工作原理 ``` fo ./corpus --specialists | +-- Universal tier Every file: checksum, MIME, signatures, routing +-- Baseline tier Text files: encoding, preview, tags, chatlog detection +-- Structural tier Text files: title, headings, keys, technology hints +-- Specialist tier Format-specific: PDF, images, video, audio, email, spreadsheets, documents, presentations +-- Vector pass chatlog, reference_tokens, filename_patterns (per-file) +-- Corpus vectors author_aggregate (after all files processed) +-- Summary Human-readable paragraph + per-directory breakdown | +-- Output: manifest.json + report.md ``` 单个文件出现故障永远不会中断扫描。错误将按文件、按阶段捕获。清单始终是完整的。 ## 可配置深度 | 配置 | 基线 | 专家 | 用例 | |---|---|---|---| | `fast_sort` | 8KB | 关闭 | 快速分诊、文件路由 | | `general` | 64KB | 关闭 | 标准观察 | | `deep_extract` | 1MB | 开启 | 完整元数据提取 | 针对特定扩展名的覆盖允许你为特定格式分配更多的预算: ``` fo ./docs --specialists --extension-override .pdf:specialist_budget=524288 ``` ## 规模化验证 File Observer 已经在包含 28,756 个文件的 12 个真实语料库中完美运行——**零致命错误**。(这衡量的是稳健性,而不是提取准确性;精度/召回率基准测试已计划中。) | 语料库 | 文件数 | 测试内容 | |---|---|---| | Apache Tika | 4,366 | 152 个文档专家、69 个 PDF、57 个电子表格、13 封电子邮件 | | OBS Studio | 5,201 | 大型 C/C++ 项目,91 个文件名模式 | | AutoGPT | 3,945 | AI 平台,1,612 个 @提及;chatlog 误报强化验证(原始检测在 v1.2.x 中被大幅削减) | | FastAPI | 3,002 | 包含大量文档的 Python 项目,chatlog 调优验证 | | OpenPreserve | 753 | 对抗性格式样本,285 个 PDF | | Claude Code logs | 125 | 真实的 AI 对话记录,JSONL chatlog 检测 | | Flask, tmux, self-scan | 11K+ | 多样化的代码仓库 | ## 文档 | 文档 | 涵盖内容 | |---|---| | [**教程**](docs/TUTORIAL.md) | 从首次扫描到 pipeline 集成的引导之旅——从这里开始 | | [**示例**](examples/) | 可运行的独立示例,每个概念一个 | | [SCHEMA.md](docs/SCHEMA.md) | 完整的输出接口(由 `--schema` 生成) | | [HISTORY.md](docs/HISTORY.md) | 从 v0.1 到当前版本的每一个版本,包含规范和合规报告 | | [PUBLIC_CONTRACT.md](docs/PUBLIC_CONTRACT.md) | 消费者稳定性承诺——你可以依赖的内容 | | [LIMITATIONS.md](docs/LIMITATIONS.md) | File Observer 刻意不做的事情 | | [CONVENTIONS.md](docs/CONVENTIONS.md) | 内部命名、版本控制和跟踪 | | [v1.42.0 RFC 规范](docs/v1.42.0_RFC_Specification.md) | 当前发布规范——**筛选回执 + 桥接 ID (`--receipt`)。**扫描的紧凑、审计友好的投影:一个信封(版本、`manifest_checksum` + 签名、`scan_id`、`dictionary_id`)+ 每个文件的记录(`receipt_id`、`path_id`、校验和、大小、mime、safety_flags、词典命中摘要)。**`receipt_id`** 是(`manifest_checksum` + 路径 + 文件哈希)的防篡改哈希——下游读取/跳过日志引用的显式连接键,因此 fo 的观察与编排器的决策实现了“真实对接”。构造上是安全的(无原始路径)。fo 从不记录读取/跳过决定(章程边界)。现有观察的投影 → `LOGIC_VERSION`/`SCHEMA_VERSION` 保持不变 (1.22.0 / 1.23);新增 `receipt_doc_version`。r/mcp 消费安全计划的第 3 阶段。v1.0.0 RFC 仍然是具有约束力的 schema 冻结合约。 | | [v1.41.0 RFC 规范](docs/v1.41.0_RFC_Specification.md) | 上一发布规范——**针对源自文件的元数据的词典自扫描。** v1.38 词典扫描文件正文;风险术语可能隐藏在正文扫描永远看不到的元数据中(文件名、EXIF 品牌/型号、PDF producer、办公应用程序字符串)。v1.41 在源自文件的元数据字符串上重用相同的匹配器,并报告一个新的临时 `lexicon_match.metadata` 子块——与正文分开保存,以便消费者看到它*在哪里*。`lexicon_match` safety_flag 现在在正文或元数据命中时触发。没有词典时处于休眠状态(现有清单字节完全一致);`LOGIC_VERSION` 1.21.0→1.22.0(仅在词典扫描时值发生移动),SCHEMA 1.22→1.23(附加,临时)。r/mcp 消费安全计划的第 2 阶段(检测)。v1.0.0 RFC 仍然是具有约束力的 schema 冻结合约。 | | [v1.40.0 RFC 规范](docs/v1.40.0_RFC_Specification.md) | 上一发布规范——**字段信任分类 + `--trusted-only` 安全模式。**每个清单字段都被分类为 `fo_derived`(受信任——计数、类型、哈希、标志)或 `file_derived`(不受信任/攻击者可控——文件名、`content_preview`、提取的元数据字符串)。`--schema` 现在为每个字段标注其 `trust`,而 `--trusted-only` 输出仅保留受信任字段的投影(将攻击者可控的字符串置空,添加 `path_id` 关联句柄)——在构造上是安全的,可提供给模型。现有观察的投影:默认清单字节完全一致,因此 `LOGIC_VERSION`/`SCHEMA_VERSION` 保持不变 (1.21.0 / 1.22;`schema_doc_version` 因标注而增加)。v1.0.0 RFC 仍然是具有约束力的 schema 冻结合约。 | | [v1.39.0 RFC 规范](docs/v1.39.0_RFC_Specification.md) | 上一发布规范——**MCP 前门:词典 + 差异。** v1.37 MCP 服务器早于 v1.38 词典,因此代理前门无法运行防护栏预筛查。v1.39 将词典(服务器启动 `--lexicon` 标志——术语永远不通过 MCP 线路传输)+ 差异扫描(`previous_manifest_path` 工具参数)贯穿现有工具。前门:清单与使用相同设置的 CLI 扫描字节完全一致,因此 `LOGIC_VERSION`/`SCHEMA_VERSION` 保持不变 (1.21.0 / 1.22)。v1.0.0 RFC 仍然是具有约束力的 schema 冻结合约。 | | [v1.38.0 RFC 规范](docs/v1.38.0_RFC_Specification.md) | 上一发布规范——**自带词典术语观察器。**当消费者提供带有类别标记的词典时(`--lexicon` / `ScannerConfig(lexicon=…)`),fo 会在对整个文件进行有限读取时,统计每个类别的术语(词边界、字面量)在每个文本文件中的出现次数,并报告每个类别的计数 + 密度——这是一种**观察,绝不是判决**(由消费者设定阈值)——并在出现任何命中时加上 `lexicon_match` safety_flag。价值中立的引擎:术语是消费者私有的运行时配置,**永远不会回显到清单中**(仅包含计数 + 内容哈希 `dictionary_id`)。未提供词典时处于休眠状态(现有清单字节完全一致)。新增临时 `lexicon_match` 命名空间 + `lexicon` 向量。`LOGIC_VERSION` 1.20.0→1.21.0(仅在提供词典的扫描时发生移动);SCHEMA 1.21→1.22(附加)。v1.0.0 RFC 仍然是具有约束力的 schema 冻结合约。 | | [v1.37.0 RFC 规范](docs/v1.37.0_RFC_Specification.md) | 上一发布规范——**MCP 服务器(代理原生前门)。**一个新的 `file-observer-mcp` stdio 服务器 + `[mcp]` 扩展通过模型上下文协议 (Model Context Protocol) 暴露了 fo 现有的清单/摘要/schema——只读、确定性、4 个具有渐进式展开的工具 (`scan_summary`/`scan_file`/`scan_directory`/`describe_surface`)。为面对不受信任文件的代理提供了一种安全的“先查看,再触碰”的原语。一个新的接口:清单与 `scan()` 的校验和完全一致,因此 `LOGIC_VERSION`/`SCHEMA_VERSION` 保持不变(v1.26 `scan()` / v1.28 `--stdout` 前门先例)。v1.0.0 RFC 仍然是具有约束力的 schema 冻结合约。 | | [v1.36.0 RFC 规范](docs/v1.36.0_RFC_Specification.md) | 上一发布规范——**defusedxml → purexml XML 依赖项切换。**fo 的 XML 加固(OOXML/ODF 专家层 + 结构化 XML 键层)从 `defusedxml` 转移到 **purexml**——一个纯标准库、零依赖、由预言机门控的 defusedxml 直接替代品(MIT 许可证;在 fo 自己的语料库上证明了 2,695/0 的能力)。fo 选择了 purexml 的结构上限(`max_depth`/`max_attributes`/`max_bytes`),因此它现在会拒绝 defusedxml 可以解析的病态深度过大 XML(一个可捕获的 `ValueError` → 字段降级,从不崩溃)。在真实文件上解析输出的字节完全相同。`LOGIC_VERSION` 1.19.0→1.20.0(仅针对病态输入的结构上限行为);SCHEMA 不变 (1.21)。⚠ `manifest_checksum` 在每个清单上都会发生移动(依赖记录已更改)。v1.0.0 RFC 仍然是具有约束力的 schema 冻结合约。 | | [v1.35.0 RFC 规范](docs/v1.35.0_RFC_Specification.md) | 上一发布规范——**AI session 按模型使用情况归因。**一个新的临时 `ai_session.usage_by_model`——按模型统计 token 使用量总和的确定性列表,以与每个使用情况字典共存的模型为键(Claude `message.model` OpenAI `response.model` / Gemini `modelVersion`,全部测量为 100% 共存)。不变式:`usage` == `usage_by_model` 的逐元素总和(session 路径未被触碰)。模型逐字记录(包括标记);无模型的轮次 → 进入 null 桶;从不定价。`LOGIC_VERSION` 1.18.0→1.19.0(在 ai_session 语料库上值会移动;ai_session method_version 1→2);SCHEMA 1.20→1.21(新字段,临时)。v1.0.0 RFC 仍然是具有约束力的 schema 冻结合约。 | | [v1.34.0 RFC 规范](docs/v1.34.0_RFC_Specification.md) | 上一发布规范——**Chatlog session 维度。**chatlog 专家发出三个新的临时平面标量:`first_timestamp`/`last_timestamp`(轮次时间戳的最小值/最大值,标准化为规范的 ISO-8601 UTC)+ `cwd`(session 的工作目录)。文件(观察而非推导)的确定性纯函数——为下游索引提供时间轴和该 session 的项目属性。`LOGIC_VERSION` 1.17.0→1.18.0(值在带有时间戳/cwd 的 chatlog 语料库上发生移动);SCHEMA 1.19→1.20(新字段,临时)。v1.0.0 RFC 仍然是具有约束力的 schema 冻结合约。 | | [v1.33.0 RFC 规范](docs/v1.33.0_RFC_Specification.md) | **AI session 观察,增量 1。**在检测到 `is_chatlog` 的 AI session 日志(Claude Code / OpenAI / Gemini)上,fo 发出一个新的临时 `ai_session` 命名空间:token 使用**总和**(规范的 fo 名称,缺失则为 null,保留供应商的原始键)+ 锚定在 ID 前缀 + 对象类型上的**生产者 schema 指纹**(`vendor`/`surface`/`models`/`id_prefix`/`object_types`/`schema_mismatch`)。仅限观察——总和**从不定价**。`LOGIC_VERSION` 1.16.0→1.17.0(值会移动 AI session 语料库上的 `manifest_checksum`);SCHEMA 1.18→1.19(新命名空间,临时)。v1.0.0 RFC 仍然是具有约束力的 schema 冻结合约。 | | [v1.32.0 RFC 规范](docs/v1.32.0_RFC_Specification.md) | **通用 kv-fact-block 专家 (FR #114)。**当文本文件的正文(移除 frontmatter)主要由 `key: value` 行组成时,fo 会逐字发出观察到的键值对 + 通用(新的临时 `fact_block` 命名空间:`pair_count`/`pairs`/`duplicate_keys`)——而不是每个消费者的 schema。一个像 `is_chatlog` 那样的内容形状检测器;句子值否决权使其远离对话(测量优先:散文 0/397,对话 0/60,fact-blocks 497/497)。`LOGIC_VERSION` 1.15.3→1.16.0(新的内容检测路由);SCHEMA 1.17→1.18(新命名空间,临时)。v1.0.0 RFC 仍然是具有约束力的 schema 冻结合约。 | | [v1.31.0 RFC 规范](docs/v1.31.0_RFC_Specification.md) | 上一版本——**提升阶段:capture-metadata → 稳定。** v1.16 的图像 EXIF 字段(`make`/`model`/`orientation`/`datetime_original`/`gps_present`/`xmp_present`)以及整个 v1.17–1.20 的 `video` 命名空间从临时升级为稳定——发布以来确定的逻辑,经 exiftool 预言机验证、语料库证明并经过红队测试。仅限指定:清单字节完全相同(稳定性仅存在于 `--schema` 中);`LOGIC_VERSION` 不变 (1.15.3);SCHEMA 1.16→1.17(提升 = 合约变更)。v1.0.0 RFC 仍然是具有约束力的 schema 冻结合约。 | | [v1.30.0 RFC 规范](docs/v1.30.0_RFC_Specification.md) | 上一版本——**CLI 稳健性**:在无效输入时大声报错(`rc=2`)(不存在/非目录源、`--workers < 1`、`--preview-max < 0`;缺少 `--previous-manifest` 会发出警告),而不是静默的 `rc=0` 空清单;并且默认输出从安装包目录移至当前工作目录下的 `./file-observer-manifests/`(并且现在发现机制**会跳过该目录**,这样重新扫描就不会观察到它自己的输出)。有效的扫描结果字节完全相同,除非目录树中包含 fo 自己的 `file-observer-manifests/` 目录,该目录现在被跳过 (LOGIC 1.15.0→1.15.1;SCHEMA 不变)。通过 GitHub-issues 反馈模型由 bestiary 浮现 (#109/#110)。v1.0.0 RFC 仍然是具有约束力的 schema 冻结合约。 | | [v1.29.0 RFC 规范](docs/v1.29.0_RFC_Specification.md) | 上一版本——chatlog 检测可识别**代理(工具轮次)会话**:具有对话角色 + 文本块(向后兼容)或独特代理块(thinking / tool_use / tool_result)的轮次将计入检测和轮次计数信号中。恢复以文本为中心的闸门错过的、大量使用工具的 Claude Code 日志。`is_chatlog` 严格附加;代理日志的 chatlog 信号值发生移动 (LOGIC 1.14.1→1.15.0;SCHEMA 不变)。v1.0.0 RFC 仍然是具有约束力的 schema 冻结合约。 | | [v1.28.0 RFC 规范](docs/v1.28.0_RFC_Specification.md) | `--stdout`:将清单写入 stdout(无文件),对 Docker/pipeline 友好(`file-observer . --stdout \| jq`)。仅输出路由;清单字节完全相同(LOGIC + SCHEMA 不变)。v1.0.0 RFC 仍然是具有约束力的 schema 冻结合约。 | | [v1.27.0 RFC 规范](docs/v1.27.0_RFC_Specification.md) | JSON Schema artifact —— JSON Schema artifact:一个提交并生成的 `docs/manifest.schema.json` (draft 2020-12),用于任何语言的清单验证/代码生成,由 `--schema --schema-format json-schema` 发出。描述清单;LOGIC + SCHEMA 不变。v1.0.0 RFC 仍然是具有约束力的 schema 冻结合约。 | | [v1.26.0 RFC 规范](docs/v1.26.0_RFC_Specification.md) | 单次调用公共 API (`scan` / `scan_to_json`):`from file_observer import scan; m = scan("./folder")`。仅限 Python 表面工效学;清单字节完全相同(LOGIC + SCHEMA 不变)。 | | [v1.25.0 RFC 规范](docs/v1.25.0_RFC_Specification.md) | 音频和旧版演示文稿提取(候选 B,阶段 2):`.mp3`(新的 `audio` 命名空间——ID3 标签 + 格式/比特率/时长)+ 旧版 `.ppt` (OLE2 title/author/application/slide_count,扩展 `presentation`) (SCHEMA 1.16)。 | | [v1.24.0 RFC 规范](docs/v1.24.0_RFC_Specification.md) | Office 和媒体提取(候选 B,阶段 1):OOXML/ODF office (`.pptx`/`.odp`/`.odt`/`.ods`) + `.jp2`/`.tiff` 尺寸和 EXIF 提取;新的 `presentation` 命名空间 (SCHEMA 1.15)。 | | [v1.20.0 RFC 规范](docs/v1.20.0_RFC_Specification.md) | 上一版本——`video.creation_date_qt`(Apple QuickTime creationdate 键,拍摄时刻 + 时区)。 | | [v1.19.0 RFC 规范](docs/v1.19.0_RFC_Specification.md) | 上一版本——人类可读界面刷新(扫描 `summary` + `--schema --format summary` 散文)。 | | [v1.18.0 RFC 规范](docs/v1.18.0_RFC_Specification.md) | 上一版本——视频拍摄设备 + GPS 存在状态(Apple QuickTime 键)。 | | [v1.17.0 RFC 规范](docs/v1.17.0_RFC_Specification.md) | 上一版本——视频容器元数据(编解码器/时长/尺寸/创建日期)。 | | [v1.16.0 RFC 规范](docs/v1.16.0_RFC_Specification.md) | 上一版本——图像拍摄元数据(用于 JPEG 和 HEIC 的 EXIF,GPS 存在状态 → `geotagged`)。 | | [v1.15.0 RFC 规范](docs/v1.15.0_RFC_Specification.md) | 上一版本——跨平台加固(CI OS 矩阵 + HEIC/HEIF/AVIF 检测)。 | ## API 参考 ### 核心类 ``` Scanner(source_dir: Path, config: ScannerConfig | None = None) Scanner.scan() -> ScanManifest ``` ### 配置 ``` ScannerConfig( enable_specialists=False, # Enable format-specific extraction preview_max_chars=1000, # Content preview length sample_size=8192, # Binary detection sample baseline_max_bytes=65536, # Text decode limit specialist_budget=131072, # OOXML read budget format="json", # "json" or "jsonl" exclude_hidden=False, # Skip dot-files ignore_file=None, # Path to .scannerignore previous_manifest=None, # Delta scan reference signing_key=None, # HMAC signing key ) ``` ### 输出 ``` manifest_to_json(manifest) # Pretty-printed JSON manifest_to_jsonl(manifest) # NDJSON streaming format manifest_to_markdown(manifest) # Human-readable report ``` ### 关键数据类 - **`ScanManifest`** —— 顶层:context、stats、quality、vectors_collected、summary、files[] - **`FileRecord`** —— 单个文件:path、mime、checksum、encoding、specialist_metadata、reference_tokens、filename_patterns、safety_flags、signal_provenance、errors - **`ScanContext`** —— 环境指纹:versions、platform、dependencies - **`VectorRecord`** —— 向量标识、digest、scope、applied count、summary ## 贡献 我们欢迎贡献。有关完整指南,请参阅 [CONTRIBUTING.md](https://github.com/russalo/file-observer/blob/main/CONTRIBUTING.md)。 **快速版本:** 1. Fork 并 clone 2. `pip install -e ".[dev]"` 并运行测试 3. 在你的第一个 PR 上签署 [CLA](https://github.com/russalo/file-observer/blob/main/CLA.md) 4. 每个 PR 只关注一个问题,必须有测试,保持确定性 ## 许可证 File Observer 采用双重许可: - **开源** [AGPL-3.0](https://github.com/russalo/file-observer/blob/main/LICENSE) —— 免费使用,回馈社区 - 在 AGPL 条款不适用的情况下提供**商业许可** ### 哪个适用于你 **AGPL 是可以的——无需商业许可——适用于:** - 内部使用:在你自己的组织内部运行 File Observer,包括在私有服务器上,没有义务发布任何内容。 - 个人项目、研究和评估。 - 本身与 AGPL 兼容的开源项目。 **你可能在以下情况需要商业许可:** - **将** File Observer **嵌入**到你分发但未根据 AGPL 发布该产品源代码的专有产品中。 - **通过网络作为服务提供**(SaaS)。AGPL 的网络条款 (§13) 意味着,如果用户通过网络与修改后的版本进行交互,你必须向他们提供其完整的对应源代码。商业许可可免除该义务。 - 在没有 AGPL 源代码披露要求的情况下,向第三方**分发** File Observer(或其衍生作品)。 简而言之:AGPL 的义务由**分发**和**修改版本的网络使用**触发,而不是由私人的内部使用触发。如果你不确定你的使用是否会触发它们,那正是商业许可所解决的问题。 联系 **russalo@russalo.com** 获取商业条款。 ### 商标 Apache® 和 Apache Tika™ 是 [Apache 软件基金会](https://www.apache.org/)的商标。Unix `file` 命令仅用于描述性引用。File Observer 是一个独立的项目,**与 Apache 软件基金会没有关联、未受其认可或赞助**;任何对 Apache Tika 的引用仅用于比较。 *由 [Russalo](https://russalo.com) 构建。扫描器负责记录。消费者负责解读。身份摘要使记录可审计。*
标签:Python, 元数据管理, 数据处理流水线, 数据提取, 文件解析, 无后门, 请求拦截, 逆向工具