十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

智能体开发入门:30行代码实现自主循环与工具调用

智能体开发入门:30行代码实现自主循环与工具调用 1. 智能体开发入门从“自己跑命令接着干”开始理解如果你刚开始接触智能体开发看到各种框架和复杂概念有点懵那我建议你先别急着研究那些大而全的架构。最核心、最值得先搞懂的一件事就是智能体如何实现“自主循环”——也就是它怎么在收到一个任务后能自己思考、执行、检查结果然后决定下一步做什么直到任务完成。这个“自主循环”听起来高级但它的内核可以极其简单。就像标题里说的一个while True循环加上Bash命令执行模型自己就能跑命令接着干。这30行代码的Demo比任何长篇大论都更能帮你建立对智能体工作流的直觉。它解决的核心问题是如何让一个语言模型比如Claude Code从被动的“问答机”变成一个能主动调用工具、与环境交互的“执行者”。这节内容特别适合两类人一是想快速理解智能体核心机制的新手开发者二是已经用过一些智能体框架但想回归本质看看底层到底是怎么转起来的实践者。最关键的价值在于你能亲手搭建一个最小可运行的智能体循环亲眼看到模型是如何分析任务、生成命令、执行并基于结果进行下一步决策的。理解了这一点后面再去看那些封装好的框架比如提到的Agent Harness你就能一眼看穿它们到底在哪些层面做了增强和抽象。2. 环境准备Claude Code、终端与最小依赖在动手写那30行代码之前我们需要先把运行环境搭好。这个环境不复杂但每一步都关系到后面能否顺利跑起来。2.1 核心工具Claude Code 是什么首先得弄清楚我们用的“大脑”是什么。Claude Code 并不是一个独立的桌面软件它通常指的是 Anthropic 公司 Claude 模型系列中针对代码生成、理解和工具调用能力特别优化的版本或访问方式。在开发上下文中我们往往是通过其提供的 API 来调用它。所以你不需要去搜索“Claude Code 桌面版”或“Claude Code 下载”。对于开发而言关键是要有一个能调用 Claude API 的途径。这通常意味着获取 API 密钥你需要注册 Anthropic 的开发者平台创建一个项目并获取你的 API Key。这是模型服务的“门票”。选择调用方式你可以直接使用官方 SDK如anthropicPython 库或者使用一些第三方封装了 Claude 能力的命令行工具。我们为了简化后续示例会假设你已安装好anthropicPython 库。注意网络热词中提到的deepseek-v4-pro或deepseek-v4-flashis not a model this version of claude code recognizes 这类错误提示我们一个重要信息模型名称必须匹配。Claude API 有自己支持的模型列表如claude-3-5-sonnet-20241022你不能随意传入其他公司的模型名称。调用前务必查阅官方文档使用正确的模型标识符。2.2 执行环境Bash 终端智能体需要“手”来操作环境这里我们选择最通用的“手”——Bash Shell。无论是在 macOS、Linux还是通过 Git Bash 或 WSL 在 Windows 上你都需要一个能可靠执行ls,cat,grep,mkdir,python等命令的 Bash 环境。macOS/Linux系统自带的终端Terminal通常就是 Bash 或兼容 Bash 的 Zsh直接使用即可。Windows推荐安装Git Bash它是 Git for Windows 的一部分或启用WSL (Windows Subsystem for Linux)。这能为你提供一个接近 Linux 的 Bash 环境避免因路径、命令差异带来的问题。不要使用Windows 自带的 CMD 或 PowerShell 作为本例的执行环境因为命令语法和路径格式不同会增加不必要的复杂度。验证打开你的终端输入echo $SHELL和bash --version确认能正常返回信息。2.3 代码环境Python 与必要库我们的控制循环会用 Python 来写因为它连接 API 和调用子进程执行Bash命令都很方便。安装 Python确保你的系统安装了 Python 3.8 或更高版本。在终端输入python3 --version或python --version检查。安装依赖库我们将主要用到两个库。# 使用 pip 安装 pip install anthropic # Claude 官方 SDK pip install openai # 某些情况下工具可能兼容OpenAI格式先装上备用设置 API 密钥环境变量为了安全不建议将 API Key 硬编码在代码里。通常设置为环境变量。# 在终端中执行仅当前会话有效 export ANTHROPIC_API_KEY你的-api-key-here更持久的设置方法将上述export命令添加到你的 shell 配置文件如~/.bashrc或~/.zshrc中然后执行source ~/.bashrc。2.4 避坑点路径、权限与编码在真正跑起来之前还有几个容易忽略的坑工作目录你的 Python 脚本在哪里运行Bash 命令就会在哪里执行。建议在一个专门的新建目录里操作避免误操作系统文件。命令执行权限如果智能体需要执行你目录下的某个脚本文件比如.sh文件记得用chmod x script.sh给它加上可执行权限。否则会遇到Permission denied错误。编码问题这是一个经典陷阱。Bash 终端和你的 Python 程序默认编码通常是 UTF-8。但如果某些命令或文件产生了 GBK 或其他编码的输出可能会导致乱码或程序解析错误。如果遇到gbk 输出无法正常渲染或bad interpreter之类的错误首先要检查相关文件的编码格式并在 Python 中处理文本时注意指定编码如open(file, ‘r’, encoding‘utf-8’)。环境准备好后我们就可以开始构建最核心的循环了。3. 核心循环拆解While True Bash 的 30 行魔法现在我们进入最核心的部分。下面这段伪代码/简化代码展示了智能体循环的基本骨架。我会逐块解释并给出一个可运行的、加强版的示例。3.1 循环骨架观察 - 思考 - 行动智能体的核心就是一个永不满足的循环直到它自己判断任务完成。这个循环遵循经典的OODAObserve, Orient, Decide, Act或ReActReason Act模式。import subprocess import anthropic import os # 初始化 Claude 客户端 client anthropic.Anthropic(api_keyos.environ.get(“ANTHROPIC_API_KEY”)) # 定义系统提示词告诉模型它是什么角色能做什么 system_prompt “”” 你是一个运行在 Bash 环境中的智能助手。你可以通过执行命令来操作文件系统、运行程序、获取信息。 你的目标是根据用户请求规划并执行一系列 Bash 命令来完成任务。 在每次输出中你必须只返回一个有效的 Bash 命令。不要输出任何解释或额外文本。 如果任务完成请输出 ‘DONE’。 “”” # 初始化对话历史和上下文 messages [{“role”: “user”, “content”: “用户的任务是列出当前目录下所有的.txt文件并统计每个文件的行数。”}] # 核心循环开始 while True: # 1. 观察 (Observe): 将当前上下文历史对话最新结果发送给模型 response client.messages.create( model“claude-3-haiku-20240307”, # 选用一个快速且便宜的模型做实验 max_tokens500, systemsystem_prompt, messagesmessages ) # 提取模型生成的文本即它认为下一步该执行的命令 thought response.content[0].text.strip() print(f“[AI 思考]{thought}”) # 2. 决策 (Decide): 判断是否结束 if thought.upper() “DONE”: print(“任务完成”) break # 3. 行动 (Act): 在 Bash 中执行模型生成的命令 try: # 使用 subprocess.run 执行命令捕获输出和错误 result subprocess.run(thought, shellTrue, capture_outputTrue, textTrue, timeout30) # 组合标准输出和错误输出 command_output result.stdout if result.stderr: command_output “\n[错误]” result.stderr print(f“[命令输出]\n{command_output}”) # 4. 观察 (Observe Again): 将执行结果作为新的上下文反馈给模型 messages.append({“role”: “assistant”, “content”: thought}) messages.append({“role”: “user”, “content”: f“命令执行结果\n{command_output}\n\n请根据结果继续下一步。如果任务已完成请输出 ‘DONE’。否则输出下一个 Bash 命令。”}) except subprocess.TimeoutExpired: error_msg “命令执行超时30秒。” print(f“[错误]{error_msg}”) messages.append({“role”: “assistant”, “content”: thought}) messages.append({“role”: “user”, “content”: f“命令执行超时。请尝试一个更轻量的命令或检查命令是否正确。”}) except Exception as e: error_msg f“执行失败{e}” print(f“[错误]{error_msg}”) messages.append({“role”: “assistant”, “content”: thought}) messages.append({“role”: “user”, “content”: f“命令执行失败{error_msg}。请调整命令。”})3.2 关键代码行详解while True:这就是循环的起点。只要任务没完成模型没返回DONE就一直在里面转。system_prompt这是模型的“宪法”定义了它的身份、能力和行为规范。这里严格限制它只输出命令避免了它输出长篇解释干扰程序解析。这是让循环稳定运行的关键约束。client.messages.create(...)调用 Claude API将当前的对话历史messages发送过去请求模型生成下一步动作。thought.upper() ‘DONE’退出循环的检查点。模型需要自己判断任务是否达成并通过输出特定关键词来通知循环结束。subprocess.run(thought, shellTrue, ...)这是智能体的“手”。它将模型生成的文本thought作为 Bash 命令执行。shellTrue允许使用管道|、重定向等 Shell 特性。capture_outputTrue和textTrue确保我们能捕获命令执行的文本结果。timeout30是安全护栏防止某个命令无限期挂起。messages.append(...)这是构建对话历史的关键。每次都将模型的想法命令和环境的反馈命令输出或错误按顺序追加到messages列表中。这样模型在下一轮就能看到完整的“历史记录”从而做出基于上下文的决策。这就是智能体“记忆”的实现方式。3.3 跑起来看看一个完整交互示例假设我们运行上述脚本任务目标是“列出当前目录下所有的.txt文件并统计每个文件的行数”。交互过程可能如下简化输出[AI 思考]find . -name “*.txt” -type f [命令输出] ./notes.txt ./data/readme.txt [AI 思考]wc -l ./notes.txt ./data/readme.txt [命令输出] 10 ./notes.txt 25 ./data/readme.txt 35 total [AI 思考]DONE 任务完成看智能体自己“想”到了先用find命令定位文件再用wc -l统计行数最后判断任务完成。这就是一个完整的、自主的 OODA 循环。4. 从 Demo 到实战增强鲁棒性与扩展性上面的30行代码展示了核心原理但真要用于稍微复杂的场景它还很脆弱。我们需要把它变成一个更健壮、更可用的“智能体内核”。4.1 增强一更安全的命令执行与解析原始版本直接执行模型返回的任何文本这非常危险。我们需要一个“安全检查层”。import shlex def safe_execute_command(command_str): “””安全地执行命令并返回解析后的结果和状态。””” # 1. 基础清洗和检查 command_str command_str.strip() if not command_str or command_str.upper() ‘DONE’: return None, ‘TERMINATE’, ‘’ # 2. 危险命令黑名单可根据需要扩展 dangerous_keywords [‘rm -rf’, ‘dd’, ‘format’, ‘:(){ :|: };:’] # 最后一个是的fork炸弹 for kw in dangerous_keywords: if kw in command_str: return None, ‘DANGEROUS’, f“检测到危险命令关键字 ‘{kw}’已阻止执行。” # 3. 白名单或路径限制可选更严格 # allowed_commands [‘ls’, ‘cat’, ‘grep’, ‘find’, ‘wc’, ‘echo’] # first_cmd command_str.split()[0] # if first_cmd not in allowed_commands: # return None, ‘NOT_ALLOWED’, f“命令 ‘{first_cmd}’ 不在允许列表中。” # 4. 安全地执行 try: # 使用 shlex.split 可以更好地处理带引号和空格的参数 # 但对于需要管道等shell特性的命令仍需谨慎使用 shellTrue # 这里为演示仍使用 shellTrue但加入了危险检查 result subprocess.run(command_str, shellTrue, capture_outputTrue, textTrue, timeout30, cwd‘./workspace’) # 限制工作目录 output result.stdout if result.stderr: output “\n[STDERR]: “ result.stderr return output, ‘SUCCESS’, ‘’ except subprocess.TimeoutExpired: return None, ‘TIMEOUT’, ‘命令执行超时。’ except Exception as e: return None, ‘EXECUTION_ERROR’, str(e)在循环中我们不再直接subprocess.run而是调用safe_execute_command并根据返回的状态码SUCCESS,DANGEROUS,TIMEOUT等来决定如何构建给模型的反馈信息。这能有效防止智能体“自杀”或破坏系统。4.2 增强二给模型提供“工具”与上下文模型可能不知道系统里有哪些可用工具。我们可以在system_prompt或每轮对话中动态注入上下文信息。def get_environment_context(): “””获取当前环境的一些信息作为上下文提供给模型。””” context [] # 示例列出当前目录 try: ls_result subprocess.run([‘ls’, ‘-la’], capture_outputTrue, textTrue, cwd‘./workspace’) context.append(f“当前工作目录 (‘./workspace’) 内容\n{ls_result.stdout}”) except: pass # 示例检查 Python 版本 try: py_result subprocess.run([‘python3’, ‘—version’], capture_outputTrue, textTrue) context.append(f“Python 环境{py_result.stdout.strip()}”) except: pass return “\n”.join(context) # 在循环中可以将上下文信息插入到发给模型的用户消息中 env_context get_environment_context() user_query_with_context f“”” 环境上下文 {env_context} 用户任务{original_task} 请根据以上环境信息规划命令来完成用户任务。 “”” messages [{“role”: “user”, “content”: user_query_with_context}]这样模型就能知道当前目录下有什么文件、系统里装了Python从而生成更合理的命令比如python3 script.py而不是直接script.py。4.3 增强三处理复杂任务与状态管理对于多步骤复杂任务模型可能会“失忆”或偏离主线。我们需要更好的状态管理和任务分解。维护任务列表将大任务拆解成子任务列表模型每完成一个就标记一个。总结历史当对话轮次过多时Claude 的上下文窗口可能不够用。可以在达到一定轮次后主动对之前的对话历史进行总结将摘要作为新的上下文替换掉冗长的原始历史。设置最大轮次在while True循环中加入计数器防止因逻辑错误导致无限循环。max_iterations 20 iteration 0 while iteration max_iterations: iteration 1 # … 循环体 … if thought.upper() ‘DONE’: break else: print(“达到最大迭代次数任务可能未完成。”)4.4 扩展从 Bash 到通用工具调用智能体的能力不限于 Bash。这个模式可以扩展为“模型生成 JSON 指令 - 主程序解析并调用对应工具 - 返回工具结果”的通用框架。这就是Agent Harness、LangChain Tools等框架在抽象层面做的事情。# 伪代码示例通用工具调用循环 while task_not_done: # 模型生成结构化指令例如{“action”: “read_file”, “args”: {“path”: “./data.json”}} model_response llm_call(messages) # 解析指令 action model_response[“action”] args model_response[“args”] # 根据 action 类型调用不同的工具函数 if action “read_file”: result tool_read_file(args[“path”]) elif action “web_search”: result tool_web_search(args[“query”]) elif action “python_exec”: result tool_execute_python(args[“code”]) # … 其他工具 … # 将结果反馈给模型 messages.append({“role”: “user”, “content”: f“工具 ‘{action}’ 的执行结果{result}”})我们的while True Bash就是这个通用模式的一个特例其中action固定为“execute_bash”args就是命令字符串。理解了这一点你就掌握了智能体框架最核心的设计思想。5. 常见问题排查与调试心得当你亲手运行这个循环时肯定会遇到各种问题。下面是我在实测中总结的排查顺序照着这个思路走大部分问题都能定位。5.1 问题一模型不输出命令而是输出解释现象模型回复“好的我将首先使用ls命令…”而不是直接输出ls。原因system_prompt约束力不够或者对话历史中混入了模型的解释性回复。解决强化系统提示词在system_prompt中使用更严厉、更清晰的指令例如“你必须且只能输出一个有效的 Bash 命令。绝对不要输出任何思考过程、解释、道歉或 Markdown 格式。你的输出将被直接传递给 Bash 解释器执行。”解析时过滤在代码中对模型输出做后处理。如果输出包含反引号尝试提取反引号内的内容或者匹配常见的命令开头如$,#, 或单词开头如ls,cat。重置对话如果历史消息已经“污染”最简单的方法是重新开始一个新的对话会话。5.2 问题二命令执行失败或权限不足现象[错误]... command not found或Permission denied。排查顺序检查命令本身将模型生成的命令复制到你的终端里手动执行看是否报错。这能立刻区分是模型生成错误还是执行环境问题。检查工作目录你的 Python 脚本运行在哪subprocess.run的cwd参数是什么模型以为的“当前目录”可能和实际不符。建议在system_prompt中明确告知模型工作目录并在执行命令时固定cwd。检查环境变量某些命令如python,pip,node依赖于PATH环境变量。在 Python 中通过subprocess执行时继承的环境变量可能与你的交互式终端不同。可以尝试在命令中使用绝对路径如/usr/bin/python3。检查文件权限如果要操作文件确保 Python 进程有读写权限。5.3 问题三陷入死循环或任务无法完成现象模型不停地在几个命令间切换或者输出的命令与任务无关始终无法输出DONE。排查顺序检查任务描述给模型的任务是否清晰、无歧义尝试用更精确的语言重新描述任务。检查反馈信息提供给模型的“命令执行结果”是否清晰如果命令输出很长很乱模型可能无法理解。可以考虑对结果进行裁剪或总结后再反馈。引入人工检查点在循环中每执行 N 条命令后将当前状态打印出来并暂停等待用户确认是否继续。这有助于调试模型“思考”的过程。简化任务先用一个极其简单的任务如“用echo ‘hello’命令创建一个文件”测试循环是否能正常开始和结束再逐步增加复杂度。5.4 问题四API 调用失败或超时现象anthropic.APIConnectionError或anthropic.RateLimitError。解决确认网络确保你的网络环境可以访问 Anthropic API。确认 API Key检查ANTHROPIC_API_KEY环境变量是否设置正确是否有余额或调用额度。处理速率限制在代码中加入简单的重试逻辑和退避策略。import time from anthropic import RateLimitError def call_claude_with_retry(messages, max_retries3): for i in range(max_retries): try: response client.messages.create(modelMODEL, messagesmessages, max_tokens500) return response except RateLimitError: wait_time 2 ** i # 指数退避 print(f“速率限制等待 {wait_time} 秒后重试…”) time.sleep(wait_time) raise Exception(“达到最大重试次数API调用失败。”)5.5 调试技巧把中间过程可视化在开发初期一定要把关键信息打印出来print(f“ 迭代第 {iteration} 轮 ”) print(f“发送给模型的历史消息长度{len(str(messages))}”) print(f“模型原始回复{response}”) # 打印整个响应对象查看结构 print(f“提取的命令‘{thought}’”) print(f“命令执行状态{status}”) print(f“命令输出预览{output[:200]}…”) # 只打印前200字符避免刷屏通过观察这些日志你能清晰地看到智能体“思考-行动-观察”的每一步快速定位问题出在提示词、命令生成、命令执行还是结果反馈环节。6. 下一步超越 Bash理解智能体开发生态当你成功运行了这个基础循环并解决了几个坑之后你就已经掌握了智能体最本质的运作原理。接下来你可以以此为基石向更实用的方向探索接入更强大的模型将claude-3-haiku换成claude-3-5-sonnet或claude-3-opus观察任务规划和命令生成的准确性是否有质的提升。也可以尝试接入其他模型的 API如 OpenAI GPT-4o, DeepSeek-V3等只需替换掉client.messages.create那部分代码。注意务必使用对应平台支持的、正确的模型名称。扩展工具集将单一的 Bash 命令执行扩展为一个工具函数字典。让模型可以调用 Python 函数处理数据、调用 Requests 库查询网页、调用 Pillow 库处理图片。这就是一个微型的“工具调用Function Calling”智能体。引入框架现在你可以去学习像Agent Harness这样的框架了。你会理解它无非是提供了更标准的工具定义方式、更强大的记忆管理向量数据库、更灵活的任务规划器Plan-and-Execute, ReWOO以及便捷的部署监控界面。你亲手写的循环就是这些框架最核心的“引擎”。设计生产级应用思考如何将这个循环封装成一个长期运行的服务Daemon如何处理多用户并发如何持久化任务状态如何为工具调用增加更严格的权限控制和审计日志。这时你面临的挑战就从“如何让智能体动起来”变成了“如何让智能体安全、可靠、高效地服务”。这个从30行while True Bash开始的旅程最终通向的是构建真正能解决复杂问题的自主 AI 系统。理解了这个循环你就拿到了智能体开发世界的钥匙。
返回列表