コンテンツにスキップ

LLMエージェント

LlmAgent(単にAgentとエイリアスされることが多い)は、ADKの中核的なコンポーネントであり、アプリケーションの「思考」部分として機能します。大規模言語モデル(LLM)の力を活用して、推論、自然言語の理解、意思決定、応答の生成、ツールとの対話を行います。

事前定義された実行パスに従う決定論的なワークフローエージェントとは異なり、LlmAgentの振る舞いは非決定的です。LLMを使用して指示とコンテキストを解釈し、どのように進めるか、どのツールを使用するか(もしあれば)、または他のエージェントに制御を移譲するかを動的に決定します。

効果的なLlmAgentを構築するには、そのアイデンティティを定義し、指示を通じてその振る舞いを明確に導き、必要なツールと機能を持たせることが含まれます。

エージェントのアイデンティティと目的の定義

まず、エージェントが何であるか、そして何のためにあるのかを確立する必要があります。

  • name(必須): すべてのエージェントには一意の文字列識別子が必要です。このnameは、特にエージェントがお互いを参照したりタスクを委任したりする必要があるマルチエージェントシステムにおいて、内部操作にとって非常に重要です。エージェントの機能を反映した説明的な名前を選択してください(例:customer_support_routerbilling_inquiry_agent)。userのような予約名は避けてください。

  • description(オプション、マルチエージェントで推奨): エージェントの能力の簡潔な要約を提供します。この説明は、主に他のLLMエージェントがこのエージェントにタスクをルーティングすべきかどうかを判断するために使用されます。同僚と区別できるほど具体的にしてください(例:「現在の請求書に関する問い合わせを処理します」であり、「請求エージェント」だけではありません)。

  • model(必須): このエージェントの推論を動かす基盤となるLLMを指定します。これは"gemini-2.0-flash"のような文字列識別子です。モデルの選択は、エージェントの能力、コスト、およびパフォーマンスに影響します。利用可能なオプションと考慮事項については、モデルのページを参照してください。

# 例:基本的なアイデンティティの定義
capital_agent = LlmAgent(
    model="gemini-2.0-flash",
    name="capital_agent",
    description="指定された国の首都に関するユーザーの質問に答えます。"
    # instructionとtoolsは次に追加します
)
// 例:基本的なアイデンティティの定義
LlmAgent capitalAgent =
    LlmAgent.builder()
        .model("gemini-2.0-flash")
        .name("capital_agent")
        .description("指定された国の首都に関するユーザーの質問に答えます。")
        // instructionとtoolsは次に追加します
        .build();

エージェントの誘導:指示 (instruction)

instructionパラメータは、LlmAgentの振る舞いを形成する上で間違いなく最も重要な要素です。これは、エージェントに以下を伝える文字列(または文字列を返す関数)です:

  • その中心的なタスクまたは目標。
  • その性格またはペルソナ(例:「あなたは親切なアシスタントです」、「あなたは機知に富んだ海賊です」)。
  • その振る舞いに関する制約(例:「Xに関する質問にのみ答える」、「Yは決して明かさない」)。
  • そのtoolsをどのように、いつ使用するか。ツール自体の説明を補足し、各ツールの目的とそれを呼び出すべき状況を説明する必要があります。
  • その出力の望ましい形式(例:「JSONで応答する」、「箇条書きで提供する」)。

効果的な指示のためのヒント:

  • 明確かつ具体的にする: 曖昧さを避けます。望ましいアクションと結果を明確に述べてください。
  • Markdownを使用する: 見出しやリストなどを使用して、複雑な指示の可読性を向上させます。
  • 例を提供する(フューショット): 複雑なタスクや特定の出力形式については、指示に直接例を含めます。
  • ツールの使用をガイドする: ツールをリストアップするだけでなく、エージェントがいつなぜそれらを使用すべきかを説明します。

状態(State):

  • instructionは文字列テンプレートであり、{var}構文を使用して動的な値をinstructionに挿入できます。
  • {var}は、varという名前の状態変数の値を挿入するために使用されます。
  • {artifact.var}は、varという名前のアーティファクトのテキストコンテンツを挿入するために使用されます。
  • 状態変数またはアーティファクトが存在しない場合、エージェントはエラーを発生させます。エラーを無視したい場合は、{var?}のように変数名に?を追加できます。
