コンテンツにスキップ

indx を拡張する

独自の読み取り機能、専用パーサー、埋め込み空間、観測、ローダー、チャンカー、言語検出器、分類器、エンティティ抽出器をインストール可能な Python 機能として追加します。

indx の拡張は通常の Python ディストリビューションです。インストールが有効化のスイッチになります。indx は標準の Python エントリーポイントからプロバイダーを検出し、軽量な宣言を機能スナップショットへ追加し、計画や実行で必要になったときだけ実装を生成します。

独自パッケージ → indx.capabilities エントリーポイント → CapabilityProvider
  → 機能スナップショット → プランナー → エグゼキューター → ブロックまたはベクトル

ルーターとエグゼキューターが独自パッケージを直接インポートすることはありません。独自コードは各実装ではなく、indx-interfaces の安定した契約だけに依存します。

種類ごとに専用ページがあり、動く実装を掲載しています。「選択方法」が指すもの — ルーティングラダー、ノミネーション、その他のルートへの扉 — はルーティングで説明しています。

追加するもの 実装するもの 選択方法
読み取り機能、OCR エンジン、ビジョンモデル CapabilityProvider + PageReader 記述子の kind によって汎用ルーティングラダー内の位置が決まります。
専用パーサー CapabilityProvider + PageReader + SignatureDetector 低コストのオプトイン検出が、汎用ラダーより前へ指名します。
埋め込み空間 CapabilityProvider + EmbeddingSpaceProvider + VectorEncoder 文書またはクエリの埋め込み時に呼び出し側が空間を指定します。
indx が観測できないメディアタイプ CapabilityProvider + SourceObserverProvider + SourceObserver インストールすることが、そのメディアタイプを計画可能にします。
indx が解決できない URI スキーム CapabilityProvider + SourceLoaderProvider + SourceLoader インストールすることが、そのスキームを取得可能にします。
埋め込み単位の異なる切り方 CapabilityProvider + ChunkerProvider + Chunker チャンクの回答は読み取り後にページごとに統合され、チャンカー順で先勝ちです。
ページの言語を名指しする CapabilityProvider + LanguageDetectorProvider + LanguageDetector バイト列ではなくリーダーが生成したテキストで尋ねられ、意見を持つ最初の検出器がそのページを取ります。
テキストが何であるかを述べる CapabilityProvider + DocumentClassifierProvider / PageClassifierProvider / ChunkClassifierProvider リクエストがその ID を名指ししたときだけ動きます。1 つの文書・ページ・チャンクの全文で尋ねられ、意見を持つ最初の有効化済み分類器がファセットを取ります。
テキストの中の名前を見つける CapabilityProvider + PageEntityExtractorProvider / ChunkEntityExtractorProvider リクエストがその ID を名指ししたときだけ動きます。1 ページまたは 1 チャンクのテキストで尋ねられ、その中を指すスパンを返します。
テキストを要約しタグ付けする CapabilityProvider + DocumentEnricherProvider / PageEnricherProvider / ChunkEnricherProvider リクエストがその ID を名指ししたときだけ動きます。1 つの文書・ページ・チャンクのテキストで尋ねられ、要約、タグ、またはその両方を返します。

プロトコルは構造的部分型を使用します。クラスは必要なメソッドシグネチャを持てばよく、indx の基底クラスを継承する必要はありません。自身の機能を宣言しないプロバイダーは indx_interfaces.Plugin を継承できます。空の descriptors() と拒否する create() を提供します。継承しても何も宣言せず何も登録せず、探索が読むのはエントリーポイントのままです。

パッケージは indx ワークスペースから独立させます。

acme-indx-capability/
├── pyproject.toml
└── src/
└── acme_indx/
└── provider.py

pyproject.toml に一つのプロバイダーを登録します。

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "acme-indx-capability"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = ["indx-interfaces>=0.1,<0.2"]
[project.entry-points."indx.capabilities"]
acme = "acme_indx.provider:Provider"
[tool.hatch.build.targets.wheel]
packages = ["src/acme_indx"]

対象の indx 環境と同じリリース系列の indx-interfaces を使います。パーサー、モデル、クライアントのライブラリは indx 本体ではなく、このパッケージへ追加します。

一つのエントリーポイントグループがすべての拡張種類を運びます。一つのプロバイダーが descriptors() で機能を宣言し、必要に応じて各ページで説明するフックから埋め込み空間、ソース観測、ソースローダー、チャンカー、言語検出器、分類器(document_classifiers()page_classifiers()chunk_classifiers())、エンティティ抽出器(page_entity_extractors()chunk_entity_extractors())も宣言できます。

