コンテンツにスキップ

DocumentClassifier

リクエストが求めたときに、文書が何であるかを述べるポート。

英語版が原文です。

プロトコル:

  • DocumentClassifier.id: ClassifierId
  • DocumentClassifier.classify(text: str) -> Mapping[str, tuple[LabelScore, ...]]
  • DocumentClassifierProvider.document_classifiers() -> tuple[DocumentClassifier, ...]

DocumentClassifier は、文書が何であるかを述べるための境界です。その種別、それを作った部門、それが属する業種。文書ごとに一度、すべてのリーダーがページを生成した後に尋ねられ、渡されるのはバイト列ではなくテキストです — 文書として読まれたテキスト全文です。

回答はファセットからラベルへの写像です。ファセットは分類器が選んだ名前(document_typeindustry)で、indx はそれを列挙しません。各ラベルは LabelScore、すなわち名前と [0, 1] の数値で、高い順に並びます。回答に含まれないファセットは「意見なし」を意味し、リクエストが有効にした次の分類器がそれについて尋ねられます。LanguageScore と違い、これは主題についての主張です — だからこそ別のポートであり、別の予約キーの下にあります。

三つのディストリビューションが、サードパーティが使うのと同じエントリーポイントグループを通じて出荷されます。indx-classifier-words(単語リストとしきい値。エクストラなし。素のインストールが持つもの)、indx-classifier-zeroshot(onnxruntime 経由の多言語 NLI モデル。zeroshot エクストラの背後)、indx-classifier-llm(LiteLLM が到達できる任意のモデル。llm エクストラと INDX_CLASSIFIER_LLM_MODEL の背後)。

分類器は機能種別ではありません。CapabilityKind は閉じており、その上のラダーも閉じており、ラベルは何もルーティングしません — これは LanguageDetector の形です。読み取りの後に適用され、ルーティング決定ではなく実行出力を変えるポートです。

検出器と異なる点が一つあり、それが残りすべてを形作ります。検出器は無料で、インストールされていればいつでも動きます。分類器は呼び出しに対価がかかります — モデルの一回の実行、トークン、あるいは箱の外へのリクエスト — なので、リクエストが有効にしなかったものは何も動きません。それが、検出器が ID を持たないのに分類器が id を持つ理由であり、EncodeRequest.classification が望む ID を尋ねてほしい順に名指しする理由であり、インストール済みの ID が機能スナップショットに公表されて呼び出し側が発見できる理由です。それらは resolvable と同じく、スナップショットのコンテンツハッシュの外に載ります。有効化はリクエストごとなので、分類器をインストールしても、既存の計画が下した決定は何も変わりません。

これは 1 つのうちの 1 つではなく、5 つのうちの 1 つのポートです。格子は、渡される単位 — 文書、ページ、チャンク — に対する戻り値の形 — ラベルかスパンか — であり、このポートは「文書 × ラベル」のセルです。ページ分類器とチャンク分類器 はより細かい単位での同じ回答であり、エンティティ抽出器 はもう一方の戻り値の形です。5 つすべてが 1 つの ID 名前空間を共有するので、どの 2 つも同じ文字列を主張できません。

入力は文書として読まれたテキスト全文を、ページ順に並べたものです。サンプリングは契約のものではありません。縛りを必要とする実装 — トークン窓、コストの上限、二度は走らせたくない走査 — は自分の設定モデルで自前の縛りを適用し、indx-interfaces の依存のないヘルパーを使います(ADR-0033)。渡されたものをすべて読む分類器は、300 ページの報告書に対して 3 ページのものより多く払っており、それは今やリクエストが代わりに決めることではなく、分類器自身が下す決定です。

raise はソースではなく分類器についての判定なので、エグゼキューターはそれをログに残し、次の分類器に尋ねます。すでに文書を読む対価を払ったエンコードが、その後で求められた注釈のために失敗してはなりません。

二つの拒否がソースの取得前に発火し、どちらも 422 invalid_classifier です。インストール済みの何も宣言しない ID は unknown_classifier で、メッセージは宣言されている ID を列挙します。deviceexternal の分類器は、リクエストがその制約を運ぶとき data_residency です — 飛ばすのではなく拒否します。黙って省かれた分類器は、呼び出し側が与えられたと信じて与えられなかった回答だからです。

四つの属性は意図的にプロトコルの外にあり、既定値付きで読まれます。device(不在なら CPU)、VectorEncoder と同じく classify() の後に設定され実行の usage.cost_usd に加算される cost_usd、indx が同梱するものが設定する builtin = True、そして分類器が答えられるファセットを名指しする tuple[str, ...]facets

facets は「未解決のファセットがない」を知りうるものにします。リクエストは分類器を名指しするだけでファセットは名指ししないので、この宣言がなければエグゼキューターは有効化されたすべての分類器に尋ね、最初の分類器がすでにすべてのファセットを取っていても呼び出しの対価を計上するしかありませんでした。不在は「不明」を意味し、宣言しない分類器は常に尋ねられます。宣言していれば、その単位についてタプル内のすべてのファセットが取られた時点で、分類器はまるごと飛ばされます — 呼び出しなし、コストなし、トレースにも名前は載りません。同梱の三つの分類器は読み込んだタクソノミーのファセットを宣言するので、同梱テーブル上の classification.document_ids = ["words", "llm"] は、単語リストが空けたファセットについてのみ LLM の呼び出しに払います。

  • ファセットごとに答え、推測するくらいならファセットを省く。分類器自身の下限を下回るラベルはラベルではない。
  • 各ファセットを降順に並べる。[0] を読む呼び出し側が答えを読めるように。
  • 分類器が弁護できる意味の数値を報告する。単語シグネチャの比率も NLI の含意確率も自己申告であり、どちらもラベル付きコーパスに対して較正されていない。
  • モジュールスコープを安価に保つ。検出はスナップショット構築中にすべてのプロバイダーモジュールをインポートするため、エンジンのインポートは classify() の内側に、モデルの構築は最初の呼び出しの背後に置く。
  • 動けないときは何も公表しない。エクストラなし、あるいはモデル未設定のディストリビューションは、壊れた分類器ではなく、分類器をまったく宣言しない — ホスト型埋め込みの規則。

DocumentExecutor.encode はまず有効化された ID を解決してレジデンシーを確認し、文書を読み、有効化された各分類器にリクエスト順で尋ね、統合された回答を文書ブロックの metadataCLASSIFICATION_METADATA_KEY として {facet: [{"label", "confidence"}, ...]} の形で書き込みます。あるファセットについて意見を持つ最初の分類器がそれを取ります。何も読めなかった文書は分類されず、キーを持ちません。

キーは予約されています。EncodeRequest.metadatalanguages を拒否するのと同じく classification をきっぱり拒否し、呼び出し側のラベルが静かに置き換えられることはありません。

これらのいずれについても POLICY_VERSION は動かず、機能スナップショット ID は前後でバイト単位まで同一です。ラベルは出力への注釈であり、分類器を名指しするスナップショットのフィールドは設計上ハッシュから除外されています。