feat: add deterministic message interaction stages
This commit is contained in:
@@ -85,55 +85,18 @@ export function PromptEditor({
|
||||
handlePromptVisionEnabledChange,
|
||||
handlePromptModelChange,
|
||||
}: PromptEditorProps) {
|
||||
const openingMessage = form.startup.actions.find(
|
||||
(action) => action.id === "opening_message" && action.phase === "opening",
|
||||
);
|
||||
const openingArguments = openingMessage?.arguments ?? {};
|
||||
const openingButtons = Array.isArray(openingArguments.actions)
|
||||
? openingArguments.actions
|
||||
: [];
|
||||
const openingButton =
|
||||
openingButtons[0] && typeof openingButtons[0] === "object"
|
||||
? (openingButtons[0] as Record<string, unknown>)
|
||||
: {};
|
||||
const showMessageTools = tools
|
||||
.filter(
|
||||
(tool) =>
|
||||
tool.status === "active" &&
|
||||
tool.type === "client" &&
|
||||
tool.functionName === "show_message" &&
|
||||
tool.definition.type === "client" &&
|
||||
tool.definition.config.waitForResponse &&
|
||||
tool.definition.config.responseWaitMode === "session",
|
||||
)
|
||||
.map((tool) => ({ value: tool.id, label: tool.name }));
|
||||
const openingMessage = form.startup.openingMessage;
|
||||
|
||||
function setOpeningMessage(enabled: boolean) {
|
||||
const otherActions = form.startup.actions.filter(
|
||||
(action) => action.id !== "opening_message",
|
||||
);
|
||||
const defaultToolId = showMessageTools[0]?.value ?? "";
|
||||
updateForm("startup", {
|
||||
executionMode: "sequential",
|
||||
actions: enabled
|
||||
? [
|
||||
...otherActions,
|
||||
{
|
||||
id: "opening_message",
|
||||
phase: "opening",
|
||||
toolId: defaultToolId,
|
||||
required: true,
|
||||
arguments: {
|
||||
title: "重要提示",
|
||||
message: "请确认已阅读以上信息。",
|
||||
actions: [
|
||||
{ id: "confirmed", label: "确认", style: "primary" },
|
||||
],
|
||||
dismissible: false,
|
||||
},
|
||||
},
|
||||
]
|
||||
: otherActions,
|
||||
...form.startup,
|
||||
openingMessage: enabled
|
||||
? {
|
||||
title: "重要提示",
|
||||
message: "请确认已阅读以上信息。",
|
||||
confirmLabel: "确认",
|
||||
}
|
||||
: null,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -143,16 +106,10 @@ export function PromptEditor({
|
||||
if (!openingMessage) return;
|
||||
updateForm("startup", {
|
||||
...form.startup,
|
||||
actions: form.startup.actions.map((action) =>
|
||||
action.id === openingMessage.id ? { ...action, ...patch } : action,
|
||||
),
|
||||
openingMessage: { ...openingMessage, ...patch },
|
||||
});
|
||||
}
|
||||
|
||||
function updateOpeningArguments(patch: Record<string, unknown>) {
|
||||
updateOpeningMessage({ arguments: { ...openingArguments, ...patch } });
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="-mt-6 flex h-full flex-col gap-4">
|
||||
<div className="flex shrink-0 items-center justify-between gap-6 border-b border-hairline pb-3 pt-1">
|
||||
@@ -235,33 +192,21 @@ export function PromptEditor({
|
||||
<div className="mt-4 space-y-3 border-t border-hairline pt-4">
|
||||
<ToggleRow
|
||||
title="开场确认弹窗"
|
||||
description="与开场白同时显示,确认且播报完成后才允许用户开始对话。"
|
||||
description="与开场白同时显示,用户确认后即可开始对话,不等待播报完成。"
|
||||
checked={Boolean(openingMessage)}
|
||||
onChange={setOpeningMessage}
|
||||
/>
|
||||
|
||||
{openingMessage && (
|
||||
<div className="space-y-3 rounded-xl border border-hairline bg-canvas-soft p-4">
|
||||
<ResourceSelectField
|
||||
label="消息工具"
|
||||
value={openingMessage.toolId}
|
||||
options={showMessageTools}
|
||||
noneLabel="请选择 show_message 工具"
|
||||
onChange={(toolId) => updateOpeningMessage({ toolId })}
|
||||
/>
|
||||
{showMessageTools.length === 0 && (
|
||||
<p className="text-xs leading-5 text-muted-foreground">
|
||||
请先在组件管理中创建 functionName 为 show_message、会话内等待的 Client Tool。
|
||||
</p>
|
||||
)}
|
||||
<label className="block">
|
||||
<span className="mb-1.5 block text-sm font-medium text-foreground">
|
||||
弹窗标题
|
||||
</span>
|
||||
<Input
|
||||
value={String(openingArguments.title ?? "")}
|
||||
value={openingMessage.title}
|
||||
onChange={(event) =>
|
||||
updateOpeningArguments({ title: event.target.value })
|
||||
updateOpeningMessage({ title: event.target.value })
|
||||
}
|
||||
placeholder="重要提示"
|
||||
className="border-hairline-strong bg-background"
|
||||
@@ -269,8 +214,8 @@ export function PromptEditor({
|
||||
</label>
|
||||
<TextAreaField
|
||||
label="重要信息"
|
||||
value={String(openingArguments.message ?? "")}
|
||||
onChange={(message) => updateOpeningArguments({ message })}
|
||||
value={openingMessage.message}
|
||||
onChange={(message) => updateOpeningMessage({ message })}
|
||||
placeholder="请输入需要用户确认的重要信息"
|
||||
rows={4}
|
||||
/>
|
||||
@@ -279,16 +224,10 @@ export function PromptEditor({
|
||||
确认按钮
|
||||
</span>
|
||||
<Input
|
||||
value={String(openingButton.label ?? "")}
|
||||
value={openingMessage.confirmLabel}
|
||||
onChange={(event) =>
|
||||
updateOpeningArguments({
|
||||
actions: [
|
||||
{
|
||||
id: "confirmed",
|
||||
label: event.target.value,
|
||||
style: "primary",
|
||||
},
|
||||
],
|
||||
updateOpeningMessage({
|
||||
confirmLabel: event.target.value,
|
||||
})
|
||||
}
|
||||
placeholder="确认"
|
||||
@@ -296,8 +235,8 @@ export function PromptEditor({
|
||||
/>
|
||||
</label>
|
||||
<p className="text-xs leading-5 text-muted-foreground">
|
||||
弹窗不可跳过;失败时将结束本次通话。该工具仅用于开场,
|
||||
如需允许模型后续调用,请另外在“工具”区域勾选。
|
||||
弹窗不可跳过;失败时将结束本次通话。这是平台内置开场阶段,
|
||||
不会作为工具暴露给模型。
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
@@ -318,10 +257,14 @@ export function PromptEditor({
|
||||
value={form.runtimeMode}
|
||||
onChange={(runtimeMode) => {
|
||||
updateForm("runtimeMode", runtimeMode);
|
||||
if (runtimeMode === "realtime" && form.startup.actions.length) {
|
||||
if (
|
||||
runtimeMode === "realtime" &&
|
||||
(form.startup.actions.length || form.startup.openingMessage)
|
||||
) {
|
||||
updateForm("startup", {
|
||||
executionMode: "sequential",
|
||||
actions: [],
|
||||
openingMessage: null,
|
||||
});
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -177,7 +177,11 @@ function blankPromptForm(name: string): AssistantForm {
|
||||
knowledgeRetrievalConfig: defaultKnowledgeRetrievalConfig(),
|
||||
enableInterrupt: true,
|
||||
turnConfig: defaultTurnConfig(),
|
||||
startup: { executionMode: "sequential", actions: [] },
|
||||
startup: {
|
||||
executionMode: "sequential",
|
||||
actions: [],
|
||||
openingMessage: null,
|
||||
},
|
||||
visionEnabled: false,
|
||||
visionModelResourceId: "",
|
||||
toolIds: [],
|
||||
@@ -466,7 +470,11 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
a.knowledgeRetrievalConfig ?? defaultKnowledgeRetrievalConfig(),
|
||||
enableInterrupt: a.enableInterrupt,
|
||||
turnConfig: normalizeTurnConfig(a.turnConfig),
|
||||
startup: a.startup ?? { executionMode: "sequential", actions: [] },
|
||||
startup: {
|
||||
executionMode: "sequential",
|
||||
actions: a.startup?.actions ?? [],
|
||||
openingMessage: a.startup?.openingMessage ?? null,
|
||||
},
|
||||
visionEnabled: a.visionEnabled,
|
||||
visionModelResourceId: a.visionModelResourceId ?? "",
|
||||
toolIds: a.toolIds ?? [],
|
||||
@@ -536,7 +544,11 @@ export function AssistantPage(props: AssistantPageProps) {
|
||||
greeting: "",
|
||||
enableInterrupt: true,
|
||||
turnConfig: defaultTurnConfig(),
|
||||
startup: { executionMode: "sequential", actions: [] },
|
||||
startup: {
|
||||
executionMode: "sequential",
|
||||
actions: [],
|
||||
openingMessage: null,
|
||||
},
|
||||
visionEnabled: false,
|
||||
visionModelResourceId: null,
|
||||
modelResourceIds: {},
|
||||
|
||||
@@ -27,7 +27,12 @@ export function GenericNode({ id, type, data, selected }: NodeProps) {
|
||||
|
||||
const nodeData = data as WorkflowNodeData;
|
||||
const Icon = spec.icon;
|
||||
const preview = (nodeData.greeting || nodeData.prompt || nodeData.message || "")
|
||||
const preview = (
|
||||
nodeData.prompt ||
|
||||
nodeData.speech ||
|
||||
nodeData.message ||
|
||||
""
|
||||
)
|
||||
.toString()
|
||||
.trim();
|
||||
const entryModeLabel = {
|
||||
@@ -50,8 +55,17 @@ export function GenericNode({ id, type, data, selected }: NodeProps) {
|
||||
nodeData.visionEnabled ? "视觉理解" : null,
|
||||
].filter(Boolean)
|
||||
: type === "action" && nodeData.toolId
|
||||
? ["确定性工具"]
|
||||
: [];
|
||||
? [
|
||||
"确定性工具",
|
||||
nodeData.userInputPolicy === "block" ? "禁止输入" : null,
|
||||
].filter(Boolean)
|
||||
: type === "message"
|
||||
? [
|
||||
nodeData.speech ? "固定播报" : null,
|
||||
nodeData.showMessage ? "客户端消息" : null,
|
||||
nodeData.requireConfirmation ? "等待确认" : null,
|
||||
].filter(Boolean)
|
||||
: [];
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -192,6 +206,7 @@ export function GenericNode({ id, type, data, selected }: NodeProps) {
|
||||
export const nodeTypes = {
|
||||
start: GenericNode,
|
||||
agent: GenericNode,
|
||||
message: GenericNode,
|
||||
action: GenericNode,
|
||||
handoff: GenericNode,
|
||||
end: GenericNode,
|
||||
|
||||
@@ -63,21 +63,30 @@ let nodeSeq = 0;
|
||||
function defaultNodeData(spec: RuntimeNodeSpec): WorkflowNodeData {
|
||||
const data: WorkflowNodeData = {
|
||||
name: spec.displayName,
|
||||
...(spec.type === "agent"
|
||||
? {
|
||||
contextPolicy: "inherit",
|
||||
inheritGlobalConfig: true,
|
||||
entryMode: "wait_user",
|
||||
entrySpeech: "",
|
||||
}
|
||||
: spec.type === "action"
|
||||
? {
|
||||
arguments: {},
|
||||
resultAssignmentMode: "inherit",
|
||||
userInputPolicy: "queue",
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
if (spec.type === "agent") {
|
||||
Object.assign(data, {
|
||||
contextPolicy: "inherit",
|
||||
inheritGlobalConfig: true,
|
||||
entryMode: "wait_user",
|
||||
entrySpeech: "",
|
||||
});
|
||||
} else if (spec.type === "action") {
|
||||
Object.assign(data, {
|
||||
arguments: {},
|
||||
resultAssignmentMode: "inherit",
|
||||
userInputPolicy: "queue",
|
||||
});
|
||||
} else if (spec.type === "message") {
|
||||
Object.assign(data, {
|
||||
speech: "",
|
||||
showMessage: false,
|
||||
title: "重要提示",
|
||||
message: "",
|
||||
confirmLabel: "确认",
|
||||
requireConfirmation: false,
|
||||
});
|
||||
}
|
||||
for (const field of spec.fields) {
|
||||
if (field.default !== undefined) data[field.key] = field.default;
|
||||
}
|
||||
|
||||
128
frontend/src/components/workflow/panels/MessageNodePanel.tsx
Normal file
128
frontend/src/components/workflow/panels/MessageNodePanel.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
"use client";
|
||||
|
||||
import { MessageSquareText } from "lucide-react";
|
||||
|
||||
import { SectionCard } from "@/components/editor/section-card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
|
||||
import type { WorkflowNodeData } from "../specs";
|
||||
|
||||
export function MessageNodePanel({
|
||||
draft,
|
||||
set,
|
||||
setPatch,
|
||||
}: {
|
||||
draft: WorkflowNodeData;
|
||||
set: (key: string, value: unknown) => void;
|
||||
setPatch: (patch: Partial<WorkflowNodeData>) => void;
|
||||
}) {
|
||||
const showMessage = Boolean(draft.showMessage);
|
||||
const requireConfirmation = Boolean(draft.requireConfirmation);
|
||||
|
||||
return (
|
||||
<SectionCard
|
||||
icon={<MessageSquareText size={15} />}
|
||||
title="播报与确认"
|
||||
description="播放固定话术,并可同时显示平台内置的客户端消息"
|
||||
>
|
||||
<label className="block">
|
||||
<div className="mb-1.5 text-sm font-medium text-foreground">
|
||||
固定播报(可选)
|
||||
</div>
|
||||
<Textarea
|
||||
rows={4}
|
||||
value={draft.speech ?? ""}
|
||||
onChange={(event) => set("speech", event.target.value)}
|
||||
placeholder="例如:您好,在开始服务前请确认以下重要信息。"
|
||||
className="field-sizing-fixed min-h-28 resize-y border-hairline-strong bg-background text-sm text-foreground placeholder:text-muted-soft"
|
||||
/>
|
||||
<span className="mt-1.5 block text-xs leading-5 text-muted-foreground">
|
||||
支持 {"{{variable}}"} 动态变量。仅播报时,播放完成后进入下一节点。
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<label className="flex items-start justify-between gap-4 rounded-xl border border-hairline bg-canvas-soft px-4 py-3">
|
||||
<span>
|
||||
<span className="block text-sm font-medium text-foreground">
|
||||
显示客户端消息
|
||||
</span>
|
||||
<span className="mt-1 block text-xs leading-5 text-muted-foreground">
|
||||
使用平台内置弹窗,不需要创建或绑定 Client Tool。
|
||||
</span>
|
||||
</span>
|
||||
<Switch
|
||||
checked={showMessage}
|
||||
onCheckedChange={(checked) =>
|
||||
setPatch({
|
||||
showMessage: checked,
|
||||
...(!checked ? { requireConfirmation: false } : {}),
|
||||
})
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
|
||||
{showMessage && (
|
||||
<div className="space-y-3 rounded-xl border border-hairline bg-canvas-soft p-4">
|
||||
<label className="block">
|
||||
<span className="mb-1.5 block text-sm font-medium text-foreground">
|
||||
弹窗标题
|
||||
</span>
|
||||
<Input
|
||||
value={draft.title ?? ""}
|
||||
onChange={(event) => set("title", event.target.value)}
|
||||
placeholder="重要提示"
|
||||
className="border-hairline-strong bg-background"
|
||||
/>
|
||||
</label>
|
||||
<label className="block">
|
||||
<span className="mb-1.5 block text-sm font-medium text-foreground">
|
||||
重要信息 <span className="text-destructive">*</span>
|
||||
</span>
|
||||
<Textarea
|
||||
rows={4}
|
||||
value={draft.message ?? ""}
|
||||
onChange={(event) => set("message", event.target.value)}
|
||||
placeholder="请输入需要向用户展示的重要信息"
|
||||
className="field-sizing-fixed min-h-28 resize-y border-hairline-strong bg-background text-sm text-foreground placeholder:text-muted-soft"
|
||||
/>
|
||||
</label>
|
||||
<label className="block">
|
||||
<span className="mb-1.5 block text-sm font-medium text-foreground">
|
||||
按钮文字
|
||||
</span>
|
||||
<Input
|
||||
value={draft.confirmLabel ?? ""}
|
||||
onChange={(event) => set("confirmLabel", event.target.value)}
|
||||
placeholder="确认"
|
||||
className="border-hairline-strong bg-background"
|
||||
/>
|
||||
</label>
|
||||
<label className="flex items-start justify-between gap-4 border-t border-hairline pt-3">
|
||||
<span>
|
||||
<span className="block text-sm font-medium text-foreground">
|
||||
等待用户确认
|
||||
</span>
|
||||
<span className="mt-1 block text-xs leading-5 text-muted-foreground">
|
||||
确认前禁止语音、文字和图片输入;确认后立即进入下一节点,不等待播报结束。
|
||||
</span>
|
||||
</span>
|
||||
<Switch
|
||||
checked={requireConfirmation}
|
||||
onCheckedChange={(checked) =>
|
||||
set("requireConfirmation", checked)
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!draft.speech?.trim() && !showMessage && (
|
||||
<p role="alert" className="text-xs leading-5 text-destructive">
|
||||
Message 至少需要固定播报或客户端消息。
|
||||
</p>
|
||||
)}
|
||||
</SectionCard>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { Flag, PhoneForwarded, Play, Tag } from "lucide-react";
|
||||
import { Flag, PhoneForwarded, Tag } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
import { SectionCard } from "@/components/editor/section-card";
|
||||
@@ -11,6 +11,7 @@ import { Textarea } from "@/components/ui/textarea";
|
||||
|
||||
import { ActionNodePanel } from "./ActionNodePanel";
|
||||
import { AgentNodePanel } from "./AgentNodePanel";
|
||||
import { MessageNodePanel } from "./MessageNodePanel";
|
||||
import { NodeSelect, ToolOptionPicker } from "./controls";
|
||||
import type { RuntimeNodeSpec, WorkflowNodeData } from "../specs";
|
||||
import type { ModelOption, WorkflowSettings } from "../types";
|
||||
@@ -82,6 +83,7 @@ export function NodeSettingsPanel({
|
||||
spec={spec}
|
||||
draft={draft}
|
||||
set={set}
|
||||
setPatch={setPatch}
|
||||
toolOptions={toolOptions}
|
||||
argumentsJson={argumentsJson}
|
||||
assignmentsJson={assignmentsJson}
|
||||
@@ -247,6 +249,10 @@ export function NodeSettingsPanel({
|
||||
/>
|
||||
)}
|
||||
|
||||
{spec.type === "message" && (
|
||||
<MessageNodePanel draft={draft} set={set} setPatch={setPatch} />
|
||||
)}
|
||||
|
||||
{spec.type === "handoff" && (
|
||||
<NodeSelect
|
||||
label="转交类型"
|
||||
@@ -283,6 +289,7 @@ function WorkflowNodePanelForm({
|
||||
spec,
|
||||
draft,
|
||||
set,
|
||||
setPatch,
|
||||
toolOptions,
|
||||
argumentsJson,
|
||||
assignmentsJson,
|
||||
@@ -294,6 +301,7 @@ function WorkflowNodePanelForm({
|
||||
spec: RuntimeNodeSpec;
|
||||
draft: WorkflowNodeData;
|
||||
set: (key: string, val: unknown) => void;
|
||||
setPatch: (patch: Partial<WorkflowNodeData>) => void;
|
||||
toolOptions: ModelOption[];
|
||||
argumentsJson: string;
|
||||
assignmentsJson: string;
|
||||
@@ -321,27 +329,6 @@ function WorkflowNodePanelForm({
|
||||
</label>
|
||||
</SectionCard>
|
||||
|
||||
{spec.type === "start" && (
|
||||
<SectionCard
|
||||
icon={<Play size={15} />}
|
||||
title="开场白"
|
||||
description="会话建立后首先向用户播放的固定内容"
|
||||
>
|
||||
<label className="block">
|
||||
<div className="mb-1.5 text-sm font-medium text-foreground">
|
||||
固定开场白
|
||||
</div>
|
||||
<Textarea
|
||||
rows={5}
|
||||
value={draft.greeting ?? ""}
|
||||
onChange={(event) => set("greeting", event.target.value)}
|
||||
placeholder="例如:你好,我是 AI 视频助手,有什么可以帮你?"
|
||||
className="field-sizing-fixed min-h-28 resize-y border-hairline-strong bg-background text-sm text-foreground placeholder:text-muted-soft"
|
||||
/>
|
||||
</label>
|
||||
</SectionCard>
|
||||
)}
|
||||
|
||||
{spec.type === "action" && (
|
||||
<ActionNodePanel
|
||||
draft={draft}
|
||||
@@ -356,6 +343,10 @@ function WorkflowNodePanelForm({
|
||||
/>
|
||||
)}
|
||||
|
||||
{spec.type === "message" && (
|
||||
<MessageNodePanel draft={draft} set={set} setPatch={setPatch} />
|
||||
)}
|
||||
|
||||
{spec.type === "handoff" && (
|
||||
<SectionCard
|
||||
icon={<PhoneForwarded size={15} />}
|
||||
|
||||
@@ -6,7 +6,13 @@ import { Circle, type LucideIcon } from "lucide-react";
|
||||
import type { NodeSpecDto, TurnConfig } from "@/lib/api";
|
||||
import { defaultTurnConfig } from "@/lib/turn-config";
|
||||
|
||||
export type WorkflowNodeType = "start" | "agent" | "action" | "handoff" | "end";
|
||||
export type WorkflowNodeType =
|
||||
| "start"
|
||||
| "agent"
|
||||
| "message"
|
||||
| "action"
|
||||
| "handoff"
|
||||
| "end";
|
||||
export type ContextPolicy = "inherit" | "fresh";
|
||||
export type KnowledgeMode = "automatic" | "on_demand" | "disabled";
|
||||
export type AgentEntryMode = "wait_user" | "generate" | "fixed_speech";
|
||||
@@ -26,7 +32,6 @@ export type ExpressionOperator =
|
||||
|
||||
export type WorkflowNodeData = {
|
||||
name: string;
|
||||
greeting?: string;
|
||||
prompt?: string;
|
||||
contextPolicy?: ContextPolicy;
|
||||
inheritGlobalConfig?: boolean;
|
||||
@@ -49,6 +54,11 @@ export type WorkflowNodeData = {
|
||||
resultAssignmentMode?: ActionResultAssignmentMode;
|
||||
resultAssignments?: Record<string, string>;
|
||||
userInputPolicy?: ActionUserInputPolicy;
|
||||
speech?: string;
|
||||
showMessage?: boolean;
|
||||
title?: string;
|
||||
confirmLabel?: string;
|
||||
requireConfirmation?: boolean;
|
||||
targetType?: "ai" | "human" | "queue" | "phone";
|
||||
target?: string;
|
||||
message?: string;
|
||||
@@ -224,13 +234,26 @@ export function defaultGraph(): WorkflowGraph {
|
||||
position: { x: 360, y: 60 },
|
||||
data: {
|
||||
name: "Start",
|
||||
greeting: "你好,我是 AI 视频助手,有什么可以帮你?",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "message-1",
|
||||
type: "message",
|
||||
position: { x: 360, y: 260 },
|
||||
data: {
|
||||
name: "开场消息",
|
||||
speech: "你好,我是 AI 视频助手,有什么可以帮你?",
|
||||
showMessage: false,
|
||||
title: "重要提示",
|
||||
message: "",
|
||||
confirmLabel: "确认",
|
||||
requireConfirmation: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "agent-1",
|
||||
type: "agent",
|
||||
position: { x: 360, y: 300 },
|
||||
position: { x: 360, y: 480 },
|
||||
data: {
|
||||
name: "Agent",
|
||||
prompt: "了解用户需求并提供清晰、准确的帮助。",
|
||||
@@ -243,7 +266,7 @@ export function defaultGraph(): WorkflowGraph {
|
||||
{
|
||||
id: "end",
|
||||
type: "end",
|
||||
position: { x: 360, y: 540 },
|
||||
position: { x: 360, y: 700 },
|
||||
data: {
|
||||
name: "End",
|
||||
message: "感谢你的来电,再见。",
|
||||
@@ -253,8 +276,14 @@ export function defaultGraph(): WorkflowGraph {
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: "e-start-agent",
|
||||
id: "e-start-message",
|
||||
source: "start",
|
||||
target: "message-1",
|
||||
data: { mode: "always", priority: 0 },
|
||||
},
|
||||
{
|
||||
id: "e-message-agent",
|
||||
source: "message-1",
|
||||
target: "agent-1",
|
||||
data: { mode: "always", priority: 0 },
|
||||
},
|
||||
|
||||
@@ -207,6 +207,11 @@ export type StartupAction = {
|
||||
export type StartupConfig = {
|
||||
executionMode: "sequential";
|
||||
actions: StartupAction[];
|
||||
openingMessage: {
|
||||
title: string;
|
||||
message: string;
|
||||
confirmLabel: string;
|
||||
} | null;
|
||||
};
|
||||
|
||||
/** 后端 AssistantOut(宽表 STI:瘦字段平铺,workflow 用 graph)。apiKey 读时打码 */
|
||||
|
||||
Reference in New Issue
Block a user