# 例:指示の追加
capital_agent = LlmAgent(
    model="gemini-2.0-flash",
    name="capital_agent",
    description="指定された国の首都に関するユーザーの質問に答えます。",
    instruction="""あなたは国の首都を提供するエージェントです。
ユーザーが国の首都を尋ねたとき:
1. ユーザーのクエリから国名を特定します。
2. `get_capital_city`ツールを使用して首都を見つけます。
3. ユーザーに明確に応答し、首都を述べます。
クエリの例: "What's the capital of {country}?"
応答の例: "The capital of France is Paris."
""",
    # toolsは次に追加します
)
// 例:指示の追加
LlmAgent capitalAgent =
    LlmAgent.builder()
        .model("gemini-2.0-flash")
        .name("capital_agent")
        .description("指定された国の首都に関するユーザーの質問に答えます。")
        .instruction(
            """
            あなたは国の首都を提供するエージェントです。
            ユーザーが国の首都を尋ねたとき:
            1. ユーザーのクエリから国名を特定します。
            2. `get_capital_city`ツールを使用して首都を見つけます。
            3. ユーザーに明確に応答し、首都を述べます。
            クエリの例: "What's the capital of {country}?"
            応答の例: "The capital of France is Paris."
            """)
        // toolsは次に追加します
        .build();

(注:システム内のすべてのエージェントに適用される指示については、ルートエージェントでglobal_instructionを使用することを検討してください。詳細はマルチエージェントセクションで説明します。)

エージェントの装備:ツール (tools)

ツールは、LLMの組み込み知識や推論能力を超えた機能をLlmAgentに与えます。これにより、エージェントは外部の世界と対話し、計算を実行し、リアルタイムのデータを取得し、または特定のアクションを実行できます。

  • tools(オプション): エージェントが使用できるツールのリストを提供します。リストの各項目は以下のいずれかです:
    • ネイティブ関数またはメソッド(FunctionToolとしてラップされます)。Python ADKはネイティブ関数を自動的にFunctionToolにラップしますが、JavaメソッドはFunctionTool.create(...)を使用して明示的にラップする必要があります。
    • BaseToolから継承したクラスのインスタンス。
    • 別のエージェントのインスタンス(AgentTool、エージェント間の委任を可能にする - マルチエージェントを参照)。

LLMは、関数/ツール名、説明(docstringまたはdescriptionフィールドから)、およびパラメータスキーマを使用して、会話とその指示に基づいてどのツールを呼び出すかを決定します。

# ツール関数を定義
def get_capital_city(country: str) -> str:
  """指定された国の首都を取得します。"""
  # 実際のロジックに置き換える(例:API呼び出し、データベース検索)
  capitals = {"france": "Paris", "japan": "Tokyo", "canada": "Ottawa"}
  return capitals.get(country.lower(), f"申し訳ありませんが、{country}の首都はわかりません。")

# ツールをエージェントに追加
capital_agent = LlmAgent(
    model="gemini-2.0-flash",
    name="capital_agent",
    description="指定された国の首都に関するユーザーの質問に答えます。",
    instruction="""あなたは国の首都を提供するエージェントです...(前の指示テキスト)""",
    tools=[get_capital_city] # 関数を直接提供
)
import com.google.genai.types.Schema;
import java.util.HashMap;
import java.util.Map;

// ツール関数を定義
// 指定された国の首都を取得します。
public static Map<String, Object> getCapitalCity(
        @Schema(name = "country", description = "首都を取得する国")
        String country) {
  // 実際のロジックに置き換える(例:API呼び出し、データベース検索)
  Map<String, String> countryCapitals = new HashMap<>();
  countryCapitals.put("canada", "Ottawa");
  countryCapitals.put("france", "Paris");
  countryCapitals.put("japan", "Tokyo");

  String result =
          countryCapitals.getOrDefault(
                  country.toLowerCase(), "申し訳ありませんが、" + country + "の首都は見つかりませんでした。");
  return Map.of("result", result); // ツールはMapを返さなければならない
}

