Geminiを使ってシンプルなAIエージェントを作ってみよう

はじめに
最近CursorやClineなど、AIが自律的に問題解決を行うAIエージェント(以下、エージェント)が話題です。 CursorやClineといったAIエージェントツールは確かに強力ですが、専用のIDE環境が必要で、CI/CDパイプラインに組み込んだり、サーバー上で自動実行させたりするのは簡単ではありません。 本格的に業務に組み込むには、どうしても「自分たちの環境に合わせたエージェント」が必要になります。
エージェントを自作するためのライブラリとしてはAutoGenやLangChainなどが有名です。 しかし、ライブラリは機能が豊富な一方、学習コストが高く、アップデートにより破壊的変更が発生することもあります。
そこでこの記事では、Gemini APIだけを使ってシンプルなエージェントをゼロから作る方法について解説します。
この記事は、以下のような方を対象としています。
- Pythonの基本的な文法を理解している方
- エージェントの仕組みに興味がある方
1. 基本的な対話機能:AIとのチャット実装
まずはもっともシンプルな形でAIとチャットする機能を実装します
必要なライブラリ:
pip install google-genai
最小構成のチャットスクリプト (main.py):
from google import genai
from google.genai import types
# Gemini API クライアント生成(環境変数 GOOGLE_API_KEY を自動取得)
client = genai.Client()
simple_system_instruction = "あなたは親切なAIアシスタントです。日本語で応答してください。"
# チャットセッションの作成
config = types.GenerateContentConfig(system_instruction=simple_system_instruction)
chat = client.chats.create(
model='gemini-2.5-flash-preview-04-17',
config=config
)
print("AIとチャットを開始します。終了するには 'exit' と入力してください。")
# メインのチャットループ
while True:
user_input = input("あなた: ")
if user_input.lower() == "exit":
print("チャットを終了します。")
break
try:
response = chat.send_message(user_input)
print(f"AI: {response.text}")
except Exception as e:
print(f"エラーが発生しました: {e}")
break
注意: この記事では gemini-2.5-flash-preview-04-17
というモデルを使用していますが、これはPreview版のため、将来のアップデートでモデル名が変更される可能性があります。最新のモデル一覧はGoogle AI Gemini APIのドキュメントで確認できますので、実装時には最新のモデルを指定することをオススメします。
コードを実行する前に、Gemini APIのAPIキーを環境変数として設定する必要があります:
export GOOGLE_API_KEY="あなたのAPIキー"
python main.py
これを実行すると、以下のようにAIとチャットができます。
AIとチャットを開始します。終了するには 'exit' と入力してください。
あなた: こんにちは
AI: こんにちは!
非常にシンプルなAIとのチャット機能ができました 。
2. AIの思考を覗いてみよう:思考プロセスの実装
エージェントを作る上で、AIが「なぜそのような応答をしたのか」という思考プロセスを見ることができると便利です。システムプロンプトを拡張して、思考プロセスを表示する機能を追加しましょう。
まず、システムプロンプトを修正し、AIに思考プロセスを表示するようにします。
from google import genai
from google.genai import types
# Gemini API クライアント生成
client = genai.Client()
# 拡張したシステムプロンプト
system_instruction = """あなたは親切なAIアシスタントです。簡潔で役立つ回答を日本語で提供してください。
あなたは以下の特殊な機能を使えます:
1. あなたの思考プロセスを表示するには以下のフォーマットを使ってください:
<thinking>
ここにあなたの思考プロセス、推論、判断根拠などを詳しく記述してください。
</thinking>
回答を提供する際は、必ず思考プロセスを<thinking>タグ内に含めてください。
"""
# チャットセッションの作成
config = types.GenerateContentConfig(system_instruction=system_instruction)
chat = client.chats.create(
model='gemini-2.5-flash-preview-04-17',
config=config
)
print("AIとチャットを開始します。終了するには 'exit' と入力してください。")
# メインのチャットループ
while True:
user_input = input("あなた: ")
if user_input.lower() == "exit":
print("チャットを終了します。")
break
try:
response = chat.send_message(user_input)
print(f"AI: {response.text}")
except Exception as e:
print(f"エラーが発生しました: {e}")
break
このコードを実行すると、AIは応答の中に<thinking>
タグで囲まれた思考プロセスを表示します。
タグがそのままユーザーに表示されると読みにくいため、XML のパース処理を追加して読みやすくしましょう。
import re
from google import genai
from google.genai import types
# Gemini API クライアント生成
client = genai.Client()
# 拡張したシステムプロンプト
system_instruction = """あなたは親切なAIアシスタントです。簡潔で役立つ回答を日本語で提供してください。
あなたは以下の特殊な機能を使えます:
1. あなたの思考プロセスを表示するには以下のフォーマットを使ってください:
<thinking>
ここにあなたの思考プロセス、推論、判断根拠などを詳しく記述してください。
</thinking>
回答を提供する際は、必ず思考プロセスを<thinking>タグ内に含めてください。
"""
# チャットセッションの作成
config = types.GenerateContentConfig(system_instruction=system_instruction)
chat = client.chats.create(
model='gemini-2.5-flash-preview-04-17',
config=config
)
# 思考プロセスの処理と表示関数
def process_thinking(response_text):
# <thinking>タグの検出と表示
thinking_match = re.search(r'<thinking>(.*?)</thinking>', response_text, re.DOTALL)
if thinking_match:
thinking_text = thinking_match.group(1).strip()
print(f"\n{'-'*25}AI思考プロセス{'-'*25}\n{thinking_text}\n{'-'*58}")
# 思考プロセスタグを除去して返す
return re.sub(r'<thinking>.*?</thinking>', '', response_text, flags=re.DOTALL).strip()
print("AIとチャットを開始します。終了するには 'exit' と入力してください。")
# メインのチャットループ
while True:
user_input = input("あなた: ")
if user_input.lower() == "exit":
print("チャットを終了 します。")
break
try:
response = chat.send_message(user_input)
# 思考プロセスの処理と表示
ai_message = process_thinking(response.text)
# 思考プロセス除去後のメッセージを表示
if ai_message:
print(f"AI: {ai_message}")
except Exception as e:
print(f"エラーが発生しました: {e}")
break
このスクリプトを実行すると、以下のような出力が得られます:
AIとチャットを開始します。終了するには 'exit' と入力してください。
あなた: こんにちは
-------------------------AI思考プロセス-------------------------
ユーザーが簡単な挨拶「こんにちは」をしました。
これに対して、親切なAIとして丁寧な挨拶で応答するのが適切です。
日本語で、簡潔に返信します。
思考プロセスを含める必要があります。
----------------------------------------------------------
AI: こんにちは!何かお手伝いできることはありますか?
この機能により、AIの思考プロセスを可視化できるようになりました。エージェントの動作を理解しやすくなり、デバッグや開発が容易になります。
このように、XMLタグを追加することでAIに新しい機能を付与でき、最終的にエージェントとしての機能を持たせることができます。
3. ユーザーへの質問機能:情報収集の自動化
前のステップまでで、基本的な対話と思考プロセスの可視化ができました。次に、エージェントがより自律的に問題解決を行うための機能を追加します。
エージェントが情報不足を判断し、ユーザーに質問する機能です。たとえば天気について尋ねられた際、場所が不明であれば自動で質問して必要な情報を収集できるようにします。
これを実現するために、ユーザーへの質問用のXMLタグを追加しましょう。
import re
from google import genai
from google.genai import types
# Gemini API クライアント生成
client = genai.Client()
# さらに拡張したシステムプロンプト
system_instruction = """あなたは親切なAIアシスタントです。簡潔で役立つ回答を日本語で提供してください。
ユーザーから十分な情報を得られない場合や詳細を確認したい場合は、質問機能を使用してください。
あなたは以下の特殊な機能を使えます:
1. ユーザーに質問するには以下のフォーマットを使ってください:
<user_question>
<question>ユーザーへの質問をここに記入</question>
</user_question>
2. あなたの思考プロセスを表示するには以下のフォーマットを使ってください:
<thinking>
ここにあなたの思考プロセス、推論、判断根拠などを詳しく記述してください。
</thinking>
回答を提供する際は、必ず思考プロセスを<thinking>タグ内に含めてください。
"""
# チャットセッションの作成
config = types.GenerateContentConfig(system_instruction=system_instruction)
chat = client.chats.create(
model='gemini-2.5-flash-preview-04-17',
config=config
)
# 思考プロセスとユーザー質問を処理する関数
def handle_tags(response_text):
# 思考プロセスの抽出と表示
response_text = re.sub(
r'<thinking>(.*?)</thinking>',
lambda m: print(f"{'-'*25}AI思考プロセス{'-'*25}\n{m.group(1).strip()}\n{'-'*58}") or '',
response_text,
flags=re.DOTALL
)
# ユーザーへの質問を検出
question_match = re.search(r'<user_question>\s*<question>(.*?)</question>', response_text, re.DOTALL)
if question_match:
question_to_user = question_match.group(1).strip()
print(f"質問: {question_to_user}")
user_answer = input("あなた: ")
# ユーザーの回答をLLMに送信し、新しい応答を返す
return chat.send_message(f"ユーザーの回答は「{user_answer}」です。これを踏まえて応答を続けてください。")
# ユーザー質問タグを除去して表示
clean_response = re.sub(r'<user_question>.*?</user_question>', '', response_text, flags=re.DOTALL).strip()
if clean_response:
print("AI:", clean_response)
return None
print("AIとチャットを開始します。終了するには 'exit' と入力してください。")
# メインのチャットループ
while True:
user_input = input("あなた: ")
if user_input.lower() == "exit":
print("チャット を終了します。")
break
try:
response = chat.send_message(user_input)
# 応答を処理し、必要に応じて新しい応答を取得
next_response = handle_tags(response.text)
# ユーザー質問があった場合、その応答も処理
while next_response:
next_response = handle_tags(next_response.text)
except Exception as e:
print(f"エラーが発生 しました: {e}")
break
実際に天気について質問してみましょう:
AIとチャットを開始します。終了するには 'exit' と入力してください。
あなた: 明日の天気は?
-------------------------AI思考プロセス-------------------------
ユーザーは明日の天気について質問しています。
天気予報は場所によって異なるため、場所の情報が必要です。
場所が提供されていないため、ユーザーに場所を尋ねる必要があります。
質問機能を使って場所を尋ねます。
----------------------------------------------------------
質問: 知りたい地域の名前(たとえば:東京都千代田区、大阪市中央区など)を教えていただけますか?
あなた: 東京都渋谷区
-------------------------AI思考プロセス-------------------------
ユーザーは前回の質問に対して「東京都渋谷区」という場所を回答しました。
これにより、明日の天気予報を知りたい場所が明確になりました。
しかし、私はリアルタイムの、常に更新される天気予報データにアクセスできません。天気は頻繁に変化するため、正確な情報を提供するには最新のデータが必要です。
そのため、直接的な天気予報を提供するのではなく、ユーザーが信頼できる情報源を確認するように促すのが最も適切で安全な方法です。
応答として、場所を受け取ったことを伝え、リアルタイム予報ができない理由を説明し、信頼できる天気情報源(天気予報サイト、アプリなど)を参照するように案内します。
----------------------------------------------------------
AI: 東京都渋谷区ですね。明日の天気について承知いたしました。
恐れ入りますが、私はリアルタイムで変動する天気予報を直接提供することはできません。天気情報は常に更新されており、最新かつ正確な情報が必要だからです。
お手数ですが、お近くの天気予報サイトや天気アプリ、テレビの気象情報などで「東京都渋谷区 明日の天気」をご確認いただくようお願いいたします。そちらで最新の情報が得られます。
この例からわかるように、エージェントは必要な情報(地域名)が不足していることを認識し、自ら質問を行って情報を収集できました。ただし、リアルタイムの天気情報にアクセスできないため、適切な回答には至っていません。
次のステップでは、実際に天気情報を取得する機能を追加してみましょう。
4. 外部情報の取得:天気機能の実装
これまでと同様に、新しいXMLタグを追加して機能を拡張します。 エージェントの強みは、外部ツールやAPIと連携してリアルタイムの情報を取得できることです。簡単な例として、天気情報を取得できる機能を追加してみましょう。
import re
from google import genai
from google.genai import types
# Gemini API クライアント生成
client = genai.Client()
# 天気機能を追加したシステムプロンプト
system_instruction = """あなたは親切なAIアシスタントです。簡潔で役立つ回答を日本語で提供してください。
ユーザーから十分な情報を得られない場合や詳細を確認したい場合は、質問機能を使用してください。
天気に関する質問には天気機能を使用してください。
あなたは以下の特殊な機能を使えます:
1. ユーザーに質問するには以下のフォーマットを使ってください:
<user_question>
<question>ユーザーへの質問をここに記入</question>
</user_question>
2. 天気情報を取得するには以下のフォーマットを使ってください:
<get_weather>
<location>天気を知りたい場所をここに記入</location>
</get_weather>
3. あなたの思考プロセスを表示するには以下のフォーマットを使ってください:
<thinking>
ここにあなたの思考プロセス、推論、判断根拠などを詳しく記述してください。
</thinking>
回答を提供する際は、必ず思考プロセスを<thinking>タグ内に含めてください。
"""
# チャットセッションの作成
config = types.GenerateContentConfig(system_instruction=system_instruction)
chat = client.chats.create(
model='gemini-2.5-flash-preview-04-17',
config=config
)
# 応答内のタグを処理する関数
def handle_tags(response_text):
# 思考プロセスの抽出と表示
response_text = re.sub(
r'<thinking>(.*?)</thinking>',
lambda m: print(f"{'-'*25}AI思考プロセス{'-'*25}\n{m.group(1).strip()}\n{'-'*58}") or '',
response_text,
flags=re.DOTALL
)
# ユーザーへの質問を検出
m = re.search(r'<user_question>\s*<question>(.*?)</question>', response_text, re.DOTALL)
if m:
question_to_user = m.group(1).strip()
print(f"質問: {question_to_user}")
user_answer = input("あなた: ")
return chat.send_message(f"ユーザーの回答は「{user_answer}」です。これを踏まえて応答を続けてください。")
# 天気情報の取得を検出
m = re.search(r'<get_weather>\s*<location>(.*?)</location>', response_text, re.DOTALL)
if m:
location = m.group(1).strip()
# 実際のAPI呼び出しの代わりにダミーデータを返す
weather_info = f"{location}の天気は晴れです。"
print(f"天気情報を取得: {weather_info}")
return chat.send_message(f"天気情報: {weather_info}")
# XMLタグ を除去して表示
clean_response = re.sub(r'<[^>]+>', '', response_text, flags=re.DOTALL).strip()
if clean_response:
print("AI:", clean_response)
return None
print("AIとチャットを開始します。終了するには 'exit' と入力してください。")
# メインのチャットループ
while True:
user_input = input("あなた: ")
if user_input.lower() == "exit":
print("チャットを終了します。")
break
try:
response = chat.send_message(user_input)
# 応答を処理し、必要に応じて新しい応答を取得
next_response = handle_tags(response.text)
# ツール使用があった場合、その応答も処理
while next_response:
next_response = handle_tags(next_response.text)
except Exception as e:
print(f"エラーが発生しました: {e}")
break
このコードでは、エージェントに天気情報取得用の<get_weather>
タグが使えるようにシステムプロンプトを拡張し、handle_tags()
関数に天気情報取得リクエストを検出する処理を追加しました。
今回は天気APIの処理を書くと冗長になってしまうため、必ず「晴れ」と回答するダミー関数を用意しています。
実行してみましょう:
AIとチャットを開始します。終了するには 'exit' と入力してください。
あなた: 東京の今日の天気は?
-------------------------AI思考プロセス-------------------------
ユーザーが東京の今日の天気について質問しています。これは天気に関する質問なので、天気機能を使用すべきです。天気情報を取得するために<get_weather>タグを使用し、locationとして「東京」を指定します。
----------------------------------------------------------
天気情報を取得: 東京の天気は晴れです。
-------------------------AI思考プロセス-------------------------
天気情報が取得できました。東京の天気は晴れだということがわかりました。この情報をもとにユーザーへ回答します。
----------------------------------------------------------
AI: 東京の今日の天気は晴れです。お出かけ日和ですね。
あなた: 大阪はどうですか?
-------------------------AI思考プロセス-------------------------
ユーザーが大阪の天気について質問しています。これも天気に関する質問なので、天気機能を使用して大阪の天気情報を取得します。
----------------------------------------------------------
天気情報を取得: 大阪の天気は晴れです。
-------------------------AI思考プロセス-------------------------
大阪の天気情報が取得できました。大阪の天気も晴れです。この情報をもとにユーザーへ回答します。
----------------------------------------------------------
AI: 大阪の天気も晴れです。関西地方も良い天気のようですね。
これで、エージェントは天気に関する質問に対して、自律的に天気機能を呼び出して情報を取得し、その結果に基づいて回答できるようになりました。 同様に機能を追加すれば、さまざまな外部情報源と連携するエージェントを構築できます。
5. タスク完了機能の追加:完成版エー ジェント
最後に、エージェントがタスクを完了したことを明示的に示せる機能を追加して、エージェントを完成させましょう。これは長期的なタスクや複数ステップのタスクを管理する際に便利です。
import re
from google import genai
from google.genai import types
# Gemini API クライアント生成
client = genai.Client()
# 完全なシステムプロンプト
system_instruction = """あなたは親切なAIアシスタントです。簡潔で役立つ回答を日本語で提供してください。
ユーザーから十分な情報を得られない場合や詳細を確認したい場合は、質問機能を使用してください。
天気に関する質問には天気機能を使用してください。
あなたは以下の特殊な機能を使え ます:
1. ユーザーに質問するには以下のフォーマットを使ってください:
<user_question>
<question>ユーザーへの質問をここに記入</question>
</user_question>
2. 天気情報を取得するには以下のフォーマットを使ってください:
<get_weather>
<location>天気を知りたい場所をここに記入</location>
</get_weather>
3. あなたの思考プロセスを表示するには以下のフォーマットを使ってください:
<thinking>
ここにあなたの思考プロセス、推論、判断根拠などを詳しく記述してください。
</thinking>
4. タスクの完了を宣言するには以下のフォーマットを使ってください:
<attempt_completion>
<confirmation>ユーザーとの対話を通じてすべてのステップが成功したことを確認しました。タスクを完了します。</confirmation>
</attempt_completion>
"""
# チャットセッションの作成
config = types.GenerateContentConfig(system_instruction=system_instruction)
chat = client.chats.create(
model='gemini-2.5-flash-preview-04-17',
config=config
)
# 応答内のタグを処理する関数
def handle_tags(resp_text):
# 思考プロセスの抽出と表示
resp_text = re.sub(
r'<thinking>(.*?)</thinking>',
lambda m: print(f"{'-'*25}AI思考プロセス{'-'*25}\n{m.group(1).strip()}\n{'-'*58}") or '',
resp_text,
flags=re.DOTALL
)
# ユーザーへの質問を検出
m = re.search(r'<user_question>\s*<question>(.*?)</question>', resp_text, re.DOTALL)
if m:
return chat.send_message(input(f"質問: {m.group(1).strip()}\nあなた: "))
# 天気情報の取得を検出
m = re.search(r'<get_weather>\s*<location>(.*?)</location>', resp_text, re.DOTALL)
if m:
return chat.send_message(f"天気情報: {m.group(1).strip()}の天気は晴れです。")
# タスク完了の宣言を検出
m = re.search(r'<attempt_completion>\s*<confirmation>(.*?)</confirmation>', resp_text, re.DOTALL)
if m:
print(f"タスク完了: {m.group(1).strip()}")
exit()
# XMLタグを除去して表示
print("AI:", re.sub(r'<[^>]+>', '', resp_text).strip())
return None
print("AIとチャットを開始します。終了するには 'exit' と入力してください。")
# メインのチャットループ
while (msg := input("あなた: ")) != "exit":
resp = chat.send_message(msg)
while resp:
resp = handle_tags(resp.text)
print("チャットを終了します。")
これを実行して、今日の天気を聞いてみます。
あなた: 今日の天気を教えて
-------------------------AI思考プロセス-------------------------
ユーザーは今日の天気を求めていますが、場所が指定されていません。
天気機能を使うためには場所が必要です。
したがって、ユーザーに場所を尋ねる必要があります。
質問機能を使って場所を尋ねます。
--------------------------------------------------
AI: 今日の天気ですね 。どちらの地域の天気予報を知りたいですか?
あなた: 渋谷
-------------------------AI思考プロセス-------------------------
ユーザーが場所として「渋谷」を指定しました。
天気機能を使って渋谷の今日の天気を取得します。
--------------------------------------------------
-------------------------AI思考プロセス-------------------------
ユーザーから渋谷の天気情報(晴れ)を受け取りました。
この情報を使って、親切かつ簡潔に回答します。
タスクは完了しました。
--------------------------------------------------
AI: 渋谷の今日の天気は晴れですね!良い一日になりますように。
あなた: ありがとう!
-------------------------AI思考プロセス-------------------------
ユーザーがお礼を言っています。
感謝の言葉に対し、親切に返答します。
天気情報の提供という最初のタスクは完了しており、ユーザーとの対話も円滑に進んでいます。
特に新たなタスクや質問はないため、対話を終了しても良い段階です。
--------------------------------------------------
タスク完了: ユーザーとの対話を通じてすべてのステップが成功したことを確認しました。タスクを完了します。
天気を回答したあと、エージェントがタスク完了を通知しました。
他のパターンも試してみましょう。 直接天気を尋ねず、天気の情報が必要だとAIが類推して機能を使えるか試します。
あなた: 明日洗濯物干してもいいかな?
-------------------------AI思考プロセス-------------------------
ユーザーは明日の天気、特に洗濯物を外に干せるかどうかを知りたいようです。
これには明日の天気情報が必要です。
天気情報を取得するには場所の情報が必要です。
ユーザーは場所を指定していないため、場所を尋ねる必要があります。
質問機能を使って場所を尋ねるフォーマットを作成します。
--------------------------------------------------
質問: どちらの地域の明日のお天気について知りたいですか?市区町村まで教えていただけますか?
あなた: 東京都渋谷区
-------------------------AI思考プロセス-------------------------
ユーザーから場所情報(東京都渋谷区)を得られました。
これで明日の天気情報を取得できます。
天気機能を使って東京都渋谷区の明日の天気を取得します。
取得した天気情報に基づいて、洗濯物を干せるかどうかを判断し、回答を作成します。
--------------------------------------------------
-------------------------AI思考プロセス-------------------------
天気情報を受け取りました。東京都渋谷区の明日の天気は「晴れ」です。
「晴れ」であれば、洗濯物を外に干すのに適しています。
この情報をもとに、洗濯物を干せる旨と、念のため降水確率や風の情報もあればさらに良いですが、今回は提供されていないので、晴れであることに基づいて回答します。
シンプルに「晴れなので大丈夫です」と伝えます。
--------------------------------------------------
AI: 東京都渋谷区の明日は晴れの予報ですので、洗濯物 を外に干しても大丈夫そうです。
あなた: ありがとう!
-------------------------AI思考プロセス-------------------------
ユーザーから感謝の言葉を受け取りました。
質問に回答し、ユーザーのニーズを満たすことができました。
タスクは完了したと判断できます。
完了の宣言をします。
--------------------------------------------------
タスク完了: ユーザーへの天気情報の提供と洗濯物の判断について回答を終え、ユーザーの満足を確認しました。
こちらも天気を直接尋ねてはいませんが、エージェントが天気情報が必要だと判断し、回答してくれました。
これで、基本的なエージェントの実装が完成しました。ユーザーとの対話、思考プロセスの表示、追加質問、天気情報の取得を通じて自律的に問題を解決するシンプルなエージェントが作成できました。
おわりに
この記事ではGemini APIを使って最小限の機能からスタートし、段階的に機能を追加しながらエージェントを構築する方法を解説しました。
解説した方法では、AutoGenやLangChainなどの複雑なフレームワークを学ぶ必要がなく、Pythonの基本知識とGemini APIの理解だけでエージェントが作成できます。XMLタグベースの仕組みにより必要な機能を柔軟に追加でき、外部ライブラリへの依存が少ないためフレームワークのアップデートによる破壊的変更の影響も受けにくくなります。
非常にシンプルなXMLベースの機能指示でも、実用的なエージェントを作成できることがお分かりいただけたでしょう。 このやり方を応用すれば、CLIツール、Slackボット、Webアプリケーションなど、さまざまな形でエージェントを組み込むことができるでしょう。
この記事が皆様のAI活用の一助となれば幸いです。