第2週では、ローカルLLMを使ってツール(関数)を呼び出し、AIが判断した内容を実際の処理につなげる仕組みを実装しました。
これにより、AIは「答えるだけ」ではなく、「行動する」ことができるようになりました。
しかし、現時点ではまだ1回の判断と実行で処理が終了する単発の動きです。
実際のAIエージェントは、状況に応じて何度も判断を繰り返しながら、目的を達成するまで動き続けます。
本記事では、AIが「終わるまで考え続ける」ループ型エージェントを実装していきます。
AIエージェントとは
AIエージェントとは、入力に対して一度だけ応答するのではなく、状況に応じて判断と実行を繰り返し、目的達成まで処理を継続する仕組みです。
第3週では、その「繰り返し処理(ループ)」を実装します。
学習ロードマップ
| 週 | 内容 |
|---|---|
| 第1週 | チャット + JSON出力 |
| 第2週 | ツール呼び出し |
| 第3週 | ループ型エージェント |
| 第4週 | マルチエージェント |
作業環境について
本記事は、第1週の内容を前提として進めています。
そのため、以下の環境がすでに整っている状態を想定しています。
- ローカルLLM実行環境:Ollama
- 使用モデル:qwen3
- Python仮想環境(venv)
- ollama Pythonライブラリのインストール済み
作業環境の準備ができていない場合
第1週の記事で、環境構築からJSON出力までを解説しています。
未実施の場合は、先に第1週から進めることをおすすめします。
AIエージェント入門|ローカルLLMで学ぶチャットとJSON出力【第1週】
第3週のゴール
この週では、AIエージェントの「継続的に動く仕組み」を理解します。
- AIが複数回判断できる
- 処理を繰り返せる
- 完了条件まで動き続ける
なぜループが必要なのか?
第2週では、AIが判断した内容をもとに関数を呼び出し、実際の処理を実行する仕組みを作りました。
これにより、AIは「答えるだけ」でなく「行動する」ことができるようになりました。
しかし、この仕組みにはまだ重要な制約があります。
なぜ処理を繰り返す必要があるのか?
第2週の処理の流れは次の通りです。
AIが判断 → 関数を実行 → 終了一見すると問題なさそうですが、この流れでは1回の処理で必ず終了してしまうという特徴があります。
AIは1回の判断だけでは、処理が本当に完了したかどうかを検証できません。
1回の判断では不十分な理由
例えば「タスクを追加する」という処理でも、次のような点を確認する必要があります。
- 本当にタスクが追加されたか
- 同じ内容が重複していないか
- 他に必要な処理が残っていないか
これらはすべて、関数を実行した後の状態を見ないと判断できません。
重要なポイント
ここで押さえておきたいのは、AIが見ている情報の範囲です。
AIは最初、関数を実行する前の状態しか知らない
しかし実際には、次のような流れが必要になります。
実行後の状態を確認する
↓
次の行動を決めるつまり、結果を見てから次を判断する仕組みが必要になります。
だからループが必要になる
この問題を解決するのが、ループという仕組みです。
判断
↓
実行
↓
結果を確認
↓
再判断
↓
必要なら続けるAIは一度で終わるのではなく、
- 実行結果を確認し
- 必要であれば再度判断し
- 次の処理を決める
という流れを繰り返します。

