AI-CLI 笔记
基于 AI-CLI v0.2.4 源码深度分析

AI-CLI 深度笔记

从源码出发,逐行解析终端 AI 工程师的每个模块 —— 不是概念介绍,而是真正的工程拆解

23
内置工具
4层
记忆系统
4种
Agent 类型
458+
测试用例
10
深度章节
🤖01

ReAct 循环引擎

Agent 的心脏:思考 → 行动 → 观察的无限循环,直到任务完成或需要人类介入。本章深入 AgentSession.run() 的每一行代码。

ReAct 循环完整架构 — AgentSession.run() 内部数据流
用户输入 (userInput)
initializeContext() 构建消息
7层上下文注入
LLM 思考 (llmStream)
检查 finishReason
→ tool_calls
安全确认
executeSingleTool()
结果回注 messages
compressIfNeeded()
上下文压缩
DriftSignals 检测
偏航检测
LLM 再思考 ↩
循环
→ stop
autoExtractMemories()
最终回复用户

上下文初始化:7 层消息注入

initializeContext() 是每次会话启动时最关键的函数。它不是简单地拼接用户输入,而是按照 7 层优先级依次注入上下文信息,确保 LLM 在第一轮推理时就拥有足够的背景知识。这 7 层分别是:系统提示词(System Prompt)、项目规则文件(.ai-cli.md)、工作区上下文(Git 状态等)、长期记忆(语义搜索匹配)、会话记忆(本次会话目标与已完成动作)、用户偏好、以及最终的用户输入。

值得注意的是,长期记忆的注入使用了语义搜索而非关键词匹配。系统会将用户输入向量化,在 SQLite 中搜索最相关的 3 条记忆。这意味着即使用户使用了不同的措辞,只要语义相关,历史决策和偏好仍然会被召回。例如,用户上次说过"我偏好函数式组件",这次问"帮我写个列表页面",系统依然会注入该偏好。

context-initializer.ts — 7层上下文注入
export async function initializeContext(
  config: AgentSessionConfig,
  events: AgentStreamCallbacks,
  userInput: string,
): Promise<InitializedContext> {
  const aiMessages: Message[] = [];

  // 1. System Prompt — 注入工具定义和项目指令
  const toolDefs = mcpManager.getToolDefinitions();
  const systemPrompt = buildSystemPrompt({
    tools: toolDefs,
    projectInstructions,
    cwd
  });
  aiMessages.push({ role: 'system', content: systemPrompt });

  // 2. Project rules file (.ai-cli.md) — 项目级约定
  const { content: rulesContent, hasRules } = loadProjectRules(cwd);
  if (hasRules)
    aiMessages.push({ role: 'system', content: formatRulesForContext(rulesContent) });

  // 3. Workspace context — git分支、最近提交、已修改文件
  const ctx = getCachedContext() || await captureContext(cwd);
  if (ctx.workspaceName)
    aiMessages.push({ role: 'system', content: formatContextForPrompt(ctx) });

  // 4. Long-term memories — 语义搜索匹配(最多3条)
  const memories = await smartSearchMemory(userInput, chatFn, { limit: 3, cwd });
  if (memories.length > 0)
    aiMessages.push({ role: 'system', content: `[相关记忆]\n${memories.map(m => m.content).join('\n')}` });

  // 5. Session memory — 当前会话目标与已完成动作
  const loaded = loadSessionMemory(sessionId, cwd);
  if (loaded && (loaded.goal || loaded.completedActions.length > 0))
    aiMessages.push({ role: 'system', content: formatSessionMemoryForContext(loaded) });

  // 6. User preferences — 用户偏好设置
  const prefs = getUserPreferences(cwd);
  if (prefs.length > 0)
    aiMessages.push({ role: 'system', content: `[用户偏好]\n${prefs.join('\n')}` });

  // 7. User input — 最终的用户输入
  aiMessages.push({ role: 'user', content: userInput });

  return { messages: aiMessages, toolDefs };
}

流式回调类型体系

AgentSession 与外界的所有通信都通过 AgentStreamCallbacks 完成。这个接口定义了 7 个回调函数,覆盖了从文本输出到工具执行的完整生命周期。每个回调都在特定时机被触发,使得上层(CLI 界面、Web GUI、守护进程)可以实时感知 Agent 的状态变化。

其中 onStatus 回调特别重要——它报告 Agent 当前处于 thinking、executing、confirming、compressing 还是 idle 状态。Web GUI 用这个状态来渲染不同的 UI 动画,守护进程用它来记录任务进度日志。

types.ts — 核心回调类型
export interface AgentStreamCallbacks {
  onText: (delta: string, fullContent: string) => void;
  onToolCalls: (calls: ToolCallRequest[]) => void;
  onToolResults: (results: ToolResultEntry[]) => void;
  onStatus: (status: AgentStatus) => void;
  onError: (error: string) => void;
  onComplete: (result: AgentCompleteResult) => void;
  onTokenUsage?: (usage: { input: number; output: number }) => void;
}

export interface AgentStatus {
  type: 'thinking' | 'executing' | 'confirming' | 'compressing' | 'idle';
  detail?: string;
}

export interface LLMStreamResult {
  content: string;
  tool_calls?: ToolCallRequest[];
  finishReason?: 'stop' | 'length' | 'tool_calls' | 'content_filter' | 'aborted' | 'error';
  usage?: { input: number; output: number };
  reasoning_content?: string;
}

偏航检测:防止 Agent 无限空转

DriftSignals 是 ReAct 循环中最容易被忽视却极其重要的子系统。它的作用是监控 Agent 的每一轮迭代,判断是否陷入了无效循环。如果不做偏航检测,Agent 可能会反复调用同一个工具、持续返回空结果、或者产出越来越短的内容——白白消耗 token 而无法完成任务。

系统定义了 6 个核心信号:consecutiveFailures(连续失败次数)、noProgressTurns(无进展轮数)、outputLengthDrop(输出长度骤降比)、repeatedToolFingerprints(重复工具指纹)、refusalCount(拒绝执行次数)以及 pendingToolCount(待执行工具数)。

当连续失败达到 8 次或空闲轮数超过 10 轮时,系统判定为严重偏航,直接终止循环并向用户报告。轻度偏航时,系统会尝试自动纠偏策略,如注入提示让 LLM 换个思路、或者主动向用户询问更多信息。

