はじめに
LLMを呼び出すと便利ですが、毎回呼んでいてはコストがかさみ、レスポンスも不安定になります。
ここで役立つのが、Google ADK(Agent Development Kit)のcallbackです。
callbackで出力を制御することで、指定したレスポンスを返したり、使い方によっては不要なLLM呼び出しを防ぐこともできます!
本記事では、そんなcallbackの使い方について紹介します。
callbackとは
LLMの前後やツール呼び出しの直前など、エージェント実行の節目で処理を差し込むことができる機能。
これにより、エージェントの出力そのものを制御することが可能です。
出力制御の実践
callbackを使用した例をいくつか紹介します。
1. ガードレール
まずはベーシックな利用方法として、「必ず所定の形式で返す」「禁止ワードを含んでいれば差し替える」といったガードレール的な使い方をご紹介します。
以下の例では、「Paris」というワードが入っていたらLLMを実行せずcallbackがブロックします。
from google.adk.agents import LlmAgent
from google.adk.agents.callback_context import CallbackContext
from google.adk.models import LlmResponse, LlmRequest
def block_paris_guardrail(
callback_context: CallbackContext, llm_request: LlmRequest
) -> Optional[LlmResponse]:
"""
LLMリクエストに'Paris'が含まれているかチェックします。
もしそうなら、LLM実行をブロックし、特定のエラー文を返します。
それ以外の場合は、Noneを返してLLM呼び出しを続行させます。
"""
# 直近のユーザーメッセージを取得
last_user_message = ""
if llm_request.contents and llm_request.contents[-1].role == 'user':
if llm_request.contents[-1].parts:
last_user_message = llm_request.contents[-1].parts[0].text
# メッセージに"Paris"が含まれるかチェック
if "Paris" in last_user_message:
# LLM呼び出しを中止 → callbackから直接textを返す
return LlmResponse(
content=types.Content(
role="model",
parts=[types.Part(text="LLM呼び出しはブロックされました。")],
)
)
else:
# Noneを返す場合、その後のLLMは通常通り動く
return None
root_agent = LlmAgent(
name="RootAgent",
model="gemini-2.5-pro",
description="ガードレール確認エージェント",
instruction="""とりあえず「こんにちは」と挨拶してください""",
before_model_callback=block_paris_guardrail, # LLMモデル実行前にcallback関数を実行
)
callbackの特徴として、None
以外を返すと直接返り値が出力される仕様になっています。
そのため、LLMの前後で処理を行いたいけど、特に出力に影響を与える必要がない場合はNone
を返せばOKです。
2. tool使用後の強制終了
エージェント内でtoolを実行したらそのまま処理を終了したい場合があると思います。
(例えば、メールを送信してそのまま終了するなど)
その場合は、tool, state, callbackを組み合わせればOKです。
- tool:AIエージェントに提供される特定の機能(アクション実行や外部連携が可能)
- state:エージェントが会話や処理を覚えておくためのメモリのような機能
tool実行後そのまま強制終了するので、無駄なLLM呼び出しを防ぐことが可能です。
例として、以下のような構成を考えてみましょう。
最後に呼び出されるサブエージェント(Agent_2)でtool実行後、処理をそのまま終了する流れです。

