diff --git a/docs/01-AI-Agent步数预算与incomplete.md b/docs/01-AI-Agent步数预算与incomplete.md new file mode 100644 index 0000000..000b672 --- /dev/null +++ b/docs/01-AI-Agent步数预算与incomplete.md @@ -0,0 +1,29 @@ +# 01 - AI Agent 步数预算与 incomplete(opsxshell) + +> // Logan.Li +> Shell/GUI 侧 Agent 循环边界。统一语义见 aiops/opsxcli 的 **13-运行结果状态与可续跑契约**。 + +## 现状 + +| 项 | 值 / 行为 | +| ----------------------------------- | --------------------------------------------------------------------- | +| `AiTerminalPanel` `MAX_AGENT_STEPS` | **12** | +| 触顶 | 抛错(用户感知为「没跑完就挂」) | +| 执行绑定 | 当前 SSH **`connectionId`**(与三产品边界一致,不得漂移到未选中连接) | + +## 目标 + +| 项 | 目标行为 | +| ---- | ----------------------------------------------------------------- | +| 触顶 | `incomplete` + `max_steps`,展示已用步数/预算 | +| 续跑 | 在**同一 connectionId** 下注入 prior/轨迹后继续;或提示用户加预算 | +| 边界 | **不**引入跨入口通用远程执行器 | + +## 链接 + +- 契约:aiops `docs/ai-agent/13-运行结果状态与可续跑契约.md`(与 opsxcli 同步) +- 三产品边界:aiops `docs/ai-agent/09-三产品定位与执行边界.md` + +--- + +_新建: 2026-09-18 CST_ diff --git a/docs/README.md b/docs/README.md index e7c60c4..f041b34 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,18 +1,7 @@ -# OpsXShell 文档 +# opsxshell docs -文档按使用、开发、验收和发布分开维护,避免把规划能力写成已支持功能。 +| 文档 | 说明 | +| --------------------------------------------------------------------- | ----------------------------------------- | +| [01-AI-Agent步数预算与incomplete](01-AI-Agent步数预算与incomplete.md) | MAX_AGENT_STEPS=12 与 incomplete/续跑目标 | -| 文档 | 内容 | -| -------------------------------------------------- | --------------------------------------------------------- | -| [使用指南](./user-guide.md) | SSH、SFTP、代理、串口、AI 助手和数据安全 | -| [架构说明](./architecture.md) | Tauri/Rust 分层、原生服务、AI 与安全边界 | -| [架构重构轨道](./architecture-refactor.md) | EventSink / specta / infrastructure(未开始,单独做) | -| [SSH Agent CLI](./ai-agent.md) | ReAct 执行循环、工具、风险、记忆取舍与评测 | -| [测试验收](./testing-acceptance.md) | 本地门禁、真实 SSH 探针和发版验收 | -| [发布指南](./release.md) | 构建、Tauri DMG、本机签名;公证 staple 与自动更新仍待完成 | -| [Apple 开发者签名](../download/Apple开发者签名.md) | 申请 Developer Program、Developer ID、导入钥匙串 | -| [路线图](../ROADMAP.md) | 已完成基线与下一阶段(根目录权威副本) | -| [当前任务](../TASKS.md) | Hermes / OpenClaw 可勾选任务 | -| [测试计划](../TDD_PLAN.md) | 巡检、门禁与下一任务的测试先行 | - -项目定位、功能概览和开发命令见仓库根目录的 [README](../README.md),未完成事项只在 [ROADMAP.md](../ROADMAP.md) / [TASKS.md](../TASKS.md) 维护。 +跨产品契约以 aiops/opsxcli 的 `13-运行结果状态与可续跑契约.md` 为准。 diff --git a/docs/ai-agent.md b/docs/ai-agent.md index 86c58cf..3ae85dd 100644 --- a/docs/ai-agent.md +++ b/docs/ai-agent.md @@ -153,3 +153,9 @@ src-tauri/src/services/ai/ | 输出包含 Token/私钥 | Provider 请求前脱敏,脱敏计数可见 | 只有模型质量、工具效率、安全拦截、失败恢复、上下文成本和不持久化边界都能自动回归时,才可以继续提升自主性。 + +--- + +## 步数预算与 incomplete(2026-09-18) + +详见 [01-AI-Agent步数预算与incomplete](01-AI-Agent步数预算与incomplete.md)。三产品统一契约见 opsxcli/aiops 的 `13-运行结果状态与可续跑契约.md`(`MAX_AGENT_STEPS=12` 触顶应为 incomplete,不是假装完成)。 diff --git a/src/components/ai/AiTerminalPanel.tsx b/src/components/ai/AiTerminalPanel.tsx index a418ef2..fd6cfb0 100644 --- a/src/components/ai/AiTerminalPanel.tsx +++ b/src/components/ai/AiTerminalPanel.tsx @@ -61,6 +61,8 @@ interface ThreadMessage { role: 'user' | 'assistant' content: string steps?: AgentStep[] + outcome?: 'completed' | 'incomplete' | 'failed' | 'cancelled' + reason?: string } const MAX_AGENT_STEPS = 12 @@ -396,7 +398,25 @@ export default function AiTerminalPanel({ consecutiveFailures = step.status === 'error' ? consecutiveFailures + 1 : 0 setLiveSteps([...completedSteps]) } - throw new Error(i18n.t('ai:error.maxSteps', { max: MAX_AGENT_STEPS })) + // incomplete:max_steps — 保留已完成步骤,允许用户追问续跑(契约 13) // Logan.Li + const incompleteMsg = i18n.t('ai:error.maxStepsIncomplete', { + max: MAX_AGENT_STEPS, + defaultValue: i18n.t('ai:error.maxSteps', { max: MAX_AGENT_STEPS }), + }) + setError(incompleteMsg) + setMessages((prev) => [ + ...prev, + { + id: messageId(), + role: 'assistant', + content: incompleteMsg, + steps: completedSteps.map((step) => ({ ...step })), + outcome: 'incomplete', + reason: 'max_steps', + }, + ]) + setLiveSteps([]) + return } catch (caught) { const message = translateErrorMessage(caught instanceof Error ? caught.message : String(caught)) setError(message) diff --git a/src/i18n/locales/en/ai.json b/src/i18n/locales/en/ai.json index 93c8f3e..4f425a4 100644 --- a/src/i18n/locales/en/ai.json +++ b/src/i18n/locales/en/ai.json @@ -81,7 +81,8 @@ "execUnknown": "Unknown error", "execNoOutput": "Command finished with no output", "maxSteps": "Reached {{max}}-step limit; narrow the scope and retry", - "noOutput": "No terminal output to summarize" + "noOutput": "No terminal output to summarize", + "maxStepsIncomplete": "Incomplete (max_steps): hit the {{max}}-step budget. Narrow the task or follow up to resume with prior steps." }, "output": { "empty": "(empty output)", diff --git a/src/i18n/locales/en/errors.json b/src/i18n/locales/en/errors.json index 86692f9..47c558f 100644 --- a/src/i18n/locales/en/errors.json +++ b/src/i18n/locales/en/errors.json @@ -165,7 +165,8 @@ "criticalBlocked": "Command hit a destructive hard block; no override: {{command}}", "execFailed": "Remote execution failed: {{error}}", "maxSteps": "Reached {{max}}-step limit; narrow the scope and retry", - "sshRequired": "A connected remote SSH session is required" + "sshRequired": "A connected remote SSH session is required", + "maxStepsIncomplete": "Incomplete (max_steps): hit the {{max}}-step budget. Narrow scope or follow up to resume." }, "zmodem": { "readFailed": "Read failed: {{name}}", diff --git a/src/i18n/locales/zh-CN/ai.json b/src/i18n/locales/zh-CN/ai.json index 9a09575..16e8f0d 100644 --- a/src/i18n/locales/zh-CN/ai.json +++ b/src/i18n/locales/zh-CN/ai.json @@ -81,7 +81,8 @@ "execUnknown": "未知错误", "execNoOutput": "命令执行完成,无输出", "maxSteps": "已达 {{max}} 步上限,请缩小范围后重试", - "noOutput": "终端没有可总结的输出" + "noOutput": "终端没有可总结的输出", + "maxStepsIncomplete": "未完成(incomplete:max_steps):已达 {{max}} 步预算。可缩小范围、或直接追问继续(会带着已有步骤续跑)。" }, "output": { "empty": "(空输出)", diff --git a/src/i18n/locales/zh-CN/errors.json b/src/i18n/locales/zh-CN/errors.json index cc5d5f2..f0c4daf 100644 --- a/src/i18n/locales/zh-CN/errors.json +++ b/src/i18n/locales/zh-CN/errors.json @@ -165,7 +165,8 @@ "criticalBlocked": "命令命中破坏性操作硬红线,不提供确认放行:{{command}}", "execFailed": "远程执行失败:{{error}}", "maxSteps": "已达 {{max}} 步上限,请缩小范围后重试", - "sshRequired": "需要已连接的远程 SSH" + "sshRequired": "需要已连接的远程 SSH", + "maxStepsIncomplete": "未完成(incomplete:max_steps):已达 {{max}} 步预算。可缩小范围或追问续跑。" }, "zmodem": { "readFailed": "读取失败: {{name}}",