Enhance workflow routing and agent configuration management
- Introduce WorkflowLLMRouter for pre-response LLM routing, allowing agents to determine the appropriate function to call based on user input. - Implement UserTurnRoutingProcessor to manage user turns before reaching the LLM, ensuring proper routing and handling of user messages. - Refactor WorkflowBrain to integrate new routing logic and enhance agent stage configuration, including entry modes and resource management. - Update service factory to support dynamic LLM resource configuration based on workflow settings. - Add tests for new routing functionality and ensure proper handling of user messages in various scenarios.
This commit is contained in:
@@ -3,12 +3,28 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from services.node_specs import normalize_graph
|
||||
from services.runtime_variables import DynamicVariableStore
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AgentStageConfig:
|
||||
"""The complete assistant configuration active inside one Agent node."""
|
||||
|
||||
inherits_global: bool
|
||||
llm_resource_id: str
|
||||
asr_resource_id: str
|
||||
tts_resource_id: str
|
||||
tool_ids: tuple[str, ...]
|
||||
knowledge_base_id: str | None
|
||||
knowledge_mode: str
|
||||
knowledge_top_n: int
|
||||
knowledge_score_threshold: float
|
||||
|
||||
|
||||
class WorkflowEngine:
|
||||
def __init__(self, graph: dict[str, Any]):
|
||||
self.graph = normalize_graph(graph)
|
||||
@@ -20,7 +36,11 @@ class WorkflowEngine:
|
||||
}
|
||||
self.edges: list[dict] = list(self.graph.get("edges") or [])
|
||||
self.start_id = next(
|
||||
(node_id for node_id, node in self.nodes.items() if node.get("type") == "start"),
|
||||
(
|
||||
node_id
|
||||
for node_id, node in self.nodes.items()
|
||||
if node.get("type") == "start"
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
@@ -38,7 +58,13 @@ class WorkflowEngine:
|
||||
|
||||
def outgoing(self, node_id: str | None) -> list[dict]:
|
||||
result = [edge for edge in self.edges if edge.get("source") == node_id]
|
||||
return sorted(result, key=lambda edge: int((edge.get("data") or {}).get("priority", 10)))
|
||||
return sorted(
|
||||
result,
|
||||
key=lambda edge: int((edge.get("data") or {}).get("priority", 10)),
|
||||
)
|
||||
|
||||
def has_outgoing(self, node_id: str | None) -> bool:
|
||||
return any(edge.get("source") == node_id for edge in self.edges)
|
||||
|
||||
def edge_mode(self, edge: dict) -> str:
|
||||
return str((edge.get("data") or {}).get("mode") or "always")
|
||||
@@ -60,15 +86,49 @@ class WorkflowEngine:
|
||||
if not edge:
|
||||
return ""
|
||||
data = edge.get("data") or {}
|
||||
return str(data.get("transitionSpeech") or data.get("transition_speech") or "")
|
||||
return str(
|
||||
data.get("transitionSpeech") or data.get("transition_speech") or ""
|
||||
)
|
||||
|
||||
def global_prompt(self) -> str:
|
||||
return str(self.settings.get("globalPrompt") or "").strip()
|
||||
|
||||
def inherits_global_config(self, node_id: str) -> bool:
|
||||
"""Return the Agent's explicit configuration scope, defaulting to global."""
|
||||
return bool(self.data(node_id).get("inheritGlobalConfig", True))
|
||||
|
||||
def agent_stage_config(self, node_id: str) -> AgentStageConfig:
|
||||
"""Resolve either Workflow defaults or one Agent's complete override."""
|
||||
data = self.data(node_id)
|
||||
inherits_global = self.inherits_global_config(node_id)
|
||||
source = self.settings if inherits_global else data
|
||||
llm_key = "defaultLlmResourceId" if inherits_global else "llmResourceId"
|
||||
asr_key = "defaultAsrResourceId" if inherits_global else "asrResourceId"
|
||||
tts_key = "defaultTtsResourceId" if inherits_global else "ttsResourceId"
|
||||
knowledge_base_id = str(source.get("knowledgeBaseId") or "")
|
||||
return AgentStageConfig(
|
||||
inherits_global=inherits_global,
|
||||
llm_resource_id=str(source.get(llm_key) or ""),
|
||||
asr_resource_id=str(source.get(asr_key) or ""),
|
||||
tts_resource_id=str(source.get(tts_key) or ""),
|
||||
tool_ids=tuple(str(tool_id) for tool_id in source.get("toolIds") or []),
|
||||
knowledge_base_id=knowledge_base_id or None,
|
||||
knowledge_mode=(
|
||||
str(source.get("knowledgeMode") or "automatic")
|
||||
if knowledge_base_id
|
||||
else "disabled"
|
||||
),
|
||||
knowledge_top_n=int(source.get("knowledgeTopN") or 5),
|
||||
knowledge_score_threshold=float(
|
||||
source.get("knowledgeScoreThreshold") or 0.0
|
||||
),
|
||||
)
|
||||
|
||||
def prompt_for(self, node_id: str, store: DynamicVariableStore) -> str:
|
||||
"""Build the Agent system prompt according to its inheritance setting."""
|
||||
prompt = store.render(str(self.data(node_id).get("prompt") or "").strip())
|
||||
sections = [f"[当前阶段:{self.name(node_id)}]"]
|
||||
if self.global_prompt():
|
||||
if self.inherits_global_config(node_id) and self.global_prompt():
|
||||
sections.append(f"[全局规则]\n{store.render(self.global_prompt())}")
|
||||
if prompt:
|
||||
sections.append(f"[当前阶段任务]\n{prompt}")
|
||||
@@ -111,7 +171,11 @@ class WorkflowEngine:
|
||||
results.append(matched)
|
||||
if not results:
|
||||
return False
|
||||
return all(results) if expression.get("combinator", "and") == "and" else any(results)
|
||||
return (
|
||||
all(results)
|
||||
if expression.get("combinator", "and") == "and"
|
||||
else any(results)
|
||||
)
|
||||
|
||||
def deterministic_edge(
|
||||
self,
|
||||
|
||||
Reference in New Issue
Block a user