1. לולאת הסוכן
"לולאה אחת ו-Bash זה כל מה שצריך"
חדש בנושא?
מה זה API?
API (ממשק תכנות יישומים) הוא דרך לתוכנות לדבר זו עם זו. כשאנחנו קוראים ל-API של Claude, אנחנו שולחים טקסט ומקבלים תשובה — כמו לשלוח הודעה לחבר מאוד חכם.
מה זה 'while True'?
זו לולאה שרצה לנצח עד שמשהו אומר לה לעצור. בסוכן שלנו, היא ממשיכה לרוץ עד שהמודל מחליט שהוא סיים (stop_reason שונה מ-'tool_use').
מה זה קריאת כלי (tool call)?
כשמודל הבינה המלאכותית רוצה לעשות משהו בעולם האמיתי (להריץ פקודה, לקרוא קובץ), הוא שולח בחזרה הודעת 'tool_use' מיוחדת במקום טקסט רגיל. הקוד שלנו מבצע את הפעולה ושולח את התוצאה בחזרה.
הבעיה
איך מודל שפה עובר מייצור טקסט לעשייה בעולם האמיתי?
המודל יכול לחשוב, לתכנן ולייצר קוד — אבל אין לו ידיים. הוא לא יכול להריץ פקודה, לקרוא קובץ או לבדוק תוצאה. הוא מוח בצנצנת.
הפתרון
לולאה אחת. כלי אחד. זו כל הארכיטקטורה.
while True:
response = LLM(messages, tools)
if stop_reason != "tool_use": return
execute tools
append results
loop back
המודל מחליט מתי לקרוא לכלים ומתי לעצור. הקוד רק מבצע את מה שהמודל מבקש.
הלולאה המרכזית
def agent_loop(messages):
while True:
response = client.messages.create(
model=MODEL, system=SYSTEM,
messages=messages, tools=TOOLS, max_tokens=8000,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
return
results = []
for block in response.content:
if block.type == "tool_use":
output = TOOL_HANDLERS[block.name](**block.input)
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": output,
})
messages.append({"role": "user", "content": results})
ארבעה שלבים, חוזרים על עצמם:
- הוסף את ההנחיה של המשתמש להיסטוריית ההודעות
- שלח הודעות + הגדרות כלים ל-LLM
- בדוק
stop_reason— אם זה לאtool_use, המודל סיים - בצע כל קריאת כלי, הוסף את התוצאות, חזור ללולאה
כלי ה-Bash
TOOLS = [{
"name": "bash",
"description": "Run a shell command.",
"input_schema": {
"type": "object",
"properties": {"command": {"type": "string"}},
"required": ["command"],
},
}]
def run_bash(command: str) -> str:
dangerous = ["rm -rf /", "sudo", "shutdown", "reboot", "> /dev/"]
if any(d in command for d in dangerous):
return "Error: Dangerous command blocked"
r = subprocess.run(command, shell=True, cwd=os.getcwd(),
capture_output=True, text=True, timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
הגדרת כלי אחת. handler אחד. כעת יש למודל ידיים — הוא יכול להריץ כל פקודת shell ולקרוא את הפלט.
המימוש המלא
#!/usr/bin/env python3
"""s01_agent_loop.py - The Agent Loop"""
import os, subprocess
from anthropic import Anthropic
from dotenv import load_dotenv
load_dotenv(override=True)
client = Anthropic()
MODEL = os.environ["MODEL_ID"]
SYSTEM = f"You are a coding agent at {os.getcwd()}. Use bash to solve tasks."
TOOLS = [{
"name": "bash",
"description": "Run a shell command.",
"input_schema": {
"type": "object",
"properties": {"command": {"type": "string"}},
"required": ["command"],
},
}]
def run_bash(command: str) -> str:
dangerous = ["rm -rf /", "sudo", "shutdown", "reboot", "> /dev/"]
if any(d in command for d in dangerous):
return "Error: Dangerous command blocked"
try:
r = subprocess.run(command, shell=True, cwd=os.getcwd(),
capture_output=True, text=True, timeout=120)
out = (r.stdout + r.stderr).strip()
return out[:50000] if out else "(no output)"
except subprocess.TimeoutExpired:
return "Error: Timeout (120s)"
def agent_loop(messages: list):
while True:
response = client.messages.create(
model=MODEL, system=SYSTEM, messages=messages,
tools=TOOLS, max_tokens=8000,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
return
results = []
for block in response.content:
if block.type == "tool_use":
print(f"\033[33m$ {block.input['command']}\033[0m")
output = run_bash(block.input["command"])
print(output[:200])
results.append({"type": "tool_result",
"tool_use_id": block.id,
"content": output})
messages.append({"role": "user", "content": results})
if __name__ == "__main__":
history = []
while True:
try:
query = input("\033[36ms01 >> \033[0m")
except (EOFError, KeyboardInterrupt):
break
if query.strip().lower() in ("q", "exit", ""):
break
history.append({"role": "user", "content": query})
agent_loop(history)
מסקנה מרכזית
הסוד השלם של סוכן קוד מבוסס AI הוא הלולאה הזו. המודל הוא הבינה — הוא מחליט מה לעשות. הקוד הוא הרתמה — הוא נותן למודל כלי ומזין חזרה תוצאות. בסשן הבא (שימוש בכלים), נוסיף עוד כלים מבלי לשנות את הלולאה כלל.
מדריך קוד אינטראקטיבי
1def agent_loop(messages):2 while True:3 response = client.messages.create(4 model=MODEL, system=SYSTEM,5 messages=messages, tools=TOOLS,6 max_tokens=8000,7 )8 messages.append({"role": "assistant",9 "content": response.content})10 11 if response.stop_reason != "tool_use":12 return13 14 results = []15 for block in response.content:16 if block.type == "tool_use":17 output = TOOL_HANDLERS[block.name](**block.input)18 results.append({19 "type": "tool_result",20 "tool_use_id": block.id,21 "content": output,22 })23 messages.append({"role": "user", "content": results})24 שכפלו את הריפו, הריצו python agents/s01_agent_loop.py, ובקשו ממנו ליצור קובץ. עקבו אחר קריאות הכלים בטרמינל.
רמז
הגדירו MODEL_ID=claude-sonnet-4-20250514 בקובץ .env שלכם