Files
2026-08-31 21:53:57 +08:00

477 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# aiagent.sh — 100% POSIX-sh 自然语言运维 Agent
> 一行英文句子 = 一条运维命令。不用背参数,不用记 flag。
`aiagent.sh` 是一个**纯 `/bin/sh` 实现**的轻量级"AI Agent"。它不需要 Python、不需要 node、不需要任何运行时——单文件、零依赖,却能把你输入的**自然英语句子**解析成真实的运维操作。
```sh
$ ./aiagent.sh
aiagent> please show disk usage
[INFO] Disk usage:
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 100G 42G 58G 42% /
aiagent> what is the uptime
[INFO] System uptime:
14:32:11 up 12 days, 3:17, 2 users, load average: 0.12, 0.08, 0.01
aiagent> check port 8080
[INFO] Checking port 8080 ...
[OK] Port 8080 is IN USE
```
---
## ✨ 核心特性
| 特性 | 说明 |
|------|------|
| 🗣️ **自然语言输入** | 接受完整英文句子,不需要任何特殊语法 |
| 🧠 **意图识别** | 自动提取动词、名词、路径、端口、服务名 |
| 🔄 **同义词归一** | `show/display/list` → 统一处理,`kill/stop/terminate` → 统一处理 |
| 🎯 **名词推断动作** | 只说 `uptime` 也能理解成"show uptime" |
| 🔒 **安全确认** | 所有破坏性操作(删除、杀进程、装包)强制 `y/N` 二次确认 |
| 🛡️ **零 eval 注入** | 没有任何字符串拼接执行,包管理器直接调用 |
| ⚙️ **自动适配环境** | 自动检测 `systemctl`/`service`、`ss`/`netstat`、`apt`/`dnf`/`yum`/`apk`/`pacman` |
| 🧩 **可扩展** | 每个 `do_xxx()` 函数独立,新增操作只需加一个 case 分支和一个函数 |
| 📦 **单文件分发** | 约 1050 行,直接 `cp` 到目标机就能跑 |
---
## 🚀 快速上手
### 1. 获取脚本
```bash
# 从git仓库获取
git clone https://github.com/CancerXiaoAi/shell-ai.git
git clone https://git.cancerai.top/cancerai/shell-ai.git
```
### 2. 两种使用方式
```bash
# 方式 A:交互式 REPL(推荐日常使用)
./aiagent.sh
# 方式 B:一行命令模式(适合脚本/CI)
./aiagent.sh "please check port 443"
./aiagent.sh "what is the kernel version"
./aiagent.sh "health check"
```
### 3. 最简示例
```bash
# 系统一句话
./aiagent.sh uptime # 直接说名词就行
./aiagent.sh disk
./aiagent.sh health
# 完整句子也没问题
./aiagent.sh "please help me show memory usage"
./aiagent.sh "could you ping google.com for me"
./aiagent.sh "i want to restart nginx"
```
---
## 📖 完整命令参考
### 💡 语法约定
脚本对输入的容忍度极高。以下写法**全部等价**:
```
please help me check disk usage
help me check disk
check disk
disk
```
规则:
- `please`、`help me`、`to`、`a`、`the`、`can you`、`would you` 等纯语法词**全部自动忽略**
- 只带名词(如 `uptime`)会被自动推断为"show + 该名词"
- 只带动词加短语(如 `check port 80`)直接路由
### 🖥️ 系统信息 — System Info
| 输入示例 | 实际调用 | 说明 |
|---------|---------|------|
| `show disk usage` / `disk` | `df -h` | 磁盘使用情况 |
| `check memory` / `memory` / `ram` | `/proc/meminfo` 或 `free -h` | 内存占用 |
| `check cpu load` / `cpu` / `load` | `/proc/loadavg` | CPU 负载 |
| `what is the uptime` / `uptime` | `uptime` | 系统运行时长 |
| `show kernel version` / `version` | `/etc/os-release` + `uname` | OS 和内核版本 |
| `show listening ports` | `ss -tulnp` 或 `netstat -tulnp` | 监听端口 |
| `who is logged in` / `show users` | `who` | 当前登录用户 |
| `health check` / `check` | 一键跑 uptime + CPU + 内存 + 磁盘 | 健康检查 |
### 📂 文件操作 — Files
| 输入示例 | 实际调用 | 说明 |
|---------|---------|------|
| `list files in /var/log` | `ls -lah /var/log` | 列出目录 |
| `show me content of /etc/hosts` | `ls /etc/hosts` | 查看文件 |
| `find files named *.log in /tmp` | `find /tmp -type f -name "*.log"` | 查找文件 |
| `find process nginx` | `ps aux | grep nginx` | 按名字找进程 |
| `create directory /data` | `mkdir -p /data` | 创建目录 |
| `create file /tmp/hello.txt` | `: > /tmp/hello.txt` | 创建空文件 |
| `copy /etc/hosts /tmp/hosts.bak` | `cp -r/-f` | 自动判断文件/目录复制 |
| `move /tmp/a.log /var/log/` | `mv -f` | 移动/重命名 |
### ⚠️ 破坏性操作 — Destructive(强制 y/N 确认)
| 输入示例 | 实际调用 | 说明 |
|---------|---------|------|
| `please remove /tmp/cache` | `rm -rf`(确认后) | 递归删目录 |
| `delete file /tmp/bad.log` | `rm -f`(确认后) | 删除文件 |
| `clean old log files in /var/log` | `find -delete`(确认后) | 清理日志 |
| `kill process nginx` | `kill` + PID 列表(确认后) | 按名字杀进程 |
| `kill the process on port 8080` | 先 lsof/ss 找 PID(确认后) | 按端口杀进程 |
| `stop nginx` | `systemctl stop`(确认后) | 停服务 |
### 🔌 服务管理 — Services
| 输入示例 | 实际调用 | 说明 |
|---------|---------|------|
| `check status of sshd` | `systemctl status` | 查看服务状态 |
| `start docker service` | `systemctl start` | 启动服务 |
| `restart nginx` | `systemctl restart` | 重启服务 |
| `nginx` | 自动识别服务名 → 查状态 | 裸服务名默认查状态 |
### 🌐 网络 — Network
| 输入示例 | 实际调用 | 说明 |
|---------|---------|------|
| `check port 443` / `port 443` | `ss`/`netstat`/`nc` | 检查端口监听状态 |
| `ping google.com` | `ping -c 3 -W 2` | 连通性测试(3 包) |
| `show listening ports` | `ss -tulnp` | 所有监听端口 |
### 📦 包管理 — Packages
| 输入示例 | 实际调用 | 说明 |
|---------|---------|------|
| `please install package htop` | 自动检测 `apt-get`/`dnf`/`yum`/`apk`/`pacman` 并调用 | 安装系统包(确认后) |
### 📜 日志 — Logs
| 输入示例 | 实际调用 | 说明 |
|---------|---------|------|
| `tail /var/log/syslog` | `tail -30` | 直接路径 |
| `tail nginx log` | 自动匹配 `/var/log/nginx/access.log` | 按服务名猜路径 |
| `follow docker log` | glob `/var/log/*docker*` | 模糊匹配 |
### 🎛️ 其他
| 输入 | 说明 |
|------|------|
| `help` | 打印帮助手册 |
| `quit` / `exit` / `bye` | 退出 REPL |
---
## 🧠 自然语言解析原理
整个解析管线分为 **5 层**,每一层都只使用 POSIX sh 原语实现:
```
用户输入:please help me check port 8080
│
▼
┌─────────────────────────────────┐
│ 第 1 层:strip_fillers() │
│ 丢弃纯语法词:please, help, me │
└─────────────────────────────────┘
│ 得到:check port 8080
▼
┌─────────────────────────────────┐
│ 第 2 层:跳过引导词 │
│ 跳过开头的 help/what/can 等客气词│
└─────────────────────────────────┘
│ 得到:check port 8080
▼
┌─────────────────────────────────┐
│ 第 3 层:canonical_verb() │
│ 动词→同义词归一 │
│ 名词→推断动作(health→check) │
└─────────────────────────────────┘
│ verb=check, phrase="port 8080"
▼
┌─────────────────────────────────┐
│ 第 4 层:dispatch() 一级匹配 │
│ case "$phrase" in *port*) │
│ do_check_port; _matched=1 ;; │
└─────────────────────────────────┘
│
▼
┌─────────────────────────────────┐
│ 第 5 层:do_check_port() │
│ 从短语中提取 "8080" │
│ 依次尝试 ss → netstat → nc │
│ 输出 [OK]/[WARN] 状态 │
└─────────────────────────────────┘
```
### 动词同义词映射(canonical_verb)
```
show ← show, display, list, print, tell, what, reveal, dump
check ← check, monitor, verify, inspect, diagnose
find ← find, locate, search, lookup, track
remove← remove, delete, erase, rm, wipe, cleanup, clean
kill ← kill, stop, terminate, shutdown, halt
start ← start, launch, run, begin, enable
...
```
### 名词 → 推断动作
```
uptime, disk, memory, cpu, version, port, process ... → 自动当作 show
health, status, connection, reachability → 自动当作 check
```
### 三级 fallback 匹配机制
每个规范动词的路由都有三级兜底:
1. **一级关键词匹配**:`*disk*` → do_show_disk,`*port*` → do_check_port
2. **二级兜底**:短语是纯数字 → 当作端口号;短语含已知服务名 → do_service_status
3. **三级兜底**:`check` 空短语 → do_health_check;其他 → 提示用户重述
---
## 🔒 安全设计
### 强制二次确认
以下操作**必须**输入 `y` 或 `yes` 才会继续,其他任何输入都视为取消:
- 删除目录/文件(`remove`, `delete`)
- 杀进程(`kill`)
- 停服务(`stop service`)
- 安装系统包(`install`)
```
[CONFIRM] Recursively remove directory '/tmp/cache' ? (y/N):
```
### 零 eval 注入
整个脚本**没有任何 `eval` 调用**。包管理器的调用采用直接分支方式:
```sh
if command -v apt-get >/dev/null 2>&1; then
apt-get install -y -- "$_pkg" # 直接调用,参数安全引用
elif command -v dnf >/dev/null 2>&1; then
dnf install -y -- "$_pkg"
...
```
### 路径安全
`rm -rf`、`mv`、`cp` 等危险操作都使用了 `--` 分隔符,防止路径以 `-` 开头被解释为选项。
### 信号处理
REPL 模式下按 Ctrl+C 会优雅退出,不会留下终端异常状态:
```sh
trap 'echo; printf "Bye.\n"; exit 130' INT TERM
```
---
## 🏗️ 架构总览
```
aiagent.sh (~1050 lines, 单文件)
├── 辅助函数层
│ ├── info() / warn() / error() / success() # 带颜色的日志输出
│ └── confirm() # y/N 安全确认
│
├── 解析层(第 1–3 层)
│ ├── strip_fillers() # 丢弃纯语法词
│ ├── canonical_verb() # 同义词归一 + 名词推断
│ ├── _extract_path() # 从短语中提取路径
│ ├── _extract_svcname() # 从短语中提取服务名
│ └── _SERVICES_RE # 已知服务名正则
│
├── 路由层(第 4 层)
│ └── dispatch() # 大 case 分支,三级 fallback
│
├── 执行层(第 5 层)
│ ├── do_show_disk / _memory / _cpu / _uptime / _version / ...
│ ├── do_check_port / do_ping_host / do_service_status / do_health_check
│ ├── do_find_process / do_find_file
│ ├── do_remove_dir / _file / _log
│ ├── do_kill_process / do_kill_by_port
│ ├── do_service_start / _restart / _stop
│ ├── do_create_dir / _file
│ ├── do_copy_path / do_move_path
│ ├── do_install_pkg
│ └── do_tail_log
│
├── REPL 层
│ ├── run_one() # 单次命令处理管线
│ └── repl() # 交互式循环
│
└── 入口
└── main() # 分交互式/一行模式
```
### 依赖关系图
```
main() ─→ repl() 或 run_one()
│
▼
dispatch()
│
┌─────────┼─────────────┬──────────┬────────────┐
▼ ▼ ▼ ▼ ▼
show() check() find() remove() install()
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
do_show_* do_check_* do_find_* do_remove_* do_install_pkg
│
▼
confirm() ←── 所有 destructive 函数必经
```
---
## ⚙️ 运行要求
### 最低要求
| 项目 | 要求 |
|------|------|
| Shell 解释器 | `/bin/sh`(POSIX sh,任何实现均可:dash / busybox / bash --posix) |
| 操作系统 | Linux(主要目标);macOS 可读但部分功能受限(无 systemctl/ss) |
| 权限 | 普通用户即可运行,执行破坏性/管理类命令时需要相应权限 |
### 可选依赖(根据操作自动探测)
| 操作 | 依赖(择一) |
|------|-------------|
| 磁盘/内存/CPU | `df`, `/proc/meminfo` 或 `free`, `/proc/loadavg` |
| 服务管理 | `systemctl` 或 `service` |
| 端口检查 | `ss` / `netstat` / `nc` |
| 进程查找 | `ps`, `pgrep`(可选) |
| 进程/端口杀 | `pgrep`, `lsof`, `fuser`(择一) |
| 网络 | `ping` |
| 文件查找 | `find` |
| 包安装 | `apt-get` / `dnf` / `yum` / `apk` / `pacman`(择一) |
### 跨包管理器支持
| 发行版 | 自动使用 |
|--------|---------|
| Debian / Ubuntu | `apt-get` |
| RHEL 8+ / Fedora / CentOS 8+ | `dnf` |
| RHEL 7 / CentOS 7 | `yum` |
| Alpine | `apk` |
| Arch / Manjaro | `pacman` |
---
## ⚡ 性能
解析管线全部使用 POSIX sh 内建实现(`case` / `for` / `if` / `read` / `printf`),**不 fork 任何外部进程**——除了 `grep -qE` 和 `awk` 在少数 fallback 分支里会被调用。整个"strip → canonical → dispatch → route"管线的纯解析开销**可以忽略不计**,实际耗时主要来自底层命令本身(如 `df -h`、`ps aux`、`systemctl` 等)。
---
## 🧪 快速验证
在 Docker 容器里跑一下(无需在本机安装任何东西):
```bash
# Debian
docker run --rm -it -v $(pwd)/aiagent.sh:/aiagent.sh debian:latest \
sh -c 'chmod +x /aiagent.sh && /aiagent.sh "health check"'
# Alpine
docker run --rm -it -v $(pwd)/aiagent.sh:/aiagent.sh alpine:latest \
sh -c 'chmod +x /aiagent.sh && /aiagent.sh "check port 22"'
# CentOS / Rocky / AlmaLinux
docker run --rm -it -v $(pwd)/aiagent.sh:/aiagent.sh rocky:9 \
sh -c 'chmod +x /aiagent.sh && /aiagent.sh "restart sshd"'
```
---
## 🛠️ 扩展指南
想新增一个操作(比如"备份数据库")?三个步骤:
**步骤 1**:加一个规范动词映射(如果是新动词)
```sh
canonical_verb() {
...
backup|dump) echo "backup" ;;
...
}
```
**步骤 2**:在 `dispatch()` 的 case 分支里加路由
```sh
dispatch() {
...
backup)
do_backup_db "$phrase" ;;
...
}
```
**步骤 3**:实现 `do_backup_db()` 函数
```sh
do_backup_db() {
_db=""
for _tok in $1; do
case "$_tok" in mysql|postgres|redis|mongodb) _db="$_tok" ;; esac
done
[ -z "$_db" ] && { error "No DB type. Example: 'backup mysql'"; return 1; }
confirm "Backup $_db ?" || return 1
# 实际备份逻辑
case "$_db" in
mysql) mysqldump ... ;;
postgres) pg_dump ... ;;
...
esac
}
```
完成。现在 `./aiagent.sh "please backup mysql database"` 就能工作了。
---
## 🐛 已知限制
| 限制 | 说明 |
|------|------|
| 只能识别**英文** | 整个关键词库是英语 hardcode 的,不支持中文 |
| 正则只支持 POSIX ERE | 不支持 PCRE(但对运维场景已足够) |
| 交互式 REPL 不支持命令历史 | POSIX sh 的 `read` 没有 readline;可以通过 `rlwrap ./aiagent.sh` 获得历史 |
| 端口号从短语提取用的是 `[0-9]*` | 如果句子里有多个数字,会取最后一个(对"check port 80 on server 192.168.1.1"这种会取 1) |
| macOS 功能受限 | 没有 `systemctl`/`ss`/`/proc/*`,部分操作会降级或报依赖缺失 |
| 包管理器自动检测 | 只是检测命令是否存在,不检查是否真的可用(例如在 chroot 里) |
---
## 📄 License
MIT License. 复制、修改、商用均自由,唯一要求保留版权声明。
---
## 🙏 致谢
- POSIX 标准委员会:没有你们就没有可移植的 shell
- 所有写出漂亮运维命令的前辈:你们的 `df -h`、`ps aux`、`ss -tulnp` 是这个项目的基石