1. 引言

AI Agent(智能体)是当前 AI 应用开发中最热门的范式之一。一个 AI Agent 能够理解用户的目标,自主规划步骤,调用工具(如搜索引擎、计算器、文件系统)来完成任务,而不仅仅是生成一段文本。

CLI(命令行界面)是 Agent 最自然的载体之一。通过终端,Agent 可以轻松地执行系统命令、读写文件、调用 API,甚至管理整个开发流程。

本文将带你从零开始,使用 Python 和 LangChain 框架,一步步构建一个功能完整的 AI Agent CLI。你将学到:

  • Agent 的核心概念与运行原理
  • 如何定义 Agent 可以使用的工具
  • 如何构建 Agent 的思考与行动循环
  • 如何将 Agent 封装成一个可交互的命令行应用
  • 如何扩展 Agent 的能力

2. 环境准备与项目初始化

2.1 创建项目

首先,创建一个新的项目目录并初始化 Python 虚拟环境。

mkdir ai-agent-cli
cd ai-agent-cli
python3 -m venv venv
source venv/bin/activate  # Linux/macOS
# venv\Scripts\activate  # Windows

2.2 安装依赖

我们需要安装以下核心库:

  • langchain:LangChain 框架核心
  • langchain-openai:OpenAI 模型接口
  • openai:OpenAI Python 客户端
  • python-dotenv:管理环境变量
pip install langchain langchain-openai openai python-dotenv

2.3 配置 API Key

在项目根目录创建 .env 文件,填入你的 OpenAI API Key(或其他兼容的 LLM API Key):

OPENAI_API_KEY=sk-your-api-key-here
# 如果你使用其他模型,可以配置
# OPENAI_API_BASE=https://api.xxx.com/v1
# LLM_MODEL=gpt-4o-mini

3. 核心概念:Agent 的工作原理

在开始编码之前,理解 Agent 的核心循环至关重要。一个典型的 ReAct(Reasoning + Acting)Agent 遵循以下步骤:

  1. 思考(Thought):Agent 接收用户输入和当前状态,决定下一步该做什么。
  2. 行动(Action):Agent 选择一个工具并传入参数来执行。
  3. 观察(Observation):Agent 接收工具执行后的结果。
  4. 循环:Agent 根据观察结果继续思考,直到它认为任务完成,然后给出最终答案(Final Answer)。

这个循环由 LLM 的推理能力驱动。我们通过给 LLM 一个包含可用工具描述和调用格式的 Prompt,让它学会“思考-行动-观察”的模式。

4. 第一步:定义 Agent 的工具

工具是 Agent 与外部世界交互的接口。每个工具都需要有清晰的名称、描述和参数定义,这样 LLM 才能正确地选择和使用它。

4.1 创建工具模块

在项目根目录创建 tools.py 文件。

# tools.py
from langchain.tools import tool
import subprocess
import os
import json

@tool
def run_shell_command(command: str) -> str:
    """
    执行一个 shell 命令并返回输出结果。
    适用于运行脚本、编译代码、查看文件等。
    """
    try:
        result = subprocess.run(
            command,
            shell=True,
            capture_output=True,
            text=True,
            timeout=30
        )
        output = result.stdout
        if result.stderr:
            output += f"\n[STDERR]\n{result.stderr}"
        if result.returncode != 0:
            output += f"\n[Exit code: {result.returncode}]"
        return output
    except subprocess.TimeoutExpired:
        return "错误:命令执行超时(30秒)"
    except Exception as e:
        return f"错误:执行命令时发生异常 - {str(e)}"

@tool
def read_file(file_path: str) -> str:
    """
    读取指定文件的全部内容。文件路径应为绝对路径或相对于当前工作目录的路径。
    """
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            content = f.read()
        return content
    except FileNotFoundError:
        return f"错误:文件 '{file_path}' 未找到"
    except Exception as e:
        return f"错误:读取文件时发生异常 - {str(e)}"

@tool
def write_file(file_path: str, content: str) -> str:
    """
    将内容写入指定文件。如果文件不存在则创建,如果存在则覆盖。
    """
    try:
        with open(file_path, 'w', encoding='utf-8') as f:
            f.write(content)
        return f"成功:内容已写入 '{file_path}'"
    except Exception as e:
        return f"错误:写入文件时发生异常 - {str(e)}"

@tool
def list_directory(path: str = ".") -> str:
    """
    列出指定目录下的文件和文件夹。
    """
    try:
        items = os.listdir(path)
        result = []
        for item in items:
            full_path = os.path.join(path, item)
            if os.path.isdir(full_path):
                result.append(f"[DIR]  {item}")
            else:
                size = os.path.getsize(full_path)
                result.append(f"[FILE] {item} ({size} bytes)")
        return "\n".join(result) if result else "目录为空"
    except FileNotFoundError:
        return f"错误:目录 '{path}' 未找到"
    except Exception as e:
        return f"错误:列出目录时发生异常 - {str(e)}"

