はじめに

近年、Llama 3やGemma 4といった高性能なLLMをローカル環境で動かせる「Ollama」が注目されています。
しかし、チャットUIで対話するだけでは、大量のデータを処理したり、既存のワークフローに組み込んだりするには不十分です。

特に、ClaudeやGemini、GPT-4などのクラウドLLMをスクリプトから利用(API連携)する場合、「従量課金」 という高い壁があります。数件の試行なら安価ですが、数千・数万件のチケットをバッチ処理するようなケースでは、コストが無視できないほど膨らんでしまいます。

そこで今回は、PythonからローカルLLMを制御し、コストゼロ・高セキュリティでタスクを自動化する方法を解説します。具体例として、プロジェクト管理ツール(Redmine, Jira, Backlog等)の膨大なチケットを自動要約するシナリオを実装してみます。
そしてせっかく最近リリースされたので Gemma 4 を使ってみたいと思います。

前提

今回はプロジェクト管理ツールからデータを取得する部分は対象外とします
事前に Markdown ファイルとして出力されている前提で進行します

使用したローカルLLM

gemma4:e4b

Gemma4 の E4B というモデルを使用しました
今回の用途にちょうど良さそう なのと 検証マシンのスペック 等の兼ね合いでこちらになりました

Gemma4 の他のモデルはこちら
https://ai.google.dev/gemma/docs/core/model_card_4?hl=ja

検証に使用した環境

MacBook Air ( M3 / メモリ24GB )
macOS 15.7.4

環境構築

1. Ollamaのインストール

公式サイトを確認し Ollama をインストールします
https://ollama.com/

2. カスタムモデルを作成する

2-1. Modelfile を用意する

今回は「チケットを要約させる」という用途に寄せた調整をしています

FROM gemma4:e4b

# --- 1. パラメータ調整 ---
PARAMETER temperature 0
PARAMETER num_ctx 32768
PARAMETER repeat_penalty 1.2
PARAMETER num_predict 1500

# --- 2. システムプロンプト ---
SYSTEM """
あなたはMarkdown文書の要約専門エージェントです。
入力されたMarkdownファイルを解析し、必ず以下の【固定フォーマット】のみを出力してください。
挨拶や説明、前置きは一切不要です。

【固定フォーマット】
# タイトル: [文書の主題を一行で]
## 概要
- 概要1
- 概要2
## 顛末
- 顛末1
- 顛末2

"""

# --- 3. Few-Shot(例示)---
MESSAGE user "要約依頼:"
MESSAGE assistant "# タイトル: 新システム構築プロジェクトの発足\n## 概要\n- 2026年5月までのシステム構築を目指す\n- 開発部主導のプロジェクト\n## 顛末\n"

PARAMETERの解説

パラメータ 役割 今回の調整
temperature 回答の温度設定 実行するたびに結果がブレてほしくないので低く設定
num_ctx コンテキストサイズ 検証を行うマシンで耐えそうな範囲の中で高めの値を設定
repeat_penalty 同じ表現の繰り返し抑制 ここは好みや具体的な用途によります。今回は人間も読める要約を目指したので多少抑制しています
num_predict 回答の最大長 長めのやり取りをしたチケットを要約出来るように少し余裕を持ったサイズ

SYSTEM / MESSAGE 「要約」という役割に特化させるため、固定フォーマットをSYSTEMに記載し、MESSAGE(Few-Shot)で出力例を提示することで、余計な挨拶や説明を排除しています。

2-2. カスタムモデルを作成

ticket_summarizer.Modelfile からカスタムモデルを作成します

$ ollama create ticket_summarizer -f ticket_summarizer.Modelfile

作成したモデルは list コマンドで確認出来ます

$ ollama list
NAME ID SIZE MODIFIED
ticket_summarizer:latest 0ddd22725f05 9.6 GB About an hour ago

2-3. 動作の確認

この時点でローカルLLMとして動かす事が出来るようになっています
sample.md は検証用に用意した架空の不具合チケットを markdown として出力したものです