drift-detector.ts — 偏航检测阈值与信号
const CONSECUTIVE_FAILURES_THRESHOLD = 4;   // 连续4次失败 → 警告
const NO_PROGRESS_TURNS_THRESHOLD = 5;       // 连续5轮无进展 → 警告
const OUTPUT_LENGTH_DROP_RATIO = 0.6;        // 输出长度降至60% → 可疑
const REPEATED_TOOL_PATTERN_THRESHOLD = 5;   // 同一工具模式5次 → 可疑
const SEVERE_FAILURES_THRESHOLD = 8;         // 连续8次失败 → 终止循环
const SEVERE_IDLE_THRESHOLD = 10;            // 连续10轮空闲 → 终止循环

export interface DriftSignals {
  consecutiveFailures: number;      // 连续工具执行失败计数
  noProgressTurns: number;          // 无有效输出轮数
  recentAvgOutputLength: number;    // 近期平均输出长度(基准)
  currentOutputLength: number;      // 当前输出长度
  refusalCount: number;             // LLM 拒绝执行计数
  recentToolFingerprints: string[]; // 最近N轮的工具调用指纹
  pendingToolCount: number;         // 待确认/执行的工具数
  hasToolCalls: boolean;            // 本轮是否调用了工具
  hasTextOutput: boolean;           // 本轮是否有文本输出
  turnNumber: number;               // 当前迭代轮次
}

错误恢复机制

除了偏航检测,AgentSession 还内置了多种错误恢复策略。injectLengthContinuation 在 LLM 因 finishReason === "length" 截断时自动注入续写提示,让 LLM 继续未完成的输出。checkEmptyReply 检测到空响应时会重新发送请求。isRepeatingSameTool 检测到 LLM 反复调用同一工具时,会注入反思提示,引导 LLM 改变策略。

compressIfNeeded() 是另一个关键的恢复机制。当上下文消息超过模型 token 限制时,系统会自动压缩历史消息——保留最近几轮的完整对话,将更早的轮次压缩为摘要。这确保了长会话不会因为上下文溢出而崩溃。

💡 核心洞察ReAct 循环的精髓不在于「循环」本身,而在于每一轮迭代后的质量保障。偏航检测防止空转、错误恢复保证鲁棒性、上下文压缩避免溢出——这三个子系统共同确保了 Agent 在面对复杂任务时能够持续产出有效结果,而不是在某个环节卡死后无限循环。这正是 AI-CLI 与简单的 LLM API 调用的本质区别。

🧠02

四层记忆系统

从会话到长期,AI 如何记住你的项目、偏好和历史决策。深入 autoExtract、AutoDream、语义搜索的源码实现。

名称存什么持久性触发时机
L1会话记忆完整对话历史SQLite 全量存储每轮迭代
L2工作上下文Git状态/项目规则/任务看板SQLite + 文件缓存会话初始化
L3长期记忆结构化事实/偏好/决策SQLite 索引 + 语义搜索initializeContext 语义匹配
L4自动提取LLM 事后分析 → 提取关键信息写入 L3会话结束 / AutoDream

L4 自动提取:autoExtractMemories() 源码解析

autoExtractMemories() 在每次会话结束时被调用。它不是简单地存储所有对话,而是用 LLM 对对话内容进行事后分析,只提取真正有价值的信息。提取 prompt 明确要求忽略闲聊、问候和中间过程,只保留偏好、决策、事实和命令四类信息。

每条提取结果都有 confidence 字段(0-100),只有置信度 ≥ 70 的条目才会被写入长期记忆。同时,系统还内置了去重机制,避免重复提取相同的信息。每日提取次数限制为 10 次(DAILY_LIMIT),防止频繁调用浪费 token。

auto-extract.ts — 自动记忆提取
const DAILY_LIMIT = 10;  // 每日最多提取10次
const EXTRACTION_PROMPT = `分析以下对话,只提取**真正有价值**的信息。输出纯 JSON 数组:
[
  { "type": "preference", "content": "用户偏好...", "confidence": 80 },
  { "type": "decision", "content": "决定使用...", "confidence": 90 },
  { "type": "fact", "content": "项目事实...", "confidence": 60 }
]
类型: preference(偏好), decision(决策), fact(事实), command(命令)
置信度: 0-100。80+:用户明确陈述;60-79:可推断;<60:不确定。
忽略闲聊、问候、中间过程和失败尝试。`;

export async function autoExtractMemories(
  messages: Message[],
  cwd: string,
  chatFn: (msgs: Message[]) => Promise<string>
): Promise<number> {
  resetDailyIfNeeded();               // 每日重置计数器
  if (todayCount >= DAILY_LIMIT) return 0;  // 超过每日限额
  const valuable = messages.filter(m => m.role === 'user' || m.role === 'assistant');
  if (valuable.length < 3) return 0;  // 消息太少,不值得提取

  // LLM 提取 + 置信度过滤(>=70) + 去重
  const extractionMsgs = [
    { role: 'system', content: EXTRACTION_PROMPT },
    { role: 'user', content: JSON.stringify(valuable.slice(-20)) }  // 只取最近20条
  ];
  const raw = await chatFn(extractionMsgs);
  const items = JSON.parse(raw)
    .filter((item: any) => item.confidence >= 70);  // 置信度阈值

  let saved = 0;
  for (const item of items) {
    const isDup = await checkDuplicate(item.content, cwd);  // 去重检查
    if (!isDup) {
      await saveMemory({ type: item.type, content: item.content, cwd });
      saved++;
    }
  }
  todayCount += saved;
  return saved;
}

AutoDream:后台自动整理记忆

AutoDream 是比 autoExtract 更深层的记忆整理机制。它不是在每次会话结束时触发,而是在后台条件满足时才运行。这个设计灵感来源于人类睡眠时的记忆整理——不是每时每刻都在整理,而是在积累了足够经验后,找个时间后台整理。

四道门控各有明确的设计意图:时间门(24小时)避免频繁整理;会话门(5次会话)确保积累了足够素材;锁门(30分钟过期)防止并发执行;预算门确保不会因整理记忆而耗尽 API 额度。

四道门控条件(全部满足才触发)
时间门 ≥ 24h会话门 ≥ 5次锁门 无并发Budget门 充足
ForkedAgent 后台分析
写入长期记忆 (L3)
auto-dream.ts — AutoDream 门控逻辑
const TIME_GATE_MS = 24 * 60 * 60 * 1000;  // 24小时
const SESSION_GATE_COUNT = 5;                    // 5次会话
const LOCK_STALE_MS = 30 * 60 * 1000;           // 30分钟锁过期

