ai_agent_cli_guide
道-方法 · 道层 skill 全文
本页是 <code>rules/skills/ai_agent_cli_guide.md</code> 的逐字投影(仅隐私清洗,零改写)。
时点提示:本页是仓内文件 rules/skills/ai_agent_cli_guide.md 的逐字投影(仅做隐私清洗:仓库根绝对路径→相对路径、家目录→~/;除此零改写)。若源文件后续有修订,以仓内真源为准。
AI CLI Agent 实用指南
元数据
- 类型: API Guide
- 适用场景: 用 CLI Agent 构建自动化流水线、AI 调用 AI
- 最后更新: 2026-07-11
何时用 CLI Agent 而非原始 API
直接调用 LLM API 在处理复杂任务时存在短板。CLI Agent 作为中间层的核心优势:
- 对抗"模型偷懒": 大任务时 API 易输出截断,Agent 天生具备循环执行和自我纠正能力
- 原生文件上下文: Agent 自动处理文件读取、编码和写入,将推理与 IO 解耦
- 继承工具链: 内置 MCP 插件,可随时调用 Tavily 搜索、执行脚本等
- 优化上下文管理: 自动处理 Context Window 消耗和长对话压缩
工具速查
| 维度 | Claude Code | Codex CLI | OpenCode |
|---|---|---|---|
| 开源 | ❌ | ❌ | ✅ 100% |
| 模型绑定 | 仅 Claude | 仅 OpenAI | Provider-agnostic(xAI, Anthropic, OpenAI, Google 等) |
| CLI 非交互 | claude --print | codex exec | opencode serve + opencode run --attach(两步) |
| Web API | ❌ | ❌ | ✅ 完整 |
| 推荐场景 | 深度推理 | 自动化 | 多模型对比、自动化 + 可视化 |
文件响应模式(核心设计原则)
原则: 在生产环境中,所有输入输出都通过文件,严禁使用管道模式处理核心逻辑。
为什么:
- 确定性: AI 在"编辑文件"时的心理模型是"完成工作并保存",不容易产生截断
- 可审计: 任务前后文件系统的变化(git diff)是唯一的真理
- 大容量: 绕过命令行参数长度限制
实现要点:
- 输入: Prompt 必须先落到本地文件,再由程序读入
- 输出: CLI 输出必须写入本地文件,再由程序读取解析
- JSON 输出: 在 Prompt 中显式要求"只输出 JSON"
Python 示例:
import subprocess
from pathlib import Path
# 1. 写 prompt 到文件
Path("prompt.txt").write_text("Your task here...")
# 2. 构造驱动 prompt
driver_prompt = (
f"Read the full prompt from {Path('prompt.txt').resolve()}\n"
f"Write ONLY a JSON object to {Path('output.json').resolve()}\n"
"Do not include Markdown or extra text."
)
# 3. 执行(以 Claude Code 为例)
subprocess.run([
"claude", "--print", "--output-format", "json",
"--model", "claude-sonnet-4-6-20260217",
driver_prompt.replace('\0', '') # 清理 null byte
])Claude Code 快速参考
基本命令: claude --print "prompt"
关键参数:
--model:claude-sonnet-4-6-20260217(推荐) 或claude-opus-4-6-20260205(深度推理)--output-format:text/json/stream-json--permission-mode:acceptEdits/bypassPermissions--json-schema: 强制输出符合 JSON Schema
推荐: Sonnet 4.6 性能接近 Opus,价格仅 1/5
Codex CLI 快速参考
基本命令: codex exec [options] "prompt"
关键参数:
-m, --model: 具体 ID 由tools/llm_runtime/providers.conf的codex:*profile 决定;GPT-5.6 默认使用 Sol-c model_reasoning_effort: Sol/Terra 当前支持low/medium/high/xhigh/max/ultra;Luna 到max-c model_context_window=272000+-c model_auto_compact_token_limit=200000+-
-c model_auto_compact_token_limit_scope='"total"': workspace 的 GPT-5.5-era - 提前压缩策略;后台 direct worker 必须显式传,不依赖 host 配置
--full-auto: 自动接受所有操作--json: JSON 输出格式
默认与命名: 用户未指定特殊 effort 时,通过 cli_agent 使用 codex:xhigh。对外显示为 x-high,传给 Codex CLI 的 wire 值必须是 xhigh;literal x-high 会被拒绝。codex:ultra 必须原样传 ultra,不得 alias 到 xhigh。当前顺序是 high → xhigh → max → ultra,因此默认 x-high 属于 Ultra 以下的高 effort,但并非紧邻 Ultra(中间还有最大单-agent推理的 max)。OpenAI 原生 Power 默认 gpt-5.6-sol + medium,不等于本仓库的调用政策默认。
推荐: 生产脚本通过 cli_agent 调 plain codex(解析为 codex:xhigh)或显式 codex:low / codex:high / codex:xhigh / codex:max / codex:ultra,不要在业务代码中硬编码具体模型 ID;直接 CLI 只用于 harness 自检或一次性实验。旧 codex:deep / codex:fast 仅作兼容 alias。
272K 防护: OpenAI GPT-5.6 API 模型页明确写明输入 >272K 后整次请求
input 2x、output 1.5x。统一 trigger 是 200K:前台/后台 direct Codex 使用上述
三个 -c 键,OpenCode/API 使用 limit.input=220000 与 reserved 20K 的等价配置。safe_input_budget=200000 只是输入文件提示,不会自动 compact;Auto Compact 本身
也只在 turn boundary 生效,单次巨型输入仍不是硬拒绝保证。当前 Codex credits
文档未明示同一个 272K 阶跃,不要把 API 结论扩张成订阅计费已确认事实。
OpenCode 快速参考
OpenCode 深度用法(CLI serve/run --attach 与 Web Server API 两种非交互调用方式、关键参数、Python 客户端、REST 端点、启动认证、五条硬规则)见 opencode_server_usage_guide。
AI 调用 AI 模式
如果你正在编写一个 Agent 来调用这些 CLI,提供如下元指令:
"当面临大规模文本处理或文件系统操作时,请调用底层的 CLI Agent:
1. 优先使用文件响应模式,先将待处理内容存入本地临时文件
2. 使用流模式 (--json) 并实时解析事件以便监控进度
3. 设置合理的推理强度(如翻译设为 low)
4. 传递 Prompt 前清理空字符 (.replace('\0', ''))
5. 对于 OpenCode,优先使用 Web Server API"
极简主义设计哲学 (pi-mono)
来自 pi-mono 项目的核心原则:"What's missing matters more than what's included"
核心理念:
- Context Engineering is Paramount: 上下文工程比工具数量更重要
- Full Observability: 完全可观测,无隐藏状态
- External State: 写文件而非维护内部状态
- Builder's Mindset: 面向构建者而非消费者设计
启发: 在复杂任务中,添加功能往往是逃避问题。真正难的决策是:什么不该有。
模型选择速查
| 任务类型 | Claude Code | Codex | OpenCode |
|---|---|---|---|
| 翻译/格式转换 | Sonnet 4.6 | gpt-5.2 + low | (你的轻量模型) |
| 常规开发 | Sonnet 4.6 | gpt-5.2 + medium | (你的标准模型) |
| 深度推理/重构 | Opus 4.6 | gpt-5.2 + high | (你的推理模型) |
配额与虚假冷却防护(跨 provider)
router(CliAgentRouter)按 quota scope 记录各 provider 的限额状态,撞限额就跳过同 scope 的 tier、走 fallback 链。"虚假配额冷却"指 provider 实际可用、却被标记冷却并被持续跳过。两个机制专门防它(2026-06-04 落地)。
第一,限额只从 provider 的 durable 错误内容识别,不被 stderr 污染。判断一个 provider 是否真限额,只看它返回的正式错误体(claude 的 result 字段、codex 的 --output-last-message 写出的内容),不扫子进程 stderr 里回显的 prompt、加载规则、skill 描述。而且无论成功路径还是错误路径,都只在短错误体(少于 600 字)上认 rate limit。这样一段正常的长输出、或一次和配额无关的失败,即便 stderr 里出现 "quota"、"429"、"rate limit" 字样,也不会被误判成限额写进冷却。识别词表是 tools/llm_runtime/rate_limits.py 的 LLM_RATE_LIMIT_REGEX,但词表只在 durable 错误内容上匹配,不在整段 stderr 上扫。
第二,跳过冷却 scope 前先 live probe,provider 恢复就自动解冻。router 要跳过一个标记冷却的 scope 前,先看冷却记录里的 next_probe_at:若已过探测点,就发一个最小 "ping" 探测,provider 已恢复就当场 record_scope_success 解冻、继续用它;仍限额就 bump_next_probe 推后探测点再跳过。这样撞过一次 429、但 provider 早已恢复的 scope,不会被一直跳到记录里的 reset 时间,而是在下一个探测点自动复活。
真冷却和虚假冷却怎么分:provider 的速率窗口(GLM 报的 "5 hour usage limit"、Kimi 的 5h API 窗口)和网页端显示的余额、月度用量是两套数字,窗口耗尽时 API 返回 429,即便网页端余额充足。所以"网页能用但 router 报冷却"有两种情形:真窗口限额(等窗口 reset 或换 provider),或虚假冷却(撞过一次但已恢复,现在 live probe 会自动解冻)。
需要手工介入的少数情况:
# 查所有 scope 状态
python3 tools/llm_runtime/quota_state.py status
# 确认 provider 可用后手工解冻某个 scope
python3 tools/llm_runtime/quota_state.py record-success --scope <zai|kimi|openai|anthropic>
# 主动探测一个 scope,按真实结果更新状态
python3 tools/llm_runtime/wake_resume.py probe --scope <s> -- <最小请求命令>各 scope 默认 backoff:ZAI 15 分钟、Codex(openai)30 分钟、Kimi 与 Anthropic 5 小时 5 分。Kimi 最长,一次误判会锁 5 小时,所以 live probe 对它收益最大。机制代码真源在 tools/llm_runtime/:rate_limits.py 识别、quota_state.py 状态与 backoff、wake_resume.py 探测;router 侧集成在 tools/cli_agent/router.py 的 dispatch fallback loop。
OpenCode 生产经验(量化交易实验总结)
以下来自在 某量化交易实验项目中用 OpenCode + Grok 4.20 跑 回测实验的实战经验。
权限模型:文件必须在项目根目录下
OpenCode agent 无法访问项目根目录之外的路径(包括 /tmp/)。所有 file IO(prompt 输入文件、JSON 输出文件)必须放在 server 启动时的工作目录下。
# ❌ 错误:/tmp 会被 agent 拒绝访问
prompt_path = Path("/tmp/opencode_prompt.txt")
# ✅ 正确:放在 run 目录或项目本地目录
prompt_path = run_dir / "opencode_prompts" / f"{invocation_id}.txt"
# 或 fallback 到项目根目录下
prompt_path = Path(".opencode_tmp") / f"{invocation_id}.txt"可靠性:stdout JSON 兜底
即使 prompt 明确要求"写入文件",agent 有时会直接在 stdout 输出 JSON 而不写文件(或写入空文件)。生产环境必须实现 stdout JSON 提取作为 fallback:
import re
def _extract_json_from_text(text: str) -> dict | None:
"""从 stdout 中提取 JSON 对象(兜底方案)"""
# 优先匹配 ```json 代码块
m = re.search(r"```json\s*(\{.*?\})\s*```", text, re.DOTALL)
if m:
return json.loads(m.group(1))
# 回退:找最大的 {...} 块
candidates = re.findall(r"\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}", text)
for c in sorted(candidates, key=len, reverse=True):
try:
return json.loads(c)
except json.JSONDecodeError:
continue
return None并发能力
一个 opencode serve 进程可以处理多个并发的 run --attach 请求。每个请求创建独立的 session。实测 2 并发稳定,更高并发取决于 LLM provider 的 rate limit。
Grok 4.20 不支持的参数
xai/grok-4.20-experimental-beta-0304-non-reasoning 不支持 presence_penalty、frequency_penalty、stop 参数。如果 OpenCode 转发了这些参数,API 会报错。
典型调用流程(文件响应模式)
1. 写 prompt → run_dir/opencode_prompts/XXXX.txt
2. 构造 driver prompt: "Read from {prompt_path}, write JSON to {response_path}"
3. opencode run --attach http://localhost:14097 -m model "driver_prompt"
4. 读 run_dir/opencode_responses/XXXX.json
5. 如果文件为空 → 从 stdout 提取 JSON(fallback)
6. 如果仍然失败 → retry(最多 3 次)关键数据
- Smoke test: 7/7 成功(零 retry),约 3 分钟 wall clock(6 个 30 分钟间隔)
- 每个 decision 平均耗时: ~20-30 秒(Grok 4.20 non-reasoning)
- Server 启动命令:
opencode serve --port 14097(从 <your-project> 目录启动)