feat(ai): 步数触顶改为 incomplete 可续跑

MAX_AGENT_STEPS 触顶保留已完成 steps,支持追问续跑;
补充 docs 与 zh/en 文案 maxStepsIncomplete。
This commit is contained in:
Logan.Li committed 2026-09-19 00:06:53 +08:00
1 parent ae6093c9b3
commit 96497092a8
8 files changed
+69 -21

No files matched your search

@@ -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_
+5 -16
View File
@@ -1,18 +1,7 @@
# OpsXShell 文档 # opsxshell docs
文档按使用、开发、验收和发布分开维护,避免把规划能力写成已支持功能。 | 文档 | 说明 |
| --------------------------------------------------------------------- | ----------------------------------------- |
| [01-AI-Agent步数预算与incomplete](01-AI-Agent步数预算与incomplete.md) | MAX_AGENT_STEPS=12 与 incomplete/续跑目标 |
| 文档 | 内容 | 跨产品契约以 aiops/opsxcli 的 `13-运行结果状态与可续跑契约.md` 为准。
| -------------------------------------------------- | --------------------------------------------------------- |
| [使用指南](./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) 维护。
+6
View File
@@ -153,3 +153,9 @@ src-tauri/src/services/ai/
| 输出包含 Token/私钥 | Provider 请求前脱敏,脱敏计数可见 | | 输出包含 Token/私钥 | Provider 请求前脱敏,脱敏计数可见 |
只有模型质量、工具效率、安全拦截、失败恢复、上下文成本和不持久化边界都能自动回归时,才可以继续提升自主性。 只有模型质量、工具效率、安全拦截、失败恢复、上下文成本和不持久化边界都能自动回归时,才可以继续提升自主性。
---
## 步数预算与 incomplete(2026-09-18)
详见 [01-AI-Agent步数预算与incomplete](01-AI-Agent步数预算与incomplete.md)。三产品统一契约见 opsxcli/aiops 的 `13-运行结果状态与可续跑契约.md`(`MAX_AGENT_STEPS=12` 触顶应为 incomplete,不是假装完成)。
+21 -1
View File
@@ -61,6 +61,8 @@ interface ThreadMessage {
role: 'user' | 'assistant' role: 'user' | 'assistant'
content: string content: string
steps?: AgentStep[] steps?: AgentStep[]
outcome?: 'completed' | 'incomplete' | 'failed' | 'cancelled'
reason?: string
} }
const MAX_AGENT_STEPS = 12 const MAX_AGENT_STEPS = 12
@@ -396,7 +398,25 @@ export default function AiTerminalPanel({
consecutiveFailures = step.status === 'error' ? consecutiveFailures + 1 : 0 consecutiveFailures = step.status === 'error' ? consecutiveFailures + 1 : 0
setLiveSteps([...completedSteps]) 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) { } catch (caught) {
const message = translateErrorMessage(caught instanceof Error ? caught.message : String(caught)) const message = translateErrorMessage(caught instanceof Error ? caught.message : String(caught))
setError(message) setError(message)
+2 -1
View File
@@ -81,7 +81,8 @@
"execUnknown": "Unknown error", "execUnknown": "Unknown error",
"execNoOutput": "Command finished with no output", "execNoOutput": "Command finished with no output",
"maxSteps": "Reached {{max}}-step limit; narrow the scope and retry", "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": { "output": {
"empty": "(empty output)", "empty": "(empty output)",
+2 -1
View File
@@ -165,7 +165,8 @@
"criticalBlocked": "Command hit a destructive hard block; no override: {{command}}", "criticalBlocked": "Command hit a destructive hard block; no override: {{command}}",
"execFailed": "Remote execution failed: {{error}}", "execFailed": "Remote execution failed: {{error}}",
"maxSteps": "Reached {{max}}-step limit; narrow the scope and retry", "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": { "zmodem": {
"readFailed": "Read failed: {{name}}", "readFailed": "Read failed: {{name}}",
+2 -1
View File
@@ -81,7 +81,8 @@
"execUnknown": "未知错误", "execUnknown": "未知错误",
"execNoOutput": "命令执行完成,无输出", "execNoOutput": "命令执行完成,无输出",
"maxSteps": "已达 {{max}} 步上限,请缩小范围后重试", "maxSteps": "已达 {{max}} 步上限,请缩小范围后重试",
"noOutput": "终端没有可总结的输出" "noOutput": "终端没有可总结的输出",
"maxStepsIncomplete": "未完成(incomplete:max_steps):已达 {{max}} 步预算。可缩小范围、或直接追问继续(会带着已有步骤续跑)。"
}, },
"output": { "output": {
"empty": "(空输出)", "empty": "(空输出)",
+2 -1
View File
@@ -165,7 +165,8 @@
"criticalBlocked": "命令命中破坏性操作硬红线,不提供确认放行:{{command}}", "criticalBlocked": "命令命中破坏性操作硬红线,不提供确认放行:{{command}}",
"execFailed": "远程执行失败:{{error}}", "execFailed": "远程执行失败:{{error}}",
"maxSteps": "已达 {{max}} 步上限,请缩小范围后重试", "maxSteps": "已达 {{max}} 步上限,请缩小范围后重试",
"sshRequired": "需要已连接的远程 SSH" "sshRequired": "需要已连接的远程 SSH",
"maxStepsIncomplete": "未完成(incomplete:max_steps):已达 {{max}} 步预算。可缩小范围或追问续跑。"
}, },
"zmodem": { "zmodem": {
"readFailed": "读取失败: {{name}}", "readFailed": "读取失败: {{name}}",