Skip to content

AIAgent / init_agent

源码版本v2026.7.20

職務

init_agent は agent の「組み立て工房」:provider、toolsets、圧縮 (compression) 閾値、カスタム provider の extra_body、記憶と学習コンポーネントなどを agent インスタンスに注入し、一往復の対話に必要な依存を全て整える。AIAgent クラス自体はその産物の薄い殻に過ぎない。

設計動機

設定と実行時を分けるのは、「一往復の対話を走らせる」コードパスを可能な限りステートレスにするためだ。init_agent が起動期のすべての意思決定(provider 選択、圧縮戦略、toolsets のオンオフ、extra_body のマージ)を一度に確定させた上で、run_conversation は「メッセージを投げ→ループを回す」リズムに入り、設定に戻って触れない。これにより run_conversation は安心して並行実行できる——同じ agent インスタンスが複数の入站メッセージで同時に駆動されても、「設定が書き換え中」という競合は起きない。

圧縮閾値を圧縮トリガ時ではなく起動期に解析するもう一つの動機:Codex gpt-5.x 系の 272K ウィンドウは、閾値を「自動で引き上げて」ウィンドウを使い切る必要があり、この引き上げは一度だけユーザーに通知したい。実行時に毎回計算すると通知が何度も出るか、抜けが出る。起動期に一度計算して marker ファイルに書き込み、以降は marker を読んで判定する。

主要ファイル

データフロー

  1. AIAgent.__init__(run_agent.py:423) は構築引数をそのまま収集する。
  2. init_agent:276 を呼び、順に:
  3. 組み立て完了した agent は run_conversation メソッド(run_agent.py:6350)を露出し、モジュールレベルの agent/conversation_loop.py:588 に転送する。
  4. ゲートウェイの GatewayRunner(gateway/run.py:3029)が組み立て済み agent を保持し、入站メッセージを渡す。

provider 名は init_agent 内で小文字に規整され、空白は削除される。provider はルーティングヒントにも api_mode 推断の根拠にも使われる。

python
agent.base_url = base_url or ""
provider_name = provider.strip().lower() if isinstance(provider, str) and provider.strip() else None
agent.provider = provider_name or ""
...
if api_mode in {"chat_completions", "codex_responses", "anthropic_messages", "bedrock_converse", "codex_app_server"}:
    agent.api_mode = api_mode
elif agent.provider == "openai-codex":
    agent.api_mode = "codex_responses"
elif agent.provider in {"xai", "xai-oauth"}:
    agent.api_mode = "codex_responses"

圧縮閾値の解析は _resolve_compression_threshold で行う。Codex の autoraise は一方向だ——ユーザーが設定した高い閾値を下げることはなく、上げるだけできる。

python
if model_cthresh is None:
    return global_threshold, None
if is_codex_autoraise:
    if model_cthresh <= global_threshold + 1e-9:
        # Autoraise never lowers; keep the user's higher/equal threshold.
        return global_threshold, None
    return model_cthresh, {
        "model": model,
        "from": global_threshold,
        "to": model_cthresh,
    }
return model_cthresh, None

カスタム provider の extra_body マージの核心は、base_url + model で設定項目を突き合わせ、見つければ extra_bodyrequest_overrides に取り込むことだ。既存のキーはユーザーが自分で設定した値を優先して保持する。

python
merged_extra_body = dict(extra_body)
existing_extra_body = overrides.get("extra_body")
if isinstance(existing_extra_body, dict):
    merged_extra_body.update(existing_extra_body)
overrides["extra_body"] = merged_extra_body
agent.request_overrides = overrides

update の向きは「設定の extra_body を底に、ユーザーが既に持っている extra_body を上に重ねる」だ。だから実行時に request_overrides["extra_body"] に差し込んだフィールドが起動設定で上書きされることはない。

境界と失敗

  • provider 名から api_mode が推断できない:ユーザーが未知の provider="foobar" を渡すと、api_mode は推断されず、デフォルトの chat_completions パスに落ちる。もし foobar が実際には codex_responses しかサポートしなければ、最初のメッセージで 400 が返る。init_agent は provider がホワイトリストにあるかを検証せず、大小文字の規整だけを行う。
  • custom extra_body のマージ衝突:同じ base_url に複数の entry があり、同じ model にマッチした場合、_custom_provider_extra_body_for_agent は「最初の model 明示マッチが勝ち」とし、以降は無視する。entry に model フィールドがないものは fallback 扱いで、明示的な model マッチ項目がないときだけ使われる。ユーザーが設定を変えたが agent を再起動せず、新しい extra_body が効いていると思い込んでいると、実際には古い fallback がまだ効いていることがある。
  • 圧縮閾値の不正値:global_thresholdNone または負の数のとき、_resolve_compression_threshold は範囲検証を行わず、そのまま透過する。負の数なら圧縮は永遠に発火せず、None なら比較時に TypeError が飛ぶ。設定層(hermes config)には範囲チェックがあるが、init_agent は重複検証せず、config の産物をそのまま信頼する。
  • autoraise notice の marker 書き込み失敗:$HERMES_HOME が読み取り専用またはディスク一杯のとき、_record_codex_gpt55_autoraise_notice は黙ってスキップし、代償として次回 init で通知が再度出る。これは意図的なトレードオフで、marker 書き込みの失敗で agent が起動できなくなるのは避けたい。
  • _ra() の遅延 import:init_agent 内の補助関数は _ra()run_agent モジュールを取得し、トップレベルで import しない。これはテストが run_agent.OpenAI などの属性を monkeypatch してから init_agent を呼べるようにするためだ。代償として初回呼び出しに一回の import が乗り、patch のタイミングが遅れると既に束縛された属性は新しい patch で上書きされない。

まとめ

init_agent は設定→実行時の翻訳層であり、起動期の意思決定(provider 選択、圧縮戦略、toolsets のオンオフ)はすべてここで一度に確定する。以後 agent はステートレスな「メッセージを投げ→ループを回す」リズムに入り、設定を戻って変更することはない。

非公式コミュニティ学習サイト。MIT ライセンスの NousResearch/hermes-agent ソースに基づく。