Phase 0 · Agent 核心循环

v0: Bash就能搞定一切

原创教程·2026/7/22·11 阅读

v0: Bash 就是你需要的一切

终极简化:约 50 行代码,1 个工具,完整的智能体能力。

在构建了 v1、v2 和 v3 之后,一个问题浮现了:智能体的本质是什么?

v0 通过向后退来回答这个问题——剥离一切,直到只剩下核心。

核心洞察

Unix 哲学:一切皆文件,一切皆可管道。Bash 是通往这个世界的网关:

你需要Bash 命令
读取文件cat, head, grep
写入文件echo '...' > file
搜索find, grep, rg
执行python, npm, make
子智能体python v0_bash_agent.py "任务"

最后一行是关键洞察:通过 bash 调用自身实现子智能体。不需要任务工具,不需要智能体注册表——只需要递归。

完整代码

#!/usr/bin/env python
from anthropic import Anthropic
import subprocess, sys, os

client = Anthropic(api_key="your-key", base_url="...")
TOOL = [{
    "name": "bash",
    "description": """执行 shell 命令。模式:
- 读取:cat/grep/find/ls
- 写入:echo '...' > file
- 子智能体:python v0_bash_agent.py '任务描述'""",
    "input_schema": {"type": "object", "properties": {"command": {"type": "string"}}, "required": ["command"]}
}]
SYSTEM = f"CLI 智能体位于 {os.getcwd()}。使用 bash。为复杂任务生成子智能体。"

def chat(prompt, history=[]):
    history.append({"role": "user", "content": prompt})
    while True:
        r = client.messages.create(model="...", system=SYSTEM, messages=history, tools=TOOL, max_tokens=8000)
        history.append({"role": "assistant", "content": r.content})
        if r.stop_reason != "tool_use":
            return "".join(b.text for b in r.content if hasattr(b, "text"))
        results = []
        for b in r.content:
            if b.type == "tool_use":
                out = subprocess.run(b.input["command"], shell=True, capture_output=True, text=True, timeout=300)
                results.append({"type": "tool_result", "tool_use_id": b.id, "content": out.stdout + out.stderr})
        history.append({"role": "user", "content": results})

if __name__ == "__main__":
    if len(sys.argv) > 1:
        print(chat(sys.argv[1]))  # 子智能体模式
    else:
        h = []
        while (q := input(">> ")) not in ("q", ""):
            print(chat(q, h))

这就是整个智能体。约 50 行代码。

子智能体如何工作

主智能体
  └─ bash: python v0_bash_agent.py "分析架构"
       └─ 子智能体(独立进程,全新历史)
            ├─ bash: find . -name "*.py"
            ├─ bash: cat src/main.py
            └─ 通过 stdout 返回摘要

进程隔离 = 上下文隔离

  • 子进程有自己的 history=[]
  • 父进程捕获 stdout 作为工具结果
  • 递归调用支持无限嵌套

v0 牺牲了什么

功能v0v3
智能体类型explore/code/plan
工具过滤白名单
进度显示纯 stdout内联更新
代码复杂度约 50 行约 450 行

v0 证明了什么

复杂能力源于简单规则:

  1. 一个工具就够了 — Bash 是通往一切的网关
  2. 递归 = 层级 — 自调用实现子智能体
  3. 进程 = 隔离 — 操作系统提供上下文分离
  4. 提示 = 约束 — 指令塑造行为

核心模式永远不变:

while True:
    response = model(messages, tools)
    if response.stop_reason != "tool_use":
        return response.text
    results = execute(response.tool_calls)
    messages.append(results)

其他一切——待办事项、子智能体、权限——都是围绕这个循环的细化。


学习笔记

重点关注什么

v0_bash_agent.py 作为一个小型可运行的编码智能体模型来阅读。文件虽小,但已经包含了核心架构:

LLM + 一个工具 + 反馈循环 = 智能体

不要一开始就记住每一行。首先理解五个移动部分:

  1. clientMODEL
  2. TOOL
  3. SYSTEM
  4. chat()
  5. __main__ 代码块

