Files
GoLoom/backend/internal/service/assembler.go
hhs 8c515a8b3e
All checks were successful
GoLoom CI / Lint (push) Successful in 3m7s
GoLoom CI / Test (push) Successful in 5s
GoLoom CI / Build (push) Successful in 24s
refactor: 重构目录结构,后端代码统一到 backend/ 目录
2026-06-10 15:35:32 +08:00

111 lines
3.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)
}