feat(service): 实现阶段 4 业务逻辑 — Agent、Runner、Assembler、ChatService
This commit is contained in:
@@ -1 +1,110 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"ai-agent-scaffold-go/internal/config"
|
||||
"ai-agent-scaffold-go/internal/llm"
|
||||
"ai-agent-scaffold-go/internal/model"
|
||||
)
|
||||
|
||||
// AssembleAll 从配置表批量组装 Agent
|
||||
func AssembleAll(ctx context.Context, tables map[string]model.AiAgentConfigTable, timeout time.Duration) ([]model.RegisteredAgent, error) {
|
||||
var agents []model.RegisteredAgent
|
||||
for _, table := range tables {
|
||||
agent, err := assembleOne(ctx, table, timeout)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("assemble %s: %w", table.AppName, err)
|
||||
}
|
||||
agents = append(agents, *agent)
|
||||
}
|
||||
return agents, nil
|
||||
}
|
||||
|
||||
// assembleOne 组装单个 Agent 配置
|
||||
func assembleOne(ctx context.Context, table model.AiAgentConfigTable, timeout time.Duration) (*model.RegisteredAgent, error) {
|
||||
apiCfg := table.Module.AiAPI
|
||||
completionsURL := strings.TrimRight(apiCfg.BaseURL, "/") + "/" + strings.TrimLeft(apiCfg.CompletionsPath, "/")
|
||||
|
||||
// 1. 创建 OpenAI 客户端
|
||||
client := llm.NewOpenAIClient(completionsURL, apiCfg.APIKey, table.Module.ChatModel.Model, timeout)
|
||||
|
||||
// 2. 创建 ChatModel(当前无外部工具,后续可扩展)
|
||||
chatModel := llm.NewChatModelAdapter(client, nil)
|
||||
|
||||
// 3. 构建 Agent 映射表
|
||||
agentMap := map[string]model.Agent{}
|
||||
for _, agentCfg := range table.Module.Agents {
|
||||
agent := NewLLMAgent(agentCfg.Name, agentCfg.Instruction, agentCfg.Description, agentCfg.OutputKey, chatModel)
|
||||
agentMap[agentCfg.Name] = agent
|
||||
}
|
||||
|
||||
// 4. 构建 Workflow Agent
|
||||
for _, wfCfg := range table.Module.AgentWorkflows {
|
||||
subs := make([]model.Agent, 0, len(wfCfg.SubAgents))
|
||||
for _, subName := range wfCfg.SubAgents {
|
||||
sub, ok := agentMap[subName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("workflow %q references unknown agent %q", wfCfg.Name, subName)
|
||||
}
|
||||
subs = append(subs, sub)
|
||||
}
|
||||
var wfAgent model.Agent
|
||||
switch wfCfg.Type {
|
||||
case model.WorkflowTypeSequential:
|
||||
wfAgent = NewSequentialAgent(wfCfg.Name, wfCfg.Description, subs)
|
||||
case model.WorkflowTypeParallel:
|
||||
wfAgent = NewParallelAgent(wfCfg.Name, wfCfg.Description, subs)
|
||||
case model.WorkflowTypeLoop:
|
||||
wfAgent = NewLoopAgent(wfCfg.Name, wfCfg.Description, subs, wfCfg.MaxIterations)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown workflow type: %s", wfCfg.Type)
|
||||
}
|
||||
agentMap[wfCfg.Name] = wfAgent
|
||||
}
|
||||
|
||||
// 5. 解析入口 Agent
|
||||
entryName := table.Module.Runner.AgentName
|
||||
entryAgent, ok := agentMap[entryName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("entry agent %q not found", entryName)
|
||||
}
|
||||
|
||||
// 6. 创建 Runner
|
||||
runner := NewRunner(table.AppName, entryAgent)
|
||||
|
||||
return &model.RegisteredAgent{
|
||||
AppName: table.AppName,
|
||||
AgentID: table.Agent.AgentID,
|
||||
AgentName: table.Agent.AgentName,
|
||||
AgentDesc: table.Agent.AgentDesc,
|
||||
Runner: runner,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// LoadAndAssemble 从配置文件路径列表加载并组装所有 Agent
|
||||
func LoadAndAssemble(ctx context.Context, paths []string, timeout time.Duration) ([]model.RegisteredAgent, error) {
|
||||
merged := make(map[string]model.AiAgentConfigTable)
|
||||
for _, raw := range paths {
|
||||
path := strings.TrimSpace(raw)
|
||||
if path == "" {
|
||||
continue
|
||||
}
|
||||
expanded := os.ExpandEnv(path)
|
||||
tables, err := config.LoadAgentTablesFile(expanded)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for name, table := range tables {
|
||||
merged[name] = table
|
||||
}
|
||||
}
|
||||
if len(merged) == 0 {
|
||||
return nil, fmt.Errorf("no agent tables loaded")
|
||||
}
|
||||
return AssembleAll(ctx, merged, timeout)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user