export function checkAutoDream(cwd: string): boolean {
  // 1. 时间门控:距上次dream已超过24小时
  const lastDream = getLastDreamTime(cwd);
  if (Date.now() - lastDream < TIME_GATE_MS) return false;

  // 2. 会话门控:自上次dream后已有5次会话
  const sessionsSince = getSessionsSinceLastDream(cwd);
  if (sessionsSince < SESSION_GATE_COUNT) return false;

  // 3. 锁门控:没有正在运行的dream
  const lock = getDreamLock(cwd);
  if (lock && Date.now() - lock.timestamp < LOCK_STALE_MS) return false;

  // 4. 预算门控:token预算充足
  const budget = getTokenBudget(cwd);
  if (budget.remaining < DREAM_MIN_BUDGET) return false;

  return true;  // 全部通过,可以触发AutoDream
}

记忆搜索:从关键词到语义匹配

searchMemory() 实现了多层过滤的记忆检索。首先对查询进行分词,然后通过 SQL LIKE 进行预过滤来减少候选行数。接着匹配标签(#tag 格式),再根据重要性分数进行加权排序。这种混合策略兼顾了检索速度和准确性——纯向量搜索在大规模数据上较慢,而纯关键词搜索会遗漏语义相关但措辞不同的记忆。

工作区上下文捕获

context-capture.ts — Git 上下文捕获
export async function captureContext(cwd: string): Promise<WorkspaceContext> {
  // 并行执行4个git命令,减少等待时间
  const [gitBranch, gitRemote, gitLog, gitStatus] = await Promise.all([
    execSafe(['git', 'branch', '--show-current'], cwd),    // 当前分支
    execSafe(['git', 'remote', 'get-url', 'origin'], cwd), // 远程仓库
    execSafe(['git', 'log', '--oneline', '-5'], cwd),      // 最近5条提交
    execSafe(['git', 'status', '--short'], cwd),           // 文件变更状态
  ]);

  return {
    workspaceName: path.basename(cwd),
    gitBranch: gitBranch.stdout,
    gitRemote: gitRemote.stdout,
    recentCommits: gitLog.stdout.split('\n').filter(Boolean),
    modifiedFiles: gitStatus.stdout.split('\n').filter(Boolean),
  };
}

记忆管理命令

📝
/remember <内容>

手动保存一条记忆到L3长期记忆库

🔍
/recall <关键词>

语义搜索长期记忆,返回最相关结果

🗑️
/forget <id>

删除指定ID的记忆条目

📦
/compact

手动压缩当前上下文,释放token空间

🌙
/dream

手动触发AutoDream整理记忆

📊
/memory stats

查看记忆库统计信息(数量/类型分布)

💡 核心洞察四层记忆的设计哲学是 「越用越快」。同类问题第二次遇到时,L3 长期记忆已存有相关事实,LLM 不需要从零分析项目结构,速度提升数倍。而 L4 自动提取确保了记忆的积累不需要用户手动干预——这是系统级的设计,而非可选功能。AutoDream 的四道门控则体现了 「整理是有成本的」 这一工程现实:只有积累到足够有价值时,才值得花费 token 去整理。

🔧03

MCP 工具系统

23 个内置工具分 7 大类,覆盖从文件操作到 Agent 调度的全链路。深入 executeSingleTool() 的完整生命周期,以及外部 MCP 的 stdio/SSE 接入方式。

📁 文件
file-systemfile-editorarchivenotebook-editor
🔧 开发
shellcode-runnernpmtypecheckformatdev-tools
🔍 搜索
search-webread-docsknowledge
📦 Git
gitrepo-tools
🧪 测试
test-runnerdatabase
🤖 Agent
agent-toolparallel-taskstodo
⚙️ 系统
process-managersshsleepmemory

executeSingleTool() 完整生命周期

每次工具调用都经过 executeSingleTool() 这个统一的执行入口。这个函数负责从 JSON 参数解析、安全分类、工具执行、结果持久化到重试跟踪的完整流程。它接受一个 retryTracker Map 来跨轮次跟踪工具调用的重试次数,防止 LLM 反复用相同参数调用同一失败工具。

参数解析使用 JSON.parse(tc.arguments),失败时直接返回错误信息而非抛异常。安全分类分为两级:classifyTool(toolDef) 基于工具声明确定静态类别,runtimeClassify() 结合运行时危险检查确定最终类别。工具指纹(toolFingerprint())用于识别「同一工具+相似参数」的重复调用模式。

executor.ts — 工具执行核心
export async function executeSingleTool(
  tc: ToolCallRequest,
  toolCtx: ToolExecutionContext,
  retryTracker: Map<string, number>,
  cachedDangerResult?: 'safe' | { level: 'caution' | 'danger'; reason: string },
): Promise<ToolResultEntry> {
  // Step 1: 解析参数
  let parsedArgs: Record<string, unknown>;
  try { parsedArgs = JSON.parse(tc.arguments); }
  catch { return { error: 'JSON解析失败' }; }

  // Step 2: 查找工具定义 + 静态分类
  const toolDef = mcpManager.getTool(tc.name);
  const staticCat = classifyTool(toolDef);

  // Step 3: 运行时危险检查(dangerCheck 函数)
  const dangerCheckResult = cachedDangerResult
    ?? toolDef?.dangerCheck?.(parsedArgs, toolCtx.cwd);
  const errorCategory = runtimeClassify(staticCat, tc.name, dangerCheckResult);

  // Step 4: 执行工具
  let toolResult = await mcpManager.executeTool(
    { name: tc.name, arguments: parsedArgs }, toolCtx
  );

  // Step 5: 应用上下文修改器
  if (toolResult.contextModifier) toolResult.contextModifier(toolCtx);

  // Step 6: 重试指纹跟踪
  let retryCount = 0;
  if (!toolResult.success) {
    const fp = toolFingerprint(tc.name, parsedArgs);
    retryCount = (retryTracker.get(fp) ?? -1) + 1;
    retryTracker.set(fp, retryCount);
  }

  return { ...toolResult, retryCount, errorCategory };
}

大结果持久化:避免上下文爆炸

工具执行的结果可能非常大(例如 shell 命令输出数百行日志,或 file-system 读取大文件)。如果将这些完整结果注入消息列表,会迅速耗尽上下文窗口。

persistLargeResult() 在结果超过 3000 字符时自动触发:将完整内容保存到 /tmp/ai-cli-results/ 目录,消息列表中只保留摘要(前30行 + 后10行 + 省略提示)。同时,后台异步清理超过 24 小时的临时文件。

result-storage.ts — 大结果持久化
const RESULT_THRESHOLD = 3000;  // 超过3000字符触发持久化

export function persistLargeResult(toolName: string, content: string) {
  if (content.length <= RESULT_THRESHOLD) return { summary: content };

  // 保存完整结果到临时文件
  const timestamp = Date.now();
  const filePath = `/tmp/ai-cli-results/${timestamp}-${toolName}.txt`;
  fs.writeFileSync(filePath, content, 'utf-8');

  // 生成摘要:前30行 + 最后10行 + 省略提示
  const lines = content.split('\n');
  const head = lines.slice(0, 30).join('\n');
  const tail = lines.slice(-10).join('\n');
  const omitted = lines.length - 40;
  const summary = `${head}\n... (省略 ${omitted} 行) ...\n${tail}`;

  // 异步清理24小时前的文件
  scheduleCleanup(24 * 60 * 60 * 1000);

  return {
    summary,
    filePath,
    totalLines: lines.length,
    totalSize: content.length,
  };
}

外部 MCP 支持:stdio 与 SSE 双协议

除了 23 个内置工具,AI-CLI 支持通过 stdio(本地子进程通信)和 SSE(Server-Sent Events 远程服务)两种协议接入外部 MCP 服务器。stdio 模式下,AI-CLI 会启动一个子进程(如 npx @modelcontextprotocol/server-filesystem),通过标准输入输出进行 JSON-RPC 通信。SSE 模式下,AI-CLI 连接到远程 HTTP 端点(如 GitHub MCP API),通过长连接接收事件。

每个外部工具可单独配置 dangerLevelOverride 覆盖默认安全级别。例如,将 GitHub 的 search_repositories 设为 safe 自动放行,但 create_issue 设为 danger 强制确认。这种细粒度控制确保了外部工具的安全性不会成为系统的薄弱环节。

💡 实用技巧工具系统的设计遵循了 「管道 + 过滤器」 模式。每个工具是独立的过滤器,输入参数经过 Zod 校验、安全分类、执行、结果持久化、重试跟踪等多层管道处理。这种架构使得新增工具只需定义 schema + execute 函数,无需关心安全、持久化、重试等横切关注点。而 contextModifier 机制更允许工具在执行后修改全局上下文,例如 git commit 后自动更新工作区上下文。

🔒04

安全引擎

三级安全 + AI 分类器:安全操作自动放行,危险操作必须确认。深入分类器 prompt 设计和运行时危险检查的实现细节。

Level名称行为典型工具
safe安全自动执行,无需确认读文件、搜索代码、ls/cat
caution注意AI 分类器动态判断写文件、shell命令、npm install
danger危险必须用户确认删除文件、git push、rm -rf

AI 分类器:完整 Prompt 与决策逻辑

AI 分类器是安全引擎最核心的创新。传统的做法是将工具声明为固定的 safe/dangerous,但这种方式无法处理「同一工具在不同参数下安全性不同」的情况。例如 shell 工具执行 ls 是安全的,但执行 rm -rf / 是危险的。AI 分类器用轻量 LLM 在 200-500ms 内对 caution 级别的工具调用做出动态判断。

分类器的 system prompt 精心设计了一张常见场景判断表,覆盖了文件操作、Shell 命令、Git 操作等主要场景。输出格式要求严格 JSON,只返回 {"decision":"safe"|"risky","reason":"..."}。这种约束确保了解析的可靠性和低延迟。

classifier.ts — AI 安全分类器完整 Prompt
const CLASSIFIER_SYSTEM_PROMPT = [
  '你是工具安全分类器。判断一次工具调用是否可以在不询问用户的情况下自动执行。',
  '决策标准:',
  '- "safe" = 可以自动执行,无需用户确认',
  '- "risky" = 需要用户确认后再执行',
  '',
  '常见场景判断:',
  '| 读取文件/搜索代码 | safe | 无害操作 |',
  '| 写入/修改现有文件 | risky | 可能覆盖重要内容 |',
  '| 创建新文件 | safe | 新增内容不破坏已有 |',
  '| 删除文件/目录 | risky | 不可逆操作 |',
  '| shell: ls/cat/grep/pwd/which | safe | 只读命令 |',
  '| shell: rm/mv/cp/dd/format | risky | 破坏性命令 |',
  '| shell: mkdir/touch | safe | 创建行为无害 |',
  '| shell: npm/pip/cargo install | risky | 修改依赖锁文件 |',
  '| shell: git commit/push | risky | 不可逆版本操作 |',
  '| 编译/构建 | safe | 构建输出可预览 |',
  '',
  '输出格式(仅 JSON):',
  '{"decision":"safe"|"risky","reason":"简短解释(10-30字)"}',
].join('\n');

export async function classifyToolAction(
  options: ClassifyOptions
): Promise<ClassifyResult> {
  // 使用轻量 LLM 调用,延迟 200-500ms
  // 输入:工具名 + 参数 + 工作上下文
  // 输出:{ decision: 'safe' | 'risky', reason: string }
  const result = await lightweightLLMCall({
    system: CLASSIFIER_SYSTEM_PROMPT,
    user: JSON.stringify({
      tool: options.toolName,
      args: options.args,
      context: options.workingContext,
    }),
    maxTokens: 100,  // 极短输出,保证速度
  });
  return JSON.parse(result);
}
安全决策完整流程
工具调用请求
声明级 dangerLevel
safe → 直接放行
danger → 直接拦截
运行时 dangerCheck(args)
检查参数中的危险模式
caution 级别 → 需要动态判断
AI 分类器 (轻量 LLM)
safe → 自动执行
risky → 弹确认框

dangerCheck 运行时检查

danger-check 运行时逻辑
// 每个工具可以定义自己的 dangerCheck 函数
// shell 工具的 dangerCheck 示例:
function shellDangerCheck(args: { command: string }, cwd: string) {
  const cmd = args.command.trim().toLowerCase();
  const dangerousPatterns = [
    /rm\s+-rf/, /format/, /dd\s+if=/,          // 破坏性命令
    /git\s+push/, /git\s+reset\s+--hard/,      // 不可逆Git操作
    /npm\s+publish/, /docker\s+rm/,             // 发布/删除操作
    /curl.*\|\s*sh/, /wget.*\|\s*bash/,       // 远程脚本执行
  ];
  const safePatterns = [
    /^ls/, /^cat/, /^grep/, /^find/, /^pwd/,      // 只读命令
    /^echo/, /^mkdir/, /^touch/, /^which/,         // 创建/查询
    /^git\s+status/, /^git\s+log/, /^git\s+diff/, // Git只读
  ];

  for (const p of dangerousPatterns) {
    if (p.test(cmd)) return { level: 'danger', reason: '检测到破坏性命令模式' };
  }
  for (const p of safePatterns) {
    if (p.test(cmd)) return 'safe';
  }
  return { level: 'caution', reason: '需要AI分类器判断' };
}

💡 核心洞察AI 分类器的价值在于 减少 50-80% 确认弹框。传统的安全模型只有 safe/danger 两级,导致大量实际安全的操作(如写入新文件、执行 ls 命令)也需要确认。caution 级别的引入 + AI 动态判断,让系统能够根据具体参数做更精确的决策。分类器使用轻量 LLM,单次延迟仅 200-500ms,相比频繁打断用户的工作流,这点延迟完全可以接受。

🔀05

子Agent系统

4 种内置 Agent 类型,支持并行执行和递归安全控制。深入 ForkedAgent 的工具过滤策略和进度回调机制。

Type名称权限默认轮次使用场景
general-purpose通用读写10子任务孵化(可读写文件)
explore探索只读8代码探索(搜索/阅读/理解)
plan规划只读5方案设计(分析/规划/不执行)
verify验证只读5结果检查(验证/确认/对比)

ForkedAgent 完整配置

ForkedAgent 的配置接口设计体现了几个重要的工程决策:maxTurns 根据类型设置不同默认值,explore/plan/verify 类型轮次更少,鼓励快速收敛;parentDepth 限制递归深度,防止 Agent 无限嵌套;canUseTool 允许父 Agent 对子 Agent 的工具使用进行精细控制。

onProgress 回调每 30 秒触发一次,向父 Agent 报告子 Agent 的进度摘要。这个机制解决了长时间运行子任务时的「黑盒」问题——父 Agent 可以在等待子任务完成的同时,向用户展示实时进度。

forked-agent.ts — ForkedAgent 配置接口
export interface ForkedAgentOptions {
  prompt: string;                    // 子任务描述
  llmStream: LLMStreamFn;           // LLM 流式调用函数
  cwd: string;                       // 工作目录
  maxTurns?: number;                 // 最大轮次(按类型默认:general=10, explore=8, plan/verify=5)
  parentDepth?: number;              // 递归深度限制(防止无限嵌套)
  agentType?: BuiltinAgentType;      // 'general-purpose' | 'explore' | 'plan' | 'verify'
  description?: string;              // 子任务简述(用于日志和UI展示)
  onProgress?: (summary: string, turn: number, toolCount: number) => void;
  progressIntervalMs?: number;       // 进度回调间隔,默认 30000 (30s)
  rawOutput?: boolean;               // 是否返回原始输出(不做结论格式化)
  canUseTool?: (toolName: string, args: any) => boolean | Promise<boolean>;
}

// 默认工具过滤器 — 防止无限递归
async function defaultCanUseTool(toolName: string): Promise<boolean> {
  if (toolName === 'agent_tool') return false;    // 禁止子Agent再派生子Agent
  if (toolName.startsWith('memory_')) return false; // 禁止修改记忆
  // explore/plan/verify 类型只允许只读工具
  if (['explore', 'plan', 'verify'].includes(agentType)) {
    return isReadOnlyTool(toolName);  // 只有搜索、读取类工具
  }
  return true;  // general-purpose 允许所有工具
}

Agent 类型元提示词

每种 Agent 类型都有一段精心设计的元提示词(Meta Prompt),注入到子 Agent 的系统提示中。这些提示词约束了子 Agent 的行为边界——只做分配的事、不扩展范围、快速收敛。特别是结论格式要求(结论 + 证据 + 下一步建议),确保父 Agent 能够快速理解子 Agent 的输出并做出下一步决策。

agent-types.ts — 共享元提示词
const SHARED_META = `
## 你是子任务执行者
### 通用规则
1. 只做分配的事,不扩展范围
2. 快速收敛,每轮自查"我离目标还有多远"
3. 完成或遇到阻碍后,用结论格式结束

### 结束格式要求
结论: <一句话总结>
证据: <关键发现列表>
下一步: <建议父 Agent 做什么>
`;

// explore 类型额外约束
const EXPLORE_META = SHARED_META + `
### 额外约束
- 只使用只读工具(搜索、读取、查看)
- 不修改任何文件
- 专注于理解和发现信息
- 最多8轮,第6轮起准备收尾
`;

// plan 类型额外约束
const PLAN_META = SHARED_META + `
### 额外约束
- 只使用只读工具进行分析
- 输出结构化的执行方案
- 明确每一步的输入/输出/风险
- 不执行任何实际修改
`;

并行执行架构

agent-tool
派生单个 ForkedAgent
parallel-tasks
管理多个并行 Agent
progress callback every 30s
Agent-1
探索代码结构
Agent-2
搜索相关文档
Agent-3
验证现有测试

💡 递归安全控制parentDepth 限制嵌套深度,防止 Agent 无限递归派生子 Agent。agent_tool 始终从子 Agent 的可用工具列表中过滤,确保子 Agent 不能再派生自己的子 Agent。这是系统级的硬性约束,无法通过配置绕过。这种设计借鉴了操作系统进程树的概念——每个子进程继承父进程的部分权限,但绝不能获得比父进程更高的权限。

🛡️06

守护进程 + Web GUI

不在终端前也能跑:4 层守护架构 + 5 种触发器 + 浏览器双通道控制。深入 daemon 进程管理和 cron 调度器的实现。

守护进程 4 层架构

守护进程让 AI-CLI 能在用户不主动操作时自动执行任务。整个架构分为 4 层:CLI 层是用户交互入口,提供 start/stop/status/trigger 四个命令;IPC 层通过 HTTP 服务(默认端口 3051)实现进程间通信;调度层管理 5 种触发器,根据配置的 cron 表达式、时间间隔、Git 变化或文件变化来触发任务;执行层启动无头模式的 AgentSession(DAEMON_TASK_TURNS = 20),在没有用户交互的情况下运行 Agent。

守护进程的启停使用 PID 文件机制:启动时写入 PID 文件,停止时读取 PID 发送 SIGTERM 信号。进程异常退出时,PID 文件会留下僵尸记录,系统会检查进程是否存活来处理这种情况。

CLI 层 (start/stop/status/trigger)
用户交互入口
IPC 层 (HTTP :3051)
进程间通信
调度层 (cron/git/file/manual)
触发器管理
执行层 (AgentSession 无头模式, 20轮)
任务执行
daemon.ts — 守护进程生命周期
const DAEMON_TASK_TURNS = 20;  // 守护任务最多20轮

// 启动守护进程
async function start(config: DaemonConfig) {
  // 1. 写入 PID 文件
  fs.writeFileSync(PID_PATH, process.pid.toString());

  // 2. 初始化 MCP 工具
  await mcpManager.initialize(config.mcpServers);

  // 3. 启动调度器
  const scheduler = new Scheduler(config.triggers);

  // 4. 启动 IPC HTTP 服务
  const server = createIPCServer(scheduler);
  server.listen(3051);
}

// 停止守护进程
async function stop() {
  const pid = readPID();  // 读取 PID
  if (pid && isProcessAlive(pid)) {
    process.kill(pid, 'SIGTERM');  // 优雅停止
    await cleanup(pid);            // 清理资源
  }
}

// 查看状态
function status() {
  const pid = readPID();
  return {
    running: pid && isProcessAlive(pid),
    pid,
    uptime: getUptime(pid),
    pendingTasks: scheduler.getPendingCount(),
  };
}

Cron 调度器实现

调度器实现了完整的 cron 表达式解析,支持 */N(每N单位)、逗号分隔(1,15)、通配符(*)等标准语法。每分钟检查一次是否有匹配的触发器。getNextCronTime() 函数计算下一次触发时间,用于 UI 展示和延迟调度。

scheduler.ts — Cron 解析与匹配
// Cron 表达式解析
function parseCron(cronExpr: string): CronFields {
  const [minute, hour, dayOfMonth, month, dayOfWeek] = cronExpr.split(' ');
  return {
    minute: parseField(minute, 0, 59),
    hour: parseField(hour, 0, 23),
    dayOfMonth: parseField(dayOfMonth, 1, 31),
    month: parseField(month, 1, 12),
    dayOfWeek: parseField(dayOfWeek, 0, 6),
  };
}

// 匹配当前时间
function matchesCron(cron: CronFields, date: Date): boolean {
  return cron.minute.has(date.getMinutes())
    && cron.hour.has(date.getHours())
    && cron.dayOfMonth.has(date.getDate())
    && cron.month.has(date.getMonth() + 1)
    && cron.dayOfWeek.has(date.getDay());
}

// 计算下一次触发时间
function getNextCronTime(cronExpr: string, from: Date): Date {
  const cron = parseCron(cronExpr);
  let next = new Date(from.getTime() + 60000); // 从下一分钟开始
  next.setSeconds(0, 0);
  while (!matchesCron(cron, next)) {
    next = new Date(next.getTime() + 60000);
  }
  return next;
}

5 种触发器类型

timer (cron)

每分钟检查 cron 表达式,支持标准 cron 语法,精确调度

timer (interval)

setInterval 定时执行,适合固定间隔的简单场景

git-hook

轮询 .git/logs/HEAD 变化,commit/push 时自动触发

file-watch

fs.watch + 防抖(500ms),文件变化时触发

manual

HTTP POST /trigger 手动触发,可远程触发

Web GUI 架构

浏览器
WebSocket
AgentSession
流式文本工具确认模型切换进度监控

💡 实用场景设置一个 cron 守护任务每天早上9点自动生成 changelog,或者监听 git-hook 在每次 commit 后自动执行代码 review。Web GUI 让你在手机上也能实时查看 AI 的工作进度和执行结果。守护进程 + Web GUI 的组合,本质上是把 AI-CLI 从「被动工具」变成了「主动协作者」——它不需要你下达指令,自己就会在合适的时机执行预设任务。

🔌07

插件 + Hook 系统

三大开放协议:Plugin API + Tool Hook + External MCP。深入 PluginManager 的加载机制和 Hook 的前后拦截逻辑。

PluginManager:插件加载与隔离

PluginManager 采用独立加载、故障隔离的策略。每个插件独立加载,某个插件加载失败不会影响其他插件的运行。插件通过三个核心 API 与系统集成:registerTool() 注册自定义 MCP 工具,registerCommand() 注册斜杠命令,getConfig() 读取私有配置。这种设计使得插件之间完全解耦,每个插件拥有独立的配置空间和工具命名空间。

manager.ts — PluginManager 核心实现
export class PluginManager {
  private active: Map<string, ActivePlugin> = new Map();
  private commands: Map<string, CommandHandler> = new Map();

  async loadFromConfig(config: AIConfig): Promise<PluginLoadResult[]> {
    const results: PluginLoadResult[] = [];
    for (const [name, pluginConfig] of Object.entries(config.plugins ?? {})) {
      try {
        const plugin = await this.loadPlugin(name, pluginConfig);
        this.active.set(name, plugin);
        results.push({ name, status: 'loaded' });
      } catch (err) {
        // 单个插件失败不影响其他插件
        results.push({ name, status: 'failed', error: String(err) });
      }
    }
    return results;
  }

  // 插件 API
  registerTool(name: string, tool: ToolDefinition) { ... }
  registerCommand(name: string, handler: CommandHandler) { ... }
  getConfig(pluginName: string, key: string): unknown { ... }
}
registerTool()

注册自定义 MCP 工具,支持 Zod Schema 定义参数

registerCommand()

注册自定义斜杠命令,拦截并处理用户输入

getConfig()

读取插件私有配置,隔离不同插件配置空间

Tool Hook:前后拦截脚本

Tool Hook 是一种声明式的工具增强机制,无需写代码即可在工具执行前后注入自定义逻辑。pre hook 在工具执行前运行,可以修改工具参数(updatedInput)、阻止执行(block)或注入额外上下文(additionalContexts)。post hook 在工具执行后运行,可以修改执行结果、注入上下文或阻止 Agent 继续循环(preventContinuation)。

prePhase 控制前置 hook 的触发时机:beforeConfirm 在安全确认之前(可以阻止危险操作),beforeExecute 在安全确认之后、实际执行之前(确认安全后做最后处理)。

tool-hooks.ts — Hook 结果类型
export interface PreToolHookResult {
  updatedInput?: Record<string, unknown>;  // 修改工具参数
  block?: { reason: string };              // 阻止执行
  additionalContexts?: string[];            // 注入额外上下文
}

export interface PostToolHookResult {
  updatedResult?: Partial<ToolResult>;     // 修改执行结果
  additionalContexts?: string[];           // 注入额外上下文
  preventContinuation?: boolean;           // 阻止 Agent 继续循环
}

// 配置示例
export const LIFECYCLE_EVENTS = [
  'session_start', 'user_prompt_submit', 'pre_compact', 'post_compact',
  'session_end', 'subagent_start', 'subagent_stop',
  'post_tool_use_failure', 'stop', 'stop_failure',
] as const;
config.yaml — Tool Hook 完整配置
toolHooks:
  - tools: ["write_file", "file_system*"]    # 通配符匹配
    pre: "npx eslint --fix {{file}}"         # 写入前自动格式化
    post: "npm test -- --changed"            # 写入后自动运行测试
    prePhase: "beforeConfirm"                # 在安全确认前执行
    timeout: 15000                           # 15秒超时

  - tools: ["shell"]
    pre: "echo '即将执行: {{command}}'"      # 执行前记录日志
    post: "echo '执行完成,退出码: {{exitCode}}'"
    timeout: 5000

生命周期 Hook

session_startuser_prompt_submitpre_compactpost_compactsession_endsubagent_startsubagent_stoppost_tool_use_failurestopstop_failure
config.yaml — 生命周期 Hook 配置
lifecycleHooks:
  session_start:
    - script: "echo '新会话启动' && npx tsc --noEmit"
      timeout: 30000
  session_end:
    - script: "echo '会话结束,清理临时文件'"
  stop_failure:
    - script: "notify-send 'AI-CLI 任务失败'"
      timeout: 5000

三大开放协议总结

Plugin APInpm 包形式注册工具和命令,拥有独立配置空间,适合复杂扩展
Tool HookYAML 配置前后拦截脚本,无需写代码即可增强工具行为,适合轻量定制
External MCPstdio/SSE 协议接入第三方服务,复用整个 MCP 生态,适合集成外部能力

💡 设计哲学三大开放协议的设计遵循了 「渐进式扩展」 原则:最简单的需求用 Tool Hook(YAML 配置),中等复杂度用 External MCP(接入第三方),最复杂的用 Plugin API(写代码)。用户不需要为了一个简单的 lint 前置检查去写一个完整的插件——一条 YAML 配置就够了。

📦08

三大产品线

ai-cli / cocos-cli / robot-cli:同一核心引擎,三种产品形态,服务不同场景的用户需求。

AI-CLI 并非单一产品,而是基于同一核心引擎(AgentSession + 记忆系统 + 工具系统)构建的三条产品线。三条产品线共享底层架构,但在工具集、Agent 类型和交互模式上有显著差异,分别面向个人开发者、游戏开发团队和自动化运维场景。

维度ai-clicocos-clirobot-cli
定位通用 AI 编程助手游戏开发专用 CLI无人值守自动化 Agent
目标用户全栈开发者Cocos Creator 游戏开发者DevOps / SRE 团队
交互模式终端对话 + Web GUI终端对话 + 编辑器集成守护进程 + Web Dashboard
核心工具23 个通用工具通用 + 游戏专用工具通用 + 运维专用工具
Agent 类型4 种内置4 种 + 游戏专用4 种 + 自动化专用
守护进程可选可选核心功能
使用场景写代码/调试/重构场景搭建/资源管理/构建部署监控/告警/自动修复
ai-cli — 通用 AI 编程助手

ai-cli 是三条产品线中的旗舰产品,面向所有软件开发者。它提供 23 个内置工具,覆盖文件操作、代码执行、搜索、Git 操作等通用场景。4 种内置 Agent 类型(通用、探索、规划、验证)可以组合使用,完成从代码探索到方案设计再到结果验证的完整工作流。

典型使用场景包括:让 AI 阅读代码库并解释架构、自动修复 lint 错误、重构老旧模块、编写单元测试、生成 API 文档等。守护进程是可选功能,适合需要定时执行代码 review 或自动生成 changelog 的团队。

23 内置工具4 种 Agent4 层记忆MCP 扩展
cocos-cli — 游戏开发专用 CLI

cocos-cli 在 ai-cli 基础上增加了游戏开发专用工具:场景管理工具(创建/修改/预览 Cocos 场景)、资源管线工具(纹理压缩、图集打包、资源引用分析)、构建部署工具(多平台构建、热更新包生成)和调试工具(性能分析、内存泄漏检测)。

它还添加了游戏开发专用的 Agent 类型:SceneBuilder(场景搭建)、AssetOptimizer(资源优化)和 BuildPipeline(构建管线)。这些 Agent 预置了游戏开发的领域知识,能够理解场景层级、组件系统和资源依赖关系。

场景管理资源管线多平台构建编辑器集成
robot-cli — 无人值守自动化 Agent

robot-cli 将守护进程从可选功能提升为核心功能。它专为无人值守场景设计,预置了运维专用工具:服务监控工具(健康检查、日志分析、指标采集)、告警工具(通知分发、升级策略)、自动修复工具(服务重启、配置回滚、扩容缩容)和部署工具(蓝绿部署、金丝雀发布)。

专用的 Agent 类型包括:MonitorAgent(持续监控,异常检测)、FixerAgent(自动诊断和修复)和 DeployAgent(安全部署流程)。robot-cli 的守护进程支持多任务并行,Web Dashboard 提供实时监控视图和告警管理界面。

监控告警自动修复安全部署Web Dashboard

💡 产品架构哲学三条产品线的设计遵循了 「核心不变,外围可变」 的原则。AgentSession、记忆系统、安全引擎是三条线共享的不可变核心;工具集、Agent 类型、交互模式是可变的外围。这种架构使得新功能只需在核心层实现一次,三条产品线自动受益。同时,每条产品线的专用工具和 Agent 不会污染核心,保持了系统的简洁和可维护性。

⚙️09

配置体系

config.yaml 完整解读:Provider 配置、MCP 服务器接入、Hook 定义、插件加载。一个 YAML 文件掌控全局。

AI-CLI 的所有配置集中在一个 config.yaml 文件中。这个文件控制着 LLM Provider 选择、MCP 服务器连接、工具 Hook、生命周期 Hook、插件加载等所有行为。配置文件支持项目级.ai-cli/config.yaml)和用户级~/.ai-cli/config.yaml)两级,项目级配置覆盖用户级配置。