import os
from typing import Optional, Dict, Any, List
from google.adk.agents import LlmAgent, SequentialAgent
from google.adk.tools import FunctionTool
from google.adk.tools.tool_context import ToolContext
from google.adk.agents.callback_context import CallbackContext
from google.adk.models.llm_request import LlmRequest
from google.adk.models.llm_response import LlmResponse
from google.adk.tools.base_tool import BaseTool
from google.genai import types
def Tool_1(tool_context: ToolContext) -> None:
"""
このツールの機能を説明します。
"""
tool_context.state["is_finish"] = True
Tool_1_tool = FunctionTool(func=Tool_1)
def Callback_1(callback_context: CallbackContext, llm_request: LlmRequest):
"""
State: is_finishを確認
"""
if callback_context.state.get("is_finish") == True:
return LlmResponse(
content=types.Content(
role="model",
parts=[types.Part(text='処理を終了します')]
)
)
Agent_1 = LlmAgent(
name="Agent_1",
model="gemini-2.5-pro",
description="""これはtestですと出力して""",
instruction="""これはtestですと出力して""",
output_key=None
)
Agent_2 = LlmAgent(
name="Agent_2",
model="gemini-2.5-pro",
description="""tool :test_toolを実行する""",
instruction="""最初にtool :test_toolを1回だけ、必ず実行する""",
tools=[Tool_1_tool],
before_model_callback=Callback_1,
output_key=None
)
RootAgent = SequentialAgent(
name="SeqAgent",
description="パイプラインエージェント",
sub_agents=[Agent_1, Agent_2],
)
root_agent = RootAgent
ポイントとしては、以下あたりです。
- 最後に呼び出されるAgent_2にて
before_model_callback
を設定 -
before_model_callback
では、stateis_finish
をチェック
⇒ Trueであればcallbackが直接レスポンスを返す - Tool_1を呼び出した際に
is_finish
をTrueに設定
要は、毎回エージェントを呼び出す前に必ずcallbackを実行してフラグのチェックを行い、Trueだったらそのまま指定したテキストを返す、ということをしています。
実際にadk web
コマンドを使用してADK web UIを立ち上げ、動作を確認してみます。
Agent_2がTool_1を呼び出して実行した後、call_llm
が実行されずに指定した文章がちゃんと返ってきていますね。
3. (おまけ) callbackを使わずに強制終了する
先ほどのやり方と別で、tool内でそのまま処理を強制終了するには、もう1つ方法があります。
こちらはもう少しシンプルに実装することができます。
import os
from typing import Optional, Dict, Any, List
from google.adk.agents import LlmAgent
from google.adk.tools import FunctionTool
from google.adk.tools.tool_context import ToolContext
def TestTool(query: str, tool_context: ToolContext) -> str:
"""
ユーザーからの質問内容を検索するTool
"""
tool_context._invocation_context.end_invocation = True
return "処理を終了します"
TestTool_tool = FunctionTool(func=TestTool)
root_agent = LlmAgent(
name="RootAgent",
model="gemini-2.5-pro",
description="""とりあえずtool: TestToolを呼び出して回答して""",
instruction="""とりあえずtool: TestToolを呼び出して回答して""",
tools=[TestTool_tool],
output_key=None
)
ポイントとしてはtool_context
のend_invocation
をTrueに設定するところです。
end_invocation
は、ツール実行中に現在の要求-応答のサイクルを終了するためのフラグで、エージェント内でこのフラグをチェックする仕組みになっています。
tool強制終了のcallbackありなしの使い分け
では、なぜわざわざcallbackを使用して強制終了する必要があるのか?こちらの方がシンプルなのでは?という意見が出てくるかと思います。
実はこのend_invocation
は、SequentialAgentやLoopAgentなどのサブエージェント内では機能しません。
理由としては、SequentialAgentやLoopAgentは、サブエージェントを実行する場合end_invacation
フラグをチェックしない仕組みだからです。
そのため、LlmAgentであればend_invocation
を使用し、SequentialAgentなどの場合はcallbackとstateを使用する、みたいに使い分ける必要があります。
おわりに
callbackを使用してエージェントの出力を制御し、不要なLLM呼び出しを防ぐ方法をご紹介しました。
意外とLLMで考えずに定型文で返せばOKみたいなことも多いと思います。
また、最後にLLMが呼び出されることで処理の終了を伝えたいだけなのに返答に時間がかかる、みたいなこともあるかと思います。
そういった場面で、本記事で紹介した内容は活用できるかと思いますので、ぜひ参考にしてもらえればと思います!