30分钟带你从0手搓一个AI-Cli命令行工具

这篇教程就是带大家完整的手搓一个 ai-cli,可以调用硅基流动的AI模型,实现一个AI对话的命令行工具。

什么是 cli?

CLI 是 Command-Line Interface(命令行界面)的缩写。它是一种通过文本命令与计算机程序交互的方式,比如你在终端里输入的 git clone、npm install、python script.py 都属于 CLI 程序。

与之相对的是 GUI(图形用户界面),也就是你用鼠标点击窗口、按钮的那种交互方式。

特点

  • 高效 熟练后操作速度远超鼠标点击
  • 可脚本化 可以写 Shell 脚本批量执行命令
  • 资源占用少 不需要渲染图形界面
  • 远程友好 通过 SSH 连接服务器后,只能使用 CLI
  • 学习曲线 需要记忆命令和参数

一个 CLI 程序的基本组成

一个典型的 CLI 工具通常包含:

  • 入口点:用户输入的命令名称,比如 mycli

  • 参数解析:处理用户输入的选项(--help、-v)和参数(input.txt)

  • 核心逻辑:实际执行的功能

  • 输出:打印结果到终端(stdout / stderr)

  • 退出码:0 表示成功,非 0 表示失败(方便脚本判断)

使用场景

几乎每一个开发者、运维、数据科学家都会用到 CLI。常见场景包括:

  1. 开发工具 ● 代码版本控制(git) ● 包管理(npm, pip, go mod) ● 构建编译(make, webpack, tsc) ● 代码检查与测试(eslint, pytest)

  2. 服务器运维 ● 通过 SSH 连接远程 Linux 服务器后,只有 CLI 可用 ● 管理进程、查看日志、配置网络、定时任务(cron)

  3. 数据处理 ● 用 grep, awk, sed 处理文本日志 ● 用 ffmpeg 命令行转码视频 ● 用 jq 处理 JSON 数据

  4. AI / 大模型调用 ● 很多 AI 服务提供 CLI 工具,比如 ollama run llama3 ● 你可以自己写一个 CLI,调用 OpenAI / 硅基流动的 API,实现"对话式编程"

  5. 自动化脚本 ● 每天凌晨自动备份数据库 ● 批量重命名 1000 个文件 ● 自动部署项目到服务器

  6. 你自制的工具 ● 一个"代码生成器":输入需求描述,自动调用 AI 生成代码文件 ● 一个"图片压缩工具":imgmin input.png output.jpg ● 一个"日报生成器":report --date 2026-04-16

如何开发一个 CLI?

开发一个 CLI 程序,本质上就是写一个接收命令行参数的程序,然后把它变成可以直接在终端敲命令运行。 核心步骤:

  1. 选择一门语言(Node.js / Python / Go / Rust / Shell 等)
  2. 解析命令行参数(用户输入的 --name、-v 等)
  3. 实现核心逻辑(你要让这个命令做什么)
  4. 输出结果到终端(console.log / print / fmt.Println)
  5. 打包成可执行命令(让它在任何目录都能被调用)

最终效果预览

js 复制代码
# 安装后,在任何目录执行
ai-cli ask "什么是闭包?"
ai-cli code "用Python写一个快速排序"
ai-cli chat --model qwen --system "你是一个幽默的助手"

第一步:环境准备

确保你已经安装了:

  • Node.js(v18 或更高,建议 v20)
  • npm(通常随 Node 安装)

检查方法:

复制代码
node -v
npm -v

如果没有,去 nodejs.org 下载安装。


第二步:创建项目

打开终端,创建一个新文件夹并初始化:

js 复制代码
mkdir ai-cli
cd ai-cli
npm init -y

然后安装必要的依赖:

js 复制代码
npm install typescript @types/node ts-node --save-dev
npm install commander chalk ora openai dotenv
  • commander:解析命令行参数
  • chalk:彩色输出
  • ora:显示加载动画
  • openai:调用 OpenAI 兼容的 API(硅基流动)
  • dotenv:读取 .env 文件中的 API Key

