Building a Coding Agent with Claude: A 200‑Line Python Walkthrough

This article explains how to construct a functional coding agent by combining a large language model, a bash tool, and a message history loop, showing step‑by‑step code, system prompts, error handling, and a complete execution example.

inShocking
inShocking
inShocking
Building a Coding Agent with Claude: A 200‑Line Python Walkthrough

From chat‑only AI to acting AI

In a traditional chat the model only returns text (e.g., it suggests ls but the user must run the command). An Agent‑style interaction lets the model request tool execution and receive the result, so the AI both decides and acts.

Three core components of an Agent

🧠 Brain (LLM)

The large language model interprets the user request, creates a plan, and decides the next step.

response = client.messages.create(
    model=MODEL,               # Claude / GPT etc.
    system=SYSTEM,             # system prompt (role description)
    messages=messages,         # conversation history (memory)
    tools=TOOLS,               # list of available tools
    max_tokens=8000
)
Analogy: after reading the "job description" and "task history", the brain decides what to do next.

🔧 Tools

Tools give the Agent hands. The example defines a bash tool that can run arbitrary shell commands.

TOOLS = [{
    "name": "bash",
    "description": "Run a shell command.",
    "input_schema": {
        "type": "object",
        "properties": {"command": {"type": "string"}},
        "required": ["command"]
    }
}]

With this tool the Agent can list files, run scripts, install packages, query databases, etc.

A simple blacklist blocks obviously dangerous commands:

dangerous = ["rm -rf /", "sudo", "shutdown", "reboot", "> /dev/"]
if any(d in command for d in dangerous):
    return "Error: Dangerous command blocked"

💬 Memory (Messages)

The Agent’s memory is a growing list of messages. Each tool result is fed back to the model as a user‑role message, following Anthropic’s API design.

history = []
history.append({"role": "user", "content": "帮我查文件"})
history.append({"role": "assistant", "content": response.content})
history.append({"role": "user", "content": tool_results})
Analogy: the intern (AI) says “I’ll check”, performs the check, reports back, then decides the next step.

Agent Loop

The loop repeatedly performs “think → act → feedback → think” until the model signals completion.

def agent_loop(messages: list):
    while True:
        response = client.messages.create(...)
        messages.append({"role": "assistant", "content": response.content})
        if response.stop_reason != "tool_use":
            return  # task finished
        for block in response.content:
            if block.type == "tool_use":
                output = run_bash(block.input["command"])
                # Append tool result as a user‑role message
                messages.append({"role": "user", "content": output})

The loop logic can be described step‑by‑step:

Ask the LLM what to do next.

The LLM replies with a tool_use request.

Execute the requested command and feed the result back as a user message.

Repeat until response.stop_reason is "end_turn".

The two possible stop_reason values are: "tool_use" – the task is not finished; continue the loop. "end_turn" – the LLM considers the task complete; exit the loop.

Full execution example

User input:

帮我统计当前目录有多少个 Python 文件

Round 1:

Message list (including user request) sent to the LLM.

LLM returns a tool_use request: find . -name "*.py" | wc -l.

Command executes, output is 42.

Result 42 is appended to the message list as a user‑role message.

Round 2:

Updated message list sent to the LLM.

LLM returns the final answer:

当前目录共有 42 个 Python 文件。
stop_reason = "end_turn"

; the loop ends.

Final output:

当前目录共有 42 个 Python 文件。

System Prompt (Job Description)

SYSTEM = f"You are a coding agent at {os.getcwd()}. Use bash to solve tasks. Act, don't explain."

The clause Act, don't explain forces the model to execute commands directly instead of describing how to do them.

Error handling

Common API errors are caught and reported with user‑friendly messages:

except PermissionDeniedError:
    print("请求被服务端策略拦截")
except RateLimitError:
    print("请求频率过高,请稍后重试")
except APIConnectionError:
    print("网络连接失败")
except APIStatusError as exc:
    print(f"服务返回错误(status={status_code})")

Data flow diagram (textual)

User Input
 ↓
[message list] history
 ↓
Claude API (LLM)
 ↓
stop_reason == "tool_use" ?
   No → print result, finish
   Yes → run bash command
 ↓
Append result to history
 ↓
Back to Claude API

Component summary (list)

LLM – brain, makes decisions; invoked via client.messages.create(...).

Tools – hands, perform actions; e.g., run_bash(command).

Message history – memory, provides context; stored in a messages list.

Agent Loop – coordinator, drives execution; implemented with a while True loop.

Reference

Implementation is based on the file learn-claude-code/agents/s01_agent_loop_annotated.py.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

PythonLLMPrompt Engineeringtool integrationAI AgentClaudeagent loop
inShocking
Written by

inShocking

Occasional sharing

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.