📌

Ollamaを使用したオンプレミスLLMによる構造化データ抽出

に公開

Cover

このブログでは、Ollamaを使用して構造化データを抽出する方法を紹介します。これはローカルで実行でき、独自のクラウドやサーバーにデプロイすることができます。

完全なコードはこちらで見ることができます。わずか約100行のPythonコードです。ぜひチェックしてみてください🤗!

前提条件

Postgresのインストール

Postgresがインストールされていない場合は、インストールガイドを参照してください。

ollamaのインストール

Ollamaを使用すると、LLMモデルを簡単にローカルマシンで実行できます。始めるには:

Ollamaをダウンロードしてインストールします。
ollama pullコマンドでお好みのLLMモデルを取得します。例えば:

ollama pull llama3.2

マークダウンファイルから構造化データを抽出する

1. 出力を定義する

Pythonマニュアルから以下の情報を構造化データとして抽出します。

そこで、以下のように出力データクラスを定義します。目標はModuleInfoを抽出して入力することです。

@dataclasses.dataclass
class ArgInfo:
    """Information about an argument of a method."""
    name: str
    description: str

@dataclasses.dataclass
class MethodInfo:
    """Information about a method."""
    name: str
    args: cocoindex.typing.List[ArgInfo]
    description: str

@dataclasses.dataclass
class ClassInfo:
    """Information about a class."""
    name: str
    description: str
    methods: cocoindex.typing.List[MethodInfo]

@dataclasses.dataclass
class ModuleInfo:
    """Information about a Python module."""
    title: str
    description: str
    classes: cocoindex.typing.List[ClassInfo]
    methods: cocoindex.typing.List[MethodInfo]

2. cocoIndex フローを定義する

マークダウンから構造化データを抽出するためのcocoIndexフローを定義しましょう。これは非常にシンプルです。

まず、ソースとしてマークダウン形式のPythonドキュメントを追加します。PDFの読み込み方法については、後のセクションで説明します。

@cocoindex.flow_def(name="ManualExtraction")
def manual_extraction_flow(flow_builder: cocoindex.FlowBuilder, data_scope: cocoindex.DataScope):
    data_scope["documents"] = flow_builder.add_source(
        cocoindex.sources.LocalFile(path="markdown_files")) 

    modules_index = data_scope.add_collector()

flow_builder.add_sourceは以下のサブフィールドを持つテーブルを作成します。詳細はドキュメントをご覧ください。

  • filename (キー, 型: str): ファイルの名前、例: dir1/file1.md
  • content (型: str (binaryFalseの場合)、それ以外はbytes): ファイルの内容

次に、マークダウンファイルから構造化データを抽出しましょう。これは非常に簡単で、LLM仕様を提供し、定義された出力タイプを渡すだけです。

CocoIndexはLLMを使用してデータを処理する組み込み関数(例:ExtractByLlm)を提供しています。Ollamaの組み込みサポートがあり、ローカルマシンで簡単にLLMモデルを実行できます。モデルの完全なリストはこちらで確認できます。また、OpenAI APIもサポートしています。完全なドキュメントと手順はこちらで確認できます。

    # ...
    with data_scope["documents"].row() as doc:
        doc["module_info"] = doc["content"].transform(
            cocoindex.functions.ExtractByLlm(
                llm_spec=cocoindex.LlmSpec(
                     api_type=cocoindex.LlmApiType.OLLAMA,
                     # See the full list of models: https://ollama.com/library
                     model="llama3.2"
                ),
                output_type=ModuleInfo,
                instruction="Please extract Python module information from the manual."))

抽出後、上記で定義したデータスコープのコレクターからcollect関数を使用して、出力から好きなものを選んで取得するだけです。

    modules_index.collect(
        filename=doc["filename"],
        module_info=doc["module_info"],
    )

最後に、抽出したデータをテーブルにエクスポートします。

    modules_index.export(
        "modules",
        cocoindex.storages.Postgres(table_name="modules_info"),
        primary_key_fields=["filename"],
    )

3. インデックスのクエリとテスト

🎉 これで準備完了です!

以下のコマンドを実行してインデックスをセットアップし、更新します。

python main.py cocoindex setup
python main.py cocoindex update

インデックスの更新状態はターミナルに表示されます。

Index Updates

インデックスが構築されると、modules_infoという名前のテーブルが作成されます。いつでもクエリを実行できます。例えば、Postgresシェルを開始します。

psql postgres://cocoindex:cocoindex@localhost/cocoindex

そして、SQLクエリを実行します。

SELECT filename, module_info->'title' AS title, module_summary FROM modules_info;

ドキュメントから抽出された構造化データを確認できます。以下は抽出されたモジュール情報のスクリーンショットです。

module info

CocoInsight

CocoInsightは、データパイプラインとデータインデックスを理解するためのツールです。
CocoInsightは現在早期アクセス中(無料)です😊。CocoInsightのクイック3分チュートリアルはYouTubeで見る

