1、什么是 AI Agent
一句话定义:AI Agent = LLM(大语言模型,Large Language Model) + 工具调用 + 循环决策。
一个 AI Agent 本质上是一个以 LLM 为大脑、以工具为手脚、以循环为心跳的自主系统。它接收用户指令,自主决定“要不要调工具、调哪个工具、调完之后怎么办”,直到给出最终答案。
也有人这样表示,Agent = LLM + 上下文 + 工具,并采取更直观的说法为 Agent = 大脑 + 眼睛 + 手脚。大脑负责思考和决策,眼睛接收环境提供的观察,手脚将决策转化为作用于环境的行动。
这里的上下文,就是丢给 LLM 的内容,内容随着工具的调用动态地变化,同时可附载更多有比如记忆、历史信息,从而让事情逐步往前推动。
2、实现 AI Agent 的主流架构 ReAct
ReAct(Reasoning + Acting,推理 + 行动),是实现 AI Agent 最经典、最主流的架构范式之一(这也意味着 Agent 不只有 ReAct 这一种实现方案)。它采用 Thought(思考)→ Action(行动)→ Observation(观察) 的迭代闭环,将大模型推理与外部工具调用交替执行,依靠工具返回的真实反馈持续调整决策,直至任务完成,能够有效缓解大模型幻觉问题,也是 LangChain、OpenAI Agent SDK 等开发组件内置支持的基础范式。
3、Agent 框架骨架之万能循环
一般 Agent 框架的共同核心:
while 没有得到最终答案:1. 组装上下文 = 系统提示词 + 历史消息 + 当前输入(含可用工具列表)2. LLM 决策 → 输出: 自然语言回答 OR 工具调用3. 如果是工具调用: 执行工具, 把结果回填到消息列表4. 如果是自然语言: 退出循环, 返回给用户
这时,容易想到死循环,因此在循环中加以最大循环次数的限制是需要的。
4、工具调用的约定
与大模型沟通中对于工具调用的方式,OpenAI 在 2023 年 6 月推出了 Function Calling,为 API 约定了特定的参数属性,包括工具清单描述(tools 哪些工具可用)、工具调用清单(tool_calls 回复哪些工具需要调用)以及调用结果描述(执行完工具后,把结果作为 role="tool" 的消息追加到消息列表)。
5、命令行助手练手
命令行助手这个 AI Agent 中,工具清单描述属性 tools 如果逐个命令去描述,不仅庞大,而且描述清楚是很难的。但实际上,AI 更清楚具体命令所需要的东西,而我们可以提供执行命令的命令让 AI 去发挥,使得工具清单描述只需要一项。这让结构变得简单,功能却可以十分强大。这时,也容易想到安全问题。是的,存在执行危险命令的风险,这也是一个成熟的面向大众的产品并不容易的原因之一。
看看工具清单描述。
RUN_BASH_TOOL = {"name": "run_bash","description": ("在当前工作目录的 Git Bash 中执行一条 shell 命令,并返回命令的标准输出和错误信息。""支持所有常见 bash 命令:ls, cat, grep, find, git, python, pip, mkdir, cp, mv 等。""可以执行多步任务:先看目录结构,再读文件,再修改文件,等等。""【重要】如果命令会产生交互式提示(如需要输入 y/n),请加 -y 或 --yes 等非交互参数。"),"parameters": {"type": "object","properties": {"command": {"type": "string","description": "要执行的 bash 命令字符串,例如 'ls -la'、'cat lesson-01.md | head -20'",}},"required": ["command"],},}
有了基本原理的了解与需求的确定,可以比较容易在 AI 辅助编码下生成 Python 代码。运行效果如下:
👤 你想让 Agent 做什么?(输入 quit 退出)> 本机的外网 ip 是什么============================================================🔄 第 1 轮 LLM 调用============================================================⚡ 调用工具 run_bash:curl ifconfig.me📋 工具返回(预览):222.244.218.30[stderr]% Total % Received % Xferd Average Speed Time Time Time CurrentDload Upload Total Spent Left Speed0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0100 14 100 14 0 0 18 0 --:--:-- --:--:-- --:--:-- 18100 14 100 14 0 0 18 0 --:--:-- --:-...============================================================🔄 第 2 轮 LLM 调用============================================================💭 LLM 思考/回答:我执行了 `curl ifconfig.me` 命令来获取本机的外网 IP 地址。您的本机外网 IP 地址是:**222.244.218.30**✅ LLM 选择不再调用工具,循环结束。============================================================🎯 最终结果:我执行了 `curl ifconfig.me` 命令来获取本机的外网 IP 地址。您的本机外网 IP 地址是:**222.244.218.30**============================================================
访问网络,写文件,都不在话下,因为 bash 下的命令丰富着。
6、附提示词与 Demo 代码
本示例在 trae 中实现,使用的提示词为:
对于AI Agent,如果我的 tool 就定义为 git bash 中运行命令,这样定义一个工具,后边的工具调用都很灵活了。刚好 bit bash 那些命令不熟悉,这样一来,一个自然语言执行命令的工具就出来了。请分析我这个描述,就形成一个 python 实现,模型我将使用 deepseek,apikey 位置预留即可。
完整示例代码如下:
"""bash_agent.py - 单工具 AI Agent:用自然语言驱动 Git Bash 执行命令核心设计思想:AI Agent = LLM + 工具调用 + 循环决策只定义 1 个工具 run_bash(command),LLM 把自然语言转化为 bash 命令执行,再根据执行结果决定下一步,直到完成用户任务。模型:DeepSeek(兼容 OpenAI SDK 格式)工具:run_bash(command: str) -> str 在 Git Bash 中执行命令并返回 stdout/stderr"""import jsonimport subprocessimport osfrom openai import OpenAI# ============================================================# 配置区:DeepSeek API Key 预留,替换为自己的 Key 即可运行# ============================================================DEEPSEEK_API_KEY = "sk-24179d----------" # <-- 替换你的 API KeyDEEPSEEK_BASE_URL = "https://api.deepseek.com/v1"DEEPSEEK_MODEL = "deepseek-chat" # 或 deepseek-coder# Windows 下 Git Bash 路径(根据实际安装位置调整)GIT_BASH_PATH = r"C:\Program Files\Git\bin\bash.exe"# 危险命令黑名单(LLM 再聪明也需要安全护栏)DANGEROUS_PATTERNS = ["rm -rf /", "rm -rf /*", "rm -rf ~","mkfs", "dd if=", ":(){ :|:& };:", "fork bomb","del /s /q C:\\", "format C:",]MAX_ITERATIONS = 15# ============================================================# 工具实现:run_bash —— 在 Git Bash 里跑一条命令# ============================================================def run_bash(command: str) -> str:"""在 Git Bash(Windows)中执行一条 shell 命令,返回 stdout 和 stderr。Args:command: 要执行的 bash 命令,如 "ls -la"、"cat README.md"Returns:命令的标准输出 + 标准错误(合并为一个字符串)"""for pattern in DANGEROUS_PATTERNS:if pattern in command:return f"[安全拦截] 检测到危险命令模式 '{pattern}',已拒绝执行。"try:if os.name == "nt" and os.path.exists(GIT_BASH_PATH):result = subprocess.run([GIT_BASH_PATH, "-c", command],capture_output=True,text=True,timeout=60,cwd=os.getcwd(),)else:result = subprocess.run(["bash", "-c", command],capture_output=True,text=True,timeout=60,cwd=os.getcwd(),)output = ""if result.stdout:output += result.stdoutif result.stderr:output += ("\n[stderr]\n" if output else "[stderr]\n") + result.stderrif result.returncode != 0:output += f"\n[exit code: {result.returncode}]"return output.strip() or "(命令无输出)"except subprocess.TimeoutExpired:return "[错误] 命令执行超时(60秒上限)"except FileNotFoundError:return f"[错误] 未找到 Bash。请确认 Git 已安装,或检查路径:{GIT_BASH_PATH}"except Exception as e:return f"[执行异常] {type(e).__name__}: {e}"# ============================================================# 工具定义(Function Calling 的 JSON Schema)# ============================================================RUN_BASH_TOOL = {"name": "run_bash","description": ("在当前工作目录的 Git Bash 中执行一条 shell 命令,并返回命令的标准输出和错误信息。""支持所有常见 bash 命令:ls, cat, grep, find, git, python, pip, mkdir, cp, mv 等。""可以执行多步任务:先看目录结构,再读文件,再修改文件,等等。""【重要】如果命令会产生交互式提示(如需要输入 y/n),请加 -y 或 --yes 等非交互参数。"),"parameters": {"type": "object","properties": {"command": {"type": "string","description": "要执行的 bash 命令字符串,例如 'ls -la'、'cat lesson-01.md | head -20'",}},"required": ["command"],},}TOOLS = [RUN_BASH_TOOL]# ============================================================# System Prompt:告诉 LLM 它的身份和行为规范# ============================================================SYSTEM_PROMPT = """你是一个 Bash 命令执行助手 Agent。你拥有一个工具 run_bash,可以在用户的 Git Bash 中执行任意命令。## 你的工作方式(ReAct 循环)1. 收到用户的自然语言请求后,先思考需要执行哪些命令2. 调用 run_bash 工具执行命令3. 根据命令的输出结果,决定下一步是继续调用工具还是给出最终回答4. 重复以上步骤,直到任务完成## 行为准则1. **先看后改**:修改文件前,先用 cat/ls 查看现有内容和目录结构,不要瞎猜2. **小步前进**:一次不要执行太复杂的命令链,拆成几步便于你根据中间结果调整3. **善用组合**:学会用管道 |、grep、head、tail、wc 等过滤输出,避免把整个大文件塞给你4. **错误处理**:如果命令报错,仔细读错误信息,调整后重试,不要直接说"失败了"5. **安全意识**:- rm -rf / 等破坏性命令已被系统拦截,你无法调用- 删除文件前先用 ls 确认路径- 覆盖文件前先看原文件内容6. **结果汇报**:完成任务后,用简洁的中文总结你做了什么、关键结果是什么,不需要把所有命令历史贴出来## 当前工作目录用户的 lessons 目录,包含课程 markdown 文件和示例代码。"""# ============================================================# Agent 核心循环:万能循环的 2.0 版本# ============================================================def agent(user_prompt: str, *, debug: bool = True) -> str:"""启动一个带单工具(run_bash)的 ReAct Agent。Args:user_prompt: 用户的自然语言任务描述debug: 是否打印每一轮的思考/行动/观察日志Returns:Agent 最终给出的自然语言回答"""client = OpenAI(api_key=DEEPSEEK_API_KEY, base_url=DEEPSEEK_BASE_URL)messages = [{"role": "system", "content": SYSTEM_PROMPT},{"role": "user", "content": user_prompt},]tool_handlers = {"run_bash": run_bash}for i in range(MAX_ITERATIONS):if debug:print(f"\n{'='*60}")print(f"🔄 第 {i+1} 轮 LLM 调用")print(f"{'='*60}")resp = client.chat.completions.create(model=DEEPSEEK_MODEL,messages=messages,tools=[{"type": "function", "function": t} for t in TOOLS],temperature=0.2,)msg = resp.choices[0].messagemessages.append(msg)if msg.content and debug:print(f"💭 LLM 思考/回答:\n{msg.content}\n")if not msg.tool_calls:if debug:print("✅ LLM 选择不再调用工具,循环结束。")return msg.content or "(无回答内容)"for call in msg.tool_calls:func_name = call.function.nametry:args = json.loads(call.function.arguments)except json.JSONDecodeError as e:args = {}result = f"[参数解析失败] JSON 格式错误:{e}\n原始 arguments:{call.function.arguments}"if debug:print(f"❌ {result}")else:if func_name not in tool_handlers:result = f"[工具不存在] 没有名为 '{func_name}' 的工具"if debug:print(f"❌ {result}")else:cmd = args.get("command", "")if debug:print(f"⚡ 调用工具 {func_name}:{cmd}")result = tool_handlers[func_name](**args)if debug:preview = result[:400] + ("..." if len(result) > 400 else "")print(f"📋 工具返回(预览):\n{preview}\n")messages.append({"role": "tool","tool_call_id": call.id,"content": str(result),})return f"⚠️ 已达到最大迭代次数({MAX_ITERATIONS} 轮),任务未完成。"# ============================================================# 交互式运行:python bash_agent.py# ============================================================if __name__ == "__main__":print("=" * 60)print(" 🤖 Bash Agent - 用自然语言驱动 Git Bash")print(f" 模型:{DEEPSEEK_MODEL} 最大轮数:{MAX_ITERATIONS}")print("=" * 60)if DEEPSEEK_API_KEY.startswith("sk-your-"):print("\n⚠️ 请先在 bash_agent.py 顶部把 DEEPSEEK_API_KEY 换成你自己的 Key!")print(" 获取地址:https://platform.deepseek.com/\n")examples = ["列出当前目录下所有的 md 文件","统计 lesson-01.md 有多少行,包含多少个 'Agent' 关键词","看看 lesson-02.md 的前 30 行讲了什么",]print("📌 可以试试这些示例:")for idx, ex in enumerate(examples, 1):print(f" {idx}. {ex}")print()while True:try:user_input = input("👤 你想让 Agent 做什么?(输入 quit 退出)\n> ").strip()except (EOFError, KeyboardInterrupt):print("\n👋 再见!")breakif not user_input:continueif user_input.lower() in ("quit", "exit", "q"):print("👋 再见!")breakanswer = agent(user_input, debug=True)print(f"\n{'='*60}")print(f"🎯 最终结果:\n{answer}")print(f"{'='*60}\n")
夜雨聆风