// ツールをエージェントに追加
FunctionTool capitalTool = FunctionTool.create(experiment.getClass(), "getCapitalCity");
LlmAgent capitalAgent =
    LlmAgent.builder()
        .model("gemini-2.0-flash")
        .name("capital_agent")
        .description("指定された国の首都に関するユーザーの質問に答えます。")
        .instruction("あなたは国の首都を提供するエージェントです...(前の指示テキスト)")
        .tools(capitalTool) // FunctionToolとしてラップされた関数を提供
        .build();

ツールの詳細については、ツールセクションを参照してください。

高度な設定と制御

主要なパラメータに加えて、LlmAgentはより細かい制御のためのいくつかのオプションを提供します:

LLM生成の微調整 (generate_content_config)

generate_content_configを使用して、基盤となるLLMが応答を生成する方法を調整できます。

  • generate_content_config(オプション): google.genai.types.GenerateContentConfigのインスタンスを渡して、temperature(ランダム性)、max_output_tokens(応答の長さ)、top_ptop_k、および安全設定などのパラメータを制御します。
from google.genai import types

agent = LlmAgent(
    # ... 他のパラメータ
    generate_content_config=types.GenerateContentConfig(
        temperature=0.2, # より決定論的な出力
        max_output_tokens=250
    )
)
import com.google.genai.types.GenerateContentConfig;

LlmAgent agent =
    LlmAgent.builder()
        // ... 他のパラメータ
        .generateContentConfig(GenerateContentConfig.builder()
            .temperature(0.2F) // より決定論的な出力
            .maxOutputTokens(250)
            .build())
        .build();

データの構造化 (input_schema, output_schema, output_key)

LLM Agentとの構造化されたデータ交換を必要とするシナリオのために、ADKはスキーマ定義を使用して、期待される入力と望ましい出力形式を定義するメカニズムを提供します。

  • input_schema(オプション): 期待される入力構造を表すスキーマを定義します。設定されている場合、このエージェントに渡されるユーザーメッセージのコンテンツは、このスキーマに準拠したJSON文字列でなければなりません。あなたの指示は、ユーザーまたは先行するエージェントをそれに応じて誘導する必要があります。

  • output_schema(オプション): 望ましい出力構造を表すスキーマを定義します。設定されている場合、エージェントの最終応答は、このスキーマに準拠したJSON文字列でなければなりません

    • 制約: output_schemaを使用すると、LLM内での制御された生成が可能になりますが、エージェントがツールを使用したり、他のエージェントに制御を移譲したりする能力が無効になります。あなたの指示は、LLMが直接スキーマに一致するJSONを生成するように導く必要があります。
  • output_key(オプション): 文字列キーを提供します。設定されている場合、エージェントの最終的な応答のテキストコンテンツは、このキーの下でセッションの状態辞書に自動的に保存されます。これは、エージェントやワークフローのステップ間で結果を渡すのに役立ちます。

    • Pythonでは、これは次のようになります:session.state[output_key] = agent_response_text
    • Javaでは:session.state().put(outputKey, agentResponseText)

入力および出力スキーマは通常、PydanticのBaseModelです。

from pydantic import BaseModel, Field

class CapitalOutput(BaseModel):
    capital: str = Field(description="国の首都。")

structured_capital_agent = LlmAgent(
    # ... name, model, description
    instruction="""あなたは首都情報エージェントです。国が与えられたら、首都を含むJSONオブジェクトのみで応答してください。フォーマット: {"capital": "capital_name"}""",
    output_schema=CapitalOutput, # JSON出力を強制
    output_key="found_capital"  # 結果をstate['found_capital']に保存
    # ここではtools=[get_capital_city]を効果的に使用できない
)

入力および出力スキーマはgoogle.genai.types.Schemaオブジェクトです。

private static final Schema CAPITAL_OUTPUT =
    Schema.builder()
        .type("OBJECT")
        .description("首都情報のためのスキーマ。")
        .properties(
            Map.of(
                "capital",
                Schema.builder()
                    .type("STRING")
                    .description("国の首都。")
                    .build()))
        .build();

