Bash is All You Need:极简Agent的核心思想
前言:回归本质
在构建复杂的 AI 编码助手时,我们往往会不断增加功能:文件读取工具、写入工具、搜索工具、编辑工具、子代理系统、任务调度器、权限管理……代码从 50 行增长到 500 行,再到 5000 行。
但如果我们停下来问一个问题:编码代理的本质到底是什么?
答案可能出乎意料的简单。
一、核心思想:一个工具,一切皆可
Unix 哲学的启示
Unix 哲学有两条核心原则:
- 一切皆文件 —— 无论是文件、目录、设备,还是进程,在 Unix 中都以文件形式存在
- 一切皆可管道 —— 任何程序的输出都可以成为另一个程序的输入
这意味着什么?
当你有一个 bash 终端时,你拥有了通往整个操作系统的入口:
| 操作类型 | Bash 命令 |
|---|---|
| 读取文件 | cat, head, tail, grep |
| 写入文件 | echo ‘…’ > file, sed -i |
| 搜索内容 | grep, find, rg, ls |
| 执行程序 | python, npm, make, go |
| 派生子进程 | python bash_agent.py “task” |
这就是核心洞察:不需要多个专用工具,一个 bash 工具就足够了。
表层 vs 深层
| 表层视角(多个工具) | 深层视角(一个工具) |
|---|---|
| read_file | cat file.txt |
| write_file | echo “content” > file.txt |
| edit_file | sed -i ‘s/old/new/g’ file.txt |
| search | grep -r “pattern” dir/ |
| execute | python script.py |
表面上是不同的功能,深层上都是 bash 命令的变体。
二、递归:层级能力的涌现
最关键的一行命令
python bash_agent.py "task description"
这看起来只是一个普通的命令,但它隐藏着一个深刻的洞察:
当代理通过 bash 调用自身时,子代理就诞生了。
进程隔离 = 上下文隔离
想象一下这个调用链:
主进程 (Parent)
│
│ bash: python bash_agent.py "分析项目架构"
▼
子进程 (Child 1) - 全新的 history=[]
│
│ bash: find . -name "*.py"
│ bash: grep -r "class.*Model" src/
▼
子进程 (Child 2) - 全新的 history=[]
│
│ bash: python bash_agent.py "总结每个模块的职责"
│
▼
... 无限嵌套可能
每个子进程都拥有:
- 独立的内存空间 ——
history = [],不会污染父进程的上下文 - 独立的执行环境 —— 工具调用的结果只影响当前进程
- 自然的隔离边界 —— 操作系统提供的进程隔离机制
递归的力量
递归在计算机科学中有一个美妙的特性:它天然地产生层级结构。
def agent(task):
if is_simple(task):
return solve_directly(task)
else:
subtask1, subtask2 = decompose(task)
result1 = bash_agent(subtask1) # 调用自己!
result2 = bash_agent(subtask2) # 调用自己!
return combine(result1, result2)
这就是任务分解的优雅实现:
- 不需要复杂的任务调度器
- 不需要子进程池管理
- 不需要父子进程通信协议
- 操作系统已经为你做好了一切
信息传递的唯一通道
在 Bash Agent 中,所有信息都通过 stdout 传递给模型:
─────────────────────────────────────────────────────────
模型
history: [消息历史]
工具调用: bash: ls -la
──────────────▶ stdout 捕获 ◀─────────────
工具结果:
drwxr-xr-x user .git/
-rw-r--r-- user main.py
-rw-r--r-- user README.md
─────────────────────────────────────────────────────────
无论执行什么命令(ls, cat, grep, find…),结果都通过 stdout 返回给模型。这带来两个好处:
- 统一的接口 —— 所有操作的输出格式一致,模型容易理解
- 自然的过滤 —— stderr 可以单独处理,只将 stdout 作为主要信息源
三、四大法则
法则一:一个工具足够
Bash 是通往一切操作的统一接口。
不要因为操作看起来不同就创建不同的工具。文件读写、代码搜索、程序执行、网络请求——本质上都是 bash 命令。
统一接口的优势:
- 认知负担低 —— 只需理解一种工具模式
- 实现简单 —— 只需维护一个工具实现
- 扩展性强 —— 任何新的操作类型都可以通过新命令实现
法则二:递归 = 层级
自我调用自然产生树形结构。
递归是计算机科学中最强大的抽象之一。通过递归,我们可以:
- 将复杂任务分解为子任务
- 并行处理独立的子任务
- 形成自然的调用层次
无需显式的子代理系统,递归调用本身就实现了子代理。
法则三:进程 = 隔离
操作系统提供的隔离机制免费可用。
不要自己实现复杂的上下文管理。操作系统的进程隔离已经完美解决了:
- 独立的内存空间
- 独立的文件描述符
- 独立的信号处理
你只需要:
subprocess.run(command, capture_output=True)
法则四:提示词 = 约束
通过提示词塑造代理的行为。
代码只是执行框架,真正的智能来自提示词:
你是一个 CLI 代理。使用 bash 命令解决问题。
规则:
- 优先使用工具而非长篇大论
- 对于复杂任务,派生子代理保持上下文清晰
python bash_agent.py "探索 src/ 并总结架构"
何时使用子代理:
- 任务需要读取多个文件
- 任务独立且自包含
- 避免用中间细节污染当前对话
提示词定义了代理的"性格"和"行为模式"。
四、核心循环模式
无论代理多么复杂,其核心永远不变:
while True:
# 1. 让模型思考并决定
response = model(messages, tools)
# 2. 如果模型决定不再调用工具,任务完成
if response.stop_reason != "tool_use":
return response.text
# 3. 执行工具调用
results = execute(response.tool_calls)
# 4. 将结果反馈给模型,继续循环
messages.append(results)
这个循环的精髓在于:
- 模型是决策者 —— 决定调用哪个工具、以什么顺序、何时停止
- 代码是执行者 —— 提供工具并执行模型的决定
- 循环是催化剂 —— 让模型可以多轮思考、多步执行
其他一切——待办事项、子代理、权限、进度显示——都是围绕这个循环的装饰。
五、涌现式设计
复杂能力从简单规则中涌现。
我们不需要显式设计子代理系统。当我们提供:
- 一个 bash 工具
- 允许递归调用
- 通过提示词指导何时递归
子代理能力就自然涌现了。
这就是涌现式设计的魅力:简单规则 + 递归 + 约束 → 复杂行为。
六、完整代码示例(bash_agent.py)
#!/usr/bin/env python
"""
Bash is All You Need - 极简编码代理
核心思想:
1. 一个 bash 工具包罗万象
2. 通过递归调用实现子代理
3. 利用操作系统进程隔离
4. 提示词定义行为约束
"""
from zai import ZhipuAiClient
from dotenv import load_dotenv
import subprocess
import sys
import os
# 加载环境变量
load_dotenv()
# 初始化客户端(当前使用的智谱的模型,可以替换其他模型)
client = ZhipuAiClient(
api_key=os.getenv("ANTHROPIC_API_KEY"),
)
MODEL = os.getenv("MODEL_NAME", "glm-4.7")
# 唯一的工具:bash
TOOL = [{
"type": "function",
"function": {
"name": "bash",
"description": """执行 shell 命令。常见用法:
- 读取:cat/head/tail, grep/find/rg/ls
- 写入:echo '...' > file, sed -i 's/old/new/g' file
- 子代理:python bash_agent.py 'task description' (派生隔离代理,返回摘要)""",
"parameters": {
"type": "object",
"properties": {"command": {"type": "string"}},
"required": ["command"]
}
}
}]
# 系统提示词
SYSTEM = f"""你是位于 {os.getcwd()} 的 CLI 代理。使用 bash 命令解决问题。
规则:
- 优先使用工具而非长篇大论。先执行,简短解释在后
- 读取文件:cat, grep, find, rg, ls, head, tail
- 写入文件:echo '...' > file, sed -i, 或 cat << 'EOF' > file
- 子代理:对于复杂的子任务,派生子代理保持上下文清晰:
python 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 = []
# 初始化:添加系统提示词
if len(history) == 0:
history.append({"role": "system", "content": SYSTEM})
history.append({"role": "user", "content": prompt})
# 主循环
while True:
# 调用模型
response = client.chat.completions.create(
model=MODEL,
messages=history,
tools=TOOL,
max_tokens=8000,
)
message = response.choices[0].message
text_content = message.content or ""
tool_calls = message.tool_calls or []
# 构建助手消息
assistant_msg = {"role": "assistant", "content": text_content}
if tool_calls:
assistant_msg["tool_calls"] = tool_calls
history.append(assistant_msg)
# 如果没有工具调用,完成
if not tool_calls or response.choices[0].finish_reason != "tool_calls":
return text_content
# 执行工具调用
for tool_call in tool_calls:
cmd = tool_call.function.arguments
print(f"$ {cmd}")
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 = "(timeout after 300s)"
print(output or "(empty)")
history.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": output[:50000]
})
def main():
"""交互式 REPL 模式"""
history = []
while True:
try:
query = input(">> ")
except (EOFError, KeyboardInterrupt):
break
if query in ("q", "exit", ""):
break
print(chat(query, history))
if __name__ == "__main__":
if len(sys.argv) > 1:
# 子代理模式
print(chat(sys.argv[1]))
else:
# 交互模式
main()
七、总结
什么是 Bash Agent?
约 50 行核心代码,1 个 bash 工具,完整的编码代理能力。
核心思想
| 概念 | 本质 |
|---|---|
| 一个工具 | Bash 是通往一切操作的统一入口 |
| 递归 | 自我调用自然产生层级结构和子代理 |
| 进程隔离 | 操作系统提供的免费上下文分离 |
| 提示词 | 通过约束塑造代理的行为 |
精髓
复杂能力从简单规则中涌现。
你不需要:
- 多个专用工具(read_file, write_file, edit_file…
- 复杂的子代理系统(Agent Registry, Task Pool…)
- 精细的上下文管理(Context Isolation, Memory Pool…)
你只需要:
- 一个 bash 工具
- 允许递归调用
- 核心代理循环
剩下的,交给模型和操作系统。
Bash is All You Need。
更多推荐



所有评论(0)