第三步:配置 TypeScript

创建 tsconfig.json

js 复制代码
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

第四步:创建源代码目录和文件

js 复制代码
mkdir src
cd src
touch index.ts

第五步:编写核心代码

src/index.ts 中写入以下代码:

js 复制代码
#!/usr/bin/env node

import { Command } from 'commander';
import chalk from 'chalk';
import ora from 'ora';
import OpenAI from 'openai';
import dotenv from 'dotenv';
import { readFileSync } from 'fs';
import { join } from 'path';

// 加载 .env 文件
dotenv.config();

// 初始化 OpenAI 客户端(兼容硅基流动)
const openai = new OpenAI({
  baseURL: 'https://api.siliconflow.cn/v1', // 硅基流动的 API 地址
  apiKey: process.env.SILICONFLOW_API_KEY || '',
});

const program = new Command();

program
  .name('ai-cli')
  .description('你的 AI 命令行助手')
  .version('1.0.0');

// 子命令:ask
program
  .command('ask <question>')
  .description('向 AI 提问')
  .option('-m, --model <model>', '模型名称', 'Qwen/Qwen2.5-7B-Instruct')
  .action(async (question, options) => {
    const spinner = ora(chalk.blue('AI 正在思考...')).start();
    try {
      const completion = await openai.chat.completions.create({
        model: options.model,
        messages: [
          { role: 'user', content: question }
        ],
        temperature: 0.7,
      });
      spinner.stop();
      const answer = completion.choices[0]?.message?.content || '无回复';
      console.log(chalk.green('\n🤖 回答:\n'));
      console.log(answer);
    } catch (error: any) {
      spinner.stop();
      console.error(chalk.red('❌ 出错了:'), error.message);
    }
  });

// 子命令:code(专门生成代码)
program
  .command('code <prompt>')
  .description('让 AI 生成代码')
  .option('-l, --lang <language>', '编程语言', 'python')
  .action(async (prompt, options) => {
    const systemPrompt = `你是一个专业的程序员。请只输出代码,不要解释。代码语言:${options.lang}。`;
    const spinner = ora(chalk.blue('正在生成代码...')).start();
    try {
      const completion = await openai.chat.completions.create({
        model: 'Qwen/Qwen2.5-7B-Instruct',
        messages: [
          { role: 'system', content: systemPrompt },
          { role: 'user', content: prompt }
        ],
        temperature: 0.5,
      });
      spinner.stop();
      const code = completion.choices[0]?.message?.content || '// 无法生成代码';
      console.log(chalk.cyan('\n💻 代码:\n'));
      console.log(code);
    } catch (error: any) {
      spinner.stop();
      console.error(chalk.red('❌ 出错了:'), error.message);
    }
  });

// 子命令:chat(交互式对话,带 system 提示)
program
  .command('chat')
  .description('进入交互式对话模式')
  .option('-m, --model <model>', '模型', 'Qwen/Qwen2.5-7B-Instruct')
  .option('-s, --system <prompt>', '系统提示词', '你是一个有用的助手。')
  .action(async (options) => {
    const readline = require('readline');
    const rl = readline.createInterface({
      input: process.stdin,
      output: process.stdout,
    });
    console.log(chalk.yellow(`\n进入对话模式(模型:${options.model}),输入 exit 退出\n`));
    const messages = [
      { role: 'system', content: options.system }
    ];
    const askQuestion = () => {
      rl.question(chalk.blue('你:'), async (input: string) => {
        if (input.toLowerCase() === 'exit') {
          console.log(chalk.yellow('再见!'));
          rl.close();
          return;
        }
        messages.push({ role: 'user', content: input });
        const spinner = ora('AI 正在回复...').start();
        try {
          const completion = await openai.chat.completions.create({
            model: options.model,
            messages: messages,
            temperature: 0.7,
          });
          const reply = completion.choices[0]?.message?.content || '无回复';
          spinner.stop();
          console.log(chalk.green(`\n🤖 AI:${reply}\n`));
          messages.push({ role: 'assistant', content: reply });
          askQuestion();
        } catch (error: any) {
          spinner.stop();
          console.error(chalk.red('出错:'), error.message);
          askQuestion();
        }
      });
    };
    askQuestion();
  });