LlmAgent structuredCapitalAgent =
    LlmAgent.builder()
        // ... name, model, description
        .instruction(
                "あなたは首都情報エージェントです。国が与えられたら、首都を含むJSONオブジェクトのみで応答してください。フォーマット: {\"capital\": \"capital_name\"}")
        .outputSchema(CAPITAL_OUTPUT) // JSON出力を強制
        .outputKey("found_capital") // 結果をstate.get("found_capital")に保存
        // ここではtools(getCapitalCity)を効果的に使用できない
        .build();

コンテキストの管理 (include_contents)

エージェントが以前の会話履歴を受け取るかどうかを制御します。

  • include_contents(オプション、デフォルト:'default'): contents(履歴)がLLMに送信されるかどうかを決定します。
    • 'default': エージェントは関連する会話履歴を受け取ります。
    • 'none': エージェントは以前のcontentsを一切受け取りません。現在のターンで提供された指示と入力のみに基づいて動作します(ステートレスタスクや特定のコンテキストの強制に役立ちます)。
stateless_agent = LlmAgent(
    # ... 他のパラメータ
    include_contents='none'
)
import com.google.adk.agents.LlmAgent.IncludeContents;

LlmAgent statelessAgent =
    LlmAgent.builder()
        // ... 他のパラメータ
        .includeContents(IncludeContents.NONE)
        .build();

プランニングとコード実行

python_only

複数のステップを含むより複雑な推論やコードの実行のために:

  • planner(オプション): BasePlannerインスタンスを割り当てて、実行前の複数ステップの推論とプランニングを有効にします。(マルチエージェントのパターンを参照)。
  • code_executor(オプション): BaseCodeExecutorインスタンスを提供して、エージェントがLLMの応答で見つかったコードブロック(例:Python)を実行できるようにします。(ツール/組み込みツールを参照)。

まとめ:例

Code

以下は、基本的なcapital_agentの完全な例です:

# --- Full example code demonstrating LlmAgent with Tools vs. Output Schema ---
import json # Needed for pretty printing dicts

from google.adk.agents import LlmAgent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.genai import types
from pydantic import BaseModel, Field

# --- 1. Define Constants ---
APP_NAME = "agent_comparison_app"
USER_ID = "test_user_456"
SESSION_ID_TOOL_AGENT = "session_tool_agent_xyz"
SESSION_ID_SCHEMA_AGENT = "session_schema_agent_xyz"
MODEL_NAME = "gemini-2.0-flash"

# --- 2. Define Schemas ---

# Input schema used by both agents
class CountryInput(BaseModel):
    country: str = Field(description="The country to get information about.")

# Output schema ONLY for the second agent
class CapitalInfoOutput(BaseModel):
    capital: str = Field(description="The capital city of the country.")
    # Note: Population is illustrative; the LLM will infer or estimate this
    # as it cannot use tools when output_schema is set.
    population_estimate: str = Field(description="An estimated population of the capital city.")

# --- 3. Define the Tool (Only for the first agent) ---
def get_capital_city(country: str) -> str:
    """Retrieves the capital city of a given country."""
    print(f"\n-- Tool Call: get_capital_city(country='{country}') --")
    country_capitals = {
        "united states": "Washington, D.C.",
        "canada": "Ottawa",
        "france": "Paris",
        "japan": "Tokyo",
    }
    result = country_capitals.get(country.lower(), f"Sorry, I couldn't find the capital for {country}.")
    print(f"-- Tool Result: '{result}' --")
    return result

# --- 4. Configure Agents ---

# Agent 1: Uses a tool and output_key
capital_agent_with_tool = LlmAgent(
    model=MODEL_NAME,
    name="capital_agent_tool",
    description="Retrieves the capital city using a specific tool.",
    instruction="""You are a helpful agent that provides the capital city of a country using a tool.
The user will provide the country name in a JSON format like {"country": "country_name"}.
1. Extract the country name.
2. Use the `get_capital_city` tool to find the capital.
3. Respond clearly to the user, stating the capital city found by the tool.
""",
    tools=[get_capital_city],
    input_schema=CountryInput,
    output_key="capital_tool_result", # Store final text response
)

