我如何从头开始构建一个 Agentic 编码 CLI

发布: (2026年5月4日 GMT+8 09:22)
8 分钟阅读
原文: Dev.to

Source: Dev.to

抱歉,我无法直接访问外部链接获取文章内容。请您把需要翻译的文本粘贴在这里,我会帮您翻译成简体中文。

Source:

核心洞见:这只是一个循环

每个具备代理功能的编码工具——无论多么完善——都遵循相同的基本模式:

while needs_follow_up:
    # 1️⃣ Send conversation + tools → LLM
    # 2️⃣ If LLM returns tool calls → execute them, append results, loop
    # 3️⃣ If LLM returns plain text → finish

这就是“魔法”:带函数调用的 while 循环
其余 95% 包含:

  • 上下文管理
  • 工具执行
  • 错误处理
  • 权限检查

简化的代理循环

def run_agent_loop(user_input, conversation, config):
    conversation.add_user(user_input)

    for iteration in range(config.max_iterations):
        stream = completion(
            model=routed_model,
            messages=conversation.messages,
            tools=TOOL_DEFINITIONS,
            stream=True,
        )

        text, tool_calls, usage = process_stream(stream)

        if not tool_calls:
            # No tools called — model is done
            conversation.add_assistant(content=text)
            break

        # Execute each tool, feed results back, loop
        for tc in tool_calls:
            result = execute_tool(tc.name, tc.args)
            conversation.add_tool_result(tc.id, result)

当用户说 “修复 app.py 中的 bug” 时,LLM 并不会直接编辑文件。它会:

  1. 调用 read_file("app.py") → 获取源码。
  2. 调用 edit_file(...) 并提供修复。
  3. 调用 run_command("pytest") 进行验证。

每一步都是一次工具调用,循环会执行该调用并将结果反馈到下一次迭代中。

架构

┌─────────────────────────────────────────────────┐
│                  cli.py (UI)                    │
│  REPL loop · slash commands · Rich terminal UI  │
└──────────────────────┬──────────────────────────┘

┌──────────────────────▼──────────────────────────┐
│               agent.py (Brain)                  │
│  Agentic loop · context management · permissions│
│                                                 │
│   LiteLLM ──→ Claude / GPT / Gemini / Ollama    │
└──────────────────────┬──────────────────────────┘

┌──────────────────────▼──────────────────────────┐
│               tools.py (Hands)                  │
│  read_file · write_file · edit_file             │
│  run_command · git_commit · search_text         │
└─────────────────────────────────────────────────┘
文件职责
cli.py终端 UI(REPL、斜杠命令、会话管理)
agent.py大脑(代理循环、流式处理、权限、上下文压缩)
tools.py工具(文件 I/O、bash 执行、git、搜索)

我最自豪的功能:成本感知路由

大多数 AI 编码工具只能锁定单一模型,导致你为一个简单解释付的费用和为一次完整重构付的费用相同。AgentCode 会根据请求的复杂度进行分类,并自动选择能够处理该请求且最便宜的模型。

路由表

层级示例提示模型原因
轻量“这个函数是做什么的”Haiku快速、便宜——仅仅是阅读和解释
中等“为 app.py 编写单元测试”Sonnet需要理解代码并生成新代码
重型“重构整个认证系统”Opus多文件、多步骤、架构思考

分类使用简单的模式匹配:

def classify_complexity(user_input):
    text = user_input.lower()

    heavy_score = sum(1 for p in HEAVY_PATTERNS if re.search(p, text))
    medium_score = sum(1 for p in MEDIUM_PATTERNS if re.search(p, text))

    if heavy_score >= 2:
        return "heavy"
    elif medium_score >= 1:
        return "medium"
    else:
        return "light"

透明、易于调整,并且能真正省钱。
你可以随时使用 /model 命令覆盖自动选择。

流式传输:用户体验的差异

第一个版本在显示任何内容之前会等待完整的 LLM 响应,导致终端空白 5–10 秒。加入流式传输后,体验变成了 实时对话

挑战

在一个自主循环中,LLM 可以在同一次响应中返回 纯文本 工具调用。文本 token 会逐个到达,而工具调用的参数则以片段形式出现,需要在执行前组装完整。

流式处理器

def process_stream(stream):
    full_text = ""
    tool_calls_acc = {}

    for chunk in stream:
        delta = chunk.choices[0].delta

        # Text tokens — print immediately
        if delta.content:
            print(delta.content, end="", flush=True)
            full_text += delta.content

        # Tool call fragments — accumulate silently
        if delta.tool_calls:
            for tc_delta in delta.tool_calls:
                idx = tc_delta.index
                if idx not in tool_calls_acc:
                    tool_calls_acc[idx] = {"id": "", "name": "", "arguments": ""}
                if tc_delta.function.arguments:
                    tool_calls_acc[idx]["arguments"] += tc_delta.function.arguments

    return full_text, tool_calls_acc
  • 文本实时流式输出到屏幕。
  • 工具调用在后台悄悄组装。

用户会看到文字瞬间出现,而代理则在决定下一步要做什么。

多模型支持

AgentCode 使用 LiteLLM 作为抽象层。这意味着我只需用 OpenAI 的格式编写一套工具定义,LiteLLM 会将其转换为各供应商所需的格式。

在对话中切换模型

 /model gpt-4o
 Switched to gpt-4o

 /model claude-opus-4-6
 Switched to claude-opus-4-6

 /model ollama/qwen2.5-coder
 Switched to ollama/qwen2.5-coder

相同的工具、相同的循环、不同的大脑。本地 Ollama 选项意味着你可以零 API 成本运行整个系统。

权限系统

任何写文件或执行命令的工具在操作前都会询问:

🔒 Permission Required
Tool: write_file
Args: {"path": "src/handler.py", "content": "..."}
Allow this action? [y/n] (y):

只读工具(read_filelist_directorysearch)自动批准。这使流程保持快速,同时防止代理在未经您同意的情况下进行任何破坏性操作。

我学到的

  1. 上下文管理是难点。
    代理循环本身很简单。真正的工程工作在于管理上下文窗口——压缩旧消息、进行摘要、保持正确的信息可用——这才是关键。

  2. 工具定义比提示更重要。
    具备清晰参数描述的完善工具胜过巧妙的系统提示。大语言模型会把工具模式当作文档来阅读。

  3. 流式输出改变一切。
    “等待 8 秒才得到响应”与“文字即时出现”之间的差别,就像是使用令人沮丧的工具与令人愉快的工具之间的区别。

  4. 多模型灵活性被低估了。
    不同模型在不同任务上各有优势。能够在它们之间热切换——或让路由器自行决定——就意味着你始终拥有最合适的工具来完成工作。

试一试

pip install agentcode-cli
export ANTHROPIC_API_KEY="your-key"
agentcode

代码库是可读的 Python —— 没有框架,也没有抽象。如果你想了解代理式编码工具是如何工作的,克隆它并阅读 agent.py。整个循环大约 50 行

  • GitHub:
  • PyPI:

采用 MIT 许可证。欢迎反馈和贡献。

标签: python, ai, opensource, tutorial

0 浏览
Back to Blog

相关文章

阅读更多 »

使用 AI 的开发者的四种认知原型

最近我一直在思考一件事:对大多数开发者来说,问题不再是“你在使用 AI 吗?”,而是“你是如何以及为何使用 AI?” 我的工作流...