開発中は、編集可能な拡張を indx と同じ環境へ重ねて実行します。

Terminal window
uv run --with-editable ../acme-indx-capability \
indx capabilities
uv run --with-editable ../acme-indx-capability \
indx plan --signatures file://$PWD/tests/fixtures/invoice-anthropic.pdf
uv run --with-editable ../acme-indx-capability \
indx encode --signatures file://$PWD/tests/fixtures/invoice-anthropic.pdf
uv run --with-editable ../acme-indx-capability \
indx embed --space acme-text "amount due"

ルートから新しい機能を参照するには、まず capabilities に新しい ID と利用可能状態が表示される必要があります。インストール内容が変わると、新しいコンテンツアドレス付き機能スナップショットが生成されます。以前のインベントリで作られた計画とは意図的に互換になりません。

このリポジトリには実際に動く拡張が含まれています。examples/acme-indx-capability は上記の拡張種類を実コードとして実装しています。ソース観測を伴うページ読み取り、発注書パーサー、埋め込み空間で、indx-interfaces 以外の依存はありません。

Terminal window
uv run --with-editable examples/acme-indx-capability \
python examples/04_extend.py

オーバーレイなしで実行すると、インストールされていないことを伝えます。indx が名前でインポートすることはないからです。オーバーレイを付けると三つの機能がスナップショットに現れ、パーサーが発注書を認識し、indx embed --space acme-text が新しい空間へ到達し、素の環境では解決することも読むことも観測することもできない acme:// のソースが計画されます。

設定が必要なディストリビューションは名前空間を確保して宣言します。どちらも専用のポートではなく規約 です。indx の設定は環境変数であり、設定用のポートを設けることは同じことを二通りに行うことになります。

INDX_<VENDOR>_* を名乗る。 ファーストパーティのレーンは INDX_VLM_*INDX_OCR_*INDX_EMBED_*INDX_CHUNK_*INDX_TEXT_*INDX_ROUTING_*INDX_VALIDATION_*INDX_LOADER_<SCHEME>_* を保持しています。拡張側は自分の名前を使うので、INDX_ACME_API_KEY は indx が将来出荷するどの変数とも衝突しません。

settings.pyIndxSettings のサブクラスとして宣言する。 os.environ.get と解析と範囲 チェックを並べるのではなく、また値を読む側のモジュールに紛れ込ませるのでもありません。このワーク スペースのどのディストリビューションにも settings.py はちょうど 1 つあり、その INDX_* の名前が 現れる唯一の場所です。設定項目を知りたい運用者は 1 ファイルを開けば済みます。こうすると変数名・既定値・制約が一箇所にまとまり、使えない値は何かを組み立てる前に拒否 され、メッセージは変数名だけを示して値は決して示しません。打ち間違えた鍵が呼び出し側の読む 422 に混入しないのはこのためです。

from pydantic import Field
from indx import IndxSettings, SettingsConfigDict
class AcmeSettings(IndxSettings):
model_config = SettingsConfigDict(env_prefix="INDX_ACME_")
api_key: str | None = None
dpi: int = Field(default=150, gt=0)
def dpi() -> int:
return AcmeSettings.load().dpi

呼び出しごとに生成し、キャッシュしません。このリポジトリのすべての INDX_* 読み取りが従う規則です。 変数を調整した運用者が、反映のためにプロセスを再起動する必要があってはなりません。エクスポートされて いても空の変数は既定値を意味します。検証の失敗を変数名入りの ValueError に変えるのが load() です。

足りないものは unavailable_reason に書く。 すでに install the 'ocr' extra; missing: rapidocr を運んでおり、no vision model configured; set INDX_VLM_MODEL も同じ種類の実行可能な一文です。イン ストール済みだが未設定の機能は、読み取り時の失敗ではなく、はしごが降りていく利用不可の記述子になりま す。そこに書くのは変数名であり、値や認証情報ではありません。

各拡張ページには固有のチェック項目があります。以下はすべてのプロバイダーに共通です。

  • 機能 ID と空間 ID をグローバルに一意とし、リリース間で安定させる。
  • descriptors() を軽量に保ち、利用できない実装には対応方法が分かる unavailable_reason を設定する。
  • メタデータを JSON 互換にし、認証情報、非公開エンドポイント、秘密情報を記述子へ含めない。
  • INDX_<VENDOR>_* の名前空間を名乗り、その変数を IndxSettings のサブクラスとして宣言し、呼び出しごとに読む。
  • 記述子と create() の整合、要求された全ページ、明示的な失敗、エントリーポイント探索をテストする。
  • プロバイダーを直接生成するだけでなく、インストール済みディストリビューションとして capabilitiesplanencodeembed を実行する。