$ cat sample.md | ollama run ticket_summarizer:latest
# タイトル: ホーム画面でのP2R実行による表示項目消失の不具合修正
## 概要
- ホーム画面でP2R(Push to Refresh)を実行すると、「重要なお知らせ」エリアや「未読メッセージ」エリアなどの一部の項目が表示されなくなる不具
合が発生していた。
- この不具合は、アカウントの表示名に記号を含む特定の条件を満たすアカウントでのみ発生することが判明した。
## 顛末
- 原因は、表示名のパース処理が初期読み込み時とP2R実行時で別々に定義されていたため、P2R時に不適切な処理が走っていたことによる内部エラーが原
因であった。
- 修正として、P2R時にも初期読み込み時と同じ表示名パース処理を使用するように変更し、修正版アプリ(1.0.2+22)が配布され、事象は解消した。

自動化用のスクリプトを用意する

1. ollamaライブラリのインストール

$ pip install ollama

2. スクリプトの作成

import ollama
import os
import sys

def summarize_ticket(input_path):
    # 1. 出力ファイル名の生成 (ticket.md -> ticket_summarize.md)
    root, ext = os.path.splitext(input_path)
    output_path = f"{root}_summarize{ext}"

    try:
        # 2. 入力ファイルの読み込み
        print(f"読み込み中: {input_path}...")
        with open(input_path, 'r', encoding='utf-8') as f:
            content = f.read()

        # 3. Ollamaで要約生成
        print("要約を生成中(Gemma 4)...")
        response = ollama.generate(
            model='ticket_summarizer:latest', # 作成したカスタムモデル名
            prompt=f"要約依頼:\n{content}",
            keep_alive=0  # 今回は検証なので終了後すぐに解放
        )

        summary_text = response['response']

        # 4. 結果をファイルに書き出し
        with open(output_path, 'w', encoding='utf-8') as f:
            f.write(summary_text)

        print("-" * 30)
        print(f"完了!保存先: {output_path}")
        print("-" * 30)

    except FileNotFoundError:
        print(f"Error: ファイル '{input_path}' が見つかりません。")
    except Exception as e:
        print(f"Error: {e}")

if __name__ == "__main__":
    # コマンドライン引数からファイル名を取得
    if len(sys.argv) > 1:
        summarize_ticket(sys.argv[1])
    else:
        print("使用法: python script.py [対象のファイル名.md]")

重要なのは「3. Ollamaで要約生成」の部分のみでほかは単純なファイル操作しかしていません

  • model
    • カスタムモデル名を指定します
    • ollama list で表示された名前です
  • prompt
    • LLMへ投げるプロンプトです
    • Modelfile の MESSAGE で提示した例に沿うように「要約依頼:」を付与して投げています
  • keep_alive
    • Ollama 側のプロセスの生存期間です
    • 実際に大量のチケットを要約する場合には -1 (永続) や十分に大きな数字に変更します

実際に使ってみる

$ python3 summarize.py sample.md

出力されたファイル

# タイトル: ホーム画面でのP2R実行による表示項目消失の不具合修正
## 概要
- ホーム画面でP2R(Push to Refresh)を実行すると、「重要なお知らせ」エリアや「未読メッセージ」エリアなどの一部の項目が表示されなくなる不具合が発生していた。
- この不具合は、アカウントの表示名に記号を含む特定の条件を満たすアカウントでのみ再現することが判明した。
## 顛末
- 原因は、表示名のパース処理が初期読み込み時とP2R実行時で別々に定義されていたため、P2R時に不適切な処理が走っていたことによる内部エラーが原因であった。
- 修正として、P2R時にも初期読み込み時と同じパース処理を使用するように変更し、修正版アプリ(1.0.2+22)が配布され、事象は解消された。

比較的要約しやすいサンプルデータだったのもあってよくまとまっていると思います
これを大量のチケットに対して実行し、 1ファイルに連結させれば 生成AI に読ませるファイルとしてはなかなか良いのではないでしょうか

所要時間

20秒ほどで要約が出来ていました

$ time python3 summarize.py sample.md
読み込み中: sample.md...
要約を生成中(Gemma 4)...
------------------------------
完了!保存先: sample_summarize.md
------------------------------
python3 summarize.py sample.md 0.23s user 0.08s system 1% cpu 20.040 total

今回は keep_alive を 0 にしているので毎回モデルの起動しているため時間がかかっていました
keep_alive を -1 (永続) にした場合の二回目以降の所要時間は 8〜10秒程度でした

この先の調整でもう少し時間がかかるようになっていっても十分に実用圏内だと思います

付録

テスト用のデータ

架空のチケットを Markdown 形式で用意して使用しました