这五个部分足以解释整个文件。

1. 客户端和模型

在顶部,程序加载环境变量并创建 Anthropic 客户端:

load_dotenv()
client = Anthropic(
    api_key=os.getenv("ANTHROPIC_API_KEY"),
    base_url=os.getenv("ANTHROPIC_BASE_URL")
)
MODEL = os.getenv("MODEL_NAME", "claude-sonnet-4-20250514")

这还不是智能体。这只是到模型 API 的连接。

智能体行为来自主机程序发送给模型的内容:

系统提示 + 消息 + 工具

2. 唯一的工具:bash

v0 只暴露一个工具:

TOOL = [{
    "name": "bash",
    "description": """执行 shell 命令。常见模式:
- 读取:cat/head/tail, grep/find/rg/ls, wc -l
- 写入:echo '内容' > file, sed -i 's/旧/新/g' file
- 子智能体:python v0_bash_agent.py '任务描述' ...""",
    ...
}]

工具模式告诉模型:

  • 工具名称是 bash
  • 输入必须包含一个 command 字符串
  • 允许常见的命令模式
  • 智能体可以将自己作为子智能体启动

模型执行 shell 命令。它只请求工具调用。 Python 主机执行命令。

模型:请运行 {"command": "find . -name '*.py'"}
主机:运行 subprocess.run(...)
主机:将 stdout/stderr 作为 tool_result 返回

3. 系统提示教授操作规则

SYSTEM 告诉模型在这个 CLI 环境中如何表现:

SYSTEM = f"""你是位于 {os.getcwd()} 的 CLI 智能体。使用 bash 命令解决问题。

规则:
- 优先使用工具而非文字。先行动,事后简要解释。
- 读取文件:cat, grep, find, rg, ls, head, tail
- 写入文件:echo '...' > file, sed -i, 或 cat << 'EOF' > file
- 子智能体:对于复杂的子任务,生成一个子智能体...
"""

这很重要,因为 bash 的范围非常广。提示将这个广泛的工具窄化为有用的编码智能体行为。

可以这样理解:

TOOL 说明模型可以调用什么。
SYSTEM 说明如何以及何时使用它。

4. 整个智能体就是 chat()

chat() 函数是智能体循环:

def chat(prompt, history=None):
    history.append({"role": "user", "content": prompt})

    while True:
        response = client.messages.create(...)
        history.append({"role": "assistant", "content": content})

        if response.stop_reason != "tool_use":
            return final_text

        results = execute_tool_calls(...)
        history.append({"role": "user", "content": results})

这个循环只有一个工作:不断给模型观察结果,直到模型停止请求工具。

5. 为什么工具结果必须返回 history

这一行是反馈循环:

history.append({"role": "user", "content": results})

如果程序只打印命令输出到终端,人类会看到它,但模型不会。

对于智能体,每个动作都需要一个观察:

思考 → 工具调用 → 工具结果 → 下一个思考

这就是为什么工具结果被追加到 history 并发回给模型。

6. 交互模式 vs 子智能体模式

在底部,文件有两种模式:

if len(sys.argv) > 1:
    print(chat(sys.argv[1]))
else:
    history = []
    while True:
        query = input(">> ")
        print(chat(query, history))

交互模式保持一个共享的历史:

人类 → 智能体 → 人类 → 智能体

子智能体模式运行一个任务然后退出:

python v0_bash_agent.py "探索 src/ 并摘要"

这使得递归成为可能。

7. v0 中的子智能体如何工作

v0 没有 Task 工具,也没有智能体注册表。子智能体之所以工作,是因为唯一的工具足够强大,可以启动另一个进程:

python v0_bash_agent.py "分析架构"

那个子进程有一个全新的 history。父进程只接收子进程的 stdout 作为 bash 工具结果。

父进程历史
  -> bash: python v0_bash_agent.py "分析架构"
       -> 子进程历史开始为空
       -> 子进程用 bash 探索
       -> 子进程打印最终摘要
  -> 父进程接收摘要作为 tool_result

这几乎免费地提供了上下文隔离:

进程隔离 = 上下文隔离

8. 为什么 Bash 对 v0 来说足够了

Bash 是通往许多能力的网关:

需要Bash 模式
列出文件find, ls, rg --files
读取文件cat, head, sed -n
搜索grep, rg
运行测试pytest, npm test, make test
编辑文件shell 重定向, sed, heredocs
生成子智能体python v0_bash_agent.py "任务"

这就是 v0 的教训:一个单一的通用工具可以创造出令人惊讶的强大智能体行为。

9. v0 还没有教什么

v0 是有意地不安全和最小化的。不要把它误认为是生产环境的框架。

缺失的部分包括:

  • 命令审批
  • 文件系统沙箱
  • 网络限制
  • 结构化文件编辑工具
  • 待办事项跟踪
  • 类型化子智能体
  • 技能加载
  • 健壮的错误处理

这些是后来添加的,因为 v0 的重点是看到最小可能的智能体循环。

10. 安全边界

有风险的行是:

subprocess.run(cmd, shell=True, ...)

在无限制的 shell 访问下,模型可以请求破坏性的命令。真正的编码智能体添加了沙箱、审批门、命令策略和审计日志。

对于学习来说,关键的收获不是"给智能体 shell 访问权限"。收获是:

智能体 = 模型决策 + 主机执行的工具 + 返回的观察结果

学习检查

阅读代码后,确保你能回答:

  • 单个工具在哪里定义?
  • 工具模式需要什么作为输入?
  • 程序在哪里调用模型?
  • 工具结果在哪里追加回历史?
  • 为什么 stop_reason != "tool_use" 结束循环?
  • python v0_bash_agent.py "任务" 如何创建子智能体?
  • v0 缺少哪些安全控制?

完整源码

#!/usr/bin/env python
"""
v0_bash_agent.py - Mini Claude Code:Bash 就是你需要的一切(核心约 50 行)

核心理念:"Bash 就是你需要的一切"
====================================
这是编码智能体的终极简化。在构建了 v1-v3 之后,
我们问:智能体的本质是什么?

答案是:一个工具(bash)+ 一个循环 = 完整的智能体能力。

为什么 Bash 足够:
----------------
Unix 哲学说一切皆文件,一切皆可管道。
Bash 是通往这个世界的网关:

    | 你需要        | Bash 命令                             |
    |---------------|----------------------------------------|
    | 读取文件      | cat, head, tail, grep                  |
    | 写入文件      | echo '...' > file, cat << 'EOF' > file |
    | 搜索          | find, grep, rg, ls                     |
    | 执行          | python, npm, make, 任何命令            |
    | **子智能体**  | python v0_bash_agent.py "任务"         |

最后一行是关键洞察:通过 bash 调用自身实现子智能体!
不需要 Task 工具,不需要智能体注册表——只需要通过进程生成的递归。

子智能体如何工作:
----------------
    主智能体
      |-- bash: python v0_bash_agent.py "分析架构"
           |-- 子智能体(独立进程,全新历史)
                |-- bash: find . -name "*.py"
                |-- bash: cat src/main.py
                |-- 通过 stdout 返回摘要

进程隔离 = 上下文隔离:
- 子进程有自己的 history=[]
- 父进程捕获 stdout 作为工具结果
- 递归调用支持无限嵌套

用法:
    # 交互模式
    python v0_bash_agent.py

    # 子智能体模式(由父智能体调用或直接调用)
    python v0_bash_agent.py "探索 src/ 并摘要"
"""

from anthropic import Anthropic
from dotenv import load_dotenv
import subprocess
import sys
import os

# 从 .env 文件加载环境变量
load_dotenv()

# 使用来自环境的凭证初始化 API 客户端
client = Anthropic(
    api_key=os.getenv("ANTHROPIC_API_KEY"),
    base_url=os.getenv("ANTHROPIC_BASE_URL")
)
MODEL = os.getenv("MODEL_NAME", "claude-sonnet-4-20250514")

