nikkie-ftnextの日記

イベントレポートや読書メモを発信

Google先生の新作、Geminiでテキストから任意の情報を抽出する LangExtract をお試し

はじめに

七尾百合子さん、お誕生日 136日目 おめでとうございます! nikkieです。

一夜明けたらGoogleが新しいライブラリを発表していました。
google-genaiに依存しているので、最近触っているOpenTelemetry1を導入して、動きを見てみようと思い立ちました。

目次

LangExtract 爆誕

https://pypi.org/project/langextract/

LLMを使ってテキストから情報抽出する流れ

2024年3月にまとめていました。

テキストを含んだ画像をLLMで扱い、情報を取り出す時に参考にできそうな事例をここに書き出します。

そこから各社のLLMは発展しましたね。
例えば、google-genaiではPydanticのモデルを渡して該当するデータをJSONで抽出できます。
https://googleapis.github.io/python-genai/#pydantic-model-schema-support

この流れの先にLangExtractがあるととらえています。
今回のGoogleのブログには以下が書かれています。

  • few-shot(少数例)を渡して情報抽出
  • JSONスキーマ準拠
  • Optimized long-context information extraction

    • IMO:Geminiの強さを使っていそうですね

LangExtractを使った例

例を見ると、大量のテキストからの抽出を想定しているようです。

Warning: Running this example processes a large document (~44 000 tokens) and will incur costs. For large-scale use, a Tier 2 Gemini quota is suggested to avoid rate-limit issues (details). (Romeo and Juliet Full Text Extraction)

ロミジュリの例

ブログの例の1つでは、ロミオとジュリエットの一節から情報抽出。
このチョイス、『アオのハコ』リスペクトの可能性があります2

環境変数 LANGEXTRACT_API_KEY を指定して動かします。
https://github.com/google/langextract/tree/v1.0.0?tab=readme-ov-file#setting-up-api-key-in-your-environment

% uv run script.py
LangExtract: model=gemini-2.5-pro, current=68 chars, processed=68 chars:  [00:00]
LangExtract: model=gemini-2.5-pro, current=68 chars, processed=68 chars:  [00:07]
✓ Extraction processing complete
✓ Extracted 4 entities (3 unique types)
  • Time: 7.70s
  • Speed: 9 chars/sec
  • Chunks: 1
LangExtract: Saving to extraction_results.jsonl: 1 docs [00:00, 325.42 docs/s]
✓ Saved 1 documents to extraction_results.jsonl
LangExtract: Loading extraction_results.jsonl: 100%|██████████| 1.19k/1.19k [00:00<00:00, 4.19MB/s]
✓ Loaded 1 documents from extraction_results.jsonl

プロンプト

Extract characters, emotions, and relationships in order of appearance.
Use exact text for extractions. Do not paraphrase or overlap entities.
Provide meaningful attributes for each entity to add context.
  • character、emotion、relationshipの抽出(出現順で)
  • 抽出したエンティティにはattributeを付与

lx.data.ExampleDataでそれぞれ1例ずつ示しています(few-shot)
https://github.com/google/langextract/blob/v1.0.0/langextract/data.py#L226-L236

保存したJSONです。
https://github.com/google/langextract/blob/v1.0.0/langextract/io.py#L84-L134
なお非決定的で、毎回再現しません

{
  "extractions": [
    {
      "extraction_class": "character",
      "extraction_text": "Lady Juliet",
      "char_interval": {
        "start_pos": 0,
        "end_pos": 11
      },
      "alignment_status": "match_exact",
      "extraction_index": 1,
      "group_index": 0,
      "description": null,
      "attributes": {
        "emotional_state": "longing"
      }
    },
    {
      "extraction_class": "emotion",
      "extraction_text": "gazed longingly",
      "char_interval": {
        "start_pos": 12,
        "end_pos": 27
      },
      "alignment_status": "match_exact",
      "extraction_index": 2,
      "group_index": 1,
      "description": null,
      "attributes": {
        "feeling": "yearning"
      }
    },
    {
      "extraction_class": "emotion",
      "extraction_text": "her heart aching",
      "char_interval": {
        "start_pos": 42,
        "end_pos": 58
      },
      "alignment_status": "match_fuzzy",
      "extraction_index": 3,
      "group_index": 2,
      "description": null,
      "attributes": {
        "feeling": "sorrow"
      }
    },
    {
      "extraction_class": "relationship",
      "extraction_text": "her heart aching for Romeo",
      "char_interval": {
        "start_pos": 42,
        "end_pos": 68
      },
      "alignment_status": "match_exact",
      "extraction_index": 4,
      "group_index": 3,
      "description": null,
      "attributes": {
        "type": "romantic longing"
      }
    }
  ],
  "text": "Lady Juliet gazed longingly at the stars, her heart aching for Romeo",
  "document_id": "doc_7a49f9c1"
}

テキストの部分からどんなデータを抽出したかを可視化するHTML3が作れます
https://github.com/google/langextract/blob/v1.0.0/langextract/visualization.py#L537-L608
JSONLがtest_output/ディレクトリ下に保存されているので、パスはディレクトリから指定する必要がありました(ブログから修正済み)

すっげー

OpenTelemetryで覗いたGeminiへの入出力

全容はこちら:
https://gist.github.com/ftnext/b65fdaed9c2bf4bc1eec5d2832b20e49

プロンプト
例は作ってほしいJSONに変換して渡してるんですね

Extract characters, emotions, and relationships in order of appearance.
Use exact text for extractions. Do not paraphrase or overlap entities.
Provide meaningful attributes for each entity to add context.

Examples
Q: ROMEO. But soft! What light through yonder window breaks? It is the east, and Juliet is the sun.
A: {
  "extractions": [
    {
      "character": "ROMEO",
      "character_attributes": {
        "emotional_state": "wonder"
      }
    },
    {
      "emotion": "But soft!",
      "emotion_attributes": {
        "feeling": "gentle awe"
      }
    },
    {
      "relationship": "Juliet is the sun",
      "relationship_attributes": {
        "type": "metaphor"
      }
    }
  ]
}

Q: Lady Juliet gazed longingly at the stars, her heart aching for Romeo
A: 

Geminiからの出力
JSON返してる!

{
  "extractions": [
    {
      "character": "Lady Juliet",
      "character_attributes": {
        "emotional_state": "longing"
      }
    },
    {
      "emotion": "gazed longingly",
      "emotion_attributes": {
        "feeling": "yearning"
      }
    },
    {
      "emotion": "her heart aching",
      "emotion_attributes": {
        "feeling": "sorrow"
      }
    },
    {
      "relationship": "her heart aching for Romeo",
      "relationship_attributes": {
        "type": "romantic longing"
      }
    }
  ]
}

終わりに

LangExtractを触りました。
これまでgoogle-genaiを使ってテキストから構造化はできましたが、大量のテキストデータで構造化というユースケース向けにLangExtractが提案されたのかなと思われます。

画像やPDFから抽出できると私はさらに嬉しいですが、現状はMarkItDownなどでテキスト化してからLangExtractに渡す形になりそうですね(今後LangExtractだけで完結したら、すごい!)