智能体对话选 Agent 直达、澄清问题批量提交、LLM 错误中文化等

- 前端:新会话选 Agent 后直接跳转并自动发送,智能体页输入框宽度与首页对齐
- 前端:澄清卡片支持多选+多问题合并提交,消除单题逐一发送的割裂感
- 前端:定时任务弹窗 Agent 下拉支持长描述截断,避免撑破布局
- 后端:ask_clarification 支持同回合多次调用以提多个独立问题
- 后端:LLM 错误处理与定时任务失败提示统一改为中文

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
吉鑫升andClaude Opus 4.7 committed 2026-05-22 14:24:29 +08:00
1 parent c74be2b789
commit ba2bc46042
7 files changed
+327 -73

No files matched your search

+44 -6
View File
@@ -3,7 +3,7 @@
import { BotIcon, PlusSquare } from "lucide-react";
import { useParams, useRouter } from "next/navigation";
import { useRouteBase } from "@/core/route-base";
import { useCallback, useEffect, useState } from "react";
import { useCallback, useEffect, useRef, useState } from "react";
import type { PromptInputMessage } from "@/components/ai-elements/prompt-input";
import { Button } from "@/components/ui/button";
@@ -36,13 +36,30 @@ export default function AgentChatPage() {
const routeBase = useRouteBase();
const { t } = useI18n();
const [showFollowups, setShowFollowups] = useState(false);
const [hasStartedConversation, setHasStartedConversation] = useState(false);
const router = useRouter();
const { agent_id } = useParams<{
agent_id: string;
}>();
// If ChatPage stashed an opening message for this agent, skip the landing
// screen on first render so the user doesn't see a flash of the welcome UI
// before the auto-sent message takes over.
const hasPendingForThisAgent = () => {
if (typeof window === "undefined" || !agent_id) return false;
try {
const raw = sessionStorage.getItem("pendingAgentMessage");
if (!raw) return false;
const parsed = JSON.parse(raw);
return parsed?.agentId === agent_id && !!parsed?.text;
} catch {
return false;
}
};
const [hasStartedConversation, setHasStartedConversation] = useState(
hasPendingForThisAgent,
);
const { agent } = useAgent(agent_id);
const { threadId, setThreadId, isNewThread, setIsNewThread } =
@@ -52,9 +69,10 @@ export default function AgentChatPage() {
useEffect(() => {
if (isNewThread) {
setHasStartedConversation(false);
setHasStartedConversation(hasPendingForThisAgent());
}
}, [isNewThread, threadId]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isNewThread, threadId, agent_id]);
const { showNotification } = useNotification();
const [thread, sendMessage] = useThreadStream({
@@ -100,6 +118,26 @@ export default function AgentChatPage() {
[sendMessage, threadId, agent_id],
);
// ChatPage stashes the opening message in sessionStorage when the user picks
// an agent on the landing screen, then redirects here. Replay it once so the
// user doesn't have to re-type the question on the agent chat page.
const pendingMessageHandled = useRef(false);
useEffect(() => {
if (pendingMessageHandled.current) return;
if (!agent_id || !isNewThread || hasStartedConversation) return;
let pending: { agentId?: string; text?: string } | null = null;
try {
const raw = sessionStorage.getItem("pendingAgentMessage");
if (raw) pending = JSON.parse(raw);
} catch {
pending = null;
}
if (!pending || pending.agentId !== agent_id || !pending.text) return;
pendingMessageHandled.current = true;
sessionStorage.removeItem("pendingAgentMessage");
handleSubmit({ text: pending.text, files: [] });
}, [agent_id, isNewThread, hasStartedConversation, handleSubmit]);
const handleClarificationSubmit = useCallback(
(answer: string) => {
setHasStartedConversation(true);
@@ -180,8 +218,8 @@ export default function AgentChatPage() {
"relative w-full",
isLandingState && "-translate-y-[calc(50vh-96px)]",
isLandingState
? "max-w-(--container-width-sm)"
: "max-w-(--container-width-md)",
? "max-w-(--container-width-md)"
: "max-w-(--container-width-lg)",
)}
>
<div className="absolute -top-4 right-0 left-0 z-0">
+12 -7
View File
@@ -758,11 +758,11 @@ export default function ScheduledTasksPage() {
</Dialog>
<Dialog open={creating || !!editing} onOpenChange={(open) => !open && closeDialog()}>
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogContent className="max-w-2xl overflow-hidden">
<DialogHeader className="min-w-0">
<DialogTitle>{creating ? "新建定时任务" : "编辑定时任务"}</DialogTitle>
</DialogHeader>
<div className="flex flex-col gap-3">
<div className="flex min-w-0 flex-col gap-3">
<Input
value={form.name}
onChange={(event) =>
@@ -798,15 +798,20 @@ export default function ScheduledTasksPage() {
}))
}
>
<SelectTrigger>
<SelectTrigger className="w-full min-w-0 max-w-full overflow-hidden [&>span]:min-w-0 [&>span]:flex-1 [&>span]:truncate">
<SelectValue placeholder="选择执行智能体" />
</SelectTrigger>
<SelectContent>
<SelectContent
position="popper"
className="w-[var(--radix-select-trigger-width)] max-w-[var(--radix-select-trigger-width)]"
>
<SelectItem value="__default__">默认助手(不指定)</SelectItem>
{agents.map((agent) => (
<SelectItem key={agent.id} value={agent.id}>
{agent.name}
{agent.description ? ` — ${agent.description}` : ""}
<span className="block truncate">
{agent.name}
{agent.description ? ` — ${agent.description}` : ""}
</span>
</SelectItem>
))}
</SelectContent>