diff --git a/apps/desktop/src/main/services/agent/flows/review.ts b/apps/desktop/src/main/services/agent/flows/review.ts index baa654e..b88cc25 100644 --- a/apps/desktop/src/main/services/agent/flows/review.ts +++ b/apps/desktop/src/main/services/agent/flows/review.ts @@ -105,7 +105,7 @@ export function runReviewForPr( language: getMainLanguage(), toolCatalog: buildToolCatalog(agentCfg.autopilot.grants), plan: effectivePlan, - maxFollowupAsks: agentCfg.autopilot.max_followup_asks, + maxFollowupAsks: agentCfg.strategy.max_followup_asks, summaryMaxChars: agentCfg.summary_max_chars, onStep: (sessionId, step) => runtime.emitStep(pr, sessionId, step), signal, diff --git a/apps/desktop/src/renderer/src/components/features/settings/SettingsModal.tsx b/apps/desktop/src/renderer/src/components/features/settings/SettingsModal.tsx index 2b6254c..a601203 100644 --- a/apps/desktop/src/renderer/src/components/features/settings/SettingsModal.tsx +++ b/apps/desktop/src/renderer/src/components/features/settings/SettingsModal.tsx @@ -222,6 +222,8 @@ export function SettingsModal({ diff --git a/apps/desktop/src/renderer/src/components/features/settings/hooks/useSettingsDraft.ts b/apps/desktop/src/renderer/src/components/features/settings/hooks/useSettingsDraft.ts index a08bd05..2f33956 100644 --- a/apps/desktop/src/renderer/src/components/features/settings/hooks/useSettingsDraft.ts +++ b/apps/desktop/src/renderer/src/components/features/settings/hooks/useSettingsDraft.ts @@ -41,8 +41,11 @@ export function useSettingsDraft({ // 原样回传、不被覆盖成默认值;目录经 agentDirInput、策略开关经 autoFollowup 可编辑。 const [agent] = useState(config.agent); const [agentDirInput, setAgentDirInput] = useState(config.agent.dir); - // Agent 策略(自动追问 + 代码建议数量上限)。随 config:setAgent 一并保存。 + // Agent 策略(自动追问开关 + 追问数量上限 + 代码建议数量上限)。随 config:setAgent 一并保存。 const [autoFollowup, setAutoFollowupState] = useState(config.agent.strategy.auto_followup); + const [maxFollowupAsks, setMaxFollowupAsksState] = useState( + config.agent.strategy.max_followup_asks, + ); const [maxCodeSuggestions, setMaxCodeSuggestionsState] = useState( config.agent.strategy.max_code_suggestions, ); @@ -69,6 +72,7 @@ export function useSettingsDraft({ reposDir: config.workspace.repos_dir, agentDir: config.agent.dir, autoFollowup: config.agent.strategy.auto_followup, + maxFollowupAsks: config.agent.strategy.max_followup_asks, maxCodeSuggestions: config.agent.strategy.max_code_suggestions, poller: config.poller.interval_seconds, concurrency: config.pr_agent.max_concurrency, @@ -238,6 +242,10 @@ export function useSettingsDraft({ setAutoFollowupState(v); setSaved(false); }; + const setMaxFollowupAsks = (n: number): void => { + setMaxFollowupAsksState(n); + setSaved(false); + }; const setMaxCodeSuggestions = (n: number): void => { setMaxCodeSuggestionsState(n); setSaved(false); @@ -266,6 +274,7 @@ export function useSettingsDraft({ const agentChanged = agentDirInput.trim() !== base.agentDir || autoFollowup !== base.autoFollowup || + maxFollowupAsks !== base.maxFollowupAsks || maxCodeSuggestions !== base.maxCodeSuggestions; const pollerChanged = pollerInput.trim() !== String(base.poller); const concurrencyChanged = maxConcurrencyInput !== base.concurrency; @@ -306,6 +315,7 @@ export function useSettingsDraft({ strategy: { ...agent.strategy, auto_followup: autoFollowup, + max_followup_asks: maxFollowupAsks, max_code_suggestions: maxCodeSuggestions, }, }, @@ -333,6 +343,7 @@ export function useSettingsDraft({ reposDir: reposDirInput.trim(), agentDir: agentDirInput.trim(), autoFollowup, + maxFollowupAsks, maxCodeSuggestions, poller: Number.parseInt(pollerInput, 10), concurrency: maxConcurrencyInput, @@ -390,6 +401,8 @@ export function useSettingsDraft({ pickAgentDir, autoFollowup, setAutoFollowup, + maxFollowupAsks, + setMaxFollowupAsks, maxCodeSuggestions, setMaxCodeSuggestions, reposDirInput, diff --git a/apps/desktop/src/renderer/src/components/features/settings/sections/AgentStrategySection.tsx b/apps/desktop/src/renderer/src/components/features/settings/sections/AgentStrategySection.tsx index a133c3f..41b2f00 100644 --- a/apps/desktop/src/renderer/src/components/features/settings/sections/AgentStrategySection.tsx +++ b/apps/desktop/src/renderer/src/components/features/settings/sections/AgentStrategySection.tsx @@ -1,22 +1,28 @@ import { useTranslation } from 'react-i18next'; import { Switch } from '../../../common'; +// 自动追问数量上限可选档位(1~5)。开关已独立控制启停,故不给 0(0 在 schema 等同关闭,仅手改 config 可达)。 +const MAX_FOLLOWUP_ASKS_OPTIONS = [1, 2, 3, 4, 5]; // 代码建议数量上限可选档位(2~8)。 const MAX_CODE_SUGGESTIONS_OPTIONS = [2, 3, 4, 5, 6, 7, 8]; /** * Agent 策略:扩展 Agent 行为控制的分区,子项以缩进的「功能列表」逐行展示(行首圆点 + 标题 + 说明, - * 右侧控件)。右侧控件不限于开关——自动追问用 Switch,代码建议数量用下拉(同一通用 .settings-sublist-*)。 - * 后续策略项在此追加一行即可。 + * 右侧控件)。右侧控件不限于开关——自动追问用 Switch,追问数量 / 代码建议数量用下拉(同一通用 + * .settings-sublist-*)。追问数量仅在自动追问开启时可调(关闭时下拉禁用)。后续策略项在此追加一行即可。 */ export function AgentStrategySection({ autoFollowup, onAutoFollowupChange, + maxFollowupAsks, + onMaxFollowupAsksChange, maxCodeSuggestions, onMaxCodeSuggestionsChange, }: { autoFollowup: boolean; onAutoFollowupChange: (next: boolean) => void; + maxFollowupAsks: number; + onMaxFollowupAsksChange: (next: number) => void; maxCodeSuggestions: number; onMaxCodeSuggestionsChange: (next: number) => void; }) { @@ -39,6 +45,28 @@ export function AgentStrategySection({ ariaLabel={t('settings.autoFollowupLabel')} /> +
  • +
    + {t('settings.maxFollowupAsksLabel')} + + {t('settings.maxFollowupAsksHint')} + +
    + {/* 追问数量仅在自动追问开启时生效 → 关闭时禁用,避免「开关关、数量却可调」的歧义。 */} + +
  • {t('settings.maxCodeSuggestionsLabel')} diff --git a/apps/desktop/src/renderer/src/i18n/locales/de-DE.json b/apps/desktop/src/renderer/src/i18n/locales/de-DE.json index be5a92a..1e21eab 100644 --- a/apps/desktop/src/renderer/src/i18n/locales/de-DE.json +++ b/apps/desktop/src/renderer/src/i18n/locales/de-DE.json @@ -712,6 +712,8 @@ "llmTitle": "LLM-Modelle", "maxCodeSuggestionsHint": "Max. Code-Vorschläge pro /review, /improve oder /ask.", "maxCodeSuggestionsLabel": "Code-Vorschläge", + "maxFollowupAsksHint": "Maximale Anzahl bedingter Rückfragen pro Review, wenn die automatische Rückfrage aktiv ist.", + "maxFollowupAsksLabel": "Rückfragen", "openAgentDir": "Aktuelles Verzeichnis öffnen", "openDevTools": "DevTools öffnen", "openDevToolsTitle": "Electron DevTools öffnen (separates Fenster)", diff --git a/apps/desktop/src/renderer/src/i18n/locales/en-US.json b/apps/desktop/src/renderer/src/i18n/locales/en-US.json index f177ffe..514b0d3 100644 --- a/apps/desktop/src/renderer/src/i18n/locales/en-US.json +++ b/apps/desktop/src/renderer/src/i18n/locales/en-US.json @@ -712,6 +712,8 @@ "llmTitle": "LLM Models", "maxCodeSuggestionsHint": "Max code suggestions per /review, /improve, or /ask.", "maxCodeSuggestionsLabel": "Code suggestions", + "maxFollowupAsksHint": "Maximum conditional follow-up questions per review when auto follow-up is on.", + "maxFollowupAsksLabel": "Follow-up questions", "openAgentDir": "Open current directory", "openDevTools": "Open DevTools", "openDevToolsTitle": "Open Electron DevTools (detached window)", diff --git a/apps/desktop/src/renderer/src/i18n/locales/ja-JP.json b/apps/desktop/src/renderer/src/i18n/locales/ja-JP.json index 2f2c039..495c20d 100644 --- a/apps/desktop/src/renderer/src/i18n/locales/ja-JP.json +++ b/apps/desktop/src/renderer/src/i18n/locales/ja-JP.json @@ -696,6 +696,8 @@ "llmTitle": "LLM モデル", "maxCodeSuggestionsHint": "/review・/improve・/ask が一度に生成するコード提案数の上限。", "maxCodeSuggestionsLabel": "コード提案数", + "maxFollowupAsksHint": "自動追問が有効なとき、1 回のレビューで行う条件付き追問の数の上限。", + "maxFollowupAsksLabel": "追問の数", "openAgentDir": "現在のディレクトリを開く", "openDevTools": "DevTools を開く", "openDevToolsTitle": "Electron DevTools を開く(分離ウィンドウ)", diff --git a/apps/desktop/src/renderer/src/i18n/locales/zh-CN.json b/apps/desktop/src/renderer/src/i18n/locales/zh-CN.json index c90244a..cefa030 100644 --- a/apps/desktop/src/renderer/src/i18n/locales/zh-CN.json +++ b/apps/desktop/src/renderer/src/i18n/locales/zh-CN.json @@ -696,6 +696,8 @@ "llmTitle": "LLM 模型", "maxCodeSuggestionsHint": "单次 /review、/improve、/ask 生成的代码建议数量上限。", "maxCodeSuggestionsLabel": "代码建议数量", + "maxFollowupAsksHint": "自动追问启用时,单次评审条件性追问的数量上限。", + "maxFollowupAsksLabel": "追问数量", "openAgentDir": "打开当前目录", "openDevTools": "打开 DevTools", "openDevToolsTitle": "打开 Electron 开发者工具(分离窗口)", diff --git a/docs/arch/06-agent.md b/docs/arch/06-agent.md index 0721570..74711c7 100644 --- a/docs/arch/06-agent.md +++ b/docs/arch/06-agent.md @@ -374,7 +374,7 @@ flowchart TD 2. **仅对严重问题条件性追问** —— **默认不追问**。该 PR 的 agent 读 §1 工具输出(findings 及其 `severity`,见 [05](05-review-workflow.md)),仅当出现**特别恶性 / 高严重度**的疑点(例如疑似安全漏洞、数据损坏、严重逻辑缺陷且需核实上下文)才考虑就该点补跑 - `/ask`。**硬上限 ≤2 个问题**(`agent.autopilot.max_followup_asks`,默认 2): + `/ask`。**硬上限 ≤2 个问题**(`agent.strategy.max_followup_asks`,默认 2): 没有严重问题就一个都不问,绝不为追问而追问。`/ask` 是只读工具,属红线放行范围(见 §4)。 3. **逐 PR 收尾总结(严格限长)** —— **由该 PR 的 agent 在本 PR 子任务(describe / review / 追问) 全部结束后**产出一段**严格限长**的总结(综合本 PR 的产出,受 `agent.summary_max_chars` 约束, @@ -479,7 +479,8 @@ AutoPilot 可执行自动发布 comment、自动 `approve` / `needswork`。**默 - `agent.autopilot.enabled`:AutoPilot 开关(默认 `false`)。评估节奏对齐轮询(每个 poller tick 一遍), 不再单设最小间隔配置。 - `agent.autopilot.batch_size`:单批判定的 PR 上限(默认 10)。 -- `agent.autopilot.max_followup_asks`:自动评审微流程中条件性追问 `/ask` 的硬上限(默认 2)。 +- `agent.strategy.max_followup_asks`:自动评审微流程中条件性追问 `/ask` 的硬上限(默认 2;手动自动评审与 + AutoPilot 共用,故归 `strategy` 而非 `autopilot`)。 - `agent.autopilot.max_steps`:每个 PR 的子 agent 的结构化步数 backstop(默认按微流程模板推导:≈ `3 + max_followup_asks` + 少量开销)。 - `agent.autopilot.grants`:逐项写权限授权(默认全空 = 全拒)。 diff --git a/docs/guide/04-config-reference.md b/docs/guide/04-config-reference.md index 2a57bd7..3323042 100644 --- a/docs/guide/04-config-reference.md +++ b/docs/guide/04-config-reference.md @@ -21,8 +21,11 @@ agent: autopilot: enabled: false batch_size: 10 - max_followup_asks: 2 grants: [] + strategy: + auto_followup: true + max_followup_asks: 2 + max_code_suggestions: 4 poller: interval_seconds: 300 @@ -116,6 +119,7 @@ Agent **无独立启用开关**——配置了 LLM 且 pr-agent 就绪即可用 | `max_steps` | integer | `8` | 单次会话的 Agent 规划步数上限,`1`–`50`。 | | `summary_max_chars` | integer | `800` | 收尾总结的严格篇幅上限(字符),`100`–`4000`。 | | `autopilot` | object | — | AutoPilot 预评审设置,见下。 | +| `strategy` | object | — | Agent 行为策略(作用于手动自动评审与 AutoPilot),见下。 | ### `agent.autopilot` — AutoPilot 预评审 @@ -125,9 +129,18 @@ Agent **无独立启用开关**——配置了 LLM 且 pr-agent 就绪即可用 | --- | --- | --- | --- | | `enabled` | boolean | `false` | AutoPilot 总开关。状态栏可切换;`false` 时调度逻辑完全不跑。 | | `batch_size` | integer | `10` | 单批 LLM 判定的 PR 上限,`1`–`50`。 | -| `max_followup_asks` | integer | `2` | 自动评审中针对严重问题条件性追问 `/ask` 的硬上限,`0`–`5`。 | | `grants` | array | `[]` | 逐项写权限授权(默认空 = 全拒),如 `approve` / `needs_work` / `publish_comment`;运行期按红线硬校验放行。 | +### `agent.strategy` — Agent 行为策略 + +作用于自动评审微流程(手动「自动评审」与 AutoPilot 共用),非 AutoPilot 专属。 + +| 字段 | 类型 | 默认 | 说明 | +| --- | --- | --- | --- | +| `auto_followup` | boolean | `true` | 评审阶段是否启用**自动追问**(条件性 `/ask`)。关闭则跳过判读 + 追问、直接总结,省一次 LLM 调用与追问开销。 | +| `max_followup_asks` | integer | `2` | 自动追问数量上限(条件性 `/ask` 的硬上限),`0`–`5`。仅 `auto_followup` 开启时生效;`0` 等同关闭。 | +| `max_code_suggestions` | integer | `4` | 单次 `/review`、`/improve`、`/ask` 生成的代码建议数量上限,`2`–`8`。 | + ## `poller` — PR 轮询 | 字段 | 类型 | 默认 | 说明 | diff --git a/packages/shared/src/config.ts b/packages/shared/src/config.ts index e41cab5..3dc971b 100644 --- a/packages/shared/src/config.ts +++ b/packages/shared/src/config.ts @@ -229,8 +229,6 @@ export const ConfigSchema = z.object({ // 评估节奏对齐轮询(每个 poller tick 评估一遍),不再单设最小间隔;准入门控 + 台账去重防重复。 /** 单批 LLM 判定的 PR 上限。 */ batch_size: z.number().int().min(1).max(50).default(10), - /** 自动评审微流程中条件性追问 /ask 的硬上限。 */ - max_followup_asks: z.number().int().min(0).max(5).default(2), /** * 逐项写权限授权(默认空 = 全拒)。如 'approve' / 'needs_work' / * 'publish_comment';运行期按红线硬校验放行(见「工具修改红线」)。 @@ -239,14 +237,21 @@ export const ConfigSchema = z.object({ }) .default({}), /** - * Agent 行为策略开关(扩展位,后续行为开关并入此处)。当前一项: + * Agent 行为策略(扩展位,后续行为开关并入此处)。作用于自动评审微流程(手动自动评审 + + * AutoPilot 共用),非 AutoPilot 专属。当前各项: * - auto_followup:评审运行阶段是否启用**自动追问**(条件性 /ask)。关闭则评审微流程跳过 * judge + asks 两步、直接总结,省一次 judge LLM 调用与潜在追问开销(省 token)。默认开, * 与历史行为一致。 + * - max_followup_asks:自动追问数量上限(条件性 /ask 的硬上限)。 */ strategy: z .object({ auto_followup: z.boolean().default(true), + /** + * 自动追问数量上限(条件性 /ask 的硬上限,0~5,默认 2)。仅 auto_followup 开启时生效; + * 0 等同关闭(开关已独立控制启停,故 UI 下拉只提供 1~5、不给 0 以免歧义)。 + */ + max_followup_asks: z.number().int().min(0).max(5).default(2), /** * 单次任务生成的代码建议 / 评审发现数量上限(2~8,默认 4)。统一约束三处: * - /review:pr-agent `pr_reviewer.num_max_findings`(硬上限);