config.yaml — 完整配置示例
# ============================================
# AI-CLI 完整配置文件
# ============================================

# ---- Provider 配置 ----
# 指定默认 Provider 和各 Provider 的连接信息
provider: kimi
providers:
  kimi:
    baseURL: https://api.moonshot.cn/v1
    apiKey: sk-xxx
    model: kimi-k2
  deepseek:
    baseURL: https://api.deepseek.com/v1
    apiKey: sk-xxx
    model: deepseek-chat
  openai:
    baseURL: https://api.openai.com/v1
    apiKey: sk-xxx
    model: gpt-4o

# ---- 外部 MCP 服务器 ----
# 通过 stdio 或 SSE 接入第三方工具服务
mcpServers:
  filesystem:
    command: npx
    args: ["-y", "@modelcontextprotocol/server-filesystem", "/path"]
  github:
    url: https://api.github.com/mcp/sse
    headers:
      Authorization: "Bearer ghp_xxx"
    dangerLevelOverride:
      search_repositories: safe       # 搜索自动放行
      create_issue: danger            # 创建Issue必须确认

# ---- 工具 Hook ----
# 在工具执行前后注入自定义脚本
toolHooks:
  - tools: ["write_file", "file_system*"]
    pre: "npx eslint --fix {{file}}"
    post: "npm test -- --changed"
    prePhase: "beforeConfirm"       # beforeConfirm | beforeExecute
    timeout: 15000

  - tools: ["shell"]
    pre: "echo '执行: {{command}}' >> /tmp/ai-cli.log"
    timeout: 5000