# 做一切的那一个工具
# 注意描述如何教模型常见模式以及如何生成子智能体
TOOL = [{
    "name": "bash",
    "description": """执行 shell 命令。常见模式:
- 读取:cat/head/tail, grep/find/rg/ls, wc -l
- 写入:echo '内容' > file, sed -i 's/旧/新/g' file
- 子智能体:python v0_bash_agent.py '任务描述'(生成隔离的智能体,返回摘要)""",
    "input_schema": {
        "type": "object",
        "properties": {"command": {"type": "string"}},
        "required": ["command"]
    }
}]

# 系统提示教模型如何有效地使用 bash
# 注意子智能体指导——这就是我们如何获得分层任务分解
SYSTEM = f"""你是位于 {os.getcwd()} 的 CLI 智能体。使用 bash 命令解决问题。

规则:
- 优先使用工具而非文字。先行动,事后简要解释。
- 读取文件:cat, grep, find, rg, ls, head, tail
- 写入文件:echo '...' > file, sed -i, 或 cat << 'EOF' > file
- 子智能体:对于复杂的子任务,生成一个子智能体以保持上下文干净:
  python v0_bash_agent.py "探索 src/ 并摘要架构"

何时使用子智能体:
- 任务需要读取许多文件(隔离探索)
- 任务是独立且自包含的
- 你想避免用中间细节污染当前对话

子智能体隔离运行,只返回其最终摘要。"""


def chat(prompt, history=None):
    """
    完整的智能体循环在一个函数中。

    这是所有编码智能体共享的核心模式:
        while not done:
            response = model(messages, tools)
            if no tool calls: return
            execute tools, append results

    参数:
        prompt: 用户的请求
        history: 对话历史(可变,在交互模式下跨调用共享)

    返回:
        模型的最终文本响应
    """
    if history is None:
        history = []

    history.append({"role": "user", "content": prompt})

    while True:
        # 步骤 1:用工具调用模型
        response = client.messages.create(
            model=MODEL,
            system=SYSTEM,
            messages=history,
            tools=TOOL,
            max_tokens=8000
        )

        # 步骤 2:构建助手消息内容(保留文本和 tool_use 块)
        content = []
        for block in response.content:
            if hasattr(block, "text"):
                content.append({"type": "text", "text": block.text})
            elif block.type == "tool_use":
                content.append({
                    "type": "tool_use",
                    "id": block.id,
                    "name": block.name,
                    "input": block.input
                })
        history.append({"role": "assistant", "content": content})

        # 步骤 3:如果模型没有调用工具,我们完成了
        if response.stop_reason != "tool_use":
            return "".join(b.text for b in response.content if hasattr(b, "text"))

        # 步骤 4:执行每个工具调用并收集结果
        results = []
        for block in response.content:
            if block.type == "tool_use":
                cmd = block.input["command"]
                print(f"\033[33m$ {cmd}\033[0m")  # 命令用黄色

                try:
                    out = subprocess.run(
                        cmd,
                        shell=True,
                        capture_output=True,
                        text=True,
                        timeout=300,
                        cwd=os.getcwd()
                    )
                    output = out.stdout + out.stderr
                except subprocess.TimeoutExpired:
                    output = "(300秒后超时)"

                print(output or "(空)")
                results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": output[:50000]  # 截断非常长的输出
                })

        # 步骤 5:追加结果并继续循环
        history.append({"role": "user", "content": results})


if __name__ == "__main__":
    if len(sys.argv) > 1:
        # 子智能体模式:执行任务并打印结果
        # 这就是父智能体如何通过 bash 生成子进程
        print(chat(sys.argv[1]))
    else:
        # 交互 REPL 模式
        history = []
        while True:
            try:
                query = input("\033[36m>> \033[0m")  # 青色提示
            except (EOFError, KeyboardInterrupt):
                break
            if query in ("q", "exit", ""):
                break
            print(chat(query, history))

Bash 就是你需要的一切。

← 返回 README

评论 (0)

暂无评论,快来抢沙发吧!

91学AI

© 2026 91学AI · 按岗位学 AI 与大数据. All rights reserved.