Agno でエージェントの実行のフローを制御する方法を学習します。これはまた “Human in the Loop” とも呼ばれます。Agno のユーザ制御フローは “Human in the Loop” パターンの実装を可能にします、そこではエージェントの実行中に人間の監視や入力が必要となります。
Agno : ユーザガイド : コンセプト : エージェント – ユーザ制御フロー (Human in the Loop)
作成 : クラスキャット・セールスインフォメーション
作成日時 : 07/20/2025
バージョン : Agno 1.7.4
* 本記事は docs.agno.com の以下のページを独自に翻訳した上で、補足説明を加えてまとめ直しています :
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
◆ お問合せ : 下記までお願いします。
- クラスキャット セールス・インフォメーション
- sales-info@classcat.com
- ClassCatJP
Agno ユーザガイド : コンセプト : エージェント – ユーザ制御フロー (Human in the Loop)
Agno でエージェントの実行のフローを制御する方法を学習します。これはまた “Human in the Loop” とも呼ばれます。
Agno のユーザ制御フローは “Human in the Loop” パターンの実装を可能にします、そこではエージェントの実行中に人間の監視や入力が必要となります。これは以下のために重要です :
- 機密性の高い操作の検証
- 実行前にツール呼び出しをレビュー
- 意思決定のためにユーザ入力を収集する
- 外部ツール実行の管理
ユーザ制御フローの種類
Agno は 4 つの主要なタイプのユーザ制御フローをサポートします :
- ユーザ確認 (Confirmation) : ツール呼び出しの実行前に明示的なユーザ承認を必要とします
- ユーザ入力 : 実行中にユーザから特定の情報を収集します
- 動的ユーザ入力 : エージェントにユーザ入力を必要に応じて収集させます
- 外部ツール実行 : エージェントの制御外でツールを実行します
エージェント実行の一時停止
ユーザ制御フローはエージェントの実行を中断 (interrupt) し、人間の監視を必要とします。それから continue_run メソッドを呼び出すことで実行が継続されます。
例えば :
agent.run("Perform sensitive operation")
if agent.is_paused:
# The agent will pause while human input is provided
# ... perform other tasks
# The user can then continue the run
response = agent.continue_run()
# or response = await agent.acontinue_run()
continue_run メソッドは、一時停止時のエージェントの状態を使用して継続します。また、特定の実行の run_response、または run_id を continue_run メソッドに渡すこともできます。
ユーザ確認
ユーザ確認 (confirmation) により、実行を一時停止して、ツール呼び出しを進める前に明示的なユーザの承認を要求することを可能にします。これは以下のために有用です :
- 機密性の高い操作
- データを変更する API 呼び出し
- 重要な結果を伴うアクション
次の例はユーザ確認を実装する方法を示しています。
from agno.tools import tool
from agno.agent import Agent
from agno.models.openai import OpenAIChat
@tool(requires_confirmation=True)
def sensitive_operation(data: str) -> str:
"""Perform a sensitive operation that requires confirmation."""
# Implementation here
return "Operation completed"
agent = Agent(
model=OpenAIChat(id="gpt-4o"),
tools=[sensitive_operation],
)
# Run the agent
agent.run("Perform sensitive operation")
# Handle confirmation
if agent.is_paused:
for tool in agent.run_response.tools_requiring_confirmation:
# Get user confirmation
print(f"Tool {tool.tool_name}({tool.tool_args}) requires confirmation")
confirmed = input(f"Confirm? (y/n): ").lower() == "y"
tool.confirmed = confirmed
# Continue execution
response = agent.continue_run()
ツールキットのどのツールが確認を必要とするか指定することもできます。
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.tools import tool
from agno.tools.yfinance import YFinanceTools
from agno.utils import pprint
agent = Agent(
model=OpenAIChat(id="gpt-4o-mini"),
tools=[YFinanceTools(requires_confirmation_tools=["get_current_stock_price"])],
)
agent.run("What is the current stock price of Apple?")
if agent.is_paused:
for tool in agent.run_response.tools_requiring_confirmation:
print(f"Tool {tool.tool_name}({tool.tool_args}) requires confirmation")
confirmed = input(f"Confirm? (y/n): ").lower() == "y"
if message == "n":
tool.confirmed = False
else:
# We update the tools in place
tool.confirmed = True
run_response = agent.continue_run()
pprint.pprint_run_response(run_response)
ユーザ入力
ユーザ入力フローは、実行中にユーザからの特定の情報を収集することを可能にします。これは以下のために有用です :
- 必要なパラメータの収集
- ユーザ設定の取得
- 不足している情報の収集
次の例では、ユーザからの send_email ツール用のすべての入力を必要とします。
from typing import List
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.tools.function import UserInputField
# We still provide a docstring to the tool; This will be used to populate the `user_input_schema`
@tool(requires_user_input=True)
def send_email(to: str, subject: str, body: str) -> dict:
"""Send an email to the user.
Args:
to (str): The address to send the email to.
subject (str): The subject of the email.
body (str): The body of the email.
"""
# Implementation here
return f"Email sent to {to} with subject {subject} and body {body}"
agent = Agent(
model=OpenAIChat(id="gpt-4o"),
tools=[send_email],
)
agent.run("Send an email please")
if agent.is_paused:
for tool in agent.run_response.tools_requiring_user_input:
input_schema: List[UserInputField] = tool.user_input_schema
for field in input_schema:
# Display field information to the user
print(f"\nField: {field.name} ({field.field_type.__name__}) -> {field.description}")
# Get user input
user_value = input(f"Please enter a value for {field.name}: ")
# Update the field value
field.value = user_value
run_response = (
agent.continue_run()
)
RunResponse オブジェクトはツールのリストを持ち、requires_user_input の場合、入力を必要とするツールには user_input_schema の値が設定されます。これは UserInputField オブジェクトのリストです。
class UserInputField:
name: str # The name of the field
field_type: Type # The required type of the field
description: Optional[str] = None # The description of the field
value: Optional[Any] = None # The value of the field. Populated by the agent or the user.
どのフィールドがユーザにより入力される必要がある指定することもできます、フィールドの残りはエージェントが提供します。
# You can either specify the user_input_fields leave empty for all fields to be provided by the user
@tool(requires_user_input=True, user_input_fields=["to_address"])
def send_email(subject: str, body: str, to_address: str) -> str:
"""
Send an email.
Args:
subject (str): The subject of the email.
body (str): The body of the email.
to_address (str): The address to send the email to.
"""
return f"Sent email to {to_address} with subject {subject} and body {body}"
agent = Agent(
model=OpenAIChat(id="gpt-4o"),
tools=[send_email],
)
agent.run("Send an email with the subject 'Hello' and the body 'Hello, world!'")
if agent.is_paused:
for tool in agent.run_response.tools_requiring_user_input:
input_schema: List[UserInputField] = tool.user_input_schema
for field in input_schema:
# Display field information to the user
print(f"\nField: {field.name} ({field.field_type.__name__}) -> {field.description}")
# Get user input (if the value is not set, it means the user needs to provide the value)
if field.value is None:
user_value = input(f"Please enter a value for {field.name}: ")
field.value = user_value
else:
print(f"Value provided by the agent: {field.value}")
run_response = (
agent.continue_run()
)
動的ユーザ入力
このパターンはエージェントに、ユーザ入力を必要とするときを示すツールを提供します。それは以下のために最適です :
- ユーザがエージェントとやり取りする方法が不明な場合
- ユーザと form のようなインタラクションを望む場合
次の例では、エージェントが必要なときにユーザのフィードバックを収集できるような専用ツールを使用します。
from typing import Any, Dict
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.tools import tool
from agno.tools.toolkit import Toolkit
from agno.tools.user_control_flow import UserControlFlowTools
from agno.utils import pprint
# Example toolkit for handling emails
class EmailTools(Toolkit):
def __init__(self, *args, **kwargs):
super().__init__(
name="EmailTools", tools=[self.send_email, self.get_emails], *args, **kwargs
)
def send_email(self, subject: str, body: str, to_address: str) -> str:
"""Send an email to the given address with the given subject and body.
Args:
subject (str): The subject of the email.
body (str): The body of the email.
to_address (str): The address to send the email to.
"""
return f"Sent email to {to_address} with subject {subject} and body {body}"
def get_emails(self, date_from: str, date_to: str) -> str:
"""Get all emails between the given dates.
Args:
date_from (str): The start date.
date_to (str): The end date.
"""
return [
{
"subject": "Hello",
"body": "Hello, world!",
"to_address": "test@test.com",
"date": date_from,
},
{
"subject": "Random other email",
"body": "This is a random other email",
"to_address": "john@doe.com",
"date": date_to,
},
]
agent = Agent(
model=OpenAIChat(id="gpt-4o-mini"),
tools=[EmailTools(), UserControlFlowTools()],
markdown=True,
debug_mode=True,
)
run_response = agent.run("Send an email with the body 'How is it going in Tokyo?'")
# We use a while loop to continue the running until the agent is satisfied with the user input
while run_response.is_paused:
for tool in run_response.tools_requiring_user_input:
input_schema: List[UserInputField] = tool.user_input_schema
for field in input_schema:
# Display field information to the user
print(f"\nField: {field.name} ({field.field_type.__name__}) -> {field.description}")
# Get user input (if the value is not set, it means the user needs to provide the value)
if field.value is None:
user_value = input(f"Please enter a value for {field.name}: ")
field.value = user_value
else:
print(f"Value provided by the agent: {field.value}")
run_response = agent.continue_run(run_response=run_response)
# If the agent is not paused for input, we are done
if not run_response.is_paused:
pprint.pprint_run_response(run_response)
break
外部ツール実行
外部ツール実行は、エージェントの制御の外でツールを実行することを可能にします。これは以下のために有用です :
- 外部サービス呼び出し
- データベース操作
import subprocess
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.tools import tool
from agno.utils import pprint
# We have to create a tool with the correct name, arguments and docstring for the agent to know what to call.
@tool(external_execution=True)
def execute_shell_command(command: str) -> str:
"""Execute a shell command.
Args:
command (str): The shell command to execute
Returns:
str: The output of the shell command
"""
return subprocess.check_output(command, shell=True).decode("utf-8")
agent = Agent(
model=OpenAIChat(id="gpt-4o-mini"),
tools=[execute_shell_command],
markdown=True,
)
run_response = agent.run("What files do I have in my current directory?")
if run_response.is_paused:
for tool in run_response.tools_awaiting_external_execution:
if tool.tool_name == execute_shell_command.name:
print(f"Executing {tool.tool_name} with args {tool.tool_args} externally")
# We execute the tool ourselves. You can execute any function or process here and use the tool_args as input.
result = execute_shell_command.entrypoint(**tool.tool_args)
# We have to set the result on the tool execution object so that the agent can continue
tool.result = result
run_response = agent.continue_run()
pprint.pprint_run_response(run_response)
以上