diff --git a/backend/services/pipecat/pipeline.py b/backend/services/pipecat/pipeline.py index 807c740..8360dcf 100644 --- a/backend/services/pipecat/pipeline.py +++ b/backend/services/pipecat/pipeline.py @@ -346,9 +346,18 @@ async def run_pipeline(transport, cfg: AssistantConfig) -> None: await emit_node_active(target) apply_node(target) - def make_transition_handler(target: str): + async def speak_transition(edge: dict | None) -> None: + """切换瞬间播报过渡语(可选),掩盖切节点/新一轮生成的延迟。不写入上下文。""" + speech = engine.edge_transition_speech(edge) + if speech: + await worker.queue_frame(TTSSpeakFrame(speech, append_to_context=False)) + + def make_transition_handler(edge: dict): + target = edge.get("target") + async def handler(params): logger.info(f"LLM 触发转移 → {engine.name(target)}") + await speak_transition(edge) await go_to_node(target) # 返回工具结果,pipecat 随即在新节点的提示/工具下继续生成 await params.result_callback({"status": "ok"}) @@ -372,6 +381,7 @@ async def run_pipeline(transport, cfg: AssistantConfig) -> None: ) if target and target != wf_state["current"]: logger.info(f"文本兜底触发转移 → {engine.name(target)}") + await speak_transition(engine.find_edge(wf_state["current"], target)) # 仅切换节点提示/工具,下一轮用户输入即在新节点处理 await go_to_node(target) @@ -379,10 +389,9 @@ async def run_pipeline(transport, cfg: AssistantConfig) -> None: # 由各节点的 context.tools 控制当前可见哪些)。 if workflow_active: for edge in engine.edges: - target = edge.get("target") - if target: + if edge.get("target"): llm.register_function( - engine.edge_fn_name(edge), make_transition_handler(target) + engine.edge_fn_name(edge), make_transition_handler(edge) ) apply_node(wf_state["current"]) # 设初始节点的提示与工具 diff --git a/backend/services/workflow_engine.py b/backend/services/workflow_engine.py index bf993eb..1b893fa 100644 --- a/backend/services/workflow_engine.py +++ b/backend/services/workflow_engine.py @@ -50,6 +50,18 @@ class WorkflowEngine: def edge_condition(self, edge: dict) -> str: return (edge.get("data") or {}).get("condition") or "" + def edge_transition_speech(self, edge: dict | None) -> str: + """命中该边、切换节点瞬间播报的过渡语(可选,掩盖延迟,不写入上下文)。""" + if not edge: + return "" + return (edge.get("data") or {}).get("transition_speech") or "" + + def find_edge(self, source: str | None, target: str | None) -> dict | None: + for edge in self.edges: + if edge.get("source") == source and edge.get("target") == target: + return edge + return None + def edge_description(self, edge: dict) -> str: """作为转移函数的 description 交给 LLM:满足该条件时模型应调用此函数。""" cond = self.edge_condition(edge) diff --git a/frontend/src/components/workflow/WorkflowEditor.tsx b/frontend/src/components/workflow/WorkflowEditor.tsx index 78cf0c5..c4ee1d9 100644 --- a/frontend/src/components/workflow/WorkflowEditor.tsx +++ b/frontend/src/components/workflow/WorkflowEditor.tsx @@ -120,7 +120,12 @@ function fromFlow(nodes: Node[], edges: Edge[]): WorkflowGraph { id: e.id, source: e.source, target: e.target, - data: (e.data as { condition?: string; label?: string }) ?? {}, + data: + (e.data as { + condition?: string; + label?: string; + transition_speech?: string; + }) ?? {}, })), }; } @@ -238,7 +243,14 @@ function Canvas({ ); const updateEdgeData = useCallback( - (id: string, patch: { condition?: string; label?: string }) => { + ( + id: string, + patch: { + condition?: string; + label?: string; + transition_speech?: string; + }, + ) => { setEdges((es) => es.map((e) => e.id === id ? { ...e, data: { ...(e.data ?? {}), ...patch } } : e, @@ -677,12 +689,23 @@ function EdgeForm({ onDelete, }: { edge: Edge; - onSave: (patch: { condition: string; label: string }) => void; + onSave: (patch: { + condition: string; + label: string; + transition_speech: string; + }) => void; onDelete: () => void; }) { - const data = (edge.data ?? {}) as { condition?: string; label?: string }; + const data = (edge.data ?? {}) as { + condition?: string; + label?: string; + transition_speech?: string; + }; const [label, setLabel] = useState(data.label ?? ""); const [condition, setCondition] = useState(data.condition ?? ""); + const [transitionSpeech, setTransitionSpeech] = useState( + data.transition_speech ?? "", + ); return ( <> @@ -718,6 +741,22 @@ function EdgeForm({ className="resize-none border-hairline-strong bg-background text-foreground placeholder:text-muted-soft" /> + +