program.parse();

第六步:配置 API Key

在项目根目录创建 .env 文件:

ini 复制代码
SILICONFLOW_API_KEY=你的硅基流动API密钥

如果你还没有 API Key,去 siliconflow.cn 注册,在"API Keys"页面生成一个。新用户有免费额度。

为了安全,把 .env 添加到 .gitignore(如果你会用 git)。


第七步:在本地测试运行

先用 ts-node 直接测试:

bash 复制代码
npx ts-node src/index.ts ask "你好,请介绍一下你自己"

应该能看到 AI 的回答。

测试 code 子命令:

css 复制代码
npx ts-node src/index.ts code "快速排序" --lang javascript

测试交互模式:

bash 复制代码
npx ts-node src/index.ts chat

第八步:打包成全局 CLI 命令

修改 package.json,添加 bin 字段和 build 脚本:

json 复制代码
{
  "name": "ai-cli",
  "version": "1.0.0",
  "description": "AI 命令行助手",
  "main": "dist/index.js",
  "bin": {
    "ai-cli": "./dist/index.js"
  },
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js",
    "dev": "ts-node src/index.ts"
  },
  // ... 其余内容保持不变
}

然后编译 TypeScript:

arduino 复制代码
npm run build

这会生成 dist/index.js 文件。

最后,将命令链接到全局(在项目根目录执行):

bash 复制代码
npm link

如果提示权限问题,可能需要 sudo npm link(不推荐),或者配置 npm 全局目录。一般普通用户下 npm link 即可。

现在,你可以在任何终端输入:

arduino 复制代码
ai-cli ask "什么是闭包?"

回顾:完整流程图

css 复制代码
用户输入: ai-cli ask "你好" --model Qwen/Qwen2.5-7B-Instruct
         ↓
program.parse() 解析 process.argv
         ↓
找到子命令 "ask"
         ↓
提取位置参数 question = "你好"
         ↓
提取选项 options = { model: "Qwen/Qwen2.5-7B-Instruct" }
         ↓
调用 action(question, options)
         ↓
执行你写的业务逻辑(调用 AI API)
         ↓
输出结果到终端

总结

你现在拥有一个属于自己的 AI 命令行助手了!你可以:

  • 随时在终端问问题
  • 生成代码片段
  • 进入交互式对话

如果想让其他人也能 npm install -g ai-cli 安装使用,你可以把它发布到 npm。

相关推荐
赛博切图仔2 小时前
前端性能内卷终点?Signals 正在重塑我们的开发习惯
前端·javascript·vue.js
小江的记录本2 小时前
【RAG】RAG检索增强生成(核心架构、全流程、RAG优化方案、常见问题与解决方案)
java·前端·人工智能·后端·python·机器学习·架构
程序员buddha2 小时前
SCSS从0到1精通教程
前端·css·scss
ZC跨境爬虫2 小时前
海南大学交友平台登录页开发实战day6(覆写接口+Flask 本地链接正常访问)
前端·后端·python·flask·html
Highcharts.js2 小时前
抉择之巅:从2029年回望2026年——企业可视化“战略分水岭”?
前端·javascript·信息可视化·编辑器·echarts·highcharts
沙振宇2 小时前
【Web】使用Vue3+PlayCanvas开发3D游戏(十)让人物动起来
前端·游戏·3d·人物·
军军君013 小时前
数字孪生监控大屏实战模板:空气污染监控
前端·javascript·vue.js·typescript·前端框架·echarts·数字孪生
m0_694845573 小时前
opendataloader-pdf部署教程:构建PDF数据处理系统
服务器·前端·前端框架·pdf·开源
小李子呢02113 小时前
前端八股浏览器网络(1)---响应头
前端