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

资讯详情

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

从零构建极简LLM Agent:基于Bash工具的Dora项目实践

从零构建极简LLM Agent:基于Bash工具的Dora项目实践 最近在探索 LLM Agent 的实现时发现很多框架都相当复杂依赖众多对于想快速理解 Agent 核心机制或需要一个轻量级、可嵌入脚本的解决方案来说学习成本较高。如果你也遇到过类似困扰想用一个极简的“玩具”来摸清 Agent 的工作流程那么 Dora 这个项目可能会让你眼前一亮。它自称是一个“微小的 LLM Agent”其最大的特点就是仅依赖 Bash 作为工具。本文将带你从零开始深入剖析 Dora 的设计理念、核心代码并手把手教你如何运行、扩展它最终理解一个 LLM Agent 最本质的“思考-行动-观察”循环。本文适合对 LLM 和 AI Agent 概念有基本了解并希望动手实践、理解其底层机制的开发者。无论你是想为自己的项目添加简单的自动化能力还是学习 Agent 架构这篇文章都能提供一条清晰的路径。1. 背景与核心概念什么是 LLM Agent 与 Dora在深入代码之前我们有必要厘清几个核心概念这有助于理解 Dora 究竟在解决什么问题。LLM (大语言模型)如 GPT、Claude、Llama 等它们擅长理解和生成自然语言拥有丰富的知识但本质上是一个“静态”的文本预测模型。它无法直接操作外部世界如读写文件、执行命令、查询网络。Agent (智能体)可以理解为赋予 LLM “行动能力”的框架或程序。一个典型的 Agent 包含几个关键部分核心LLM负责“思考”和规划。工具Tools是 Agent 的手和脚用于执行具体操作如调用 API、运行命令、查询数据库。记忆Memory存储对话历史、工具执行结果为后续决策提供上下文。执行循环Loop协调以上组件通常遵循“思考 - 选择工具 - 执行工具 - 观察结果 - 再次思考”的流程直到任务完成。Dora 的定位Dora 是一个极简主义的 LLM Agent 实现。它的“极简”体现在工具集极简它唯一的工具就是Bash Shell。这意味着 Agent 的所有行动都通过执行 Bash 命令来完成。依赖极简核心逻辑用 Python 编写除了必要的 LLM API 调用库如openai外没有复杂的框架依赖。概念极简它清晰地展示了 Agent 最核心的执行循环剥离了复杂的路由、编排等高级特性是学习 Agent 原理的绝佳样板。为什么选择 Bash 作为工具Bash 是 Unix/Linux 系统的通用“粘合剂”。通过 BashAgent 可以文件操作ls,cat,grep,find,mv,cp,rm等。系统信息ps,top,df,free等。网络请求借助curl或wget。运行任何已安装的命令行程序如 Python 脚本、数据库客户端、Git 等。 这赋予了 Dora 理论上非常强大的操作能力但也带来了安全风险我们会在最佳实践部分重点讨论。2. 环境准备与版本说明要运行 Dora你需要准备一个基础的 Python 开发环境并确保能够访问一个 LLM 的 API。2.1 系统与工具要求操作系统推荐 Linux 或 macOS。Windows 用户可以使用 WSL2 (Windows Subsystem for Linux) 来获得完整的 Bash 环境。Python版本 3.8 或更高。这是运行 Dora 主控逻辑的环境。Bash版本 4.0。确保/bin/bash可用。Git用于克隆项目代码。2.2 获取 Dora 项目代码Dora 是一个开源项目我们可以直接从代码仓库获取。# 克隆项目到本地 git clone https://github.com/your-username/dora.git # 注意上述URL为示例请替换为真实的Dora项目仓库地址 cd dora由于这是一个“Show HN”项目其具体仓库地址可能需要从原始帖子中获取。假设项目结构如下dora/ ├── dora.py # Agent 核心逻辑 ├── requirements.txt # Python 依赖 ├── README.md └── examples/ # 使用示例2.3 安装 Python 依赖项目根目录下通常会有requirements.txt文件。# 创建并激活一个虚拟环境推荐 python -m venv venv source venv/bin/activate # Linux/macOS # venv\Scripts\activate # Windows (CMD) # venv\Scripts\Activate.ps1 # Windows (PowerShell) # 安装依赖 pip install -r requirements.txt典型的requirements.txt可能包含openai1.0.0 # 用于调用 OpenAI API # 或 anthropic, litellm 等具体看项目实现2.4 配置 LLM API 密钥Dora 需要通过 API 调用 LLM。你需要准备相应的 API Key。以 OpenAI 为例访问 OpenAI Platform 创建 API Key。将 Key 设置为环境变量。# 在终端中设置环境变量临时 export OPENAI_API_KEYyour-api-key-here # 或者更安全的方式是写入配置文件如 ~/.bashrc 或 ~/.zshrc然后 source 它 echo export OPENAI_API_KEYyour-api-key-here ~/.bashrc source ~/.bashrc重要请妥善保管你的 API Key不要将其提交到版本控制系统。3. 核心原理与代码拆解让我们打开dora.py看看这个微型 Agent 是如何工作的。我们会逐段分析其核心逻辑。3.1 整体架构与执行循环Dora 的核心是一个while循环它不断重复以下步骤构建提示Prompt将用户目标、对话历史、工具描述和上次工具执行结果组合成给 LLM 的提示。调用 LLM请求 LLM 根据当前上下文决定下一步是“最终回答”还是“执行某个 Bash 命令”。解析 LLM 响应LLM 的响应被期望遵循特定格式如 JSON以指明下一步动作。执行动作如果是执行命令则调用子进程运行 Bash 命令并捕获输出。观察结果将命令输出或错误作为新的观察结果并入下一轮循环的上下文。判断终止如果 LLM 决定给出最终答案则跳出循环返回结果。3.2 关键代码解析以下是基于典型实现的伪代码/代码片段分析# dora.py 核心片段示意 import subprocess import json # ... 其他导入 class Dora: def __init__(self, llm_client): self.llm llm_client self.memory [] # 存储对话和观察历史 def run_bash_command(self, command: str) - str: 执行 Bash 命令并返回输出。 try: # 安全警告这里直接执行用户LLM生成的命令存在高风险 result subprocess.run( command, shellTrue, # 使用 shell 执行 capture_outputTrue, textTrue, timeout30, # 设置超时防止卡死 # 可以考虑设置 cwd工作目录进行限制 ) if result.returncode 0: return result.stdout else: return fCommand failed with return code {result.returncode}:\n{result.stderr} except subprocess.TimeoutExpired: return Error: Command timed out. except Exception as e: return fError executing command: {e} def think(self, prompt: str) - dict: 调用 LLM并期望返回一个结构化的动作决策。 response self.llm.chat.completions.create( modelgpt-4o-mini, # 或 gpt-3.5-turbo messages[{role: user, content: prompt}], temperature0.1, # 低温度使输出更确定更易解析 # 关键使用 JSON 模式或 Function Calling 引导 LLM 返回结构化数据 response_format{ type: json_object } ) # 解析 LLM 返回的 JSON 字符串 try: action json.loads(response.choices[0].message.content) return action except json.JSONDecodeError: # 解析失败处理 return {action: answer, content: I couldnt parse my own thoughts. Let me try again.} def run(self, goal: str): 主执行循环。 self.memory.append(fGoal: {goal}) max_steps 10 # 防止无限循环 for step in range(max_steps): # 1. 构建提示 prompt self._build_prompt(goal, self.memory) # 2. 思考并决策 decision self.think(prompt) # 3. 执行动作 if decision.get(action) bash: command decision.get(command) print(f[Dora] Executing: {command}) observation self.run_bash_command(command) print(f[Observation]\n{observation}) self.memory.append(fAction: {command}) self.memory.append(fObservation: {observation}) elif decision.get(action) answer: final_answer decision.get(content) print(f[Dora] Final Answer: {final_answer}) return final_answer else: # 未知动作可能记录错误并尝试继续 print(f[Dora] Unknown action: {decision}) self.memory.append(fError: Unknown action {decision}) return Reached maximum steps without completing the goal.代码关键点分析run_bash_command: 使用subprocess.run执行命令。shellTrue是功能强大的根源也是安全漏洞的根源。think: 通过设定response_format或使用function calling引导 LLM 返回可解析的 JSON。这是 Agent 可靠性的关键。_build_prompt: 这个函数未在上方完整展示负责构建有效的系统提示和用户提示告诉 LLM 它可以做什么、格式要求以及当前上下文。一个设计良好的提示是 Agent 正常工作的前提。循环与记忆self.memory列表记录了所有步骤确保 LLM 拥有完整的上下文来进行连贯的决策。3.3 提示词工程Dora 的成功很大程度上依赖于给 LLM 的提示词。一个典型的系统提示可能如下你是一个名为 Dora 的助手可以执行 Bash 命令来帮助用户完成目标。 你必须严格遵守以下规则 1. 你只能通过执行 Bash 命令与系统交互。 2. 你每次只能做一个决定要么执行一个命令要么给出最终答案。 3. 你的响应必须是严格的 JSON 格式{action: bash, command: ls -la} 或 {action: answer, content: 这里是答案}。 4. 仔细分析之前的观察结果。如果命令失败了尝试不同的方法。 5. 不要执行危险命令如 rm -rf /, dd, :(){ :|: };: 等。 6. 当前工作目录是/home/user/projects。 你的目标是{user_goal} 以下是到目前为止的历史记录 {formatted_memory}这个提示词明确了角色、规则、输出格式和安全约束是引导 LLM 行为的关键。4. 完整实战运行与扩展 Dora现在让我们实际运行 Dora并尝试扩展它。4.1 基础运行示例假设我们已经配置好环境并且dora.py有一个可执行的main函数或简单的脚本入口。# 在 dora.py 末尾添加或查看已有的启动代码 if __name__ __main__: import os from openai import OpenAI client OpenAI(api_keyos.environ.get(OPENAI_API_KEY)) agent Dora(llm_clientclient) goal 找出当前目录下所有扩展名为 .py 的文件并统计它们的总行数。 result agent.run(goal) print(\n任务完成)在终端中运行python dora.py预期交互过程[Dora] Goal: 找出当前目录下所有扩展名为 .py 的文件并统计它们的总行数。 [Dora] Thinking... [Dora] Executing: find . -name *.py -type f [Observation] ./dora.py ./examples/test_script.py ./utils/helper.py [Dora] Thinking... [Dora] Executing: wc -l $(find . -name *.py -type f) [Observation] 120 ./dora.py 45 ./examples/test_script.py 30 ./utils/helper.py 195 total [Dora] Thinking... [Dora] Final Answer: 当前目录下共有 3 个 .py 文件总行数为 195 行。 任务完成这个过程清晰地展示了 Agent 的“思考-行动-观察”循环。4.2 扩展添加新的工具虽然 Dora 只有 Bash 工具但我们可以通过“包装” Bash 命令来创建更高级、更安全的“抽象工具”。例如添加一个安全的“文件读取”工具避免 LLM 直接使用cat /etc/passwd这样的命令。# 在 Dora 类中添加新方法 class Dora: # ... 之前的代码 ... def safe_read_file(self, filepath: str) - str: 安全地读取文件。限制路径防止目录遍历。 import os # 将路径规范化为绝对路径并限制在某个安全目录下例如当前项目目录 base_dir os.path.abspath(.) target_path os.path.abspath(os.path.join(base_dir, filepath)) # 安全检查确保目标路径在基准目录下 if not target_path.startswith(base_dir): return fError: Access to path {filepath} is not allowed. # 检查是否为文件 if not os.path.isfile(target_path): return fError: {filepath} is not a file or does not exist. # 可选检查文件大小防止读取超大文件 if os.path.getsize(target_path) 1024 * 1024: # 1MB return fError: File {filepath} is too large to read. try: with open(target_path, r, encodingutf-8) as f: return f.read() except Exception as e: return fError reading file: {e} # 然后需要修改 think 方法和提示词让 LLM 知道有这个新工具。 # 提示词中需要加入新的工具描述和调用格式例如 # “你可以使用 read_file 工具来安全地读取项目内的文件格式为 {action: read_file, path: relative/path/to/file}”相应地run方法中的动作执行部分也需要扩展def run(self, goal: str): # ... 循环开始 ... decision self.think(prompt) if decision.get(action) bash: # ... 执行 bash ... elif decision.get(action) read_file: # 处理新工具 path decision.get(path) observation self.safe_read_file(path) # ... 记录到 memory ... elif decision.get(action) answer: # ... 结束 ...通过这种方式你可以逐步构建一个更可控、更强大的工具集而底层依然可以利用 Bash 的威力。4.3 实战任务让 Dora 管理一个简单任务列表让我们设计一个更复杂的任务测试 Dora 的规划能力。任务“在/tmp/dora_test目录下创建一个名为tasks.txt的文件里面包含三行任务‘1. Buy milk’, ‘2. Read paper’, ‘3. Call mom’。然后读取这个文件并告诉我第三项任务是什么。”预期 Dora 的行动序列mkdir -p /tmp/dora_testcd /tmp/dora_testecho -e 1. Buy milk\n2. Read paper\n3. Call mom tasks.txtcat tasks.txt或sed -n 3p tasks.txt给出最终答案。运行这个任务可以观察 Dora 是如何将复杂目标分解为一系列 Bash 命令的。5. 常见问题与排查思路在运行和扩展 Dora 的过程中你可能会遇到以下问题问题现象可能原因解决思路LLM 不返回 JSON 格式提示词未明确要求 JSON或模型未在 JSON 模式下调用。1. 检查_build_prompt函数确保系统提示明确要求 JSON 格式。2. 检查 API 调用确认使用了response_format{ type: json_object }或类似的参数。3. 对于不支持 JSON 模式的模型可以使用 Function Calling 或在其回复后添加 JSON 解析重试逻辑。Agent 陷入死循环或执行无关命令提示词约束不够强记忆上下文过长导致模型混乱最大步数设置过高。1. 强化提示词中的规则例如“你必须专注于当前目标”“不要重复执行相同的命令”。2. 实现记忆窗口只保留最近 N 条历史或对记忆进行摘要。3. 合理设置max_steps如 10-20并在达到后终止。Bash 命令执行权限错误Agent 尝试执行需要更高权限的命令如sudo,systemctl。1. 在提示词中明确禁止使用sudo。2. 在run_bash_command中过滤或拦截危险命令。3. 在安全沙箱如 Docker 容器中运行整个 Dora 进程。命令执行超时或无响应命令本身长时间运行如ping或进入交互模式。1. 在subprocess.run中设置合理的timeout参数。2. 避免让 LLM 执行可能进入交互式提示的命令如mysql不加参数。可以在提示词中说明。API 调用费用激增或速度慢循环次数过多每次循环都调用 LLM使用了昂贵模型。1. 优化提示词让 LLM 的决策更准确减少无效循环。2. 对于简单任务使用更便宜、更快的模型如gpt-3.5-turbo。3. 实现本地缓存对相同思考请求缓存结果。ModuleNotFoundError: No module named openaiPython 依赖未正确安装。1. 确认虚拟环境已激活。2. 运行pip install -r requirements.txt。3. 检查requirements.txt中库的名称是否正确。OPENAI_API_KEY环境变量未设置未配置 API 密钥。1. 运行echo $OPENAI_API_KEY检查是否为空。2. 按照第 2.4 节重新设置环境变量并重启终端或source配置文件。6. 最佳实践与工程建议将 Dora 这样的原型用于学习或简单自动化是可以的但要考虑将其集成到更严肃的项目中必须关注以下工程实践6.1 安全第一沙箱与命令过滤绝对不要在生产环境或存有重要数据的机器上直接运行未经加固的 Dora。使用 Docker 沙箱在 Docker 容器内运行 Dora限制其对主机系统的访问。docker run -it --rm \ -v $(pwd):/workspace \ -w /workspace \ -e OPENAI_API_KEYyour_key \ python:3.11-slim \ bash -c pip install openai python dora.py实现命令拦截器在run_bash_command函数执行前对命令进行安全检查。def is_command_allowed(command: str) - bool: dangerous_patterns [ rm -rf, mkfs, dd, chmod 777, /dev/sda, :(){ :|: };:, # Fork 炸弹 sudo, su -, ] for pattern in dangerous_patterns: if pattern in command: return False return True限制执行目录和用户使用subprocess.run的cwd参数将命令执行限制在特定目录下并以非特权用户身份运行整个 Python 进程。6.2 提示词工程优化结构化输出坚持使用 JSON 模式或 Function Calling这是稳定解析 LLM 响应的基石。提供示例在系统提示中提供一两个{“action”: “bash”, “command”: “...”}和{“action”: “answer”, “content”: “...”}的示例让 LLM 更好地遵循格式。分步引导对于复杂任务可以在用户目标中暗示步骤例如“请先列出文件然后统计行数”。设定角色和边界明确告诉 LLM 它是什么一个助手不能做什么不执行危险命令不猜测密码。6.3 性能与成本设置步数限制防止因逻辑错误导致无限循环和 API 调用暴增。使用流式响应如果最终答案较长可以考虑使用 LLM 的流式响应提升用户体验。本地模型如果任务对推理能力要求不高可以考虑使用本地部署的小模型通过ollama,llama.cpp等消除 API 成本和延迟。6.4 可观测性与调试详细日志记录每一轮的完整提示、LLM 响应、执行命令和结果。这对于调试 Agent 的“思考”过程至关重要。可视化工具可以考虑将memory中的步骤输出为结构化的日志文件甚至用简单的 Web 界面展示 Agent 的执行轨迹。6.5 从 Dora 出发集成到更成熟的框架Dora 是一个出色的教学工具。理解了它的核心循环后你可以转向更成熟、功能更全的 Agent 框架它们提供了更多开箱即用的特性LangChain / LangGraph提供了丰富的工具集成、记忆管理、复杂工作流编排。AutoGen支持多智能体对话和协作。CrewAI专注于角色扮演和任务分工。 在这些框架中你可以将 Dora 的“Bash 工具”思想封装成一个Tool类进行复用。7. 总结Dora 项目以其极简的设计完美诠释了 LLM Agent 的核心精髓一个在“思考”LLM和“行动”工具间循环的智能系统。通过将 Bash 作为万能工具它展示了如何用最小的外部依赖赋予 LLM 强大的环境交互能力。我们从零开始完成了环境搭建、核心代码解读、实际运行和扩展实践。关键收获在于Agent 循环理解了“规划 - 执行 - 观察”这一基本模式是如何用代码实现的。提示词的核心地位Agent 的行为质量高度依赖于给 LLM 的指令和约束。安全是生命线尤其是当 Agent 拥有执行任意命令的能力时沙箱、过滤和权限控制必须放在首位。从原型到产品Dora 是学习的起点真正的生产级应用需要更健壮的错误处理、状态管理、可观测性和集成到更完善的框架中。动手修改 Dora 的代码添加一个新工具或者用不同的提示词解决一个新问题是巩固学习的最佳方式。这个小小的 Agent 就像一把钥匙帮你打开了构建更复杂、更智能自动化系统的大门。
返回列表