mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-06-04 13:28:03 +02:00
158 lines
4.9 KiB
Go
158 lines
4.9 KiB
Go
package multiagent
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"cyberstrike-ai/internal/config"
|
|
|
|
"github.com/cloudwego/eino-ext/components/model/openai"
|
|
"github.com/cloudwego/eino/adk"
|
|
"github.com/cloudwego/eino/adk/prebuilt/planexecute"
|
|
"github.com/cloudwego/eino/components/model"
|
|
"github.com/cloudwego/eino/schema"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// PlanExecuteRootArgs 构建 Eino adk/prebuilt/planexecute 根 Agent 所需参数。
|
|
type PlanExecuteRootArgs struct {
|
|
MainToolCallingModel *openai.ChatModel
|
|
ExecModel *openai.ChatModel
|
|
OrchInstruction string
|
|
ToolsCfg adk.ToolsConfig
|
|
ExecMaxIter int
|
|
LoopMaxIter int
|
|
// AppCfg / Logger 非空时为 Executor 挂载与 Deep/Supervisor 一致的 Eino summarization 中间件。
|
|
AppCfg *config.Config
|
|
Logger *zap.Logger
|
|
}
|
|
|
|
// NewPlanExecuteRoot 返回 plan → execute → replan 预置编排根节点(与 Deep / Supervisor 并列)。
|
|
func NewPlanExecuteRoot(ctx context.Context, a *PlanExecuteRootArgs) (adk.ResumableAgent, error) {
|
|
if a == nil {
|
|
return nil, fmt.Errorf("plan_execute: args 为空")
|
|
}
|
|
if a.MainToolCallingModel == nil || a.ExecModel == nil {
|
|
return nil, fmt.Errorf("plan_execute: 模型为空")
|
|
}
|
|
tcm, ok := interface{}(a.MainToolCallingModel).(model.ToolCallingChatModel)
|
|
if !ok {
|
|
return nil, fmt.Errorf("plan_execute: 主模型需实现 ToolCallingChatModel")
|
|
}
|
|
planner, err := planexecute.NewPlanner(ctx, &planexecute.PlannerConfig{
|
|
ToolCallingChatModel: tcm,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("plan_execute planner: %w", err)
|
|
}
|
|
replanner, err := planexecute.NewReplanner(ctx, &planexecute.ReplannerConfig{
|
|
ChatModel: tcm,
|
|
GenInputFn: planExecuteReplannerGenInput,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("plan_execute replanner: %w", err)
|
|
}
|
|
var execHandlers []adk.ChatModelAgentMiddleware
|
|
if a.AppCfg != nil {
|
|
sumMw, sumErr := newEinoSummarizationMiddleware(ctx, a.ExecModel, a.AppCfg, a.Logger)
|
|
if sumErr != nil {
|
|
return nil, fmt.Errorf("plan_execute executor summarization: %w", sumErr)
|
|
}
|
|
execHandlers = append(execHandlers, sumMw)
|
|
}
|
|
executor, err := newPlanExecuteExecutor(ctx, &planexecute.ExecutorConfig{
|
|
Model: a.ExecModel,
|
|
ToolsConfig: a.ToolsCfg,
|
|
MaxIterations: a.ExecMaxIter,
|
|
GenInputFn: planExecuteExecutorGenInput(a.OrchInstruction),
|
|
}, execHandlers)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("plan_execute executor: %w", err)
|
|
}
|
|
loopMax := a.LoopMaxIter
|
|
if loopMax <= 0 {
|
|
loopMax = 10
|
|
}
|
|
return planexecute.New(ctx, &planexecute.Config{
|
|
Planner: planner,
|
|
Executor: executor,
|
|
Replanner: replanner,
|
|
MaxIterations: loopMax,
|
|
})
|
|
}
|
|
|
|
func planExecuteExecutorGenInput(orchInstruction string) planexecute.GenModelInputFn {
|
|
oi := strings.TrimSpace(orchInstruction)
|
|
return func(ctx context.Context, in *planexecute.ExecutionContext) ([]adk.Message, error) {
|
|
planContent, err := in.Plan.MarshalJSON()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
userMsgs, err := planexecute.ExecutorPrompt.Format(ctx, map[string]any{
|
|
"input": planExecuteFormatInput(in.UserInput),
|
|
"plan": string(planContent),
|
|
"executed_steps": planExecuteFormatExecutedSteps(in.ExecutedSteps),
|
|
"step": in.Plan.FirstStep(),
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if oi != "" {
|
|
userMsgs = append([]adk.Message{schema.SystemMessage(oi)}, userMsgs...)
|
|
}
|
|
return userMsgs, nil
|
|
}
|
|
}
|
|
|
|
func planExecuteFormatInput(input []adk.Message) string {
|
|
var sb strings.Builder
|
|
for _, msg := range input {
|
|
sb.WriteString(msg.Content)
|
|
sb.WriteString("\n")
|
|
}
|
|
return sb.String()
|
|
}
|
|
|
|
func planExecuteFormatExecutedSteps(results []planexecute.ExecutedStep) string {
|
|
capped := capPlanExecuteExecutedSteps(results)
|
|
var sb strings.Builder
|
|
for _, result := range capped {
|
|
sb.WriteString(fmt.Sprintf("Step: %s\nResult: %s\n\n", result.Step, result.Result))
|
|
}
|
|
return sb.String()
|
|
}
|
|
|
|
// planExecuteReplannerGenInput 与 Eino 默认 Replanner 输入一致,但 executed_steps 经 cap 后再写入 prompt。
|
|
func planExecuteReplannerGenInput(ctx context.Context, in *planexecute.ExecutionContext) ([]adk.Message, error) {
|
|
planContent, err := in.Plan.MarshalJSON()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return planexecute.ReplannerPrompt.Format(ctx, map[string]any{
|
|
"plan": string(planContent),
|
|
"input": planExecuteFormatInput(in.UserInput),
|
|
"executed_steps": planExecuteFormatExecutedSteps(in.ExecutedSteps),
|
|
"plan_tool": planexecute.PlanToolInfo.Name,
|
|
"respond_tool": planexecute.RespondToolInfo.Name,
|
|
})
|
|
}
|
|
|
|
// planExecuteStreamsMainAssistant 将规划/执行/重规划各阶段助手流式输出映射到主对话区。
|
|
func planExecuteStreamsMainAssistant(agent string) bool {
|
|
if agent == "" {
|
|
return true
|
|
}
|
|
switch agent {
|
|
case "planner", "executor", "replanner", "execute_replan", "plan_execute_replan":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func planExecuteEinoRoleTag(agent string) string {
|
|
_ = agent
|
|
return "orchestrator"
|
|
}
|