# ---- 生命周期 Hook ----
# 在会话关键节点执行脚本
lifecycleHooks:
  session_start:
    - script: "echo '新会话启动' && npx tsc --noEmit"
      timeout: 30000
  session_end:
    - script: "echo '会话结束,清理临时文件'"
  stop_failure:
    - script: "notify-send 'AI-CLI 任务失败'"
      timeout: 5000

# ---- 插件 ----
# 加载 npm 包形式的插件
plugins:
  my-custom-plugin:
    package: "@my-org/ai-cli-plugin"
    config:
      apiKey: "xxx"
      region: "cn"

# ---- Agent 配置 ----
agent:
  maxTurns: 50                    # 最大循环轮次
  compressionThreshold: 80000     # 上下文压缩阈值(tokens)
  autoExtract: true               # 自动记忆提取
  autoDream: true                 # AutoDream 后台整理

# ---- 守护进程 ----
daemon:
  port: 3051                      # IPC 端口
  tasks:
    - name: "每日代码Review"
      trigger: "cron"
      schedule: "0 9 * * 1-5"     # 工作日每天9点
      prompt: "审查最近24小时的代码变更"
      maxTurns: 20

配置项详解

Provider 配置

支持多 Provider 切换,每个 Provider 定义 baseURL、apiKey 和 model。通过顶层 provider 字段指定默认使用的 Provider。运行时可通过 /model 命令动态切换。

