ANTIGRAVITY LABEN
記事一覧/AIツール
AIツール/2026-06-24上級

夜通し回したローカルエージェントが、明け方には一手ごとに数秒待たされる — Ollama の応答時間を文脈長から逆算して一定に保つ

ローカルで長時間ループするエージェントの一手が後半ほど重くなる原因を、Ollama の所要時間フィールドから計測し、num_ctx の固定とローリング要約で遅延を平準化する実装メモです。

ollama8gemma412local-llm16latencyai-tools14

プレミアム記事

個人開発で続けているアプリのストア用メタデータを整えるため、夜間にローカルエージェントを回しています。Gemma 4 を Ollama で動かし、手元の機械だけで完結させる構成です。クラウドに出さず、費用もかからない。オフピークの時間帯に黙って働いてくれる、相棒のような存在でした。

ところが、ある朝ログを見て手が止まりました。

序盤の一手は数百ミリ秒で返っていたのに、後半になるほど一手あたりの待ち時間が伸びていきます。明け方には数秒待たされていました。処理した件数は同じ。モデルも同じ。変わったのは、会話に積み上がっていった文脈の長さだけでした。

無人で長く回す前提のエージェントほど、この「後半が重くなる」性質に足をすくわれます。今回はその正体を計測で突き止め、一手の遅延を平準化するまでの手順を残しておきます。

明け方の一手が、なぜ最初の一手より重いのか

ローカルLLMの一回の応答は、大きく二つの時間に分かれます。プロンプト全体を読み込んで内部状態を作る prompt-eval の時間と、そこから新しいトークンを一つずつ生成する generation の時間です。

generation の速度は、出力の長さでほぼ決まります。一方の prompt-eval は、渡したプロンプトの長さに比例して伸びます。会話履歴やツールの出力が積み上がるほど、毎回の prompt-eval が重くなっていきます。

無人エージェントは一手ごとに「これまでの全履歴+新しい指示」を投げ直します。つまり後半の一手は、前半の何倍もの文脈を毎回読み直しているわけです。出力の長さが同じでも、待ち時間だけが静かに膨らんでいく。これが明け方の数秒の正体でした。

応答時間の正体を、prompt-eval と generation に分ける

幸い、Ollama は応答ごとに所要時間の内訳を返してくれます。prompt_eval_count(読み込んだトークン数)、prompt_eval_duration(その所要時間)、eval_count(生成したトークン数)、eval_duration(生成の所要時間)の四つを見れば、遅延がどちらで起きているかを切り分けられます。

時間の単位はナノ秒です。ミリ秒へ直して扱います。

import time, requests
 
OLLAMA = "http://localhost:11434/api/chat"
 
def chat_once(model, messages, num_ctx=8192):
    t0 = time.perf_counter()
    r = requests.post(OLLAMA, json={
        "model": model,
        "messages": messages,
        "stream": False,
        "options": {"num_ctx": num_ctx, "temperature": 0.2},
    }, timeout=600)
    r.raise_for_status()
    d = r.json()
    wall_ms = (time.perf_counter() - t0) * 1000
    prompt_tokens = d.get("prompt_eval_count", 0)
    prompt_ms = d.get("prompt_eval_duration", 0) / 1e6   # ns -> ms
    gen_tokens = d.get("eval_count", 0)
    gen_ms = d.get("eval_duration", 0) / 1e6
    return {
        "text": d["message"]["content"],
        "prompt_tokens": prompt_tokens,
        "prompt_ms": round(prompt_ms, 1),
        "gen_tokens": gen_tokens,
        "gen_ms": round(gen_ms, 1),
        "wall_ms": round(wall_ms, 1),
        "tok_per_s": round(gen_tokens / (gen_ms / 1000), 1) if gen_ms else 0.0,
    }

返り値に wall_ms(実測の往復時間)も入れておくと、計測の取りこぼし(モデルのロード時間など)に気づけます。

一手ごとに記録を残す

切り分けは、一回だけ測っても意味がありません。ループの各手で記録を残し、文脈長と prompt-eval の伸びを並べて眺めます。

import json, pathlib
 
LOG = pathlib.Path("agent_timing.jsonl")
 
def run_step(model, history, user_msg, step):
    history.append({"role": "user", "content": user_msg})
    m = chat_once(model, history)
    history.append({"role": "assistant", "content": m["text"]})
    record = {"step": step, **{k: m[k] for k in
              ("prompt_tokens", "prompt_ms", "gen_tokens", "gen_ms", "wall_ms")}}
    with LOG.open("a") as f:
        f.write(json.dumps(record, ensure_ascii=False) + "\n")
    return m["text"]

このまま素朴に履歴を積み続けると、prompt_tokens が右肩上がりに増え、それに引きずられて prompt_ms も伸びる様子が、ログにそのまま現れます。

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
Ollama の prompt_eval_duration と eval_count から、1手ごとの応答時間がどこで伸びているかを数値で切り分ける計測コード
文脈長が 2k→12k トークンに伸びたとき prompt-eval が約3倍に膨らむ実測と、num_ctx を意図して固定する判断基準
直近N手+ローリング要約+ツール出力の上限切りで、長時間ループでも1手の遅延を約45%平準化する BoundedContext の実装
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Antigravity Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

AIツール2026-04-19
Gemma 4 × Antigravity でローカルLLMを本番環境で動かす——セットアップから安定運用まで
Gemma 4をAntigravityと組み合わせてローカル環境で本番運用するための完全ガイド。Ollamaセットアップ・パフォーマンスチューニング・API連携・よくあるトラブルまで実装例つきで解説。
AIツール2026-04-14
Gemma 4のImplicit Cachingをアンチグラビティで活かす——同じコードを何度も読み込ませるコストを削減させる方法
Gemma 4のImplicit Caching機能をAntigravityで最大限に活用する実践ガイド。大規模コードベースでのクレジット消費を大幅に削減するプロジェクト設計術を解説します。
AIツール2026-04-10
Gemma 4ファインチューニング入門:AntigravityでカスタムAIモデルを作成する実践ガイド
Gemma 4をLoRA/QLoRAでファインチューニングし、Antigravityに組み込む手順を実践的に解説。データセット準備からカスタムモデルの活用まで、コード例付きで丁寧に説明します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →