摘要: 随着大模型能力的跃升,终端 AI 助手已经从“单轮命令生成器”进化为“具备自主执行能力的智能体(Agent)”。凭借超长上下文(200K+)和极强的代码逻辑推理能力,Claude 3.5/3.7 系列已成为目前公认的最强编程大模型。本文将深入拆解 Anthropic 官方推出的 Claude Code 核心架构,重点探讨 MCP(模型上下文协议)如何打破数据孤岛,并带你手写一个具备自主读取文件、执行终端命令闭环的企业级 Claude CLI 工具。


一、 背景与演进:从 Codex 的“单向输出”到 Claude 的“双向交互”

在上一代终端 AI 助手(如基于 OpenAI Codex 的工具)中,工作流通常是线性的:用户输入需求 -> LLM 生成命令 -> 用户确认执行。这种模式对于简单的单行命令(如 tarfind)非常有效,但在面对复杂的项目重构时就显得力不从心。

进入 Claude 时代后,编程助手的范式发生了根本性转变:

  1. 从“补全代码”到“解决问题”:Claude 不仅能生成代码,更能通过工具调用(Tool Use)主动读取你的项目文件、查看 Git 状态、运行测试用例,然后根据报错信息自主修改代码。

  2. 200K 超大上下文窗口:传统的 Codex CLI 只能带上几百行代码作为上下文,而 Claude 可以毫不费力地将整个项目的核心源码作为 Prompt 吞入,实现“全局级”的重构。

  3. MCP(Model Context Protocol)的诞生:由 Anthropic 提出的开源协议 MCP,允许 Claude CLI 像插拔 U盘一样,动态连接本地数据库、GitHub 仓库、云端服务,彻底解决了 AI 缺乏本地实时上下文的痛点。

官方推出的 Claude Code 便是这一理念的集大成者,它不仅是一个 CLI,更是一个驻留在你终端里的初级资深工程师。


二、 Claude Code 的核心架构解析:Agentic 循环

与传统的命令生成器不同,一个合格的 Claude CLI 必须实现 Agentic Loop(智能体循环)。它的底层架构包含以下核心模块:

2.1 ReAct 思考引擎 (Reasoning and Acting)

当用户下达复杂指令(例如:“分析 src 目录下的报错并修复”)时,Claude 不会直接给出最终代码,而是通过隐式的 <thinking> 标签进行逻辑推演:

  1. 思考:我需要先列出 src 目录的文件。

  2. 行动:调用 list_files 工具。

  3. 观察:获取文件列表,发现 main.pyutils.py

  4. 思考:我需要读取报错信息和 main.py 源码。 ...以此循环,直到任务完成。

2.2 工具调用层 (Tool Use / Function Calling)

这是 Claude 改变物理世界的“手和眼”。CLI 需要向大模型注册本地能力(Tools),如:

  • bash_run: 执行终端命令。

  • read_file_content: 读取本地文件。

  • write_file_content: 修改并覆盖文件。

2.3 MCP 客户端 (Model Context Protocol Client)

如果本地文件读写是“基础工具”,那么 MCP 就是“高级外设”。通过内置 MCP 客户端,Claude CLI 可以直接与本地启动的 MCP Server(如 SQLite Server、PostgreSQL Server)通信,实现诸如“帮我查一下生产数据库里的慢查询并优化 SQL”的高阶任务。


三、 实战:从零手写一个企业级 Claude Agent CLI

为了深刻理解其工作原理,我们将使用 Python 手写一个支持自主执行终端命令带有反思机制(Agentic Loop)、并完美结合 Rich 终端 UI 的 Claude CLI 工具,我们命名为 claude-terminal

3.1 环境准备与技术栈

  • 语言: Python 3.10+

  • 核心库:

    • anthropic:Anthropic 官方 SDK,完美支持 Tool Use 功能。

    • click:构建命令行应用的最佳拍档。

    • rich:用于在终端中输出极为绚丽的 Markdown、代码高亮和 Live 状态更新。

安装依赖:

Bash

pip install click rich anthropic

3.2 核心代码实现

新建一个名为 claude_cli.py 的文件。

第 1 步:引入依赖与基础配置

我们需要处理 API Key,并初始化 Anthropic 客户端。

Python

import os
import sys
import json
import subprocess
import click
from rich.console import Console
from rich.markdown import Markdown
from rich.panel import Panel
from rich.prompt import Confirm
from anthropic import Anthropic

console = Console()

# 从环境变量中读取 API Key
API_KEY = os.getenv("ANTHROPIC_API_KEY")
if not API_KEY:
    console.print("[bold red]错误: 未设置环境变量 ANTHROPIC_API_KEY[/bold red]")
    sys.exit(1)

client = Anthropic(api_key=API_KEY)
MODEL = "claude-3-5-sonnet-latest" # 默认使用最强编程模型
第 2 步:定义本地工具 (Tools)

为了让 Claude 具备“Agent”能力,我们需要定义它能使用的工具。这里我们实现一个最核心的工具:execute_bash_command

Python

# 定义工具 Schema 供 Claude 理解
TOOLS = [
    {
        "name": "execute_bash_command",
        "description": "在用户的本地终端执行 Bash 命令。可以用来列出目录、读取文件、运行测试甚至执行脚本。",
        "input_schema": {
            "type": "object",
            "properties": {
                "command": {
                    "type": "string",
                    "description": "要执行的终端命令"
                },
                "reasoning": {
                    "type": "string",
                    "description": "为什么要执行这个命令(用于展示给用户看)"
                }
            },
            "required": ["command", "reasoning"]
        }
    }
]

def execute_bash_command(command: str) -> str:
    """本地实际执行命令的 Python 函数"""
    try:
        # 使用 subprocess 执行命令并捕获输出
        result = subprocess.run(
            command, 
            shell=True, 
            capture_output=True, 
            text=True, 
            timeout=30 # 防止死循环或阻塞
        )
        if result.returncode == 0:
            return result.stdout if result.stdout else "命令执行成功,无输出。"
        else:
            return f"执行失败 (Exit Code {result.returncode}):\n{result.stderr}"
    except subprocess.TimeoutExpired:
        return "错误: 命令执行超时。"
    except Exception as e:
        return f"发生未知错误: {str(e)}"
第 3 步:构建 Agentic 智能体循环 (The Loop)

这是与普通对话脚本最大的区别。当 Claude 决定调用工具时,我们需要拦截请求,在本地执行,然后把结果塞回给 Claude,让它继续判断是否完成了任务。

Python

def run_agentic_loop(prompt: str):
    """运行智能体循环"""
    messages = [{"role": "user", "content": prompt}]
    
    system_prompt = """你是一个顶级的全栈开发专家和系统管理员,运行在用户的终端中。
你有能力通过 `execute_bash_command` 工具执行命令、读取文件、修改代码。
工作原则:
1. 分析问题前,请使用 <thinking> 标签包裹你的思考过程。
2. 尽可能自主完成任务,通过执行命令来探索环境。
3. 当你确信任务完成后,向用户给出最终总结。"""

    console.print("\n[bold cyan]🚀 Claude Agent 已启动,正在分析任务...[/bold cyan]\n")

    while True:
        try:
            # 向 Claude 发送当前的所有对话历史和可用工具
            response = client.messages.create(
                model=MODEL,
                max_tokens=4096,
                system=system_prompt,
                messages=messages,
                tools=TOOLS
            )
            
            # 将 Claude 的回复追加到历史记录中
            messages.append({"role": "assistant", "content": response.content})

            # 遍历 Claude 的输出块
            tool_calls = []
            for block in response.content:
                if block.type == "text":
                    # 打印文本回复(通常是思考过程或最终总结)
                    console.print(Markdown(block.text))
                elif block.type == "tool_use":
                    # 记录下需要调用的工具
                    tool_calls.append(block)

            # 如果没有工具调用,说明任务完成,跳出循环
            if not tool_calls:
                break
                
            # 处理工具调用
            tool_results = []
            for tool_call in tool_calls:
                if tool_call.name == "execute_bash_command":
                    cmd = tool_call.input["command"]
                    reason = tool_call.input["reasoning"]
                    
                    console.print(f"\n[bold yellow]🛠️  Claude 提议执行命令:[/bold yellow] {reason}")
                    console.print(Panel(cmd, border_style="yellow", style="bold white on black"))
                    
                    # 【安全沙箱】强制人工确认
                    if Confirm.ask("[bold red]⚠️ 允许执行此命令吗?[/bold red]", default=True):
                        console.print("[dim]执行中...[/dim]")
                        result_text = execute_bash_command(cmd)
                        console.print(f"[dim green]执行完毕,结果已反馈给 Claude。[/dim green]\n")
                    else:
                        result_text = "用户拒绝了执行此命令。请尝试其他方法或直接回答。"
                        console.print("[dim red]已拒绝执行。[/dim red]\n")
                    
                    # 将执行结果打包成 Anthropic 规定的 tool_result 格式
                    tool_results.append({
                        "type": "tool_result",
                        "tool_use_id": tool_call.id,
                        "content": result_text
                    })
            
            # 将执行结果作为 user 角色反馈给 Claude,触发下一轮循环
            if tool_results:
                messages.append({"role": "user", "content": tool_results})

        except Exception as e:
            console.print(f"\n[bold red]API 请求或执行失败: {str(e)}[/bold red]")
            sys.exit(1)
第 4 步:CLI 路由与入口封装

使用 click 提供命令行体验。

Python

@click.command()
@click.argument('prompt', nargs=-1)
def cli(prompt):
    """
    终端 Agentic 编程助手 (Claude Code CLI)
    """
    input_prompt = " ".join(prompt)
    
    if not sys.stdin.isatty():
        piped_data = sys.stdin.read().strip()
        if piped_data:
            input_prompt = f"这是通过管道传入的上下文数据:\n<context>\n{piped_data}\n</context>\n\n用户需求:{input_prompt}"

    if not input_prompt:
        console.print("[bold red]请输入你的问题或任务。[/bold red]")
        sys.exit(1)

    run_agentic_loop(input_prompt)

if __name__ == '__main__':
    cli()

四、 核心差异:为什么 Claude 如此适合 Agentic CLI?

你可能会问,上面的代码如果换成 GPT-4o 或者 DeepSeek 也能跑,为什么一定要强调 Claude 架构

4.1 原生 XML 偏好 (XML Tags)

Claude 的基础模型经过了深度的 XML 格式微调。在 Prompt 中使用 <instruction><context><thinking> 标签来隔离变量,Claude 能够百分百准确地理解数据边界。 在上面的脚本中,当处理管道(Pipeline)输入时,我们使用了 <context> 标签包裹输入流,这有效防止了“Prompt 注入攻击”(例如,如果一段被猫踩到的报错日志里刚好包含了一句 Ignore previous instructions,Claude 不会被误导,因为它知道这只是 <context> 内部的字符串)。

4.2 Prefill(预填充)技术

Anthropic 的 API 支持将 Assistant 的回复预先填充一部分。例如,我们可以在代码里强制 Claude 输出:

JSON

{"role": "assistant", "content": "<thinking>"}

这样不仅能强制大模型进入“慢思考”状态,还能节省每次让它生成开头固定词汇的 Token 费用。这在终端交互中极大地提升了稳定性。

4.3 极低的工具调用拒绝率

在企业级实战中,许多开源模型在面对 rmgit reset --hard 等危险命令时,会触发内部的安全对齐策略(Alignment Guardrails),从而拒绝生成命令。Claude 3.5 Sonnet 在判断上下文安全(尤其是限定在代码修改域)时,具备极高的鲁棒性,极少出现“假阳性”拒绝,保证了 Agentic Loop 的畅通。


五、 进阶:接入 MCP(模型上下文协议)

我们刚才手写了一个 execute_bash_command 工具。但在真实的企业开发中,你需要查询 GitHub PR、查询 MySQL 数据库、搜索 Notion 文档。如果把这些逻辑都写在 CLI 脚本里,代码会变得极其臃肿。

MCP(Model Context Protocol) 正是为此而生。

MCP 采用 Client-Server 架构。在未来的演进中,我们的 claude-terminal 作为一个 MCP Client,可以通过标准化的 JSON-RPC 协议连接到本地运行的多个 MCP Servers

如何升级我们的 CLI 支持 MCP?

  1. 终端启动一个 sqlite-mcp-server

  2. 我们的 claude_cli.py 初始化时,向 Server 发送 tools/list 请求。

  3. Server 返回 query_database, list_tables 等工具。

  4. 我们将这些工具动态加入 TOOLS 数组并发送给 Claude。

  5. 当 Claude 决定调用 query_database 时,CLI 将请求透传给 MCP Server 执行并返回结果。

这使得终端 AI 具备了无限的扩展能力,从一个单机助手进化为了“全网资源调度中心”。


六、 安全考量与最佳实践

赋予 AI 直接执行 Shell 命令的能力,无异于给了一把双刃剑。

  1. 不可绕过的 Human-in-the-loop:在上面的代码第 3 步中,Confirm.ask 是底线。切勿为了所谓的“全自动”而注销这行代码。

  2. 只读沙箱模式:针对新人,可以增加一个 --safe-mode 参数。在该模式下,将传入给 Claude 的工具替换为仅支持 cat, ls, grep 的受限环境,屏蔽修改和删除能力。

  3. Token 开销监控:Agentic Loop 很容易陷入“死循环”(例如:执行失败 -> AI 尝试修复 -> 再次失败)。必须在循环体外设置硬性退出机制(如最大循环次数 MAX_ITERATIONS = 5)。


七、 总结

如果说早期的 Codex CLI 像是一个高级的“搜索引擎”,那么基于 Claude 和 Tool Use 构建的终端助手,已经是一个具备**感知(Observation)、规划(Planning)和执行(Execution)**的合格数字员工。

本文从底层逻辑出发,带你使用 Python 和 Anthropic 官方 SDK 从零实现了一个 Agentic CLI 工具。理解了这个 Loop 循环,你就掌握了目前硅谷最前沿的 AI 编程助手架构精髓。

快把你枯燥的运维排错、Git 冲突解决、日志清洗工作交给这只全自动的“电子工程师”吧。在终端的黑底白字间,AI Agent 的黎明已经到来。


作者声明:本文提供的源码完全开源,欢迎大家复制修改并在本地机器上进行探索实验。在享受 AI 带来便利的同时,请始终对终端执行的命令保持敬畏之心。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