MCP 服务器配置

stdio 模式用 command + args 启动子进程,SSE 模式用 url + headers 连接远程服务。dangerLevelOverride 为每个外部工具独立设置安全级别,覆盖默认分类。

Tool Hook 配置

tools 字段支持精确匹配和通配符(*)。pre/post 分别指定前置和后置脚本。{{file}}、{{command}} 等模板变量会在执行时替换为实际参数。prePhase 控制前置 hook 的触发时机。

Agent 全局配置

maxTurns 限制单次会话最大循环轮次。compressionThreshold 设置上下文压缩触发阈值。autoExtract 和 autoDream 控制记忆自动提取和后台整理功能。

💡 配置优先级配置的优先级从高到低为:命令行参数 > 环境变量 > 项目级 config.yaml > 用户级 config.yaml > 内置默认值。这种分层设计确保了团队协作时的一致性(项目级配置提交到 Git),同时保留了个人偏好的灵活性(用户级配置不提交)。

🧪10

质量保障

458+ 测试用例、分层测试策略、错误分类体系、重试机制。AI-CLI 如何确保一个会调用 rm -rf 的 AI 系统不会搞砸你的项目?

AI-CLI 是一个会执行 Shell 命令、修改文件系统、操作 Git 仓库的 AI 系统。如果质量保障不到位,后果远不止输出错误文本——它可能删除你的代码、推送错误的 commit、或者泄露敏感信息。因此,AI-CLI 的测试策略不是传统软件的「锦上添花」,而是生死攸关的基础设施

