From 647ba4d11e949a61c55481cd72153c88518bd6a0 Mon Sep 17 00:00:00 2001 From: rookie Date: Thu, 9 Jul 2026 15:01:13 +0800 Subject: [PATCH 1/8] update proto --- app/protos/grpc.proto | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/protos/grpc.proto b/app/protos/grpc.proto index 8747cb0a44..f8c9a9c7d1 100644 --- a/app/protos/grpc.proto +++ b/app/protos/grpc.proto @@ -2276,6 +2276,22 @@ message AIStartParams { // 为 true 时 request_plan 走 detached plan 审阅流程;默认 false 时走旧版异步 plan-and-execute。 // 需配合 EnablePlan=true 才会暴露 request_plan 动作。 bool EnableDetachedPlan = 47; + + // 执行策略集合。用于承载 multi-agent / goal mode 等顶层运行策略。 + AIExecutionStrategy Strategy = 48; +} + +message AIExecutionStrategy { + // 为 true 时启用多 Agent 模式:顶层 ReAct 暴露并偏好 dispatch_sub_react_agents, + // 倾向于把复杂任务拆成多个并行子 Agent 执行后再汇总。 + bool EnableMultiAgent = 1; + + // 为 true 时启用 Goal 模式:在达到 GoalMinIterations 之前禁止 finish 提前退出, + // 避免“做一点就汇报然后结束”。 + bool EnableGoalMode = 2; + + // Goal 模式下允许 finish 的最小迭代次数。<=0 时由服务端使用默认值。 + int64 GoalMinIterations = 3; } message AITaskFilter { From c633b87da7f7566d960576cac4475ae59ddac4ba Mon Sep 17 00:00:00 2001 From: rookie Date: Thu, 9 Jul 2026 18:15:35 +0800 Subject: [PATCH 2/8] =?UTF-8?q?feat(ai-agent):=20Plan=20=E6=8C=89=E9=92=AE?= =?UTF-8?q?=E6=94=B9=E9=80=A0=E4=B8=BA=E8=BF=90=E8=A1=8C=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E4=B8=8B=E6=8B=89(Plan/Multi-Agent/Goal)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - template.tsx:472 单一 Plan 标签改为 Dropdown 多选,承载 EnablePlan 与 Strategy(EnableMultiAgent/EnableGoalMode),三者可同时勾选 - grpcApi.ts: AIStartParams 增 Strategy 字段与 AIExecutionStrategy 接口,预留 HotPatchType_Strategy 热加载扩展点 - Plan 沿用 HotPatchType_EnablePlan 热更新;Strategy 暂不热更新,仅写回本地 setting 与会话 StartParams,下次启动生效 - Goal 启用后展示最小迭代次数(GoalMinIterations)输入,<=0 由服务端默认 - defaultConstant/formatAIAgentSetting 补 Strategy 默认值与兜底 --- .../src/pages/ai-agent/defaultConstant.tsx | 5 + .../ai-agent/template/template.module.scss | 59 +++++++++ .../src/pages/ai-agent/template/template.tsx | 116 ++++++++++++++++-- .../main/src/pages/ai-agent/utils/index.ts | 5 + .../main/src/pages/ai-re-act/hooks/grpcApi.ts | 21 ++++ 5 files changed, 197 insertions(+), 9 deletions(-) diff --git a/app/renderer/src/main/src/pages/ai-agent/defaultConstant.tsx b/app/renderer/src/main/src/pages/ai-agent/defaultConstant.tsx index f13b8bead7..efeb431ad9 100644 --- a/app/renderer/src/main/src/pages/ai-agent/defaultConstant.tsx +++ b/app/renderer/src/main/src/pages/ai-agent/defaultConstant.tsx @@ -117,6 +117,11 @@ export const AIAgentSettingDefault: AIAgentSetting = { EnablePlan: false, PlanExecTaskConcurrency: 2, EnableDetachedPlan: true, + Strategy: { + EnableMultiAgent: false, + EnableGoalMode: false, + GoalMinIterations: 0, + }, } /** mcp 自定义服务器配置类型选项 */ diff --git a/app/renderer/src/main/src/pages/ai-agent/template/template.module.scss b/app/renderer/src/main/src/pages/ai-agent/template/template.module.scss index 22e053dc2c..1f2600bb7e 100644 --- a/app/renderer/src/main/src/pages/ai-agent/template/template.module.scss +++ b/app/renderer/src/main/src/pages/ai-agent/template/template.module.scss @@ -128,6 +128,27 @@ textarea.qs-input-textarea { height: 14px; } } + .mode-btn { + display: flex; + align-items: center; + gap: 2px; + flex-shrink: 0; + .mode-btn-label { + font-size: 12px; + } + .mode-btn-arrow { + width: 12px; + height: 12px; + svg { + width: 12px; + height: 12px; + } + } + } + .mode-btn-active { + color: var(--Colors-Use-Main-Primary); + border-color: var(--Colors-Use-Main-Primary); + } } .footer-right { display: flex; @@ -169,3 +190,41 @@ textarea.qs-input-textarea { flex-wrap: wrap; } // #endregion + +// 运行模式下拉 +.mode-menu { + display: flex; + flex-direction: column; + gap: 8px; + padding: 8px 12px; + background: var(--Colors-Use-Basic-Background); + border-radius: 6px; + box-shadow: var(--yakit-box-shadow); + .mode-item-icon { + width: 14px; + height: 14px; + margin-right: 4px; + vertical-align: -2px; + svg { + width: 14px; + height: 14px; + } + } + .mode-goal-iter { + display: flex; + align-items: center; + gap: 8px; + padding-left: 22px; + .mode-goal-iter-label { + font-size: 12px; + color: var(--Colors-Use-Neutral-Text-2-Subtle); + white-space: nowrap; + } + .mode-goal-iter-input { + width: 96px; + } + } +} +.mode-dropdown { + z-index: 1050; +} diff --git a/app/renderer/src/main/src/pages/ai-agent/template/template.tsx b/app/renderer/src/main/src/pages/ai-agent/template/template.tsx index 4030df171d..2c9b92f1ef 100644 --- a/app/renderer/src/main/src/pages/ai-agent/template/template.tsx +++ b/app/renderer/src/main/src/pages/ai-agent/template/template.tsx @@ -20,15 +20,18 @@ import { QSInputTextareaProps, } from './type' import { Input, Tooltip } from 'antd' +import { Dropdown } from 'antd' import { YakitButton } from '@/components/yakitUI/YakitButton/YakitButton' import { OutlineArrowupIcon, OutlineAtsymbolIcon, - OutlineBanIcon, OutlineBrainCircuitIcon, OutlineCodeIcon, OutlineCogIcon, OutlineHandIcon, + OutlineChevrondownIcon, + OutlineFlagIcon, + OutlineUsergroupIcon, } from '@/assets/icon/outline' import { useCreation, useDebounceFn, useInViewport, useMemoizedFn } from 'ahooks' import { TextAreaRef } from 'antd/lib/input/TextArea' @@ -57,6 +60,8 @@ import OpenFileDropdown, { OpenFileDropdownItem } from '../aiChatWelcome/OpenFil import { UploadFileButton } from '@/pages/ai-re-act/aiReActChat/AIReActComponent' import { insertAtCurrentPosition } from '../components/aiMilkdownInput/customPlugin' import { YakitTag } from '@/components/yakitUI/YakitTag/YakitTag' +import { YakitCheckbox } from '@/components/yakitUI/YakitCheckbox/YakitCheckbox' +import { YakitInputNumber } from '@/components/yakitUI/YakitInputNumber/YakitInputNumber' import useChatIPCStore from '../useContext/ChatIPCContent/useStore' import useAIGlobalConfig from '@/pages/ai-re-act/hooks/useAIGlobalConfig' import { YakitSpin } from '@/components/yakitUI/YakitSpin/YakitSpin' @@ -70,8 +75,7 @@ import { useI18nNamespaces } from '@/i18n/useI18nNamespaces' import { AIMilkdownInputRef } from '../components/aiMilkdownInput/type' import { AICodeBlockCommandParams } from '../components/aiMilkdownInput/aiCodeBlock/aiCustomCodeBlockPlugin' import useAIAgentDispatcher from '../useContext/useDispatcher' -import { YakitCheckableTag } from '@/components/yakitUI/YakitTag/YakitCheckableTag' -import { AIInputEventHotPatchTypeEnum } from '@/pages/ai-re-act/hooks/grpcApi' +import { AIExecutionStrategy, AIInputEventHotPatchTypeEnum } from '@/pages/ai-re-act/hooks/grpcApi' import useChatIPCDispatcher from '../useContext/ChatIPCContent/useDispatcher' /** @name AI-Agent专用Textarea组件,行高为20px */ @@ -419,6 +423,46 @@ export const AIChatTextarea: React.FC = memo( }), { wait: 200, leading: true }, ).run + const enableMultiAgent = useCreation( + () => !!setting?.Strategy?.EnableMultiAgent, + [setting?.Strategy?.EnableMultiAgent], + ) + const enableGoalMode = useCreation(() => !!setting?.Strategy?.EnableGoalMode, [setting?.Strategy?.EnableGoalMode]) + const goalMinIterations = useCreation( + () => setting?.Strategy?.GoalMinIterations ?? 0, + [setting?.Strategy?.GoalMinIterations], + ) + const [modeVisible, setModeVisible] = useState(false) + const onSetStrategy = useDebounceFn( + useMemoizedFn((next: AIExecutionStrategy) => { + setSetting?.((v) => ({ + ...v, + Strategy: { ...v.Strategy, ...next }, + })) + if (activeChat?.SessionID) { + emiter.emit( + 'sessionData', + JSON.stringify({ + type: 'updateSession', + sessionId: activeChat.SessionID, + updates: { + StartParams: { + ...(activeChat.StartParams || {}), + Strategy: { ...(activeChat.StartParams?.Strategy || {}), ...next }, + }, + }, + }), + ) + } + // 热加载扩展点:后端支持 Strategy 热更新后取消下方注释启用 hotpatch + // handleSendConfigHotpatch({ + // hotpatchType: AIInputEventHotPatchTypeEnum.HotPatchType_Strategy, + // params: { Strategy: { ...setting?.Strategy, ...next } }, + // }) + }), + { wait: 200, leading: true }, + ).run + const modeActive = enablePlan || enableMultiAgent || enableGoalMode return (
= memo(
{inputFooterLeft ?? (
- - - {enablePlan ? : } - Plan - - + + + onSetPlan(e.target.checked)}> + + Plan + + + + onSetStrategy({ EnableMultiAgent: e.target.checked })} + > + + Multi-Agent + + + + onSetStrategy({ EnableGoalMode: e.target.checked })} + > + + Goal + + + {enableGoalMode && ( +
+ 最小迭代次数 + + onSetStrategy({ GoalMinIterations: (v as number) ?? 0 })} + className={styles['mode-goal-iter-input']} + /> + +
+ )} +
+ } + > + } + onClick={(e) => e.stopPropagation()} + className={classNames(styles['mode-btn'], { [styles['mode-btn-active']]: modeActive })} + > + 模式 + + + data.SyncPerceptionTrigger = setting.SyncPerceptionTrigger ?? AIAgentSettingDefault.SyncPerceptionTrigger data.EnablePlan = setting.EnablePlan ?? AIAgentSettingDefault.EnablePlan data.PlanExecTaskConcurrency = setting.PlanExecTaskConcurrency ?? AIAgentSettingDefault.PlanExecTaskConcurrency + data.Strategy = { + EnableMultiAgent: setting.Strategy?.EnableMultiAgent ?? AIAgentSettingDefault.Strategy?.EnableMultiAgent, + EnableGoalMode: setting.Strategy?.EnableGoalMode ?? AIAgentSettingDefault.Strategy?.EnableGoalMode, + GoalMinIterations: setting.Strategy?.GoalMinIterations ?? AIAgentSettingDefault.Strategy?.GoalMinIterations, + } if (setting?.AllowPlanUserInteract) { if (!isNil(setting?.PlanUserInteractMaxCount)) { diff --git a/app/renderer/src/main/src/pages/ai-re-act/hooks/grpcApi.ts b/app/renderer/src/main/src/pages/ai-re-act/hooks/grpcApi.ts index 84d99eb904..56cef6e0c4 100644 --- a/app/renderer/src/main/src/pages/ai-re-act/hooks/grpcApi.ts +++ b/app/renderer/src/main/src/pages/ai-re-act/hooks/grpcApi.ts @@ -165,6 +165,21 @@ export interface AIStartParams { * 需配合 EnablePlan=true 才会暴露 request_plan 动作。 */ EnableDetachedPlan?: boolean + /** + * 执行策略集合(多 Agent / Goal 模式等顶层运行策略)。 + * 与 EnablePlan 可同时勾选;目前策略侧不支持热更新,变更需下次启动生效。 + */ + Strategy?: AIExecutionStrategy +} + +/** 执行策略,对应后端 AIExecutionStrategy */ +export interface AIExecutionStrategy { + /** 启用多 Agent 模式:把复杂任务拆成多个并行子 Agent 执行后再汇总 */ + EnableMultiAgent?: boolean + /** 启用 Goal 模式:达到 GoalMinIterations 之前禁止提前结束 */ + EnableGoalMode?: boolean + /** Goal 模式下允许 finish 的最小迭代次数,<=0 由服务端默认 */ + GoalMinIterations?: number } interface AIEnabledCapability { @@ -178,6 +193,12 @@ export enum AIInputEventHotPatchTypeEnum { HotPatchType_AgreePolicy = 'AgreePolicy', HotPatchType_SyncPerceptionTrigger = 'SyncPerceptionTrigger', HotPatchType_EnablePlan = 'EnablePlan', + /** + * 预留:Strategy 执行策略热更新。 + * 目前策略侧暂不支持热更新,前端 onSetStrategy 仅写回本地 setting 与会话 StartParams; + * 后端支持热加载后,在 onSetStrategy 中启用该 hotpatch 调用即可。 + */ + HotPatchType_Strategy = 'Strategy', HotPatchType_EnabledCapabilities = 'EnabledCapabilities', HotPatchType_DisabledCapabilities = 'DisabledCapabilities', From d32a3fd16918352fe273a94bbbdec6bc92d9c176 Mon Sep 17 00:00:00 2001 From: hubo Date: Wed, 15 Jul 2026 17:32:52 +0800 Subject: [PATCH 3/8] =?UTF-8?q?feat(ai-agent):=20=E6=8A=BD=E7=A6=BB=20AIMo?= =?UTF-8?q?deSelect=20=E5=B9=B6=E4=BC=98=E5=8C=96=E8=BF=90=E8=A1=8C?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E9=80=89=E6=8B=A9=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/src/assets/icon/outline.tsx | 63 +++++ .../src/main/src/pages/ai-agent/AIAgent.tsx | 5 + .../ai-agent/aiAgentChat/AIAgentChat.tsx | 7 +- .../aiModeSelect/AIModeSelect.module.scss | 122 +++++++++ .../ai-agent/aiModeSelect/AIModeSelect.tsx | 251 ++++++++++++++++++ .../HistoryChatList/HistoryChatList.tsx | 5 + .../ai-agent/template/template.module.scss | 70 ----- .../src/pages/ai-agent/template/template.tsx | 152 +---------- 8 files changed, 456 insertions(+), 219 deletions(-) create mode 100644 app/renderer/src/main/src/pages/ai-agent/aiModeSelect/AIModeSelect.module.scss create mode 100644 app/renderer/src/main/src/pages/ai-agent/aiModeSelect/AIModeSelect.tsx diff --git a/app/renderer/src/main/src/assets/icon/outline.tsx b/app/renderer/src/main/src/assets/icon/outline.tsx index 79543a926c..9b3447ff27 100644 --- a/app/renderer/src/main/src/assets/icon/outline.tsx +++ b/app/renderer/src/main/src/assets/icon/outline.tsx @@ -6346,6 +6346,69 @@ export const OutlineUserRoundCogIcon = (props: Partial) => { return } +const OutlineGoal2 = () => ( + + + + + + + + + + +) +/** + * @description Outline/Goal 2 + */ +export const OutlineGoal2Icon = (props: Partial) => { + return +} + +const OutlineGrid2x2Check = () => ( + + + +) + +/** + * @description Outline/Grid2x2Check + */ +export const OutlineGrid2x2CheckIcon = (props: Partial) => { + return +} + +const OutlineBoxes = () => ( + + + +) + +/** + * @description Outline/Boxes + */ +export const OutlineBoxesIcon = (props: Partial) => { + return +} + const OutlineLandPlot = () => ( = (props) => { ...newCache, SyncPerceptionTrigger: false, EnablePlan: false, + Strategy: { + EnableMultiAgent: false, + EnableGoalMode: false, + ...AIAgentSettingDefault.Strategy, + }, }) } catch (error) {} }) diff --git a/app/renderer/src/main/src/pages/ai-agent/aiAgentChat/AIAgentChat.tsx b/app/renderer/src/main/src/pages/ai-agent/aiAgentChat/AIAgentChat.tsx index 6c5b9f0c21..ddd329a35a 100644 --- a/app/renderer/src/main/src/pages/ai-agent/aiAgentChat/AIAgentChat.tsx +++ b/app/renderer/src/main/src/pages/ai-agent/aiAgentChat/AIAgentChat.tsx @@ -285,7 +285,12 @@ export const AIAgentChat: React.FC = memo((props) => { switch (data.type as ReActChatEventEnum) { // 新开聊天对话窗 case ReActChatEventEnum.NEW_CHAT: - setSetting?.((old) => ({ ...old, SyncPerceptionTrigger: false, EnablePlan: false })) + setSetting?.((old) => ({ + ...old, + SyncPerceptionTrigger: false, + EnablePlan: false, + Strategy: { EnableMultiAgent: false, EnableGoalMode: false, GoalMinIterations: 0 }, + })) setActiveChat?.(undefined) setTimeout(() => { setMode('welcome') diff --git a/app/renderer/src/main/src/pages/ai-agent/aiModeSelect/AIModeSelect.module.scss b/app/renderer/src/main/src/pages/ai-agent/aiModeSelect/AIModeSelect.module.scss new file mode 100644 index 0000000000..29c19c4f4a --- /dev/null +++ b/app/renderer/src/main/src/pages/ai-agent/aiModeSelect/AIModeSelect.module.scss @@ -0,0 +1,122 @@ +.mode-btn { + display: flex; + align-items: center; + gap: 2px; + flex-shrink: 0; + .mode-btn-label { + font-size: 12px; + } + .mode-btn-arrow { + width: 12px; + height: 12px; + svg { + width: 12px; + height: 12px; + } + } + &:hover { + color: var(--Colors-Use-Main-Primary); + border-color: var(--Colors-Use-Main-Primary); + } +} + +// 运行模式下拉 +.mode-menu { + width: 215px; + display: flex; + flex-direction: column; + gap: 4px; + padding: 8px 4px; + background: var(--Colors-Use-Basic-Background); + border-radius: 6px; + box-shadow: var(--yakit-box-shadow); + .mode-menu-hint { + padding: 0 8px 4px; + color: var(--Colors-Use-Neutral-Text-4-Help-text); + font-size: 12px; + line-height: 16px; + } + .mode-list { + display: flex; + flex-direction: column; + gap: 2px; + } + .mode-option { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + padding: 8px; + border-radius: 4px; + cursor: pointer; + &:hover { + background: var(--Colors-Use-Neutral-Bg); + } + } + .mode-option-left { + display: flex; + align-items: center; + gap: 8px; + min-width: 0; + } + .mode-option-icon { + width: 16px; + height: 16px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + color: var(--Colors-Use-Neutral-Text-2-Primary); + svg { + width: 16px; + height: 16px; + } + } + .mode-option-label { + color: var(--Colors-Use-Neutral-Text-1-Title); + font-size: 12px; + line-height: 16px; + } + .mode-option-check { + width: 16px; + height: 16px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + svg { + width: 14px; + height: 14px; + color: var(--Colors-Use-Main-Primary); + } + } + .mode-option-check-placeholder { + width: 16px; + height: 16px; + flex-shrink: 0; + } + .mode-panel-divider { + height: 1px; + margin: 8px 8px 4px; + background: var(--Colors-Use-Neutral-Border); + } + .mode-goal-iter { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + padding: 4px 8px 0; + .mode-goal-iter-label { + font-size: 12px; + color: var(--Colors-Use-Neutral-Text-1-Title); + line-height: 16px; + white-space: nowrap; + } + .mode-goal-iter-input { + width: 96px; + } + } +} +.mode-dropdown { + z-index: 1050; +} diff --git a/app/renderer/src/main/src/pages/ai-agent/aiModeSelect/AIModeSelect.tsx b/app/renderer/src/main/src/pages/ai-agent/aiModeSelect/AIModeSelect.tsx new file mode 100644 index 0000000000..60f5f15690 --- /dev/null +++ b/app/renderer/src/main/src/pages/ai-agent/aiModeSelect/AIModeSelect.tsx @@ -0,0 +1,251 @@ +import React, { memo, useState, type FC } from 'react' +import { Tooltip } from 'antd' +import { Dropdown } from 'antd' +import { YakitButton } from '@/components/yakitUI/YakitButton/YakitButton' +import { + OutlineBoxesIcon, + OutlineBrainCircuitIcon, + OutlineChevrondownIcon, + OutlineGrid2x2CheckIcon, + OutlineGoal2Icon, +} from '@/assets/icon/outline' +import { SolidCheckIcon } from '@/assets/icon/solid' +import { useCreation, useDebounceFn, useMemoizedFn } from 'ahooks' +import classNames from 'classnames' +import styles from './AIModeSelect.module.scss' +import { YakitInputNumber } from '@/components/yakitUI/YakitInputNumber/YakitInputNumber' +import useAIAgentStore from '@/pages/ai-agent/useContext/useStore' +import useAIAgentDispatcher from '@/pages/ai-agent/useContext/useDispatcher' +import { AIExecutionStrategy, AIInputEventHotPatchTypeEnum } from '@/pages/ai-re-act/hooks/grpcApi' +import useChatIPCDispatcher from '@/pages/ai-agent/useContext/ChatIPCContent/useDispatcher' +import emiter from '@/utils/eventBus/eventBus' +import { OutlineViewGridIcon } from '@/components/yakChat/icon' + +type ModeOptionKey = 'plan' | 'multiAgent' | 'goal' + +const ModeOptionList: { + key: ModeOptionKey + label: string + icon: FC +}[] = [ + { + key: 'plan', + label: 'Plan', + icon: OutlineBrainCircuitIcon, + }, + { + key: 'multiAgent', + label: 'Multi-Agent', + icon: OutlineBoxesIcon, + }, + { + key: 'goal', + label: 'Goal', + icon: OutlineGoal2Icon, + }, +] + +const AIModeSelect: React.FC = memo(() => { + const { setting, activeChat } = useAIAgentStore() + const { setSetting } = useAIAgentDispatcher() + const { handleSendConfigHotpatch } = useChatIPCDispatcher() + + const enablePlan = useCreation(() => { + return !!setting?.EnablePlan + }, [setting?.EnablePlan]) + const onSetPlan = useDebounceFn( + useMemoizedFn((checked) => { + handleSendConfigHotpatch({ + hotpatchType: AIInputEventHotPatchTypeEnum.HotPatchType_EnablePlan, + params: { + EnablePlan: checked, + }, + }) + setSetting?.((v) => ({ + ...v, + EnablePlan: checked, + })) + if (activeChat?.SessionID) { + emiter.emit( + 'sessionData', + JSON.stringify({ + type: 'updateSession', + sessionId: activeChat.SessionID, + updates: { + StartParams: { + ...(activeChat.StartParams || {}), + EnablePlan: checked, + }, + }, + }), + ) + } + }), + { wait: 200, leading: true }, + ).run + const enableMultiAgent = useCreation( + () => !!setting?.Strategy?.EnableMultiAgent, + [setting?.Strategy?.EnableMultiAgent], + ) + const enableGoalMode = useCreation(() => !!setting?.Strategy?.EnableGoalMode, [setting?.Strategy?.EnableGoalMode]) + const goalMinIterations = useCreation( + () => setting?.Strategy?.GoalMinIterations ?? 0, + [setting?.Strategy?.GoalMinIterations], + ) + const [modeVisible, setModeVisible] = useState(false) + const onSetStrategy = useDebounceFn( + useMemoizedFn((next: AIExecutionStrategy) => { + setSetting?.((v) => ({ + ...v, + Strategy: { ...v.Strategy, ...next }, + })) + if (activeChat?.SessionID) { + emiter.emit( + 'sessionData', + JSON.stringify({ + type: 'updateSession', + sessionId: activeChat.SessionID, + updates: { + StartParams: { + ...(activeChat.StartParams || {}), + Strategy: { ...(activeChat.StartParams?.Strategy || {}), ...next }, + }, + }, + }), + ) + } + // 热加载扩展点:后端支持 Strategy 热更新后取消下方注释启用 hotpatch + // handleSendConfigHotpatch({ + // hotpatchType: AIInputEventHotPatchTypeEnum.HotPatchType_Strategy, + // params: { Strategy: { ...setting?.Strategy, ...next } }, + // }) + }), + { wait: 200, leading: true }, + ).run + + const isModeSelected = useMemoizedFn((key: ModeOptionKey) => { + switch (key) { + case 'plan': + return enablePlan + case 'multiAgent': + return enableMultiAgent + case 'goal': + return enableGoalMode + default: + return false + } + }) + + const selectedModes = useCreation(() => { + return ModeOptionList.filter((item) => { + switch (item.key) { + case 'plan': + return enablePlan + case 'multiAgent': + return enableMultiAgent + case 'goal': + return enableGoalMode + default: + return false + } + }) + }, [enablePlan, enableMultiAgent, enableGoalMode]) + + const triggerDisplay = useCreation(() => { + const count = selectedModes.length + if (count === 0) { + return { Icon: OutlineViewGridIcon, label: '模式' } + } + if (count === 1) { + return { Icon: selectedModes[0].icon, label: selectedModes[0].label } + } + return { Icon: OutlineGrid2x2CheckIcon, label: '多模式' } + }, [selectedModes]) + + const onToggleMode = useMemoizedFn((key: ModeOptionKey) => { + switch (key) { + case 'plan': + onSetPlan(!enablePlan) + break + case 'multiAgent': + onSetStrategy({ EnableMultiAgent: !enableMultiAgent }) + break + case 'goal': + onSetStrategy({ EnableGoalMode: !enableGoalMode }) + break + default: + break + } + }) + + const TriggerIcon = triggerDisplay.Icon + + return ( + +
请选择模式,可多选
+
+ {ModeOptionList.map((item) => { + const Icon = item.icon + const checked = isModeSelected(item.key) + return ( +
onToggleMode(item.key)}> +
+
+ +
+ {item.label} +
+ {checked ? ( + + ) : ( + + )} +
+ ) + })} +
+ {enableGoalMode && ( + <> +
+
+ 最小迭代次数 + +
e.stopPropagation()}> + onSetStrategy({ GoalMinIterations: (v as number) ?? 0 })} + className={styles['mode-goal-iter-input']} + /> +
+
+
+ + )} +
+ } + > + } + onClick={(e) => e.stopPropagation()} + className={styles['mode-btn']} + > + {triggerDisplay.label} + + + + ) +}) + +export default AIModeSelect diff --git a/app/renderer/src/main/src/pages/ai-agent/historyChat/HistoryChatList/HistoryChatList.tsx b/app/renderer/src/main/src/pages/ai-agent/historyChat/HistoryChatList/HistoryChatList.tsx index 5180970ceb..fe8ccecb23 100644 --- a/app/renderer/src/main/src/pages/ai-agent/historyChat/HistoryChatList/HistoryChatList.tsx +++ b/app/renderer/src/main/src/pages/ai-agent/historyChat/HistoryChatList/HistoryChatList.tsx @@ -243,6 +243,11 @@ const HistoryChatList: FC<{ ...old, SyncPerceptionTrigger: info?.StartParams?.SyncPerceptionTrigger ?? false, EnablePlan: info?.StartParams?.EnablePlan ?? false, + Strategy: { + EnableMultiAgent: info?.StartParams?.Strategy?.EnableMultiAgent ?? false, + EnableGoalMode: info?.StartParams?.Strategy?.EnableGoalMode ?? false, + GoalMinIterations: info?.StartParams?.Strategy?.GoalMinIterations ?? 0, + }, })) setActiveChat && setActiveChat(info) }) diff --git a/app/renderer/src/main/src/pages/ai-agent/template/template.module.scss b/app/renderer/src/main/src/pages/ai-agent/template/template.module.scss index 1f2600bb7e..da071a3553 100644 --- a/app/renderer/src/main/src/pages/ai-agent/template/template.module.scss +++ b/app/renderer/src/main/src/pages/ai-agent/template/template.module.scss @@ -117,38 +117,6 @@ textarea.qs-input-textarea { gap: 4px; overflow: hidden; line-height: 16px; - .plan-btn { - margin-right: 0; - border-radius: 20px; - display: flex; - gap: 2px; - align-items: center; - svg { - width: 14px; - height: 14px; - } - } - .mode-btn { - display: flex; - align-items: center; - gap: 2px; - flex-shrink: 0; - .mode-btn-label { - font-size: 12px; - } - .mode-btn-arrow { - width: 12px; - height: 12px; - svg { - width: 12px; - height: 12px; - } - } - } - .mode-btn-active { - color: var(--Colors-Use-Main-Primary); - border-color: var(--Colors-Use-Main-Primary); - } } .footer-right { display: flex; @@ -190,41 +158,3 @@ textarea.qs-input-textarea { flex-wrap: wrap; } // #endregion - -// 运行模式下拉 -.mode-menu { - display: flex; - flex-direction: column; - gap: 8px; - padding: 8px 12px; - background: var(--Colors-Use-Basic-Background); - border-radius: 6px; - box-shadow: var(--yakit-box-shadow); - .mode-item-icon { - width: 14px; - height: 14px; - margin-right: 4px; - vertical-align: -2px; - svg { - width: 14px; - height: 14px; - } - } - .mode-goal-iter { - display: flex; - align-items: center; - gap: 8px; - padding-left: 22px; - .mode-goal-iter-label { - font-size: 12px; - color: var(--Colors-Use-Neutral-Text-2-Subtle); - white-space: nowrap; - } - .mode-goal-iter-input { - width: 96px; - } - } -} -.mode-dropdown { - z-index: 1050; -} diff --git a/app/renderer/src/main/src/pages/ai-agent/template/template.tsx b/app/renderer/src/main/src/pages/ai-agent/template/template.tsx index 2c9b92f1ef..2eea6fe628 100644 --- a/app/renderer/src/main/src/pages/ai-agent/template/template.tsx +++ b/app/renderer/src/main/src/pages/ai-agent/template/template.tsx @@ -19,8 +19,7 @@ import { FooterRightTypesComponentProps, QSInputTextareaProps, } from './type' -import { Input, Tooltip } from 'antd' -import { Dropdown } from 'antd' +import { Input } from 'antd' import { YakitButton } from '@/components/yakitUI/YakitButton/YakitButton' import { OutlineArrowupIcon, @@ -29,11 +28,8 @@ import { OutlineCodeIcon, OutlineCogIcon, OutlineHandIcon, - OutlineChevrondownIcon, - OutlineFlagIcon, - OutlineUsergroupIcon, } from '@/assets/icon/outline' -import { useCreation, useDebounceFn, useInViewport, useMemoizedFn } from 'ahooks' +import { useCreation, useInViewport, useMemoizedFn } from 'ahooks' import { TextAreaRef } from 'antd/lib/input/TextArea' import classNames from 'classnames' import styles from './template.module.scss' @@ -54,14 +50,11 @@ import { YakitKeyBoard } from '@/utils/globalShortcutKey/keyboard' import { AIModelSelect } from '../aiModelList/aiModelSelect/AIModelSelect' import AIReviewRuleSelect from '@/pages/ai-re-act/aiReviewRuleSelect/AIReviewRuleSelect' import { AIFocusMode } from '@/pages/ai-re-act/aiFocusMode/AIFocusMode' -import useAIAgentStore from '../useContext/useStore' import { isString } from 'lodash' import OpenFileDropdown, { OpenFileDropdownItem } from '../aiChatWelcome/OpenFileDropdown/OpenFileDropdown' import { UploadFileButton } from '@/pages/ai-re-act/aiReActChat/AIReActComponent' import { insertAtCurrentPosition } from '../components/aiMilkdownInput/customPlugin' import { YakitTag } from '@/components/yakitUI/YakitTag/YakitTag' -import { YakitCheckbox } from '@/components/yakitUI/YakitCheckbox/YakitCheckbox' -import { YakitInputNumber } from '@/components/yakitUI/YakitInputNumber/YakitInputNumber' import useChatIPCStore from '../useContext/ChatIPCContent/useStore' import useAIGlobalConfig from '@/pages/ai-re-act/hooks/useAIGlobalConfig' import { YakitSpin } from '@/components/yakitUI/YakitSpin/YakitSpin' @@ -74,9 +67,7 @@ import { import { useI18nNamespaces } from '@/i18n/useI18nNamespaces' import { AIMilkdownInputRef } from '../components/aiMilkdownInput/type' import { AICodeBlockCommandParams } from '../components/aiMilkdownInput/aiCodeBlock/aiCustomCodeBlockPlugin' -import useAIAgentDispatcher from '../useContext/useDispatcher' -import { AIExecutionStrategy, AIInputEventHotPatchTypeEnum } from '@/pages/ai-re-act/hooks/grpcApi' -import useChatIPCDispatcher from '../useContext/ChatIPCContent/useDispatcher' +import AIModeSelect from '../aiModeSelect/AIModeSelect' /** @name AI-Agent专用Textarea组件,行高为20px */ export const QSInputTextarea: React.FC> = memo( @@ -117,7 +108,6 @@ export const AIChatTextarea: React.FC = memo( } = props const { t } = useI18nNamespaces(['aiAgent', 'yakitUi']) const { chatIPCData } = useChatIPCStore() - const { handleSendConfigHotpatch } = useChatIPCDispatcher() const execute = useCreation(() => chatIPCData.execute, [chatIPCData.execute]) const [manualAdditionVisible, setManualAdditionVisible] = useState(false) @@ -178,8 +168,6 @@ export const AIChatTextarea: React.FC = memo( return [{ type: AIInputFooterRightEnum.AIFocusMode }] }, [props.footerRightTypes, isOpen]) - const { setting, activeChat } = useAIAgentStore() - const { setSetting } = useAIAgentDispatcher() const [disabled, setDisabled] = useState(false) const { isHovering, dropRef } = useAIChatDrop({ @@ -390,79 +378,6 @@ export const AIChatTextarea: React.FC = memo( aiMilkdownInputRef.current?.setImage() }) - const enablePlan = useCreation(() => { - return !!setting?.EnablePlan - }, [setting?.EnablePlan]) - const onSetPlan = useDebounceFn( - useMemoizedFn((checked) => { - handleSendConfigHotpatch({ - hotpatchType: AIInputEventHotPatchTypeEnum.HotPatchType_EnablePlan, - params: { - EnablePlan: checked, - }, - }) - setSetting?.((v) => ({ - ...v, - EnablePlan: checked, - })) - if (activeChat?.SessionID) { - emiter.emit( - 'sessionData', - JSON.stringify({ - type: 'updateSession', - sessionId: activeChat.SessionID, - updates: { - StartParams: { - ...(activeChat.StartParams || {}), - EnablePlan: checked, - }, - }, - }), - ) - } - }), - { wait: 200, leading: true }, - ).run - const enableMultiAgent = useCreation( - () => !!setting?.Strategy?.EnableMultiAgent, - [setting?.Strategy?.EnableMultiAgent], - ) - const enableGoalMode = useCreation(() => !!setting?.Strategy?.EnableGoalMode, [setting?.Strategy?.EnableGoalMode]) - const goalMinIterations = useCreation( - () => setting?.Strategy?.GoalMinIterations ?? 0, - [setting?.Strategy?.GoalMinIterations], - ) - const [modeVisible, setModeVisible] = useState(false) - const onSetStrategy = useDebounceFn( - useMemoizedFn((next: AIExecutionStrategy) => { - setSetting?.((v) => ({ - ...v, - Strategy: { ...v.Strategy, ...next }, - })) - if (activeChat?.SessionID) { - emiter.emit( - 'sessionData', - JSON.stringify({ - type: 'updateSession', - sessionId: activeChat.SessionID, - updates: { - StartParams: { - ...(activeChat.StartParams || {}), - Strategy: { ...(activeChat.StartParams?.Strategy || {}), ...next }, - }, - }, - }), - ) - } - // 热加载扩展点:后端支持 Strategy 热更新后取消下方注释启用 hotpatch - // handleSendConfigHotpatch({ - // hotpatchType: AIInputEventHotPatchTypeEnum.HotPatchType_Strategy, - // params: { Strategy: { ...setting?.Strategy, ...next } }, - // }) - }), - { wait: 200, leading: true }, - ).run - const modeActive = enablePlan || enableMultiAgent || enableGoalMode return (
= memo(
{inputFooterLeft ?? (
- - - onSetPlan(e.target.checked)}> - - Plan - - - - onSetStrategy({ EnableMultiAgent: e.target.checked })} - > - - Multi-Agent - - - - onSetStrategy({ EnableGoalMode: e.target.checked })} - > - - Goal - - - {enableGoalMode && ( -
- 最小迭代次数 - - onSetStrategy({ GoalMinIterations: (v as number) ?? 0 })} - className={styles['mode-goal-iter-input']} - /> - -
- )} -
- } - > - } - onClick={(e) => e.stopPropagation()} - className={classNames(styles['mode-btn'], { [styles['mode-btn-active']]: modeActive })} - > - 模式 - - - + Date: Thu, 16 Jul 2026 12:00:34 +0800 Subject: [PATCH 4/8] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9ai=20Setting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/renderer/src/main/src/pages/ai-agent/AIAgent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/renderer/src/main/src/pages/ai-agent/AIAgent.tsx b/app/renderer/src/main/src/pages/ai-agent/AIAgent.tsx index e38ce2fe8d..2c98dee4c1 100644 --- a/app/renderer/src/main/src/pages/ai-agent/AIAgent.tsx +++ b/app/renderer/src/main/src/pages/ai-agent/AIAgent.tsx @@ -134,7 +134,7 @@ export const AIAgent: React.FC = (props) => { Strategy: { EnableMultiAgent: false, EnableGoalMode: false, - ...AIAgentSettingDefault.Strategy, + GoalMinIterations: AIAgentSettingDefault.Strategy?.GoalMinIterations, }, }) } catch (error) {} From dd3501076c49d8c472cbc6a62c1cb9c51b20f930 Mon Sep 17 00:00:00 2001 From: hubo Date: Fri, 17 Jul 2026 10:12:06 +0800 Subject: [PATCH 5/8] =?UTF-8?q?fix=EF=BC=9A=20ai=E8=BF=90=E8=A1=8C?= =?UTF-8?q?=E4=B8=AD=E4=B8=8D=E5=8F=AF=E4=BF=AE=E6=94=B9mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aiModeSelect/AIModeSelect.module.scss | 7 +++++++ .../ai-agent/aiModeSelect/AIModeSelect.tsx | 17 +++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/app/renderer/src/main/src/pages/ai-agent/aiModeSelect/AIModeSelect.module.scss b/app/renderer/src/main/src/pages/ai-agent/aiModeSelect/AIModeSelect.module.scss index 29c19c4f4a..58f9ad8d96 100644 --- a/app/renderer/src/main/src/pages/ai-agent/aiModeSelect/AIModeSelect.module.scss +++ b/app/renderer/src/main/src/pages/ai-agent/aiModeSelect/AIModeSelect.module.scss @@ -52,6 +52,13 @@ &:hover { background: var(--Colors-Use-Neutral-Bg); } + &.mode-option-disabled { + cursor: not-allowed; + opacity: 0.55; + &:hover { + background: transparent; + } + } } .mode-option-left { display: flex; diff --git a/app/renderer/src/main/src/pages/ai-agent/aiModeSelect/AIModeSelect.tsx b/app/renderer/src/main/src/pages/ai-agent/aiModeSelect/AIModeSelect.tsx index 60f5f15690..2ca23d5cd5 100644 --- a/app/renderer/src/main/src/pages/ai-agent/aiModeSelect/AIModeSelect.tsx +++ b/app/renderer/src/main/src/pages/ai-agent/aiModeSelect/AIModeSelect.tsx @@ -18,6 +18,7 @@ import useAIAgentStore from '@/pages/ai-agent/useContext/useStore' import useAIAgentDispatcher from '@/pages/ai-agent/useContext/useDispatcher' import { AIExecutionStrategy, AIInputEventHotPatchTypeEnum } from '@/pages/ai-re-act/hooks/grpcApi' import useChatIPCDispatcher from '@/pages/ai-agent/useContext/ChatIPCContent/useDispatcher' +import useChatIPCStore from '@/pages/ai-agent/useContext/ChatIPCContent/useStore' import emiter from '@/utils/eventBus/eventBus' import { OutlineViewGridIcon } from '@/components/yakChat/icon' @@ -49,12 +50,15 @@ const AIModeSelect: React.FC = memo(() => { const { setting, activeChat } = useAIAgentStore() const { setSetting } = useAIAgentDispatcher() const { handleSendConfigHotpatch } = useChatIPCDispatcher() + const { chatIPCData } = useChatIPCStore() + const execute = useCreation(() => !!chatIPCData.execute, [chatIPCData.execute]) const enablePlan = useCreation(() => { return !!setting?.EnablePlan }, [setting?.EnablePlan]) const onSetPlan = useDebounceFn( useMemoizedFn((checked) => { + if (execute) return handleSendConfigHotpatch({ hotpatchType: AIInputEventHotPatchTypeEnum.HotPatchType_EnablePlan, params: { @@ -95,6 +99,7 @@ const AIModeSelect: React.FC = memo(() => { const [modeVisible, setModeVisible] = useState(false) const onSetStrategy = useDebounceFn( useMemoizedFn((next: AIExecutionStrategy) => { + if (execute) return setSetting?.((v) => ({ ...v, Strategy: { ...v.Strategy, ...next }, @@ -163,6 +168,7 @@ const AIModeSelect: React.FC = memo(() => { }, [selectedModes]) const onToggleMode = useMemoizedFn((key: ModeOptionKey) => { + if (execute) return switch (key) { case 'plan': onSetPlan(!enablePlan) @@ -188,13 +194,19 @@ const AIModeSelect: React.FC = memo(() => { overlayClassName={styles['mode-dropdown']} overlay={
-
请选择模式,可多选
+
{execute ? 'AI 运行中,暂不可修改模式' : '请选择模式,可多选'}
{ModeOptionList.map((item) => { const Icon = item.icon const checked = isModeSelected(item.key) return ( -
onToggleMode(item.key)}> +
onToggleMode(item.key)} + >
@@ -221,6 +233,7 @@ const AIModeSelect: React.FC = memo(() => { type="horizontal" size="small" min={0} + disabled={execute} value={goalMinIterations} onChange={(v) => onSetStrategy({ GoalMinIterations: (v as number) ?? 0 })} className={styles['mode-goal-iter-input']} From 1df2470aad67fceaba6e22f465ab6013044b9453 Mon Sep 17 00:00:00 2001 From: hubo Date: Fri, 17 Jul 2026 12:55:33 +0800 Subject: [PATCH 6/8] =?UTF-8?q?refactor:=20=E5=B0=86=20AIModeSelect=20?= =?UTF-8?q?=E9=87=8D=E5=91=BD=E5=90=8D=E4=B8=BA=20AIRunModeSelect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AIRunModeSelect.module.scss} | 0 .../AIRunModeSelect.tsx} | 6 +++--- .../src/main/src/pages/ai-agent/template/template.tsx | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename app/renderer/src/main/src/pages/ai-agent/{aiModeSelect/AIModeSelect.module.scss => aiRunModeSelect/AIRunModeSelect.module.scss} (100%) rename app/renderer/src/main/src/pages/ai-agent/{aiModeSelect/AIModeSelect.tsx => aiRunModeSelect/AIRunModeSelect.tsx} (98%) diff --git a/app/renderer/src/main/src/pages/ai-agent/aiModeSelect/AIModeSelect.module.scss b/app/renderer/src/main/src/pages/ai-agent/aiRunModeSelect/AIRunModeSelect.module.scss similarity index 100% rename from app/renderer/src/main/src/pages/ai-agent/aiModeSelect/AIModeSelect.module.scss rename to app/renderer/src/main/src/pages/ai-agent/aiRunModeSelect/AIRunModeSelect.module.scss diff --git a/app/renderer/src/main/src/pages/ai-agent/aiModeSelect/AIModeSelect.tsx b/app/renderer/src/main/src/pages/ai-agent/aiRunModeSelect/AIRunModeSelect.tsx similarity index 98% rename from app/renderer/src/main/src/pages/ai-agent/aiModeSelect/AIModeSelect.tsx rename to app/renderer/src/main/src/pages/ai-agent/aiRunModeSelect/AIRunModeSelect.tsx index 2ca23d5cd5..1303793846 100644 --- a/app/renderer/src/main/src/pages/ai-agent/aiModeSelect/AIModeSelect.tsx +++ b/app/renderer/src/main/src/pages/ai-agent/aiRunModeSelect/AIRunModeSelect.tsx @@ -12,7 +12,7 @@ import { import { SolidCheckIcon } from '@/assets/icon/solid' import { useCreation, useDebounceFn, useMemoizedFn } from 'ahooks' import classNames from 'classnames' -import styles from './AIModeSelect.module.scss' +import styles from './AIRunModeSelect.module.scss' import { YakitInputNumber } from '@/components/yakitUI/YakitInputNumber/YakitInputNumber' import useAIAgentStore from '@/pages/ai-agent/useContext/useStore' import useAIAgentDispatcher from '@/pages/ai-agent/useContext/useDispatcher' @@ -46,7 +46,7 @@ const ModeOptionList: { }, ] -const AIModeSelect: React.FC = memo(() => { +const AIRunModeSelect: React.FC = memo(() => { const { setting, activeChat } = useAIAgentStore() const { setSetting } = useAIAgentDispatcher() const { handleSendConfigHotpatch } = useChatIPCDispatcher() @@ -261,4 +261,4 @@ const AIModeSelect: React.FC = memo(() => { ) }) -export default AIModeSelect +export default AIRunModeSelect diff --git a/app/renderer/src/main/src/pages/ai-agent/template/template.tsx b/app/renderer/src/main/src/pages/ai-agent/template/template.tsx index 2eea6fe628..4339acc243 100644 --- a/app/renderer/src/main/src/pages/ai-agent/template/template.tsx +++ b/app/renderer/src/main/src/pages/ai-agent/template/template.tsx @@ -67,7 +67,7 @@ import { import { useI18nNamespaces } from '@/i18n/useI18nNamespaces' import { AIMilkdownInputRef } from '../components/aiMilkdownInput/type' import { AICodeBlockCommandParams } from '../components/aiMilkdownInput/aiCodeBlock/aiCustomCodeBlockPlugin' -import AIModeSelect from '../aiModeSelect/AIModeSelect' +import AIRunModeSelect from '../aiRunModeSelect/AIRunModeSelect' /** @name AI-Agent专用Textarea组件,行高为20px */ export const QSInputTextarea: React.FC> = memo( @@ -427,7 +427,7 @@ export const AIChatTextarea: React.FC = memo(
{inputFooterLeft ?? (
- + Date: Fri, 17 Jul 2026 13:01:13 +0800 Subject: [PATCH 7/8] =?UTF-8?q?fix:=20AI=20=E8=BF=90=E8=A1=8C=E4=B8=AD?= =?UTF-8?q?=E5=85=81=E8=AE=B8=E7=83=AD=E6=9B=B4=E6=96=B0=20Plan=EF=BC=8C?= =?UTF-8?q?=E9=94=81=E5=AE=9A=20Multi-Agent=20=E4=B8=8E=20Goal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ai-agent/aiRunModeSelect/AIRunModeSelect.tsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/app/renderer/src/main/src/pages/ai-agent/aiRunModeSelect/AIRunModeSelect.tsx b/app/renderer/src/main/src/pages/ai-agent/aiRunModeSelect/AIRunModeSelect.tsx index 1303793846..c1ae308677 100644 --- a/app/renderer/src/main/src/pages/ai-agent/aiRunModeSelect/AIRunModeSelect.tsx +++ b/app/renderer/src/main/src/pages/ai-agent/aiRunModeSelect/AIRunModeSelect.tsx @@ -58,7 +58,6 @@ const AIRunModeSelect: React.FC = memo(() => { }, [setting?.EnablePlan]) const onSetPlan = useDebounceFn( useMemoizedFn((checked) => { - if (execute) return handleSendConfigHotpatch({ hotpatchType: AIInputEventHotPatchTypeEnum.HotPatchType_EnablePlan, params: { @@ -168,7 +167,8 @@ const AIRunModeSelect: React.FC = memo(() => { }, [selectedModes]) const onToggleMode = useMemoizedFn((key: ModeOptionKey) => { - if (execute) return + // Plan 支持热更新,运行中可改;Multi-Agent / Goal 运行中不可改 + if (execute && key !== 'plan') return switch (key) { case 'plan': onSetPlan(!enablePlan) @@ -184,6 +184,10 @@ const AIRunModeSelect: React.FC = memo(() => { } }) + const isModeLocked = useMemoizedFn((key: ModeOptionKey) => { + return execute && key !== 'plan' + }) + const TriggerIcon = triggerDisplay.Icon return ( @@ -194,16 +198,19 @@ const AIRunModeSelect: React.FC = memo(() => { overlayClassName={styles['mode-dropdown']} overlay={
-
{execute ? 'AI 运行中,暂不可修改模式' : '请选择模式,可多选'}
+
+ {execute ? 'AI 运行中,仅 Plan 可修改,Multi-Agent / Goal 暂不可改' : '请选择模式,可多选'} +
{ModeOptionList.map((item) => { const Icon = item.icon const checked = isModeSelected(item.key) + const locked = isModeLocked(item.key) return (
onToggleMode(item.key)} > From 475358ac5f7f9365c93006946c140ce5155815de Mon Sep 17 00:00:00 2001 From: hubo Date: Fri, 17 Jul 2026 13:22:36 +0800 Subject: [PATCH 8/8] =?UTF-8?q?fix=20:ai=E8=BF=90=E8=A1=8C=E6=89=8D?= =?UTF-8?q?=E8=83=BD=E7=83=AD=E6=9B=B4=E6=96=B0=20plan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ai-agent/aiRunModeSelect/AIRunModeSelect.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/app/renderer/src/main/src/pages/ai-agent/aiRunModeSelect/AIRunModeSelect.tsx b/app/renderer/src/main/src/pages/ai-agent/aiRunModeSelect/AIRunModeSelect.tsx index c1ae308677..2bc23671fd 100644 --- a/app/renderer/src/main/src/pages/ai-agent/aiRunModeSelect/AIRunModeSelect.tsx +++ b/app/renderer/src/main/src/pages/ai-agent/aiRunModeSelect/AIRunModeSelect.tsx @@ -58,12 +58,15 @@ const AIRunModeSelect: React.FC = memo(() => { }, [setting?.EnablePlan]) const onSetPlan = useDebounceFn( useMemoizedFn((checked) => { - handleSendConfigHotpatch({ - hotpatchType: AIInputEventHotPatchTypeEnum.HotPatchType_EnablePlan, - params: { - EnablePlan: checked, - }, - }) + if (execute) { + // ai运行才能热更新 plan + handleSendConfigHotpatch({ + hotpatchType: AIInputEventHotPatchTypeEnum.HotPatchType_EnablePlan, + params: { + EnablePlan: checked, + }, + }) + } setSetting?.((v) => ({ ...v, EnablePlan: checked,