点击确认

This commit is contained in:
董宏凯 committed 2026-05-27 17:25:10 +08:00
1 parent 1ff8860b98
commit deb9993a64
3 files changed
+77 -47

No files matched your search

@@ -171,18 +171,18 @@ export function Step2Panel(props: Step2PanelProps) {
</option>
))}
</select>
{isRoundTableRunning && isOrchestrating ? (
{isRoundTableRunning ? (
<button
onClick={() => {
setIsRoundTableRunning(false);
onTriggerToast("⏸️ 会商模拟暂挂!智能体挂起...");
onTriggerToast("⏸️ 会商已暂停");
}}
className="bg-amber-100 hover:bg-amber-200 text-amber-800 text-[10px] font-bold px-2.5 py-1 rounded-xl flex items-center gap-1 transition border border-amber-200/40 shadow-sm"
>
<Pause className="w-3.5 h-3.5" />
挂起协商
暂停会商
</button>
) : !isRoundTableRunning ? (
) : (
<button
onClick={() => {
setIsRoundTableRunning(true);
@@ -193,7 +193,7 @@ export function Step2Panel(props: Step2PanelProps) {
<Play className="w-3.5 h-3.5 fill-emerald-800" />
恢复会商
</button>
) : null}
)}
</div>
</div>
@@ -280,6 +280,9 @@ export function useStep2Orchestration(
const orchestrationCancelRef = useRef<boolean>(false);
const step2InitRef = useRef<boolean>(false);
const activeAbortersRef = useRef<Set<AbortController>>(new Set());
/** 干预研审:暂停当前轮次后待执行的 leader prompt。 */
const pendingInjectPromptRef = useRef<string | null>(null);
const [injectFlushToken, setInjectFlushToken] = useState(0);
// selectedModel / agentConfigs / agentNameToRole / threadIds / coordinatorName
// 等等被 runOrchestration 在循环里反复读取。把它们打到 ref 上,可以让
// runOrchestration 的 useCallback deps 大幅收窄,避免每次模型切换、agentConfigs
@@ -1043,7 +1046,11 @@ export function useStep2Orchestration(
useEffect(() => {
if (currentStep !== 2) return;
if (!isRoundTableRunning) {
stopOrchestration();
// 干预研审会先写入 pending 再置 false;此时 stop 已在 handler 里触发,
// 避免 effect 再次 stop 误杀随后 flush 启动的 inject 编排。
if (!pendingInjectPromptRef.current) {
stopOrchestration();
}
return;
}
if (
@@ -1061,6 +1068,21 @@ export function useStep2Orchestration(
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isRoundTableRunning]);
// 干预研审:当前轮次停止后 flush 待执行的 inject 编排(编排期间 UI 仍为「恢复会商」)。
useEffect(() => {
if (isOrchestrating) return;
const pending = pendingInjectPromptRef.current;
if (!pending) return;
if (!threadIdsRef.current || !coordinatorNameRef.current) return;
pendingInjectPromptRef.current = null;
triggerToast("您的人工纠偏指令已同步至圆桌节点!");
void runOrchestration(
pending,
threadIdsRef.current,
coordinatorNameRef.current,
);
}, [isOrchestrating, injectFlushToken, runOrchestration, triggerToast]);
// ─── Public handlers ─────────────────────────────────────────────
const handleInjectToRoundtable = useCallback(() => {
if (!customAgentInput.trim()) return;
@@ -1068,28 +1090,31 @@ export function useStep2Orchestration(
triggerToast("⚠️ 圆桌尚未就绪,请稍候");
return;
}
if (isOrchestratingRef.current) {
triggerToast("⏳ 当前轮次仍在进行中,请等待或暂停后再发送");
return;
}
const userText = customAgentInput.trim();
setCustomAgentInput("");
const prompt = `用户人工干预新指令:${userText}\n\n请结合当前讨论状态,决定是否继续派活或直接给出更新后的结论。`;
pendingInjectPromptRef.current = prompt;
// 与 Step2Panel 右上角「暂停会商 / 恢复会商」联动:立即进入挂起态。
setIsRoundTableRunning(false);
stopOrchestration();
appendDialogue({
sender: "用户 (插话干预)",
avatarType: "human",
content: `💡 (人工指令介入) ${userText}`,
confidence: "最高指令",
});
triggerToast("您的人工纠偏指令已同步至圆桌节点!");
void runOrchestration(
`用户人工干预新指令:${userText}\n\n请结合当前讨论状态,决定是否继续派活或直接给出更新后的结论。`,
threadIdsRef.current,
coordinatorNameRef.current,
);
}, [appendDialogue, customAgentInput, runOrchestration, triggerToast]);
if (isOrchestratingRef.current) {
triggerToast("⏸️ 会商已暂停,当前轮次结束后将处理您的干预指令");
} else {
triggerToast("⏸️ 会商已暂停,正在处理您的干预指令…");
}
setInjectFlushToken((n) => n + 1);
}, [appendDialogue, customAgentInput, stopOrchestration, triggerToast]);
const submitClarification = useCallback(
(rawAnswer: string) => {
@@ -1237,6 +1262,7 @@ export function useStep2Orchestration(
setArtifacts([]);
seatStubMessagesRef.current = {};
setSeatStubMessages({});
pendingInjectPromptRef.current = null;
deselectArtifact();
step2InitRef.current = false;
}, [abortAllInFlight, deselectArtifact, setArtifacts]);
@@ -351,37 +351,41 @@ export default function RoundtablePlanningPage() {
新建任务
</button>
<button
id="start-clear-btn"
onClick={() => {
setCurrentStep(1);
triggerToast("已切换至第一阶段:人机问答意图澄清");
}}
className="bg-primary text-white hover:bg-[var(--brand-900)] text-xs font-bold px-3 py-2 rounded-xl flex items-center gap-1.5 transition-all shadow-sm shadow-primary/10 active:scale-95"
>
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2.5">
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
开始澄清
</button>
{currentStep !== 2 && (
<>
<button
id="start-clear-btn"
onClick={() => {
setCurrentStep(1);
triggerToast("已切换至第一阶段:人机问答意图澄清");
}}
className="bg-primary text-white hover:bg-[var(--brand-900)] text-xs font-bold px-3 py-2 rounded-xl flex items-center gap-1.5 transition-all shadow-sm shadow-primary/10 active:scale-95"
>
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2.5">
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
开始澄清
</button>
<button
id="human-intervention-btn"
onClick={() => {
setRoles((prev) =>
prev.map((r) =>
r.id === 1
? { ...r, status: r.status === "在线" ? "等待激活" : "在线" }
: r,
),
);
triggerToast("指挥专家状态变更!人工介入切换");
}}
className="bg-white hover:bg-slate-50 border border-slate-200 text-slate-700 text-xs font-bold px-3 py-2 rounded-xl flex items-center gap-1.5 transition-all dark:border-border dark:bg-secondary dark:text-foreground dark:hover:bg-accent"
>
<Users className="w-3.5 h-3.5 text-slate-500 dark:text-muted-foreground" />
人工介入
</button>
<button
id="human-intervention-btn"
onClick={() => {
setRoles((prev) =>
prev.map((r) =>
r.id === 1
? { ...r, status: r.status === "在线" ? "等待激活" : "在线" }
: r,
),
);
triggerToast("指挥专家状态变更!人工介入切换");
}}
className="bg-white hover:bg-slate-50 border border-slate-200 text-slate-700 text-xs font-bold px-3 py-2 rounded-xl flex items-center gap-1.5 transition-all dark:border-border dark:bg-secondary dark:text-foreground dark:hover:bg-accent"
>
<Users className="w-3.5 h-3.5 text-slate-500 dark:text-muted-foreground" />
人工介入
</button>
</>
)}
<button
id="save-draft-header-btn"