# Agent 2: Uses output_schema (NO tools possible)
structured_info_agent_schema = LlmAgent(
    model=MODEL_NAME,
    name="structured_info_agent_schema",
    description="Provides capital and estimated population in a specific JSON format.",
    instruction=f"""You are an agent that provides country information.
The user will provide the country name in a JSON format like {{"country": "country_name"}}.
Respond ONLY with a JSON object matching this exact schema:
{json.dumps(CapitalInfoOutput.model_json_schema(), indent=2)}
Use your knowledge to determine the capital and estimate the population. Do not use any tools.
""",
    # *** NO tools parameter here - using output_schema prevents tool use ***
    input_schema=CountryInput,
    output_schema=CapitalInfoOutput, # Enforce JSON output structure
    output_key="structured_info_result", # Store final JSON response
)

# --- 5. Set up Session Management and Runners ---
session_service = InMemorySessionService()

# Create separate sessions for clarity, though not strictly necessary if context is managed
session_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID_TOOL_AGENT)
session_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID_SCHEMA_AGENT)

# Create a runner for EACH agent
capital_runner = Runner(
    agent=capital_agent_with_tool,
    app_name=APP_NAME,
    session_service=session_service
)
structured_runner = Runner(
    agent=structured_info_agent_schema,
    app_name=APP_NAME,
    session_service=session_service
)

# --- 6. Define Agent Interaction Logic ---
async def call_agent_and_print(
    runner_instance: Runner,
    agent_instance: LlmAgent,
    session_id: str,
    query_json: str
):
    """Sends a query to the specified agent/runner and prints results."""
    print(f"\n>>> Calling Agent: '{agent_instance.name}' | Query: {query_json}")

    user_content = types.Content(role='user', parts=[types.Part(text=query_json)])

    final_response_content = "No final response received."
    async for event in runner_instance.run_async(user_id=USER_ID, session_id=session_id, new_message=user_content):
        # print(f"Event: {event.type}, Author: {event.author}") # Uncomment for detailed logging
        if event.is_final_response() and event.content and event.content.parts:
            # For output_schema, the content is the JSON string itself
            final_response_content = event.content.parts[0].text

    print(f"<<< Agent '{agent_instance.name}' Response: {final_response_content}")

    current_session = session_service.get_session(app_name=APP_NAME,
                                                  user_id=USER_ID,
                                                  session_id=session_id)
    stored_output = current_session.state.get(agent_instance.output_key)

    # Pretty print if the stored output looks like JSON (likely from output_schema)
    print(f"--- Session State ['{agent_instance.output_key}']: ", end="")
    try:
        # Attempt to parse and pretty print if it's JSON
        parsed_output = json.loads(stored_output)
        print(json.dumps(parsed_output, indent=2))
    except (json.JSONDecodeError, TypeError):
         # Otherwise, print as string
        print(stored_output)
    print("-" * 30)


# --- 7. Run Interactions ---
async def main():
    print("--- Testing Agent with Tool ---")
    await call_agent_and_print(capital_runner, capital_agent_with_tool, SESSION_ID_TOOL_AGENT, '{"country": "France"}')
    await call_agent_and_print(capital_runner, capital_agent_with_tool, SESSION_ID_TOOL_AGENT, '{"country": "Canada"}')

    print("\n\n--- Testing Agent with Output Schema (No Tool Use) ---")
    await call_agent_and_print(structured_runner, structured_info_agent_schema, SESSION_ID_SCHEMA_AGENT, '{"country": "France"}')
    await call_agent_and_print(structured_runner, structured_info_agent_schema, SESSION_ID_SCHEMA_AGENT, '{"country": "Japan"}')

if __name__ == "__main__":
    await main()
// --- Full example code demonstrating LlmAgent with Tools vs. Output Schema ---