关键点

  • 使用 @tool 装饰器将函数标记为 LangChain 工具。
  • 函数的文档字符串(docstring)就是工具的描述,LLM 会用它来决定何时调用此工具。
  • 参数的类型注解和名称也很重要,LLM 会据此生成正确的参数。

5. 第二步:构建 Agent 核心

现在我们来创建 Agent 的核心逻辑。创建 agent.py 文件。

# agent.py
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain.agents import create_react_agent, AgentExecutor
from langchain.prompts import PromptTemplate
from tools import run_shell_command, read_file, write_file, list_directory

# 加载环境变量
load_dotenv()

def create_agent(verbose: bool = True):
    """
    创建并返回一个 AI Agent 执行器。
    """
    # 1. 初始化 LLM
    llm = ChatOpenAI(
        model=os.getenv("LLM_MODEL", "gpt-4o-mini"),
        temperature=0,
        api_key=os.getenv("OPENAI_API_KEY"),
        base_url=os.getenv("OPENAI_API_BASE"),
    )

    # 2. 定义工具列表
    tools = [
        run_shell_command,
        read_file,
        write_file,
        list_directory,
    ]

    # 3. 创建 ReAct Agent
    # 使用一个清晰的 Prompt 模板来指导 Agent 的行为
    prompt = PromptTemplate.from_template(
        """你是一个强大的 AI 助手,可以通过思考和调用工具来帮助用户完成各种任务。

        你拥有以下工具:
        {tools}

        工具名称:{tool_names}

        请严格按照以下格式进行思考和行动:

        Thought: 描述你当前的想法和计划。
        Action: 你要调用的工具名称,必须是 [{tool_names}] 中的一个。
        Action Input: 传递给工具的输入参数,必须是一个有效的 JSON 对象。
        Observation: 工具返回的结果。
        ...(重复 Thought/Action/Observation 循环)
        Thought: 我现在可以给出最终答案了。
        Final Answer: 对用户问题的最终回答。

        用户的问题是:{input}

        你的工作区是当前目录,你可以自由地创建、读取和修改文件。

        {agent_scratchpad}
        """
    )

    # 4. 创建 Agent
    agent = create_react_agent(
        llm=llm,
        tools=tools,
        prompt=prompt,
    )

    # 5. 创建 Agent 执行器
    agent_executor = AgentExecutor(
        agent=agent,
        tools=tools,
        verbose=verbose,  # 打印思考过程
        handle_parsing_errors=True,  # 处理 LLM 输出解析错误
        max_iterations=15,  # 防止无限循环
    )

    return agent_executor

代码解析

  • create_react_agent:LangChain 内置的 ReAct Agent 创建函数,它自动处理了 Prompt 的组装和输出解析。
  • AgentExecutor:负责驱动 Agent 的思考-行动循环,直到得到最终答案或达到最大迭代次数。
  • verbose=True:在终端打印 Agent 的思考过程,对调试和理解 Agent 行为非常有帮助。
  • handle_parsing_errors:当 LLM 输出格式不正确时,自动重试,提高鲁棒性。

6. 第三步:创建 CLI 交互界面

现在我们把 Agent 封装成一个交互式命令行应用。创建 cli.py 文件。

# cli.py
import sys
import readline  # 提供命令行历史记录和编辑功能
from agent import create_agent

def main():
    print("=" * 60)
    print("  AI Agent CLI - 你的智能命令行助手")
    print("  输入你的问题,Agent 将思考并调用工具来帮助你。")
    print("  输入 'exit' 或 'quit' 退出。")
    print("=" * 60)

    # 创建 Agent
    print("\n正在初始化 Agent...")
    agent_executor = create_agent(verbose=True)
    print("Agent 已就绪!\n")

    while True:
        try:
            # 获取用户输入
            user_input = input("\n👤 你: ").strip()

            if user_input.lower() in ['exit', 'quit', 'q']:
                print("👋 再见!")
                break

            if not user_input:
                continue

            # 调用 Agent
            print("\n🤖 Agent 正在思考...\n")
            response = agent_executor.invoke({"input": user_input})
            
            # 输出最终答案
            print(f"\n🤖 Agent: {response['output']}")

        except KeyboardInterrupt:
            print("\n\n👋 检测到中断,再见!")
            break
        except Exception as e:
            print(f"\n❌ 发生错误: {str(e)}")

if __name__ == "__main__":
    main()

7. 运行与测试

7.1 启动 CLI

在终端运行:

python cli.py

7.2 测试示例

