Learn Claude Code
s01

The Agent Loop

Tools & Execution

Bash is All You Need

95 LOC1 toolsSingle-tool agent loop
Overview

Claude Code 内部是一个不断重复的循环。每跑一轮就跟模型对话一次:把当前的对话内容发过去,模型回一句话或者发起一次工具调用,工具的结果再回到对话里,进入下一轮。什么时候停由模型自己决定,代码不判断你的任务进行到哪一步。

Running example
Research project
耐心资本对企业 ESG 表现的实证项目 phase 2 收尾时
What happens in this section

对 docs/01_实证研究设计_耐心资本_ESG.md、CLAUDE.md、06_结果输出/tables_tsv/ 下所有 csv 表头做一次术语一致性审计——确认"耐心资本""稳定型机构投资者""关系型债权""管理者短视主义""华证 ESG 评级""KZ 指数"等 14 个保护词在跨章节落地时没有被改写成"长期资本""长期机构""长期负债"这些近义表述。

[ s01 ] s02 > s03 > s04 > s05 > s06 | s07 > s08 > s09 > s10 > s11 > s12

"One loop & Bash is all you need" -- one tool + one loop = an agent.

Harness layer: The loop -- the model's first connection to the real world.

Problem

A language model can reason about code, but it can't touch the real world -- can't read files, run tests, or check errors. Without a loop, every tool call requires you to manually copy-paste results back. You become the loop.

Solution

+--------+      +-------+      +---------+
|  User  | ---> |  LLM  | ---> |  Tool   |
| prompt |      |       |      | execute |
+--------+      +---+---+      +----+----+
                    ^                |
                    |   tool_result  |
                    +----------------+
                    (loop until stop_reason != "tool_use")

One exit condition controls the entire flow. The loop runs until the model stops calling tools.

How It Works

  1. User prompt becomes the first message.
messages.append({"role": "user", "content": query})
  1. Send messages + tool definitions to the LLM.
response = client.messages.create(
    model=MODEL, system=SYSTEM, messages=messages,
    tools=TOOLS, max_tokens=8000,
)
  1. Append the assistant response. Check stop_reason -- if the model didn't call a tool, we're done.
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
    return
  1. Execute each tool call, collect results, append as a user message. Loop back to step 2.
results = []
for block in response.content:
    if block.type == "tool_use":
        output = run_bash(block.input["command"])
        results.append({
            "type": "tool_result",
            "tool_use_id": block.id,
            "content": output,
        })
messages.append({"role": "user", "content": results})

Assembled into one function:

def agent_loop(query):
    messages = [{"role": "user", "content": query}]
    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 = run_bash(block.input["command"])
                results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": output,
                })
        messages.append({"role": "user", "content": results})

That's the entire agent in under 30 lines. Everything else in this course layers on top -- without changing the loop.

What Changed

ComponentBeforeAfter
Agent loop(none)while True + stop_reason
Tools(none)bash (one tool)
Messages(none)Accumulating list
Control flow(none)stop_reason != "tool_use"

Try It

cd claude-code-for-researchers
python agents/s01_agent_loop.py
  1. Read 02_变量字典/测算方法说明.md and list all 8 PC frameworks (A1/A2/A3/A4/B1/B2/C/D)
  2. Audit "耐心资本" usage in 07_论文写作/ against the 14-term protected list in CLAUDE.md §2
  3. Find every occurrence of "长期资本" or "长线资金" in 07_论文写作/ and report file:line
  4. Run stata-mcp codebook on 04_中间数据/main_panel.dta and report obs / firms / time span