1. CocoIndexサーバーを実行する

python main.py cocoindex server -c https://cocoindex.io

CocoInsightダッシュボードを見るにはhttps://cocoindex.io/cocoinsightにアクセスしてください。ローカルのCocoIndexサーバーにゼロデータ保存で接続します。

CocoInsightダッシュボードには2つの部分があります。

CocoInsight

  • Flows: 定義したフローを確認でき、そのデータを収集できます。
  • Data: データインデックスのデータを確認できます。

データ側では、任意のデータをクリックして下にスクロールすると詳細を確認できます。このデータ抽出の例では、マークダウンファイルから抽出されたデータと表形式で表示された構造化データを確認できます。

CocoInsight Data

例えば、arrayモジュールでは、データをクリックするとデータをプレビューできます。
CocoInsight Arrray Module

また、多くの素晴らしい更新が近日公開されます。ぜひご期待ください!

データにサマリーを追加する

cocoindexをフレームワークとして使用すると、データに対する任意の変換(LLMサマリーを含む)を簡単に追加でき、そのデータをデータインデックスの一部として収集できます。
例えば、各モジュールに簡単なサマリーを追加してみましょう。

後ほどLLMの例を追加します。

1. 出力を定義する

まず、出力定義の一部として作成したい構造を定義しましょう。

@dataclasses.dataclass
class ModuleSummary:
    """Summary info about a Python module."""
    num_classes: int
    num_methods: int

2. cocoIndex フローを定義する

次に、データをサマリーするためのカスタム関数を定義しましょう。詳細はこちらをご覧ください。

@cocoindex.op.function()
def summarize_module(module_info: ModuleInfo) -> ModuleSummary:
    """Summarize a Python module."""
    return ModuleSummary(
        num_classes=len(module_info.classes),
        num_methods=len(module_info.methods),
    )

3. フローに関数をプラグインする

    # ...
    with data_scope["documents"].row() as doc:
      # ... after the extraction
      doc["module_summary"] = doc["module_info"].transform(summarize_module)

🎉 これで準備完了です!

以下のコマンドを実行してインデックスをセットアップし、更新します。

python main.py cocoindex setup
python main.py cocoindex update

PDFファイルから構造化データを抽出する

Ollamaは直接PDFファイルを入力としてサポートしていないため、まずはmarkdownに変換する必要があります。

これを行うには、PDFをmarkdownに変換するカスタム関数をプラグインします。詳細はこちらをご覧ください。

1. 関数の仕様を定義する

関数の仕様は関数の特定のインスタンスの動作を構成します。

class PdfToMarkdown(cocoindex.op.FunctionSpec):
    """Convert a PDF to markdown."""

2. エグゼキュータークラスを定義する

エグゼキュータークラスは、関数仕様を実装するクラスです。これは関数の実際の実行を担当します。

このクラスはPDFコンテンツをバイトとして受け取り、一時ファイルに保存し、PdfConverterを使用してテキストコンテンツを抽出します。抽出されたテキストは文字列として返され、PDFをマークダウン形式に変換します。

これはspec: PdfToMarkdownによって関数仕様と関連付けられています。

@cocoindex.op.executor_class(gpu=True, cache=True, behavior_version=1)
class PdfToMarkdownExecutor:
    """Executor for PdfToMarkdown."""

    spec: PdfToMarkdown
    _converter: PdfConverter

    def prepare(self):
        config_parser = ConfigParser({})
        self._converter = PdfConverter(create_model_dict(), config=config_parser.generate_config_dict())

    def __call__(self, content: bytes) -> str:
        with tempfile.NamedTemporaryFile(delete=True, suffix=".pdf") as temp_file:
            temp_file.write(content)
            temp_file.flush()
            text, _, _ = text_from_rendered(self._converter(temp_file.name))
            return text

なぜここで単独の関数ではなく、仕様とエグゼキュータを定義したいのか疑問に思うかもしれません。主な理由は、実際のデータを処理する準備ができる前に、(パーサーの初期化など)いくつかの重い準備作業を行う必要があるからです。

3. フローに関数をプラグインする

    # Note the binary = True for PDF
    data_scope["documents"] = flow_builder.add_source(cocoindex.sources.LocalFile(path="manuals", binary=True))
    modules_index = data_scope.add_collector()

    with data_scope["documents"].row() as doc:
        # plug in your custom function here
        doc["markdown"] = doc["content"].transform(PdfToMarkdown())

🎉 これで準備完了です!

以下のコマンドを実行してインデックスをセットアップし、更新します。

python main.py cocoindex setup
python main.py cocoindex update

Community

コミュニティからのご意見をお待ちしています!GithubDiscordで私たちを見つけてください。

この投稿や私たちの活動が気に入ったら、ぜひGithub上のCocoindexに⭐スターをつけて応援してください❤️。温かいココナッツハグとともに、心から感謝します🥥🤗。 GitHub

Discussion