458+
测试用例总数
7
测试分层
95%+
代码覆盖率
0
P0 级未修复Bug

分层测试策略

AI-CLI 的测试分为 7 个层次,从最底层的纯函数测试到最顶端的端到端集成测试。每层测试有明确的职责边界和运行频率:

层次类型测试内容运行频率
L1单元测试纯函数、工具类、格式化函数每次提交
L2组件测试记忆搜索、上下文构建、配置解析每次提交
L3安全测试dangerCheck、AI 分类器、权限边界每次提交
L4集成测试工具执行链、MCP 服务器通信每日
L5Agent 测试ReAct 循环、偏航检测、错误恢复每日
L6守护进程测试cron 调度、IPC 通信、进程管理每周
L7端到端测试完整用户流程(创建项目→编码→测试→部署)每周

错误分类体系

AI-CLI 将所有错误分为 5 个类别,每个类别有不同的处理策略:Transient(临时错误,自动重试)、Validation(参数错误,返回给 LLM 让其修正)、Permission(权限错误,向用户请求授权)、Resource(资源限制,降级处理)、Fatal(致命错误,终止会话)。

Transient
自动重试
Validation
LLM修正
Permission
用户授权
Resource
降级处理
Fatal
终止会话

安全关键测试场景

安全测试是最重要的测试层次。AI-CLI 针对以下高危场景编写了专门的测试用例,确保在任何情况下这些安全边界都不会被突破:

🛡️
rm -rf 防护

验证 AI 分类器始终将 rm -rf 标记为 risky,无论参数格式如何变化

🛡️
Git push 防护

验证 git push、git reset --hard 等不可逆操作必须用户确认

🛡️
子 Agent 递归

验证子 Agent 不能调用 agent_tool 派生自己的子 Agent

🛡️
记忆隔离

验证子 Agent 不能修改长期记忆(memory_* 工具被过滤)

🛡️
工具指纹去重

验证重复工具调用模式被正确检测和限制

🛡️
偏航终止

验证连续失败8次或空闲10轮时循环被自动终止

重试机制与指数退避

对于 Transient 类别的错误,系统采用指数退避策略自动重试。重试次数、间隔和最大等待时间均可通过配置调整。重试逻辑嵌入在 executeSingleTool()retryTracker 中——每次失败都会记录工具指纹和重试次数,超过最大重试次数后放弃并向 LLM 报告错误。

💡 质量哲学AI-CLI 的质量保障哲学可以总结为 「安全第一,体验第二」。宁可多弹一次确认框(false positive),也不能让危险操作自动执行(false negative)。458+ 测试用例中,安全测试占了约 30% 的比重,而且每个安全测试都有独立的断言——确保即使其他模块出了 bug,安全边界也不会被突破。这种防御性设计是 AI 系统区别于传统软件的核心特征。

从源码到产品, AI-CLI 的工程哲学

10 个章节,10 个核心模块。每个模块独立运行又紧密协作,共同构成了一个真正能干活的终端 AI 工程师。