import com.google.adk.agents.LlmAgent;
import com.google.adk.events.Event;
import com.google.adk.runner.Runner;
import com.google.adk.sessions.InMemorySessionService;
import com.google.adk.sessions.Session;
import com.google.adk.tools.Annotations;
import com.google.adk.tools.FunctionTool;
import com.google.genai.types.Content;
import com.google.genai.types.Part;
import com.google.genai.types.Schema;
import io.reactivex.rxjava3.core.Flowable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public class LlmAgentExample {

  // --- 1. Define Constants ---
  private static final String MODEL_NAME = "gemini-2.0-flash";
  private static final String APP_NAME = "capital_agent_tool";
  private static final String USER_ID = "test_user_456";
  private static final String SESSION_ID_TOOL_AGENT = "session_tool_agent_xyz";
  private static final String SESSION_ID_SCHEMA_AGENT = "session_schema_agent_xyz";

  // --- 2. Define Schemas ---

  // Input schema used by both agents
  private static final Schema COUNTRY_INPUT_SCHEMA =
      Schema.builder()
          .type("OBJECT")
          .description("Input for specifying a country.")
          .properties(
              Map.of(
                  "country",
                  Schema.builder()
                      .type("STRING")
                      .description("The country to get information about.")
                      .build()))
          .required(List.of("country"))
          .build();

  // Output schema ONLY for the second agent
  private static final Schema CAPITAL_INFO_OUTPUT_SCHEMA =
      Schema.builder()
          .type("OBJECT")
          .description("Schema for capital city information.")
          .properties(
              Map.of(
                  "capital",
                  Schema.builder()
                      .type("STRING")
                      .description("The capital city of the country.")
                      .build(),
                  "population_estimate",
                  Schema.builder()
                      .type("STRING")
                      .description("An estimated population of the capital city.")
                      .build()))
          .required(List.of("capital", "population_estimate"))
          .build();

  // --- 3. Define the Tool (Only for the first agent) ---
  // Retrieves the capital city of a given country.
  public static Map<String, Object> getCapitalCity(
      @Annotations.Schema(name = "country", description = "The country to get capital for")
      String country) {
    System.out.printf("%n-- Tool Call: getCapitalCity(country='%s') --%n", country);
    Map<String, String> countryCapitals = new HashMap<>();
    countryCapitals.put("united states", "Washington, D.C.");
    countryCapitals.put("canada", "Ottawa");
    countryCapitals.put("france", "Paris");
    countryCapitals.put("japan", "Tokyo");

    String result =
        countryCapitals.getOrDefault(
            country.toLowerCase(), "Sorry, I couldn't find the capital for " + country + ".");
    System.out.printf("-- Tool Result: '%s' --%n", result);
    return Map.of("result", result); // Tools must return a Map
  }

  public static void main(String[] args){
    LlmAgentExample agentExample = new LlmAgentExample();
    FunctionTool capitalTool = FunctionTool.create(agentExample.getClass(), "getCapitalCity");

    // --- 4. Configure Agents ---

    // Agent 1: Uses a tool and output_key
    LlmAgent capitalAgentWithTool =
        LlmAgent.builder()
            .model(MODEL_NAME)
            .name("capital_agent_tool")
            .description("Retrieves the capital city using a specific tool.")
            .instruction(
              """
              You are a helpful agent that provides the capital city of a country using a tool.
              1. Extract the country name.
              2. Use the `get_capital_city` tool to find the capital.
              3. Respond clearly to the user, stating the capital city found by the tool.
              """)
            .tools(capitalTool)
            .inputSchema(COUNTRY_INPUT_SCHEMA)
            .outputKey("capital_tool_result") // Store final text response
            .build();

    // Agent 2: Uses an output schema
    LlmAgent structuredInfoAgentSchema =
        LlmAgent.builder()
            .model(MODEL_NAME)
            .name("structured_info_agent_schema")
            .description("Provides capital and estimated population in a specific JSON format.")
            .instruction(
                String.format("""
                You are an agent that provides country information.
                Respond ONLY with a JSON object matching this exact schema: %s
                Use your knowledge to determine the capital and estimate the population. Do not use any tools.
                """, CAPITAL_INFO_OUTPUT_SCHEMA.toJson()))
            // *** NO tools parameter here - using output_schema prevents tool use ***
            .inputSchema(COUNTRY_INPUT_SCHEMA)
            .outputSchema(CAPITAL_INFO_OUTPUT_SCHEMA) // Enforce JSON output structure
            .outputKey("structured_info_result") // Store final JSON response
            .build();

    // --- 5. Set up Session Management and Runners ---
    InMemorySessionService sessionService = new InMemorySessionService();

    sessionService.createSession(APP_NAME, USER_ID, null, SESSION_ID_TOOL_AGENT).blockingGet();
    sessionService.createSession(APP_NAME, USER_ID, null, SESSION_ID_SCHEMA_AGENT).blockingGet();

    Runner capitalRunner = new Runner(capitalAgentWithTool, APP_NAME, null, sessionService);
    Runner structuredRunner = new Runner(structuredInfoAgentSchema, APP_NAME, null, sessionService);

    // --- 6. Run Interactions ---
    System.out.println("--- Testing Agent with Tool ---");
    agentExample.callAgentAndPrint(
        capitalRunner, capitalAgentWithTool, SESSION_ID_TOOL_AGENT, "{\"country\": \"France\"}");
    agentExample.callAgentAndPrint(
        capitalRunner, capitalAgentWithTool, SESSION_ID_TOOL_AGENT, "{\"country\": \"Canada\"}");

    System.out.println("\n\n--- Testing Agent with Output Schema (No Tool Use) ---");
    agentExample.callAgentAndPrint(
        structuredRunner,
        structuredInfoAgentSchema,
        SESSION_ID_SCHEMA_AGENT,
        "{\"country\": \"France\"}");
    agentExample.callAgentAndPrint(
        structuredRunner,
        structuredInfoAgentSchema,
        SESSION_ID_SCHEMA_AGENT,
        "{\"country\": \"Japan\"}");
  }

  // --- 7. Define Agent Interaction Logic ---
  public void callAgentAndPrint(Runner runner, LlmAgent agent, String sessionId, String queryJson) {
    System.out.printf(
        "%n>>> Calling Agent: '%s' | Session: '%s' | Query: %s%n",
        agent.name(), sessionId, queryJson);

    Content userContent = Content.fromParts(Part.fromText(queryJson));
    final String[] finalResponseContent = {"No final response received."};
    Flowable<Event> eventStream = runner.runAsync(USER_ID, sessionId, userContent);

    // Stream event response
    eventStream.blockingForEach(event -> {
          if (event.finalResponse() && event.content().isPresent()) {
            event
                .content()
                .get()
                .parts()
                .flatMap(parts -> parts.isEmpty() ? Optional.empty() : Optional.of(parts.get(0)))
                .flatMap(Part::text)
                .ifPresent(text -> finalResponseContent[0] = text);
          }
        });

    System.out.printf("<<< Agent '%s' Response: %s%n", agent.name(), finalResponseContent[0]);

    // Retrieve the session again to get the updated state
    Session updatedSession =
        runner
            .sessionService()
            .getSession(APP_NAME, USER_ID, sessionId, Optional.empty())
            .blockingGet();

    if (updatedSession != null && agent.outputKey().isPresent()) {
      // Print to verify if the stored output looks like JSON (likely from output_schema)
      System.out.printf("--- Session State ['%s']: ", agent.outputKey().get());
      }
  }
}

(この例はコアコンセプトを示しています。より複雑なエージェントは、スキーマ、コンテキスト制御、プランニングなどを組み込む場合があります。)

関連コンセプト(後述のトピック)

このページではLlmAgentのコア設定について説明しましたが、いくつかの関連コンセプトがより高度な制御を提供し、他の場所で詳しく説明されています:

  • コールバック: before_model_callbackafter_model_callbackなどを使用して実行ポイント(モデル呼び出しの前後、ツール呼び出しの前後)を傍受します。コールバックを参照してください。
  • マルチエージェント制御: プランニング(planner)、エージェント転送の制御(disallow_transfer_to_parentdisallow_transfer_to_peers)、およびシステム全体の指示(global_instruction)を含む、エージェント対話のための高度な戦略。マルチエージェントを参照してください。