sample.md (長いので折りたたみ)
----
チケットの最終更新日時: 2026-02-10 10:16:03
本ファイルの最終更新日時: 2026-04-08 11:48:24
----

# 主要フィールド

| 項目 | 値 |
|----|----|
| トラッカー | 不具合 |
| ステータス | 解決 |
| 優先度 | 通常 |
| 題名 | ホーム画面でP2Rを実施すると一部の項目が消えてしまう |
| 担当者 | 担当者A |
| 作成者 | 顧客A |
| 作成日時 | 2026-02-09 02:50:09 |
| 更新日時 | 2026-02-10 10:16:03 |

# 説明

▼事象
ホーム画面で P2R を実施すると、実施前には表示されていた以下の項目の表示が消えてしまっている
- 「重要なお知らせ」エリア
- 「未読メッセージ」エリア

▼期待値
ホーム画面で P2R を実施すると、最新情報を取得し表示されるべき項目は全て表示されること

▼再現手順

前提:
- ログイン状態である

手順:
- アプリを起動する
    - ホーム画面が表示される
- 「重要なお知らせ」および「未読メッセージ」エリアが表示されている事を確認する
- P2Rを実施する
- 「重要なお知らせ」および「未読メッセージ」エリアが表示されていない事を確認する ★ 不具合

上記手順後、アプリを再起動すると再び「重要なお知らせ」および「未読メッセージ」エリアが表示されるため、データの変化による振る舞いではないと推察される

▼発生バージョン
1.0.1+21

0.0.12+12 では発生していない事を確認しています

▼再現性

10/10 (100%)

▼再現端末

Android / iOS 共に5端末づつで検証し、全てで再現を確認

▼ビジネス重要度

B (可能な限り早期の対応が必要)

▼作業優先度

C+

# 添付ファイルリスト

| ファイル名 | ファイルサイズ | オーナー | 最終更新日時 |
|----|----|----|----|
| P2R後の現象発生状態.png | 1.5MB | 顧客A | 2026/02/09 02:49:56 |
| 再現動画.mp4 | 200.4MB | 顧客A | 2026/02/09 02:49:56 |
| trace_log.tgz | 30.0MB | 顧客A | 2026/02/09 02:49:56 |

# その他のフィールド

| 項目 | 値 |
|----|----|
| プロジェクト | 不具合管理 |
| カテゴリ | |
| 対象バージョン | |
| 開始日 | 2026-02-09 |
| 期限 | |
| 進捗率 | 100% |
| 予定工数 | |
| 作業時間 | 0.0 |
| 対象OS | |
| カテゴリ | |
| 進捗状況 | |
| アプリ審査希望時期 | |
| 管理機能リリース日 | |

# コメント
### 担当者A が 2026年02月09日 02:53:14 に更新

顧客A 様

お世話になっております

本事象の再現を確認出来ませんでした。
ログインアカウントによって再現する/しないが分かれる可能性があるため検証に利用したアカウントをご教示頂けないでしょうか

### 顧客A が 2026年02月09日 03:21:11 に更新

担当者A 様

ご確認ありがとうございます

検証に使用したアカウントは以下のとおりです
ログインID: test-account-001@gmail.com
パスワード: テストアカウント管理台帳を参照下さい

### 担当者A が 2026年02月09日 04:22:11 に更新

顧客A 様

アカウント提供ありがとうございます

再現を確認出来ましたので解析を進行致します
ご迷惑をおかけし、申し訳ありませんが今しばらくお待ちください

### 担当者A が 2026年02月09日 08:22:11 に更新

顧客A 様

修正が完了致しました

原因:
特定の条件を満たすアカウントにログインした状態で P2R を行うと、内部エラーが発生し処理が中断されていた
その結果一部の項目のデータを画面に表示することができなくなり表示が消えてしまっていた

特定の条件とは以下のとおりです
* アカウントの表示名に記号を含む

修正内容:
表示名のパース処理が初期読み込み時とP2R時で別々に定義されていたため、P2Rの際も初期読み込みと同じ処理を使用するように変更を行いました

影響画面:
ホーム画面のみ

修正アプリの配布:
1.0.2+22 を本日中に配布致します

### 顧客A が 2026年02月10日 10:16:03 に更新

担当者A 様

ご対応ありがとうございます

1.0.2+22 で事象が解消されている事を確認しました
本件はクローズとします