启动后,你可以尝试以下命令来测试 Agent 的能力:

示例 1:文件操作

👤 你: 帮我创建一个名为 hello.py 的文件,内容是一个打印 "Hello, AI Agent!" 的 Python 脚本

示例 2:执行命令

👤 你: 运行刚才创建的 hello.py 文件

示例 3:组合任务

👤 你: 查看当前目录下有哪些文件,然后读取 hello.py 的内容

示例 4:复杂任务

👤 你: 创建一个名为 project 的目录,在里面创建一个 README.md 文件,写入项目简介,然后列出 project 目录的内容

8. 进阶:扩展 Agent 的能力

8.1 添加 Web 搜索工具

安装 duckduckgo-search 库:

pip install duckduckgo-search

tools.py 中添加:

from langchain.tools import tool
from duckduckgo_search import DDGS

@tool
def web_search(query: str) -> str:
    """
    使用 DuckDuckGo 搜索互联网。当你需要获取最新信息或查找资料时使用。
    """
    try:
        with DDGS() as ddgs:
            results = list(ddgs.text(query, max_results=5))
        if not results:
            return "未找到相关结果"
        formatted = []
        for i, r in enumerate(results, 1):
            formatted.append(f"{i}. {r['title']}\n   {r['href']}\n   {r['body']}")
        return "\n\n".join(formatted)
    except Exception as e:
        return f"搜索失败: {str(e)}"

然后在 agent.pytools 列表中加入这个新工具。

8.2 添加代码执行沙箱

为了安全地执行用户代码,可以使用 Docker 容器。这里提供一个简单的本地 Python 执行工具(注意安全风险):

@tool
def run_python_code(code: str) -> str:
    """
    执行一段 Python 代码并返回输出。适用于快速验证算法或数据处理。
    注意:此工具会直接执行代码,请确保代码来源可信。
    """
    try:
        import io
        import sys
        from contextlib import redirect_stdout, redirect_stderr
        
        f_stdout = io.StringIO()
        f_stderr = io.StringIO()
        
        with redirect_stdout(f_stdout), redirect_stderr(f_stderr):
            exec(code, {"__builtins__": __builtins__})
        
        output = f_stdout.getvalue()
        if f_stderr.getvalue():
            output += f"\n[STDERR]\n{f_stderr.getvalue()}"
        return output if output else "代码执行成功,无输出。"
    except Exception as e:
        return f"代码执行错误: {str(e)}"

8.3 添加记忆能力

默认情况下,Agent 每次调用都是独立的。要让它记住对话历史,可以添加记忆组件:

# agent.py 修改
from langchain.memory import ConversationBufferMemory
from langchain.agents import create_react_agent, AgentExecutor

def create_agent_with_memory(verbose: bool = True):
    # ... 初始化 llm 和 tools ...
    
    memory = ConversationBufferMemory(
        memory_key="chat_history",
        return_messages=True
    )
    
    agent_executor = AgentExecutor(
        agent=agent,
        tools=tools,
        verbose=verbose,
        handle_parsing_errors=True,
        max_iterations=15,
        memory=memory,  # 添加记忆
    )
    
    return agent_executor

9. 完整项目结构

ai-agent-cli/
├── .env                # 环境变量(API Key 等)
├── .gitignore          # Git 忽略文件
├── cli.py              # 命令行交互入口
├── agent.py            # Agent 核心逻辑
├── tools.py            # 工具定义
└── requirements.txt    # 依赖列表

requirements.txt 内容:

langchain>=0.3.0
langchain-openai>=0.2.0
openai>=1.0.0
python-dotenv>=1.0.0
duckduckgo-search>=6.0.0  # 可选,用于 Web 搜索

10. 总结与下一步

恭喜!你已经从零实现了一个功能完整的 AI Agent CLI。这个 Agent 能够:

  • 理解自然语言指令
  • 自主规划并调用工具(执行命令、读写文件、搜索网络)
  • 通过思考-行动循环完成复杂任务
  • 在终端中提供交互式体验

下一步你可以探索的方向

  1. 支持更多 LLM:通过 LangChain 的模型抽象,可以轻松切换为 Claude、Gemini 或本地模型(如 Ollama)。
  2. 添加更多工具:数据库查询、API 调用、Git 操作等。
  3. 改进用户体验:添加流式输出、进度条、彩色输出等。
  4. 安全沙箱:使用 Docker 或子进程隔离来安全执行代码。
  5. 持久化记忆:将对话历史保存到文件或数据库。
  6. 多 Agent 协作:创建多个专业 Agent(如代码 Agent、搜索 Agent)协同工作。

这个 CLI 框架为你提供了一个坚实的基础,你可以根据自己的需求不断扩展和优化它。现在,打开终端,开始构建你的 AI Agent 吧!

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