1. 引言

AI Agent 正在成为大模型应用的主流形态。相比一次性的问答,Agent 能够自主规划、调用工具、循环执行,直到完成一个复杂目标。而 CLI(命令行界面)是体验 Agent 能力最直接、最轻量的方式:没有前端、没有服务端,一个终端窗口就能跑起来。

本文将从零开始,手把手实现一个可运行的 AI Agent CLI。我们会先明确 Agent 的核心组成,再逐步搭建项目、实现工具调用循环、接入大模型,最后封装成命令行工具。全程使用 Python,代码可直接复制运行。

2. Agent 的核心概念

在动手写代码之前,先厘清几个关键概念。

2.1 什么是 Agent

Agent 是一个能感知环境、做出决策并采取行动的智能体。在 LLM 语境下,Agent 通常指:大模型作为「大脑」,通过循环调用工具(Tool)来完成任务。

2.2 ReAct 模式

ReAct(Reasoning + Acting)是 Agent 最经典的实现范式,核心循环如下:

  1. 模型根据当前状态进行推理(Thought)
  2. 模型决定调用哪个工具、传入什么参数(Action)
  3. 程序执行工具并返回结果(Observation)
  4. 模型根据观察继续推理,直到给出最终答案(Final Answer)

用户输入目标

模型推理 Thought

决定调用工具 Action

执行工具返回结果 Observation

输出最终答案 Final Answer

2.3 工具(Tool)

工具是 Agent 与外部世界交互的接口。一个工具通常包含:名称、描述、参数定义(JSON Schema)、执行函数。模型通过描述来决定何时调用、如何传参。

3. 项目初始化

我们先创建项目结构和虚拟环境。

mkdir ai-agent-cli
cd ai-agent-cli
python -m venv .venv
source .venv/bin/activate  # Windows 使用 .venv\Scripts\activate

安装依赖:

pip install openai python-dotenv

创建项目文件:

ai-agent-cli/
├── agent/
│   ├── __init__.py
│   ├── core.py       # Agent 主循环
│   ├── tools.py      # 工具定义
│   └── llm.py        # 大模型调用封装
├── main.py           # CLI 入口
└── .env              # API Key 配置

4. 定义工具层

我们先实现工具层。这里以「获取当前时间」和「计算器」两个工具为例,演示如何定义可被模型调用的工具。

# agent/tools.py
import datetime
import json
from typing import Any, Callable, Dict


def get_current_time() -> str:
    """获取当前日期和时间"""
    return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")


def calculator(expression: str) -> str:
    """计算数学表达式,如 '1 + 2 * 3'"""
    # 安全起见,只允许数字和运算符
    allowed = set("0123456789+-*/(). ")
    if not all(c in allowed for c in expression):
        return "错误:表达式包含非法字符"
    try:
        result = eval(expression, {"__builtins__": {}}, {})
        return str(result)
    except Exception as e:
        return f"计算错误:{e}"


# 工具注册表:名称 -> (函数, 描述, 参数Schema)
TOOLS: Dict[str, Dict[str, Any]] = {
    "get_current_time": {
        "function": get_current_time,
        "description": "获取当前日期和时间",
        "parameters": {
            "type": "object",
            "properties": {},
        },
    },
    "calculator": {
        "function": calculator,
        "description": "计算数学表达式",
        "parameters": {
            "type": "object",
            "properties": {
                "expression": {
                    "type": "string",
                    "description": "数学表达式,如 '1 + 2 * 3'",
                }
            },
            "required": ["expression"],
        },
    },
}


def execute_tool(name: str, arguments: Dict[str, Any]) -> str:
    """根据名称和参数执行工具"""
    if name not in TOOLS:
        return f"错误:未知工具 {name}"
    func: Callable = TOOLS[name]["function"]
    try:
        result = func(**arguments)
        return str(result)
    except Exception as e:
        return f"工具执行失败:{e}"

5. 封装大模型调用

接下来封装 OpenAI 兼容接口的调用。这里使用 openai 库,通过环境变量读取 API Key 和 Base URL,方便对接不同厂商。

# agent/llm.py
import os

from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()

client = OpenAI(
    api_key=os.getenv("OPENAI_API_KEY"),
    base_url=os.getenv("OPENAI_BASE_URL", "https://api.openai.com/v1"),
)

MODEL = os.getenv("OPENAI_MODEL", "gpt-4o-mini")


def chat(messages: list, tools: list | None = None):
    """调用大模型,支持工具调用"""
    kwargs = {"model": MODEL, "messages": messages}
    if tools:
        kwargs["tools"] = tools
    response = client.chat.completions.create(**kwargs)
    return response.choices[0].message

.env 文件中配置:

OPENAI_API_KEY=sk-xxxx
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_MODEL=gpt-4o-mini

6. 实现 Agent 主循环

这是整个 CLI 的核心。我们实现 ReAct 循环:把工具定义传给模型,模型返回工具调用请求时,我们执行工具并把结果追加到消息历史,然后再次调用模型,直到模型给出最终文本回复。

# agent/core.py
import json

from .llm import chat
from .tools import TOOLS, execute_tool

SYSTEM_PROMPT = """你是一个智能助手,可以通过调用工具来完成任务。
请根据用户的问题,自主决定是否调用工具以及调用哪个工具。
当工具返回结果后,结合结果继续推理,直到给出最终答案。"""


def build_tool_schemas() -> list:
    """把工具注册表转换为 OpenAI 工具格式"""
    schemas = []
    for name, meta in TOOLS.items():
        schemas.append(
            {
                "type": "function",
                "function": {
                    "name": name,
                    "description": meta["description"],
                    "parameters": meta["parameters"],
                },
            }
        )
    return schemas


def run_agent(user_input: str, max_steps: int = 5) -> str:
    """运行 Agent 主循环,返回最终回答"""
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": user_input},
    ]
    tool_schemas = build_tool_schemas()

    for step in range(max_steps):
        print(f"\n[Step {step + 1}] 调用模型...")
        message = chat(messages, tools=tool_schemas)

        # 模型没有工具调用请求,说明已给出最终答案
        if not message.tool_calls:
            return message.content or "(模型未返回内容)"

        # 把模型的工具调用请求加入历史
        messages.append(
            {
                "role": "assistant",
                "content": message.content,
                "tool_calls": [
                    {
                        "id": tc.id,
                        "type": "function",
                        "function": {
                            "name": tc.function.name,
                            "arguments": tc.function.arguments,
                        },
                    }
                    for tc in message.tool_calls
                ],
            }
        )

        # 逐个执行工具,并把结果加入历史
        for tc in message.tool_calls:
            name = tc.function.name
            args = json.loads(tc.function.arguments or "{}")
            print(f"  调用工具: {name}({args})")
            result = execute_tool(name, args)
            print(f"  工具返回: {result}")
            messages.append(
                {
                    "role": "tool",
                    "tool_call_id": tc.id,
                    "content": result,
                }
            )

    return "已达到最大步数,任务未完成。"

7. 编写 CLI 入口

最后,把 Agent 封装成命令行工具。支持两种模式:单次提问和交互式对话。

# main.py
import sys

from agent.core import run_agent


def interactive():
    """交互式对话模式"""
    print("AI Agent CLI 已启动,输入 exit 退出。")
    while True:
        user_input = input("\n你: ").strip()
        if user_input.lower() in ("exit", "quit"):
            break
        if not user_input:
            continue
        answer = run_agent(user_input)
        print(f"\nAgent: {answer}")


def main():
    if len(sys.argv) > 1:
        # 单次提问模式:python main.py "你的问题"
        question = " ".join(sys.argv[1:])
        print(run_agent(question))
    else:
        interactive()


if __name__ == "__main__":
    main()

8. 运行与测试

现在可以运行了。先试单次提问:

python main.py "现在几点了?"

预期输出类似:

[Step 1] 调用模型...
  调用工具: get_current_time({})
  工具返回: 2026-09-15 17:25:24
[Step 2] 调用模型...
Agent: 现在是 2026 年 9 月 15 日 17:25:24。

再试计算器:

python main.py "计算 (12 + 34) * 5 的结果"

再试交互模式:

python main.py

9. 扩展思路

到这里,一个最小可用的 AI Agent CLI 已经完成。你可以在此基础上继续扩展:

  • 增加更多工具:在 TOOLS 注册表中添加即可,如文件读写、网络请求、数据库查询。
  • 支持多轮记忆:把历史对话持久化到本地文件,实现跨会话记忆。
  • 流式输出:使用 stream=True 让模型回复逐字打印,体验更流畅。
  • 错误重试:当工具调用参数解析失败时,把错误信息回传给模型让其自我修正。
  • 子 Agent:让一个 Agent 调用另一个 Agent,实现任务分解与协作。

10. 总结

本文从零实现了一个 AI Agent CLI,核心要点如下:

  1. Agent 的本质是「模型 + 工具 + 循环」。
  2. ReAct 模式通过 Thought → Action → Observation 的循环完成任务。
  3. 工具层用统一的注册表管理,模型通过 JSON Schema 感知工具。
  4. 主循环的关键是维护消息历史,把工具调用和结果正确回传给模型。

整个实现不到 200 行代码,却具备了 Agent 最核心的能力。你可以在此基础上不断叠加功能,打造属于自己的 AI 助手。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