実装してみる
参考:
ollama-python/examples at main · ollama/ollama-python · GitHub
ollama/docs/api.md at main · ollama/ollama · GitHub
Ollamaツール使用機能(Tool Support)
完成コード
loop_agent.py
from ollama import chat
def add_todo(task: str) -> str:
return f"タスクを追加しました: {task}"
TOOLS = [
{
"type": "function",
"function": {
"name": "add_todo",
"description": "新しいToDoを追加する",
"parameters": {
"type": "object",
"properties": {
"task": {"type": "string"}
},
"required": ["task"]
}
}
}
]
messages = [
{"role": "system", "content": "あなたはToDo管理エージェントです。"},
{"role": "user", "content": "牛乳を買うタスクを追加して"}
]
for _ in range(5): # 最大5回ループ
response = chat(
model="qwen3",
messages=messages,
tools=TOOLS,
)
message = response["message"]
messages.append(message)
tool_calls = message.get("tool_calls", [])
if not tool_calls:
print("最終回答:")
print(message.get("content", ""))
break
for tool_call in tool_calls:
name = tool_call["function"]["name"]
args = tool_call["function"]["arguments"]
if name == "add_todo":
result = add_todo(args["task"])
messages.append({
"role": "tool",
"content": result
})ループ型エージェントコード解説
ループ型エージェントでは、同じ処理を繰り返しながら目的に近づいていきます。
その中で重要なポイントを順番に見ていきます。
① ループしている
for _ in range(5):最大5回まで処理を繰り返しています。
ここでは「無限ループ」を防ぐために、回数に上限を設けています。
エージェントは本来「終わるまで続ける」ものですが、安全のために制限をかけているというイメージです。
② AIの出力を蓄積する
message = response["message"]
messages.append(message)AIの返答を会話履歴に追加します。
これによりAIは、
- これまでに何をしたか
- どんな結果が出たか
を踏まえて、次の判断ができるようになります。
これは過去の情報を覚えながら考える仕組みです。messagesは以下の様に情報が追加されていきます。
参考:Generate a chat message – Ollama
③ 結果をAIに戻す
messages.append({
"role": "tool",
"content": result
})関数の実行結果をAIに渡すために、会話履歴に追加します。
AIはこの情報をもとに、
- 処理が成功したか
- 次に何をするべきか
を判断します。
ここまでの会話履歴
messages = [
{"role": "system", "content": "あなたはToDo管理エージェントです。"},
{"role": "user", "content": "牛乳を買うタスクを追加して"},
# --- AIからの回答が追加された ---
{
"role": "assistant",
"content": "",
"tool_calls": [{"function": {"name": "add_todo", "arguments": {"task": "牛乳を買う"}}}]
},
# --- 関数の実行結果が追加された---
{
"role": "tool",
"content": result
}
]④ 終了条件のチェック
tool_calls = message.get("tool_calls", [])
if not tool_calls:
print("最終回答:")
print(message.get("content", ""))
breakmessage.get("tool_calls", [])
AIの回答(message)の中に、tool_calls(関数の実行指示)が含まれているか確認します。
・含まれていない場合: デフォルト値として空のリスト [] を代入します。
・含まれている場合: その指示リストを取得します。
if not tool_calls:tool_calls が空([])の場合は、AIは「関数(ツール)を動かす必要はない。」と判断したことを意味します。
関数の流れまとめ
この4つをつなげると、以下の流れになります。
AIが判断する
↓
関数を実行する
↓
結果をAIに戻す
↓
AIが再判断する
↓
終わるまで繰り返す※AIエージェントは「1回で終わる処理」ではなく、「結果を見ながら何度も考え直す」仕組みになっています。
関数の実行
Git Bashで、保存したフォルダに移動してから実行します。
./.venv/Scripts/python.exe loop_agent.py上記コマンドは、第1週で構築したPython仮想環境での実行例です。
Python仮想環境の構築に関しては、第1週ステップ5およびステップ6を参考にしてください。
注意:ollama起動タイムアウト
ollama起動後、5分間アクセスがないとプロセスが終了します。
ollama起動タイムアウトを防止するには、以下のコマンドで環境変数を変更する必要があります。
export OLLAMA_KEEP_ALIVE="-1"実行例
正常に動けば、AIからの返答がターミナルに表示されます。
最終回答:
タスク「牛乳を買う」を正常に追加しました。何か他のご指示もお持ちですか?実行時の変数
1回目ループ(関数の実行)
LLM指示
[{'role': 'system', 'content': 'あなたはToDo管理エージェントです。'}, {'role': 'user', 'content': '牛乳を買うタスクを追加して'}]LLM回答(1回目)
model='qwen3' created_at='2026-04-19T06:56:35.2303358Z' done=True done_reason='stop' total_duration=12292463000 load_duration=130337900 prompt_eval_count=151 prompt_eval_duration=363984600 eval_count=113 eval_duration=11736540300 message=Message(role='assistant', content='', thinking='Okay, the user wants to add a task to buy milk. Let me check the tools available. There\'s a function called add_todo that takes a task as a string. So I need to call that function with the task parameter set to "牛乳を買う". I should make sure the JSON is correctly formatted with the task in the arguments. No other functions are needed here. Alright, time to structure the tool call.\n', images=None, tool_name=None, tool_calls=[ToolCall(function=Function(name='add_todo', arguments={'task': '牛乳を買う'}))]) logprobs=Nonetool_calls(1回目)
[ToolCall(function=Function(name=’add_todo’, arguments={‘task’: ‘牛乳を買う’}))]
思考プロセス(thinking)(1回目)
Okay, the user wants to add a task to buy milk. Let me check the tools available. There's a function called add_todo that takes a task as a string. So I need to call that function with the task parameter set to "牛乳を買う". I should make sure the JSON is correctly formatted with the task in the arguments. No other functions are needed here. Alright, time to structure the tool call.呼び出す関数と引数(tool_call)(1回目)
呼び出す関数(tool_call[name]):add_todo
引数(tool_call["arguments"]):{'task': '牛乳を買う'}2回目ループ(結果確認)
LLM指示(2回目):1回目のLLM指示に、1回目のLLM回答を蓄積した内容
[{'role': 'system', 'content': 'あなたはToDo管理エージェントです。'}, {'role': 'user', 'content': '牛乳を買うタスクを追加して'}, Message(role='assistant', content='', thinking='Okay, the user wants to add a task to buy milk. Let me check the tools available. There\'s a function called add_todo that takes a task as a string. So I need to call that function with the task parameter set to "牛乳を買う". I should make sure the JSON is correctly formatted with the task in the arguments. No other functions are needed here. Alright, time to structure the tool call.\n', images=None, tool_name=None, tool_calls=[ToolCall(function=Function(name='add_todo', arguments={'task': '牛乳を買う'}))]), {'role': 'tool', 'content': 'タスクを追加しました: 牛乳を買う'}]LLM回答(2回目)
model='qwen3' created_at='2026-04-19T06:56:52.4924241Z' done=True done_reason='stop' total_duration=17258729000 load_duration=141159600 prompt_eval_count=287 prompt_eval_duration=2194128300 eval_count=141 eval_duration=14817949900 message=Message(role='assistant', content='タスク「牛乳を買う」を正常に追加しました。何か他のご指示もお持ちですか?', thinking='Okay, the user asked to add a task for buying milk. I called the add_todo function with the task parameter set to "牛乳を買う". The response from the tool was a confirmation that the task was added. Now I need to inform the user that the task is successfully added. I should keep it simple and direct, maybe mention the task again to confirm. Let me check if there\'s anything else needed, but since the user only asked to add the task, a straightforward confirmation should be enough. Alright, respond in Japanese confirming the addition.\n', images=None, tool_name=None, tool_calls=None) logprobs=Nonetool_calls(2回目)
None思考プロセス(thinking)(2回目)
Okay, the user asked to add a task for buying milk. I called the add_todo function with the task parameter set to "牛乳を買う". The response from the tool was a confirmation that the task was added. Now I need to inform the user that the task is successfully added. I should keep it simple and direct, maybe mention the task again to confirm. Let me check if there's anything else needed, but since the user only asked to add the task, a straightforward confirmation should be enough. Alright, respond in Japanese confirming the addition.第2週との違い
| 項目 | 第2週 | 第3週 |
|---|---|---|
| 実行回数 | 1回 | 複数回 |
| 処理 | 単発 | ループ |
| エージェント性 | 低い | 高い |
AIエージェントの本質
AIは、1回だけ判断して終わるのではなく、
結果を見ながら何度も考え直すことで、正しい状態に近づいていきます。
役割分担
この仕組みは、次のように分かれています。
AI → 何をするかを判断する(考える)
Python → 実際の処理を実行する(動く)※AIは処理を直接実行するのではなく、「次に何をするか」を判断し続ける役割を担っています。
3週のまとめ
この週では、AIエージェントの「繰り返し動く仕組み」を学びました。
1. AIは繰り返し判断できる
AIは一度判断して終わるのではなく、
結果を見ながら何度も考え直すことができます。
2. 処理を続けることで目的に近づく
1回の処理で完了しなくても、
判断と実行を繰り返すことで、少しずつ正しい状態に近づいていきます。
3. これが「エージェントらしさ」
AIが
- 考える
- 実行する
- 結果を見てまた考える
という流れを繰り返すことで、
単なる会話AIではなく「自律的に動く存在」になります。
ここまでで、AIは単独で判断し続けることができるようになりました。
しかし実際のシステムでは、1つのAIだけではなく、役割ごとに複数のAIが協力して動くことが多くなります。
第4週では「司令塔+実行役」のマルチエージェント構成を作ります。

