python
from anthropic import Anthropic
import json, subprocess
client = Anthropic()
# 1) 도구 스펙 — 모델에게 사용 가능한 능력을 알려준다
# 1) Tool specs — declare capabilities to the model
TOOLS = [{
"name": "run_pytest",
"description": "pytest 를 주어진 경로로 실행하고 요약을 반환",
"input_schema": {"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"]}
}, {
"name": "read_file",
"description": "파일을 읽어서 내용 반환 (max 2000 lines)",
"input_schema": {"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"]}
}]
def execute_tool(name, args):
# 도구 실제 실행 — 여기서 권한, 로깅, 타임아웃을 건다
# Actual tool execution — permission, logging, timeout here
if name == "run_pytest":
r = subprocess.run(["uv", "run", "pytest", args["path"],
"-x", "-q"], capture_output=True, text=True, timeout=120)
return f"exit={r.returncode}\n{r.stdout[-2000:]}"
if name == "read_file":
with open(args["path"]) as f:
return "".join(f.readlines()[:2000])
def run(task: str, max_steps: int = 15):
messages = [{"role": "user", "content": task}]
for step in range(max_steps): # 종료조건: 스텝 한도
resp = client.messages.create(
model="claude-opus-4-7",
max_tokens=4096,
tools=TOOLS,
messages=messages,
)
messages.append({"role": "assistant", "content": resp.content})
if resp.stop_reason == "end_turn": # 종료조건: 모델 완료
return resp
# 도구 호출 처리
tool_results = []
for block in resp.content:
if block.type == "tool_use":
out = execute_tool(block.name, block.input)
tool_results.append({"type": "tool_result",
"tool_use_id": block.id,
"content": out})
messages.append({"role": "user", "content": tool_results})
raise RuntimeError("max_steps exceeded") # 종료조건: 안전장치