feat(itr/dag): add router for tool-call dispatch routing
This commit is contained in:
parent
17634aa9ac
commit
32fcd87737
1 changed files with 109 additions and 0 deletions
109
pkg/itr/dag/router.go
Normal file
109
pkg/itr/dag/router.go
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
package dag
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ToolLoopMode controls whether the agent uses the sequential ReAct loop,
|
||||
// the parallel DAG executor, or lets the router decide automatically.
|
||||
type ToolLoopMode int
|
||||
|
||||
const (
|
||||
// ModeReAct uses Fantasy's sequential ReAct loop. Best for simple, 1-3
|
||||
// tool queries where DAG overhead isn't justified.
|
||||
ModeReAct ToolLoopMode = iota
|
||||
|
||||
// ModeDAG uses the LLMCompiler-style DAG executor for parallel dispatch.
|
||||
// Best for complex multi-tool workflows.
|
||||
ModeDAG
|
||||
|
||||
// ModeAuto lets the router classify the query and pick the best mode.
|
||||
ModeAuto
|
||||
)
|
||||
|
||||
// String returns a human-readable name for the mode.
|
||||
func (m ToolLoopMode) String() string {
|
||||
switch m {
|
||||
case ModeReAct:
|
||||
return "react"
|
||||
case ModeDAG:
|
||||
return "dag"
|
||||
case ModeAuto:
|
||||
return "auto"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// RouterConfig configures the automatic mode selection heuristic.
|
||||
type RouterConfig struct {
|
||||
// ComplexityThreshold is the minimum word count in a query for DAG mode.
|
||||
// Queries shorter than this default to ReAct. Default: 30.
|
||||
ComplexityThreshold int
|
||||
|
||||
// ParallelKeywords are phrases that suggest parallel execution. If any
|
||||
// appear in the query, DAG mode is preferred.
|
||||
ParallelKeywords []string
|
||||
}
|
||||
|
||||
// DefaultRouterConfig returns sensible defaults.
|
||||
func DefaultRouterConfig() RouterConfig {
|
||||
return RouterConfig{
|
||||
ComplexityThreshold: 30,
|
||||
ParallelKeywords: []string{
|
||||
"and then",
|
||||
"in parallel",
|
||||
"simultaneously",
|
||||
"at the same time",
|
||||
"multiple",
|
||||
"several",
|
||||
"both",
|
||||
"compare",
|
||||
"aggregate",
|
||||
"combine",
|
||||
"collect",
|
||||
"gather",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Route selects the execution mode for a query. If mode is ModeAuto, it
|
||||
// applies heuristics to classify the query. Otherwise, it returns mode unchanged.
|
||||
func Route(mode ToolLoopMode, query string, cfg RouterConfig) ToolLoopMode {
|
||||
if mode != ModeAuto {
|
||||
return mode
|
||||
}
|
||||
|
||||
return classifyQuery(query, cfg)
|
||||
}
|
||||
|
||||
// classifyQuery applies heuristics to determine whether a query is better
|
||||
// served by the sequential ReAct loop or the parallel DAG executor.
|
||||
func classifyQuery(query string, cfg RouterConfig) ToolLoopMode {
|
||||
lower := strings.ToLower(query)
|
||||
words := strings.Fields(lower)
|
||||
|
||||
if len(words) >= cfg.ComplexityThreshold {
|
||||
return ModeDAG
|
||||
}
|
||||
|
||||
for _, kw := range cfg.ParallelKeywords {
|
||||
if strings.Contains(lower, kw) {
|
||||
return ModeDAG
|
||||
}
|
||||
}
|
||||
|
||||
// Count distinct tool-like references (heuristic for multi-tool queries).
|
||||
toolSignals := 0
|
||||
toolIndicators := []string{"search", "read", "write", "execute", "fetch", "list", "create", "delete", "find"}
|
||||
for _, ind := range toolIndicators {
|
||||
if strings.Contains(lower, ind) {
|
||||
toolSignals++
|
||||
}
|
||||
}
|
||||
if toolSignals >= 3 {
|
||||
return ModeDAG
|
||||
}
|
||||
|
||||
return ModeReAct
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue