ARTICLE DETAIL

资讯详情

深耕网站SEO优化与搜索引擎排名提升的一线实战洞察。

30行代码构建智能体循环:从Bash脚本到AI驱动自动化

30行代码构建智能体循环:从Bash脚本到AI驱动自动化 在实际项目中我们经常需要自动化处理一些重复性的开发任务比如代码生成、文件整理、数据转换或者执行一系列复杂的构建和部署命令。传统脚本虽然能处理固定流程但面对需要根据上下文动态决策的场景就显得力不从心。这正是智能体Agent技术开始发力的地方。一个智能体不仅能执行预设命令还能理解任务目标、分析当前状态并自主决定下一步该做什么。对于开发者而言理解智能体的核心工作机制是构建更复杂自动化工具的第一步。本文将以一个极简但功能完整的智能体循环为例带你从零理解智能体开发的核心思想。我们将聚焦于一个核心概念智能体循环Agent Loop。这个循环的本质是一个while True加上一个命令执行器如 Bash让模型能够观察环境、思考决策、执行动作并基于结果进行下一轮循环。我们将使用 Claude Code 作为模型内核来演示其核心逻辑可能精简到只有 30 行代码。通过这个案例你将掌握智能体如何自主运行命令、解析输出并持续工作的基本模式为后续开发更强大的 Agent Harness 或业务智能体打下坚实基础。本文适合对自动化脚本、命令行操作有一定了解并希望探索 AI 驱动自动化可能性的开发者。我们将从环境准备开始逐步构建一个可运行的智能体原型并深入探讨其关键组件、常见问题及优化方向。1. 理解智能体循环从脚本到自主决策在深入代码之前我们需要厘清几个核心概念智能体Agent、智能体循环Agent Loop以及它们与普通脚本的根本区别。1.1 智能体 vs. 自动化脚本一个传统的 Bash 或 Python 脚本是确定性的。你编写一系列指令脚本严格按顺序执行。如果中间某步失败脚本通常会报错退出或者按照预设的异常分支处理。脚本的逻辑在编写时就已经完全确定。而一个智能体是目标导向且具备一定自主性的。你给它一个高级目标例如“整理这个项目文档”智能体需要自己拆解目标评估当前环境有哪些文件、目录结构如何规划步骤先读取文件再提取标题最后生成索引执行具体操作运行ls,cat,sed等命令并根据执行结果调整后续计划。这个“感知-思考-行动”的循环过程就是智能体循环。1.2 智能体循环的核心组件一个典型的智能体循环包含以下四个关键阶段它们在一个while循环中不断迭代观察Observation智能体获取当前环境的状态。这可能是终端的最新输出、文件系统的当前列表、一个 API 的返回结果或者任何与任务相关的上下文信息。思考Thinking智能体通常由大语言模型驱动分析观察到的信息结合既定目标决定下一步应该做什么。这个决策通常以自然语言或结构化指令的形式产生。行动Action智能体将“思考”阶段产生的决策转化为具体的、可执行的操作。在本文的语境下这个操作就是执行一条 Bash 命令。反馈Feedback行动执行后环境会产生新的状态命令的输出、错误码、新生成的文件等。这个结果被作为下一轮循环的“观察”输入从而形成闭环。这个循环会一直持续直到智能体判断目标已达成或遇到无法克服的障碍或由外部指令终止。1.3 Claude Code 的角色Claude Code 是一个专注于代码生成和理解的大语言模型。在我们的智能体架构中它扮演“大脑”或“决策器”的角色。它的任务是理解我们给出的自然语言目标。在每一轮循环中分析当前的观察命令行输出、文件列表等。生成下一步要执行的、具体的 Bash 命令。我们通过 API 调用或本地集成的方式将 Claude Code 模型接入我们的循环程序让它来驱动“思考”阶段。2. 环境准备与工具安装为了构建和运行这个智能体我们需要准备相应的开发环境和工具链。2.1 基础环境要求确保你的系统满足以下基本条件组件要求检查命令备注操作系统macOS, Linux 或 WSL (Windows Subsystem for Linux)uname -a确保能运行标准的 Bash shell。Python版本 3.8 或更高python3 --version我们将用 Python 编写主控循环。包管理器pippip3 --version用于安装 Python 依赖。Bash版本 4.0bash --version智能体执行命令的环境。Git任意较新版本git --version用于克隆示例代码库如果需要。如果你的系统是 Windows 且没有 WSL可以考虑使用 Git Bash 来提供一个类 Bash 的环境。但请注意某些系统级命令在 Git Bash 和原生 Linux Bash 中可能存在差异对于学习原型开发影响不大但在生产环境部署时需要仔细测试。2.2 安装与配置 Claude CodeClaude Code 通常通过其提供的 SDK 或 API 进行调用。根据官方指引安装通常涉及以下步骤获取 API 密钥访问 Claude Code 的官方网站或开发者平台注册并创建一个项目获取你的 API Key。请妥善保管此密钥不要直接提交到代码仓库。安装官方 SDK使用 pip 安装 Claude Code 的 Python 客户端库。具体的包名可能为claude-code-sdk或类似请以官方文档为准。# 示例安装命令请替换为实际的包名 pip3 install claude-code-sdk环境变量配置将 API Key 设置为环境变量这是安全使用密钥的推荐做法。# 在终端中临时设置仅当前会话有效 export CLAUDE_CODE_API_KEYyour-api-key-here # 为了持久化可以将这行命令添加到你的 shell 配置文件如 ~/.bashrc, ~/.zshrc中 echo export CLAUDE_CODE_API_KEYyour-api-key-here ~/.bashrc source ~/.bashrc注意直接硬编码 API Key 在脚本中是极不安全的做法一旦代码泄露密钥也会暴露。务必使用环境变量或安全的密钥管理服务。2.3 项目初始化创建一个干净的工作目录并初始化你的项目。mkdir simple-agent-loop cd simple-agent-loop # 创建虚拟环境推荐用于隔离依赖 python3 -m venv venv # 激活虚拟环境 # 在 macOS/Linux 上 source venv/bin/activate # 在 Windows (Git Bash) 上 # source venv/Scripts/activate # 创建项目文件 touch agent_loop.py requirements.txt在requirements.txt中列出项目依赖claude-code-sdk然后安装依赖pip3 install -r requirements.txt3. 构建 30 行核心智能体循环现在我们来编写智能体循环的核心代码。我们将创建一个名为agent_loop.py的文件。3.1 循环骨架While True 状态管理智能体循环的基础是一个无限循环但我们需要有退出机制。通常当模型决定任务完成例如输出一个特殊的结束标记或遇到致命错误时循环应该终止。#!/usr/bin/env python3 极简智能体循环示例使用 Claude Code 作为决策大脑Bash 作为执行器。 import os import subprocess import sys # 假设 Claude Code SDK 的导入方式如下请根据实际 SDK 调整 from claude_code import Client def run_bash_command(command): 执行 Bash 命令并返回输出和错误码。 try: # 使用 subprocess.run 执行命令捕获输出和错误 result subprocess.run( command, shellTrue, capture_outputTrue, textTrue, encodingutf-8, # 明确指定编码避免乱码 timeout30 # 设置超时防止命令卡死 ) # 返回标准输出、标准错误和返回码 return result.stdout, result.stderr, result.returncode except subprocess.TimeoutExpired: return , Command timed out after 30 seconds., -1 except Exception as e: return , fFailed to execute command: {e}, -1 def main(): # 1. 初始化 Claude Code 客户端 api_key os.environ.get(CLAUDE_CODE_API_KEY) if not api_key: print(错误未设置 CLAUDE_CODE_API_KEY 环境变量。) sys.exit(1) client Client(api_keyapi_key) # 2. 定义智能体的初始目标和上下文 user_goal 请列出当前目录下的所有文件然后统计一个名为 ‘example.txt’ 的文件的行数。如果文件不存在就创建它并写入 ‘Hello, Agent!’。 # 系统提示词用于设定模型的角色和行为约束 system_prompt 你是一个智能助手能够通过执行 Bash 命令来完成任务。 你的思考过程应该清晰。你每次只能执行一条 Bash 命令。 命令执行后你会看到命令的输出stdout、错误信息stderr和返回码returncode。 请根据这些结果决定下一步行动。 当你认为任务已经完成时请输出 ‘TASK_COMPLETE’ 并简要总结。 如果遇到无法解决的错误请输出 ‘TASK_FAILED’ 并说明原因。 现在开始处理任务。 # 初始化对话历史和观察 conversation_history [{role: system, content: system_prompt}] conversation_history.append({role: user, content: user_goal}) # 初始观察当前工作目录 initial_observation f当前工作目录是: {os.getcwd()}\n observation initial_observation print( * 50) print(f目标: {user_goal}) print( * 50) # 3. 核心智能体循环 step 0 max_steps 10 # 防止无限循环的安全措施 while step max_steps: step 1 print(f\n--- 步骤 {step} ---) # 将当前的观察加入到对话历史中作为模型的输入上下文 if observation: conversation_history.append({role: user, content: f观察:\n{observation}}) # 调用 Claude Code 模型进行“思考”获取下一步行动指令 try: response client.chat.completions.create( modelclaude-code-latest, # 使用合适的模型名称 messagesconversation_history, max_tokens500, temperature0.2, # 较低的温度使输出更确定更适合执行命令 ) model_message response.choices[0].message.content.strip() except Exception as e: print(f调用模型失败: {e}) break print(f模型决策:\n{model_message}) # 4. 检查终止条件 if TASK_COMPLETE in model_message: print(\n✅ 任务完成) print(model_message.replace(TASK_COMPLETE, ).strip()) break if TASK_FAILED in model_message: print(\n❌ 任务失败。) print(model_message.replace(TASK_FAILED, ).strip()) break # 5. 提取并执行 Bash 命令 # 简单假设模型输出的第一行以 ‘$’ 或 ‘’ 开头的部分为命令 # 这是一个非常简单的解析实际应用中需要更鲁棒的解析逻辑 lines model_message.split(\n) command_to_run None for line in lines: line_stripped line.strip() if line_stripped.startswith() and line_stripped.endswith(): command_to_run line_stripped[1:-1].strip() # 去除反引号 break elif line_stripped.startswith($): command_to_run line_stripped[1:].strip() # 去除‘$’符号 break # 也可以尝试匹配常见的命令开头如 ls, cat, echo, wc 等 elif any(line_stripped.startswith(cmd) for cmd in [ls, cat, echo, wc, find, grep, touch, mkdir]): command_to_run line_stripped break if not command_to_run: print(无法从模型输出中解析出有效的 Bash 命令。将原始输出作为观察。) observation model_message continue print(f执行命令: {command_to_run}) stdout, stderr, returncode run_bash_command(command_to_run) # 6. 生成下一轮的观察 observation_parts [] if stdout: observation_parts.append(f标准输出:\n{stdout}) if stderr: observation_parts.append(f标准错误:\n{stderr}) observation_parts.append(f返回码: {returncode}) observation \n.join(observation_parts) print(f命令结果:\n{observation}) # 将模型的回复也加入历史保持对话连贯性 conversation_history.append({role: assistant, content: model_message}) else: # 如果循环因达到最大步数而退出 print(f\n⚠️ 已达到最大步数 ({max_steps})强制终止循环。) if __name__ __main__: main()3.2 代码关键点解析run_bash_command函数这是智能体的“手”。它使用 Python 的subprocess模块安全地执行 shell 命令。关键参数shellTrue允许使用 shell 语法如管道|、重定向。capture_outputTrue和textTrue捕获命令的输出并将其作为字符串返回。encodingutf-8强制使用 UTF-8 编码避免因终端编码问题如 GBK导致输出乱码这是搜索材料中提到的常见问题。timeout30防止智能体执行一个永不结束的命令而卡死循环。系统提示词system_prompt这是智能体的“宪法”定义了它的行为准则。我们明确要求它每次只执行一条命令。根据命令结果stdout, stderr, returncode决定下一步。使用特定的标记TASK_COMPLETE,TASK_FAILED来终止循环。对话历史conversation_history这是一个消息列表维护了与模型的整个对话上下文。每一轮循环我们都将最新的“观察”作为用户消息追加进去模型则基于全部历史生成回复。这使模型具备“记忆”能力。命令解析逻辑这是一个简化的、脆弱的解析器。它寻找以反引号、$或特定命令开头的行作为要执行的命令。在实际生产环境中你需要更强大的解析逻辑例如要求模型以固定的 JSON 格式输出{command: ls -la, reason: ...}或者使用专门的解析库。安全与终止机制max_steps防止智能体陷入死循环。超时处理在run_bash_command中。明确的终止标记依赖模型输出特定文本来结束任务。4. 运行验证与结果分析现在让我们在空目录中运行这个智能体看看它如何工作。4.1 首次运行从零开始完成任务确保已激活虚拟环境并设置好CLAUDE_CODE_API_KEY。# 在你的项目目录下 python agent_loop.py你将看到类似以下的输出具体内容因模型输出而异 目标: 请列出当前目录下的所有文件然后统计一个名为 ‘example.txt’ 的文件的行数。如果文件不存在就创建它并写入 ‘Hello, Agent!’。 --- 步骤 1 --- 模型决策: 首先我需要查看当前目录下有什么文件。我将执行 ls -la 命令。 执行命令: ls -la 命令结果: 标准输出: 总用量 8 drwxr-xr-x 3 user group 96 Apr 10 10:00 . drwxr-xr-x 10 user group 320 Apr 10 09:55 .. -rw-r--r-- 1 user group 1234 Apr 10 09:58 agent_loop.py -rw-r--r-- 1 user group 23 Apr 10 09:58 requirements.txt drwxr-xr-x 6 user group 192 Apr 10 09:55 venv 返回码: 0 --- 步骤 2 --- 模型决策: 现在我需要检查 example.txt 文件是否存在。使用 ls example.txt 或直接尝试 cat。 执行命令: ls example.txt 命令结果: 标准错误: ls: example.txt: No such file or directory 返回码: 2 --- 步骤 3 --- 模型决策: 文件不存在。根据任务要求我需要创建它并写入 ‘Hello, Agent!’。使用 echo 命令。 执行命令: echo Hello, Agent! example.txt 命令结果: 返回码: 0 --- 步骤 4 --- 模型决策: 现在文件应该已经创建。我需要验证内容并统计行数。先用 cat 查看内容再用 wc -l 统计行数。 执行命令: cat example.txt wc -l example.txt 命令结果: 标准输出: Hello, Agent! 1 example.txt 返回码: 0 --- 步骤 5 --- 模型决策: 任务已完成。我列出了目录文件确认 example.txt 不存在后创建了它写入了指定内容并统计出其行数为 1。 TASK_COMPLETE ✅ 任务完成 任务已完成。我列出了目录文件确认 example.txt 不存在后创建了它写入了指定内容并统计出其行数为 1。4.2 结果分析通过输出我们可以清晰地看到智能体循环的每一步观察初始观察是当前目录路径。第一轮命令ls -la执行后其输出成为下一轮的观察。思考模型根据观察例如ls: example.txt: No such file or directory决定下一步行动echo Hello, Agent! example.txt。行动我们的程序解析并执行了模型生成的 Bash 命令。反馈命令的执行结果输出、错误、返回码被构建成新的观察输入到下一轮循环。智能体成功地拆解了目标通过多轮命令交互完成了任务并在最后输出了终止标记TASK_COMPLETE循环正常结束。4.3 验证文件运行结束后检查目录你会发现确实生成了example.txt文件。cat example.txt # 输出: Hello, Agent! wc -l example.txt # 输出: 1 example.txt5. 常见问题与深度排查将原型投入实际使用你会遇到各种问题。以下是基于此架构的常见故障点及排查方法。5.1 模型不输出命令或输出格式错误现象模型回复了一段分析文字但没有输出可解析的 Bash 命令导致程序无法继续。原因系统提示词约束力不够。模型对输出格式的理解有偏差。对话历史过长或混乱干扰了模型。解决方案强化系统提示词在system_prompt中更明确地要求格式。例如“你每次的回复必须且只能包含一个要执行的 Bash 命令将该命令用反引号包裹。不要输出任何解释。例如ls -la”实现后处理如果模型仍然输出解释可以在解析命令前用简单的规则或正则表达式从回复中提取被反引号包裹的部分。简化历史对于长任务可以考虑只保留最近几轮的对话或者对历史进行摘要防止上下文超出模型限制或引入噪声。5.2 命令执行失败或产生副作用现象模型生成了错误的命令如rm -rf /这种危险命令或cd到不存在的目录导致执行失败或产生不可预期的影响。原因模型对系统环境或命令后果理解不足。解决方案沙箱环境这是最重要的安全措施。不要在拥有重要权限或数据的主机上直接运行智能体。应使用 Docker 容器、虚拟机或专门的隔离环境。命令白名单/黑名单在run_bash_command函数执行前对命令进行校验。例如禁止执行rm、dd、mkfs、chmod 777等危险命令或只允许执行ls,cat,grep,find,wc等安全命令。设置安全工作目录使用subprocess.run的cwd参数将命令限制在某个特定的、无关紧要的目录下执行。模拟执行Dry Run在正式执行前可以先让模型输出“计划”由人工审核后再执行。5.3 编码与乱码问题现象命令输出中包含中文或其他非 ASCII 字符时在 Python 中显示为乱码如gbk 输出无法正常渲染。原因子进程的输出编码与 Python 解释器或终端的默认编码不一致。解决方案在subprocess.run中明确指定encodingutf-8。如果命令输出确实是 GBK则需指定encodinggbk。更通用的方法是使用encodingsys.getdefaultencoding()或尝试捕获字节流 (capture_outputTrue但不指定textTrue)然后根据情况解码。确保你的终端如 Git Bash和系统区域设置都支持 UTF-8。5.4 循环无法终止或陷入死循环现象智能体一直在执行类似或无关的命令无法到达TASK_COMPLETE状态。原因任务目标模糊模型无法判断何时完成。模型决策逻辑陷入局部循环。解析错误导致有效终止标记被忽略。解决方案设定更明确的目标和完成条件在给模型的指令中明确说明“完成”的标志。例如“当你成功统计出report.md的行数并确认其大于 10 行后任务完成。”引入外部超时和步数限制就像我们代码中的max_steps和命令timeout一样这是必要的安全网。改进观察反馈如果模型因为看不到清晰的进展而困惑可以在观察中主动加入总结信息例如“当前已执行 X 步已达成目标 Y剩余目标 Z。”5.5 依赖与版本问题现象出现类似“deepseek-v4-pro” is not a model this version of claude code recognizes或ModuleNotFoundError: No module named ‘claude_code’的错误。原因SDK 版本与 API 不兼容或安装了错误的包。解决方案仔细查阅 Claude Code 官方文档确认正确的 Python 包名和安装方式。检查requirements.txt中指定的版本号尝试安装特定版本或最新版本。确认 API Key 是否有权限访问你所请求的模型如claude-code-latest。在虚拟环境中操作避免全局包冲突。6. 从原型到生产最佳实践与扩展方向这个 30 行的循环是一个强大的起点但距离一个健壮、可用的生产级智能体还有很大距离。以下是关键的优化和扩展方向。6.1 架构升级引入 Agent Harness 思想“Harness”意为“马具”在这里指一套框架或工具用于更好地控制、引导和评估智能体。你可以将核心循环封装起来并增加以下组件状态管理维护一个结构化的状态对象包含任务目标、已完成步骤、当前环境快照等而不仅仅是文本对话历史。工具Tools抽象不要只局限于 Bash。将“行动”抽象为“工具”调用。每个工具如ExecuteBashCommand,ReadFile,WriteFile,CallAPI有明确的输入输出规范。模型通过函数调用Function Calling或结构化输出来选择工具和参数。记忆Memory模块实现短期记忆对话历史和长期记忆向量数据库存储的关键信息帮助智能体在长程任务中保持一致性。规划Planning与反思Reflection让智能体在行动前先制定计划在行动后评估结果是否偏离目标并调整策略。6.2 安全性强化清单在生产环境中运行自主智能体必须慎之又慎。安全措施具体实现建议目的环境隔离在 Docker 容器或无特权的用户环境中运行智能体进程。限制文件系统、网络和系统调用权限。命令过滤实现一个命令验证层基于白名单只允许安全命令或黑名单禁止危险命令。防止执行rm -rf /、:(){ :|: };:fork 炸弹等。资源限制使用ulimit、 Docker 资源限制或 cgroups 限制 CPU、内存、进程数和执行时间。防止智能体耗尽系统资源。输入净化对模型生成的命令进行严格的转义和验证防止命令注入。防止通过精心构造的输入执行任意代码。审计日志详细记录每一轮循环的输入观察、输出模型决策、执行命令及其结果。便于事后审查、调试和归因。人工审核环对于高风险操作如删除文件、修改配置设置开关需要人工确认后才能执行。提供最终的安全屏障。6.3 提升可靠性与效率错误处理与重试当前代码在命令执行失败后只是将错误信息反馈给模型。可以增加自动重试逻辑针对网络超时等临时错误或预设的错误处理策略。并行与异步如果任务可拆分为独立子任务可以考虑让智能体并行调度多个命令执行提升效率。上下文窗口管理大语言模型有上下文长度限制。对于长对话需要实现历史消息的摘要、选择性遗忘或分块加载确保最重要的信息保留在上下文中。成本优化监控 API 调用次数和 token 消耗。对于简单、重复性的命令决策可以考虑使用更小、更便宜的模型或者缓存常见的决策模式。6.4 应用场景探索基于此核心模式你可以构建多种实用智能体开发运维助手自动执行常见的 DevOps 任务如日志分析grep,tail、服务状态检查ps,systemctl、简单的部署步骤。数据整理机器人给定一个杂乱的数据目录智能体可以按照规则重命名文件、转换格式、提取信息并生成报告。交互式 CLI 增强工具用户描述一个复杂操作“把最近一周的日志里所有 ERROR 级别的记录找出来按服务名分组统计”智能体自动生成并执行一系列命令管道。自动化测试生成器分析代码变更智能生成并执行相关的单元测试或集成测试命令。理解while True循环加上模型决策和命令执行这个核心范式是进入智能体开发世界的关键一步。它剥离了复杂框架的外壳直击智能体自主工作的本质。从这个最小可行产品出发通过逐步引入状态管理、工具抽象、安全控制和记忆模块你就能搭建起适应复杂场景的智能体系统。下一步尝试给你的智能体增加一个“读取文件内容”的工具或者让它学习在失败后尝试不同的策略你会发现这个简单循环的潜力远超想象。
返回列表