说实话,我写 CLI-Anything 的初衷特别朴素:受够了在终端和图形界面之间来回横跳。每天的工作流里,有一半时间在处理文件、查资料、改格式这种琐事——PDF 转文本、批量重命名、CSV 里筛数据、把网页正文转成 Markdown——每件单独看都不难,但每件都得打开对应工具,有些还收费、还限流、还要注册账号。作为重度终端用户,我最大的诉求就一句话:能不能在命令行里把所有这些杂活全干了,并且用自然语言说一句需求就行。
CLI-Anything 就是在这个不满下慢慢长出来的个人开源项目。它的定位概括起来很简单:基于大模型意图识别 + 插件化执行的通用命令行助手。你不需要背命令,不需要查某个工具的参数手册,你只需要告诉它“把这份 PDF 里的表格提取出来存成 Excel”,它负责拆解任务、选择合适方案、调用本地工具链、执行,然后把结果整理好反馈给你。这篇文章把我从零到一的设计思路、核心模块实现、以及踩过的坑完整记录下来,给同样想在终端里“用一句话解决杂活”的开发者一份可以直接复现的参考。
1. 这个项目到底在解决什么问题
1.1 终端党的日常割裂
先说一个真实场景。上个月我收到一个 30 页的财务 PDF,里面有十几张表需要核对。常规流程是:打开 WPS → 识别文本 → 复制到 Excel → 调格式,整个过程用了将近四十分钟。第二次遇到同类需求时,我改用命令行工具直接跑pdfplumber写脚本,五分钟搞定,但脚本写了一百多行,且只针对这一个文件结构,换个模板又得改。
这种“一次性杂活”最尴尬的地方在于:写脚本太重,手动操作太慢,忘了怎么用工具又得查文档。我电脑里散落着各种功能的命令行小工具,有些是 Python 写的,有些是 Node 写的,它们的参数风格完全不同,我经常忘记调用方式。于是每次处理文件前,先得花五分钟回忆“这个工具是-f还是--file,输出参数是-o还是--output”。
CLI-Anything 想消灭的正是这部分记忆负担和工具割裂感。用户只描述目标,不描述工具,这是它和传统命令行工具最本质的区别。
1.2 为什么不是直接开个 AI 聊天窗口
可能有人会问:现在大模型聊天工具这么成熟,直接问它“怎么提取 PDF 表格”不就行了?
我一开始也是这么干的,但用了几天就发现聊天窗口在真实工作流里有几个硬伤:
- 没有上下文感知:聊天窗口不知道我当前目录下有什么文件,不知道我这台机器装了哪些工具,它只能泛泛地给建议。
- 结果不能直接沉淀:聊天窗口里聊出来的方案,要么我手动复制执行,要么得另存对话,没法纳入脚本化、自动化的流水线。
- 没有确定性:它会一本正经地推荐一些我根本没安装的库,或者给出一段在我机器上跑不通的代码,我得反复追问、试错。
而 CLI-Anything 站在本地环境里,能感知文件系统,能实际执行命令,能把每次任务的结果回传到模型进行下一步推理。它不是替代聊天工具,而是把大模型的推理能力接入到本地命令执行环境里,形成一条“意图 → 动作 → 反馈 → 修正”的闭环。
1.3 项目的核心设计目标
动手之前我给自己定了四条硬指标,后续所有架构决策都围绕这四条:
- 零学习成本交互:用户输入一句自然语言,无论多复杂,工具自动拆解成可执行步骤。
- 安全兜底:所有命令在执行前必须经过安全检查,高危行为默认拒绝,特殊情况下也要经过用户确认。
- 确定性优先:凡是能用本地插件解决的固定任务(PDF 转换、CSV 分析),绝不依赖模型临场发挥——模型负责规划,插件负责执行。
- 可审计、可复现:每次任务都要输出“模型看到了什么、决定做什么、做了什么、结果如何”,让用户能追溯每一步。
这四条写进了项目的 README,也写进了系统提示词。后面我会逐条展开讲它们是怎么落地的。
1.4 和现有方案的边界在哪
| 方案 | 优点 | 缺点 | 适合人群 |
|---|---|---|---|
| 手动敲命令 / shell alias | 快、无依赖 | 记忆成本高,场景局限 | 资深终端用户 |
| 写一次性脚本 | 灵活可控 | 耗时,维护成本高 | 有编程习惯的人 |
| 图形化工具 | 上手容易 | 割裂,难自动化 | 非技术用户 |
| 直接问通用大模型 | 灵活 | 无本地上下文,不能执行 | 想快速获得方案的人 |
| CLI-Anything | 有上下文,能执行,可扩展 | 依赖模型接口,需配置 | 终端重度用户、自动化爱好者 |
从表格能看出,CLI-Anything 不是来取代某一类方案的,它把“自然语言理解”和“本地执行能力”组合起来,填补的是“知道要什么,但不想纠结怎么做到”的中间地带。
2. 整体架构:三层流水线 + 插件化
2.1 技术选型:为什么是 Python + Typer + Rich
技术栈我基本没犹豫,直接选了 Python。核心原因有三:一是生态覆盖杂活能力极强,PDF 处理有 pdfplumber、PyMuPDF,数据分析有 pandas,OCR 有 RapidOCR,音频转写也有现成方案,几乎所有“杂活类任务”都有成熟库;二是跨平台行为一致,同一套代码在 macOS 和 Linux 上表现稳定;三是快速迭代友好,写个插件不用编译,改完就能跑。
命令行框架选了 Typer,而不是老牌的 Click 或 argparse。Typer 基于类型注解自动生成帮助信息,代码量少一半,而且和 Rich 配合能输出非常漂亮的终端界面。项目里交互部分还用了 Rich 的Console来渲染进度条、表格和彩色日志,体感上比纯文本输出好很多。
另一个关键选型是Pydantic。所有模型返回的 JSON 都会先过 Pydantic 校验,结构不对立刻报错并触发重试,而不是带着脏数据往下跑。这一点在后面“非法 JSON 排查”部分会详细讲。
2.2 三层流水线:意图解析、规划、执行
CLI-Anything 内部是一条严格的三层流水线,每一层职责单一,层与层之间通过结构化数据交接:
用户输入 → 意图解析层 → 任务规划层 → 执行层 → 结果反馈- 意图解析层:把自然语言输入转成结构化意图。比如“提取 PDF 里的表格保存成 xlsx”,解析结果是
{"action": "extract_table", "source": "*.pdf", "target_format": "xlsx"}。这一步主要靠模型函数调用能力,我会在后面给出具体的toolsschema。 - 任务规划层:根据意图拆出步骤。如果意图匹配到已安装插件,直接走插件;如果没匹配上,规划器会生成一组 shell 命令序列,并标注每一步需要的文件路径。
- 执行层:真正干活的地方。它统一调度插件、子进程、文件读取,把每一步的退出码和标准输出收集起来,再回传给规划层做下一轮判断。
这个设计最重要的收益是可观测。每一层都有日志,用户能看到“模型判断这是 PDF 提取任务 → 调用 pdf_to_markdown 插件 → 输出文件写入 out/xxx.md”,整个推理过程透明可查,不至于像黑盒一样莫名其妙给你执行了一堆命令。
2.3 插件机制:为什么不能把一切都交给模型
最初的原型其实非常“暴力”——所有任务都让模型生成 shell 命令,然后执行。跑通之后很快发现问题:模型生成命令的上限就是模型自己的上限,它不知道本地装了哪些库,也不了解某些工具的坑。比如让模型提取 PDF 表格,它生成的 Python 代码大概率会报错,因为它不知道这台机器的 pdfplumber 版本、字体环境、中文编码等真实约束。
所以我把架构改成了“模型规划 + 插件执行”的混合模式:高频、确定性强的任务全部固化成插件;低频、个性化的任务才走动态命令生成。插件内部写死了处理细节和异常路径,模型只需要把意图映射到插件上,传对参数就行。
插件的基类定义极其简单:
# cli_anything/plugins/base.py from abc import ABC, abstractmethod class Plugin(ABC): name: str = "" description: str = "" @abstractmethod def match(self, task: str) -> float: """返回 0~1 的匹配度,越高表示越适合处理该任务""" ... @abstractmethod def execute(self, params: dict) -> str: """执行任务,返回结果摘要文本""" ...注册机制用的是装饰器 + 全局注册表,插件模块被 import 时自动注册:
# cli_anything/plugins/registry.py PLUGIN_REGISTRY = {} def register(cls): PLUGIN_REGISTRY[cls.name] = cls() return cls规划层拿到意图后,遍历注册表计算匹配度,取最高分插件执行。不是所有插件都匹配时,模型会在match阶段给出一个“置信度”,低于阈值就走命令生成兜底。这套机制的优点很直接:新增一种能力不需要改核心代码,写一个插件文件,注册,完事。
2.4 安全兜底:四条防线
安全是这个项目里我最看重的部分,毕竟一个能自动执行命令的工具,用不好就是灾难。我设计了四层防线:
第一层:静态命令黑名单。所有要执行的 shell 命令先过一个规则引擎,命中危险模式直接拒绝:
# cli_anything/safecheck.py import re DANGEROUS_PATTERNS = [ re.compile(r"\brm\s+-rf\b"), # 强制递归删除 re.compile(r"\bmkfs\b"), # 格式化 re.compile(r"\bdd\b.*\bof=/dev/"), # 写裸设备 re.compile(r"\bsudo\b"), # 提权 re.compile(r"\bchmod\s+777\b"), # 宽松权限 re.compile(r"\bcurl\b.*\|\s*(ba)?sh"), # 直接执行远程脚本 ]第二层:高危命令确认。即使没命中黑名单,只要命令涉及删除、覆盖、移动、网络下载等敏感动作,执行前必须弹确认,用户按y才继续。
第三层:dry-run 先行。默认情况下,CLI-Anything 先输出完整的执行计划,用户可以用ca --auto "任务描述"跳过确认,但需要显式声明。这样既能防止误操作,也保留了自动化能力。
第四层:沙箱模式。在配置里可以开启 Docker 沙箱,所有命令在临时容器内执行,容器销毁后环境归零。这个模式更适合处理不可信文件或批量调试阶段。
四条防线层层叠加,实际体验是:日常文件操作基本感觉不到确认的打扰,但要是有命令试图删库或者提权,立刻会被拦住。安全不能靠模型自觉,必须靠机制兜底,这是我在这个项目里最坚持的一条原则。
3. 核心模块的实现细节
3.1 意图解析模块:从一句话到结构化任务
意图解析核心是模型函数调用。所有可用能力都定义成函数 schema 传给模型,模型决定调用哪个函数、传什么参数。我定义的第一版 schema 长这样:
# cli_anything/tools_schema.py TOOLS = [ { "type": "function", "function": { "name": "run_shell", "description": "在本地终端执行一条 shell 命令,返回 stdout、stderr 和退出码", "parameters": { "type": "object", "properties": { "command": {"type": "string", "description": "要执行的完整命令"}, "reason": {"type": "string", "description": "执行原因,用于审计"} }, "required": ["command", "reason"] } } }, { "type": "function", "function": { "name": "read_file", "description": "读取文本文件内容,自动处理编码,限制 10MB 以内", "parameters": { "type": "object", "properties": { "path": {"type": "string"} }, "required": ["path"] } } } ]对应的解析循环是标准的工具调用模式:
# cli_anything/planner.py messages = [{"role": "system", "content": SYSTEM_PROMPT}] messages.append({"role": "user", "content": task}) for _ in range(MAX_TURNS): resp = client.chat.completions.create( model=model, messages=messages, tools=TOOLS, tool_choice="auto", ) msg = resp.choices[0].message if not msg.tool_calls: console.print(msg.content) break user_side = {"role": "assistant", "content": msg.content, "tool_calls": msg.tool_calls} messages.append(user_side) for tc in msg.tool_calls: result = dispatch_tool(tc.function.name, json.loads(tc.function.arguments)) messages.append({ "role": "tool", "tool_call_id": tc.id, "content": json.dumps(result, ensure_ascii=False) })这里有个细节值得说:**reason参数是刻意设计的**。模型每执行一条命令都必须给出理由,这个理由会写进审计日志。一开始我觉得这是形式主义,后来排查问题全靠它——当用户发现“怎么多了个临时文件”时,翻日志能看到当时模型的决策逻辑,非常有用。
系统提示词里我还明确了两个规则:一是凡是可以解析为插件任务的,优先调用插件工具,而不是自己写命令;二是如果发现上一步执行结果与预期不一致,主动修正参数并重新执行,不要直接放弃。这两条让模型在复杂任务里的成功率提升非常明显。
3.2 文件处理模块:PDF、图片、音频一个不落
文件处理是最能体现“Anything”价值的模块。我固定封装了三个高频能力,全部走插件实现:
PDF 内容提取。用 pdfplumber 提取文本和表格,用 PyMuPDF 渲染页面截图。针对中文 PDF 的编码问题,我加了一级检测:如果提取出的文本里问号比例超过阈值,自动切换到 OCR 通道:
# cli_anything/plugins/pdf_ops.py import pdfplumber from rapidocr_onnxruntime import RapidOCR def extract_text(path: str) -> str: with pdfplumber.open(path) as pdf: text = "\n".join(p.extract_text() or "" for p in pdf.pages) if _looks_garbled(text, threshold=0.05): ocr = RapidOCR() result = ocr(path) return "\n".join(item[1] for item in result) return text这里最常踩的坑是PDF 里的表格用extract_text()根本拿不到结构化数据,必须用page.extract_table()配合lines参数手动拼。不同 PDF 内部的表格绘制方式差异极大,没有银弹,只能多套方案依次尝试。
图片 OCR 与信息抽取。走 RapidOCR,轻量且 CPU 上就能跑,不需要 GPU。遇到图片里带表格的,我会先把图片转成 Markdown 表格文本,再让模型做后续的清洗和补全。
音频转写。用 whisper.cpp 的二进制做转写,Python 端只负责调用和结果整理。注意 whisper.cpp 需要单独编译,我在配置里允许用户指定二进制路径,否则该插件自动降级为提示“未安装”。
文件名带空格、路径含中文,是所有文件类任务的高频事故点。我的处理方式是统一用pathlib.Path管理路径,所有传给子进程的命令字符串一律用shlex.quote()包裹,后面排查章节再细说。
3.3 数据处理模块:CSV、JSON、日志分析
数据处理任务和文件任务的不同点在于:用户往往不想要一次性的结果,想要一个可重复执行的脚本。比如“统计这个月每一天的订单量”,如果只回一个数字,下周同样的问题还得再来一次。所以我的设计是:CLI-Anything 在完成分析的同时,自动生成一段可复用的 Python 脚本并保存到当前目录。
生成脚本的逻辑不是模型凭空捏造,而是执行层先把真实数据读进来、做一次样例分析,再让模型基于样例代码模板生成最终的通用脚本。这样保证生成的脚本在本地是真正能跑的,而不是看起来像那么回事。
举个例子。用户输入“统计 logs/app.log 里 ERROR 级别的日志,按小时分组,输出前 5 名”。执行层先做这几件事:
- 用
rg "ERROR" logs/app.log确认文件大小和行数,如果太大先采样; - 用一个内置模板解析常见日志时间戳格式;
- 跑通后,把统计逻辑写回
scripts/error_stats.py保存。
这个“先采样验证,再生成脚本”的顺序很关键。模型直接生成的代码经常因为日志格式差异跑不通,先给它看几行真实数据,它就能写出匹配实际格式的解析逻辑,生成成功率至少提升一倍。
3.4 两段式执行:先规划,后运行
CLI-Anything 的所有任务默认分两个阶段跑:
Plan 阶段。模型输出完整执行计划,用 Rich 渲染成步骤列表,用户当场确认:
# ca "将 data/report.pdf 中的表格提取为 xlsx" 计划: 1. 检测 PDF 页面数量:pdfinfo 2. 提取全部表格:pdfplumber 脚本 3. 合并相同结构的表格并保存:out/tables.xlsx 确认执行? [y/N]Run 阶段。确认后进入上面的工具调用循环,逐步执行并把每步结果反馈给用户。
两段式设计帮我解决了一个现实问题:模型的首次规划很可能不完美,但用户通常能一眼看出规划哪里不对。让用户在最前面做一次低成本纠偏,比让模型执行到一半才发现方向错了要高效得多。而且 Plan 阶段还可以让用户直接编辑计划,比如“把第 3 步的输出格式改成 csv”,改完直接走 Run。
这个设计也被我用在自动化场景里:CI/CD 中可以传--auto跳过确认,但每一步依然会输出审计日志。自动化的前提是可追溯,不是完全无人值守。
4. 实操:从安装到跑通第一个任务
4.1 环境准备
CLI-Anything 目前以 Python 3.11+ 为主,依赖通过 pip 管理:
pip install cli-anything安装完成后首次运行会引导生成配置文件。我建议所有依赖工具链单独维护,比如 PDF 处理需要的pdfplumber、PyMuPDF,OCR 需要的rapidocr_onnxruntime,这些按需装,避免一次性装太多拖慢启动速度。
如果你想从源码跑,克隆下来后:
git clone https://github.com/yourname/cli-anything.git cd cli-anything python -m venv .venv && source .venv/bin/activate pip install -e ".[dev]"4.2 最小可用配置
配置文件放在~/.cli-anything/config.toml,核心是模型接口和本地参数:
[model] base_url = "https://api.example.com/v1" api_key = "sk-xxx" model = "qwen2.5-coder:32b" [executor] shell = "/bin/bash" timeout = 120 confirm_high_risk = true [plugins] enable = ["pdf", "data", "web", "audio"] [audit] log_dir = "~/.cli-anything/logs"模型部分用的是 OpenAI 兼容接口,只要你的服务商支持/v1/chat/completions,都可以直接填。如果你倾向本地部署,用 Ollama 或 vLLM 起一个服务,把base_url指到http://localhost:11434/v1即可。我个人实测下来,代码能力强的模型在这个场景里表现明显更好,因为它需要生成 shell 命令和 Python 脚本。
4.3 三个真实场景演示
场景一:提取 PDF 表格。
ca "把 data/report.pdf 里的所有表格提取出来,转成 xlsx 存到 output/"执行过程中模型会先检查 PDF 存在性,再用 pdfplumber 提取表格结构,最后用 pandas 写入多个 sheet。我在日志里观察到它还会主动做一步“空表过滤”,剔除只有表头没有数据的空表,这个细节让我确认模型确实理解了表格数据结构,而不只是机械执行。
场景二:批量整理文件。
ca "把 downloads 目录下所有 .tmp 文件移到 /tmp/old_files,并在移动前打日志"这个任务走命令生成路径。模型生成的关键命令类似:
mkdir -p /tmp/old_files find ~/downloads -name "*.tmp" -exec mv {} /tmp/old_files/ \;由于包含mv移动操作,触发了高危确认,我按y后才执行。执行后审计日志里能看到每条移动记录的源路径和目标路径。
场景三:日志统计分析。
ca "分析 server.log 中 500 错误数量,按小时统计并生成趋势表"这个任务走了数据模块插件,最后不仅返回了统计结果表格,还生成了server_error_stats.py脚本。脚本里时间戳解析逻辑是模型根据 sample 数据现写的,直接复用没问题。
4.4 编写你的第一个插件
扩展能力是 CLI-Anything 的核心卖点。假设你想加一个“批量压缩图片”的插件,只需要新建一个文件:
# cli_anything/plugins/image_ops.py import pathlib from .base import Plugin, register @register class ImageCompressor(Plugin): name = "image_compress" description = "批量压缩图片,支持 jpg/png/webp,可指定质量参数" def match(self, task: str) -> float: score = 0.0 if any(w in task for w in ["压缩", "图片", "jpg", "png"]): score += 0.6 if "压缩" in task and ("图片" in task or "图片大小" in task): score += 0.4 return score def execute(self, params: dict) -> str: import tinify source_dir = pathlib.Path(params["source_dir"]) quality = params.get("quality", 80) # 具体压缩逻辑 ... return f"完成压缩,共处理 {count} 张图片,节省 {saved_mb} MB"注册完成后,重启 CLI-Anything,输入“压缩当前目录下所有图片到 80 质量”,规划层会识别到ImageCompressor的匹配度最高,走插件执行。注意match方法返回的是浮点数,多个插件同时匹配时取最高值。
写插件有三条经验:一是匹配逻辑宁可保守也不要贪婪,误匹配比不匹配更伤体验;二是execute 内部必须做全部异常处理,包括文件不存在、格式不支持、磁盘空间不足,错误要以字符串返回而不是抛出堆栈;三是尽量返回量化结果,比如“处理了多少文件、省了多少空间”,这类信息模型能直接用来给用户做总结。
5. 实战中踩过的大坑与排查方法
5.1 路径与转义的噩梦
这是出现频率最高的问题。用户输入“把 my files.txt 移动到 /tmp”,模型生成的命令往往不加引号,结果my和files.txt被当成两个参数。我试过在系统提示词里反复强调“所有路径必须用 shlex.quote”,但模型偶尔还是会违规。
最终的解法是双保险:执行层不信任模型给的命令字符串,在传给 subprocess 前统一做一次安全检查:
# cli_anything/executor.py import shlex, subprocess def run_command(cmd: str, timeout: int) -> dict: argv = shlex.split(cmd) # 对每个包含路径语义的参数做二次校验 result = subprocess.run(argv, capture_output=True, text=True, timeout=timeout) return {"stdout": result.stdout, "stderr": result.stderr, "exit_code": result.returncode}另外,所有建议用相对路径的操作,执行层会先解析成绝对路径再执行,避免工作目录不一致导致的“找不到文件”。
5.2 中文与特殊编码问题
有段时间我经常收到莫名的乱码报告。排查发现是 Windows PowerShell 的默认编码和 Python 的subprocess默认编码不一致导致的。解决办法:
- 环境变量里显式设置
PYTHONIOENCODING=utf-8; subprocess.run时显式传入encoding="utf-8", errors="replace";- 读取文件时先探测编码,用
chardet或尝试utf-8-sig、gbk依次解析。
def read_text_smart(path: str) -> str: for enc in ("utf-8-sig", "utf-8", "gbk", "latin-1"): try: return pathlib.Path(path).read_text(encoding=enc) except UnicodeDecodeError: continue return pathlib.Path(path).read_text(encoding="utf-8", errors="replace")这一条解决之后,中文文件名和内容乱码的问题基本绝迹。
5.3 模型返回不稳定的 JSON
工具调用的arguments字段理论上应该是合法 JSON,但实际使用中我遇到过:多了一个尾逗号、中文引号混入、被 Markdown 代码块包裹等情况。直接json.loads会抛异常,整个流程断掉。
我的处理分三级降级:
- 先用
json.loads尝试; - 失败后用正则提取最外层花括号/方括号内的内容再尝试;
- 再失败就用
json_repair库修复,这个库专门处理这种情况,效果显著。
# cli_anything/utils.py import json, re from json_repair import repair_json def parse_json_loose(text: str): try: return json.loads(text) except json.JSONDecodeError: pass m = re.search(r"[\[{].*[\]}]", text, re.S) if m: try: return json.loads(m.group()) except json.JSONDecodeError: pass return json.loads(repair_json(text))注意第三级修复前面千万不要做——修复库在长文本上开销很大,能正则解决的就先正则解决。
5.4 长任务超时与进程卡死
默认超时 120 秒,但 PDF 大文件 OCR 或音频转写经常超时。子进程被强制杀掉后,还会留下孤儿进程,占用资源。
我做的改进是:给subprocess.run传入start_new_session=True,超时后用os.killpg杀掉整个进程组,从根源防止孤儿进程:
import os, signal, subprocess def run_with_timeout(cmd, timeout=120): try: result = subprocess.run( cmd, capture_output=True, text=True, timeout=timeout, start_new_session=True, encoding="utf-8" ) return result except subprocess.TimeoutExpired: try: os.killpg(os.getpgid(result.pid), signal.SIGKILL) except ProcessLookupError: pass return {"stdout": "", "stderr": "timeout", "exit_code": -1}另外,插件内部也加了“长任务先降级”的策略:OCR 目标文件超过 20 页时,先转成低分辨率再识别;音频超过 30 分钟时提示用户分段处理。不要指望模型自己判断任务会不会超时,执行层要在入口就做静态检查。
5.5 常见问题速查表
| 现象 | 原因 | 解决方案 |
|---|---|---|
| 命令执行找不到文件 | 相对路径解析问题 | 统一解析为绝对路径再执行 |
| 中文输出乱码 | 编码不一致 | 显式encoding="utf-8",智能编码探测 |
| 模型返回乱 JSON | 生成端不规范 | 三级降级解析,最后用 json_repair |
| 任务跑一半就超时 | 子进程被强杀遗留孤儿 | start_new_session=True+ 杀进程组 |
| 高危命令被误拦 | 黑名单过严 | 白名单例外路径,如/tmp内移动 |
| 插件匹配错任务 | match 置信度阈值太低 | 提高阈值,增加否定词权重 |
排查有一个总原则:永远先看审计日志。CLI-Anything 的每次交互都会把消息记录到~/.cli-anything/logs/,包括模型原始输出、执行结果、每一步耗时。绝大多数问题在日志里直接能定位到是模型判断错了、命令写错了还是插件执行出了异常,根本不需要猜。
我这里再分享一个小技巧:在调试时加一个--verbose参数,将每次调用的完整 request/response 打印出来,尤其是 tool_calls 的原始 arguments。很多“模型答非所问”的问题,其实都是因为 tools schema 描述不准确,模型传了错误参数。通过 verbose 输出你可以非常直观地发现这种 mismatch,然后针对性修改描述。
后期我还在规划几个方向:一是插件市场协议,让第三方开发者能独立发布插件,用户一行命令安装;二是把常用的“分析结果 + 生成脚本”模板沉淀成社区共享库;三是探索把 CLI-Anything 嵌入到编辑器终端里,让它成为开发者工作流真正的默认入口。这些想法都是从实际使用中长出来的,比起一开始就设计一个大而全的框架,我更愿意让它沿着真实需求一点点进化。