mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-06-05 22:06:41 +02:00
Add files via upload
This commit is contained in:
+39
-10
@@ -177,6 +177,8 @@ type ChatRequest struct {
|
||||
Role string `json:"role,omitempty"` // 角色名称
|
||||
Attachments []ChatAttachment `json:"attachments,omitempty"`
|
||||
WebShellConnectionID string `json:"webshellConnectionId,omitempty"` // WebShell 管理 - AI 助手:当前选中的连接 ID,仅使用 webshell_* 工具
|
||||
// Orchestration 仅对 /api/multi-agent、/api/multi-agent/stream:deep | plan_execute | supervisor;空则等同 deep。机器人/批量等无请求体时由服务端默认 deep。
|
||||
Orchestration string `json:"orchestration,omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -673,6 +675,7 @@ func (h *AgentHandler) ProcessMessageForRobot(ctx context.Context, conversationI
|
||||
roleTools,
|
||||
progressCallback,
|
||||
h.agentsMarkdownDir,
|
||||
"deep",
|
||||
)
|
||||
if errMA != nil {
|
||||
errMsg := "执行失败: " + errMA.Error()
|
||||
@@ -1616,17 +1619,34 @@ type BatchTaskRequest struct {
|
||||
Title string `json:"title"` // 任务标题(可选)
|
||||
Tasks []string `json:"tasks" binding:"required"` // 任务列表,每行一个任务
|
||||
Role string `json:"role,omitempty"` // 角色名称(可选,空字符串表示默认角色)
|
||||
AgentMode string `json:"agentMode,omitempty"` // single | multi
|
||||
AgentMode string `json:"agentMode,omitempty"` // single | deep | plan_execute | supervisor(旧版 multi 视为 deep)
|
||||
ScheduleMode string `json:"scheduleMode,omitempty"` // manual | cron
|
||||
CronExpr string `json:"cronExpr,omitempty"` // scheduleMode=cron 时必填
|
||||
ExecuteNow bool `json:"executeNow,omitempty"` // 创建后是否立即执行(默认 false)
|
||||
}
|
||||
|
||||
func normalizeBatchQueueAgentMode(mode string) string {
|
||||
if strings.TrimSpace(mode) == "multi" {
|
||||
return "multi"
|
||||
m := strings.TrimSpace(strings.ToLower(mode))
|
||||
if m == "multi" {
|
||||
return "deep"
|
||||
}
|
||||
return "single"
|
||||
if m == "" || m == "single" {
|
||||
return "single"
|
||||
}
|
||||
switch config.NormalizeMultiAgentOrchestration(m) {
|
||||
case "plan_execute":
|
||||
return "plan_execute"
|
||||
case "supervisor":
|
||||
return "supervisor"
|
||||
default:
|
||||
return "deep"
|
||||
}
|
||||
}
|
||||
|
||||
// batchQueueWantsEino 队列是否配置为走 Eino 多代理(不含「空 agentMode + 仅 BatchUseMultiAgent」这种运行期推断)。
|
||||
func batchQueueWantsEino(agentMode string) bool {
|
||||
m := strings.TrimSpace(strings.ToLower(agentMode))
|
||||
return m == "multi" || m == "deep" || m == "plan_execute" || m == "supervisor"
|
||||
}
|
||||
|
||||
func normalizeBatchQueueScheduleMode(mode string) string {
|
||||
@@ -2093,9 +2113,9 @@ func (h *AgentHandler) startBatchQueueExecution(queueID string, scheduled bool)
|
||||
return true, fmt.Errorf("队列状态不允许启动")
|
||||
}
|
||||
|
||||
if queue != nil && queue.AgentMode == "multi" && (h.config == nil || !h.config.MultiAgent.Enabled) {
|
||||
if queue != nil && batchQueueWantsEino(queue.AgentMode) && (h.config == nil || !h.config.MultiAgent.Enabled) {
|
||||
h.unmarkBatchQueueRunning(queueID)
|
||||
err := fmt.Errorf("当前队列配置为多代理,但系统未启用多代理")
|
||||
err := fmt.Errorf("当前队列配置为 Eino 多代理,但系统未启用多代理")
|
||||
if scheduled {
|
||||
h.batchTaskManager.SetLastScheduleError(queueID, err.Error())
|
||||
}
|
||||
@@ -2252,17 +2272,26 @@ func (h *AgentHandler) executeBatchQueue(queueID string) {
|
||||
// 使用队列配置的角色工具列表(如果为空,表示使用所有工具)
|
||||
// 注意:skills不会硬编码注入,但会在系统提示词中提示AI这个角色推荐使用哪些skills
|
||||
useBatchMulti := false
|
||||
if queue.AgentMode == "multi" {
|
||||
useBatchMulti = h.config != nil && h.config.MultiAgent.Enabled
|
||||
batchOrch := "deep"
|
||||
am := strings.TrimSpace(strings.ToLower(queue.AgentMode))
|
||||
if am == "multi" {
|
||||
am = "deep"
|
||||
}
|
||||
if batchQueueWantsEino(queue.AgentMode) && h.config != nil && h.config.MultiAgent.Enabled {
|
||||
useBatchMulti = true
|
||||
batchOrch = config.NormalizeMultiAgentOrchestration(am)
|
||||
} else if queue.AgentMode == "" {
|
||||
// 兼容历史数据:未配置队列代理模式时,沿用旧的系统级开关
|
||||
useBatchMulti = h.config != nil && h.config.MultiAgent.Enabled && h.config.MultiAgent.BatchUseMultiAgent
|
||||
if h.config != nil && h.config.MultiAgent.Enabled && h.config.MultiAgent.BatchUseMultiAgent {
|
||||
useBatchMulti = true
|
||||
batchOrch = "deep"
|
||||
}
|
||||
}
|
||||
var result *agent.AgentLoopResult
|
||||
var resultMA *multiagent.RunResult
|
||||
var runErr error
|
||||
if useBatchMulti {
|
||||
resultMA, runErr = multiagent.RunDeepAgent(ctx, h.config, &h.config.MultiAgent, h.agent, h.logger, conversationID, finalMessage, []agent.ChatMessage{}, roleTools, progressCallback, h.agentsMarkdownDir)
|
||||
resultMA, runErr = multiagent.RunDeepAgent(ctx, h.config, &h.config.MultiAgent, h.agent, h.logger, conversationID, finalMessage, []agent.ChatMessage{}, roleTools, progressCallback, h.agentsMarkdownDir, batchOrch)
|
||||
} else {
|
||||
result, runErr = h.agent.AgentLoopWithProgress(ctx, finalMessage, []agent.ChatMessage{}, conversationID, progressCallback, roleTools, roleSkills)
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ type BatchTaskQueue struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Role string `json:"role,omitempty"` // 角色名称(空字符串表示默认角色)
|
||||
AgentMode string `json:"agentMode"` // single | multi
|
||||
AgentMode string `json:"agentMode"` // single | deep | plan_execute | supervisor
|
||||
ScheduleMode string `json:"scheduleMode"` // manual | cron
|
||||
CronExpr string `json:"cronExpr,omitempty"`
|
||||
NextRunAt *time.Time `json:"nextRunAt,omitempty"`
|
||||
|
||||
@@ -266,11 +266,13 @@ func (h *ConfigHandler) GetConfig(c *gin.Context) {
|
||||
subAgentCount = len(agents.MergeYAMLAndMarkdown(h.config.MultiAgent.SubAgents, load.SubAgents))
|
||||
}
|
||||
multiPub := config.MultiAgentPublic{
|
||||
Enabled: h.config.MultiAgent.Enabled,
|
||||
DefaultMode: h.config.MultiAgent.DefaultMode,
|
||||
RobotUseMultiAgent: h.config.MultiAgent.RobotUseMultiAgent,
|
||||
BatchUseMultiAgent: h.config.MultiAgent.BatchUseMultiAgent,
|
||||
SubAgentCount: subAgentCount,
|
||||
Enabled: h.config.MultiAgent.Enabled,
|
||||
DefaultMode: h.config.MultiAgent.DefaultMode,
|
||||
RobotUseMultiAgent: h.config.MultiAgent.RobotUseMultiAgent,
|
||||
BatchUseMultiAgent: h.config.MultiAgent.BatchUseMultiAgent,
|
||||
SubAgentCount: subAgentCount,
|
||||
Orchestration: config.NormalizeMultiAgentOrchestration(h.config.MultiAgent.Orchestration),
|
||||
PlanExecuteLoopMaxIterations: h.config.MultiAgent.PlanExecuteLoopMaxIterations,
|
||||
}
|
||||
if strings.TrimSpace(multiPub.DefaultMode) == "" {
|
||||
multiPub.DefaultMode = "single"
|
||||
@@ -664,11 +666,15 @@ func (h *ConfigHandler) UpdateConfig(c *gin.Context) {
|
||||
}
|
||||
h.config.MultiAgent.RobotUseMultiAgent = req.MultiAgent.RobotUseMultiAgent
|
||||
h.config.MultiAgent.BatchUseMultiAgent = req.MultiAgent.BatchUseMultiAgent
|
||||
if req.MultiAgent.PlanExecuteLoopMaxIterations != nil {
|
||||
h.config.MultiAgent.PlanExecuteLoopMaxIterations = *req.MultiAgent.PlanExecuteLoopMaxIterations
|
||||
}
|
||||
h.logger.Info("更新多代理配置",
|
||||
zap.Bool("enabled", h.config.MultiAgent.Enabled),
|
||||
zap.String("default_mode", h.config.MultiAgent.DefaultMode),
|
||||
zap.Bool("robot_use_multi_agent", h.config.MultiAgent.RobotUseMultiAgent),
|
||||
zap.Bool("batch_use_multi_agent", h.config.MultiAgent.BatchUseMultiAgent),
|
||||
zap.Int("plan_execute_loop_max_iterations", h.config.MultiAgent.PlanExecuteLoopMaxIterations),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1341,6 +1347,7 @@ func updateMultiAgentConfig(doc *yaml.Node, cfg config.MultiAgentConfig) {
|
||||
setStringInMap(maNode, "default_mode", cfg.DefaultMode)
|
||||
setBoolInMap(maNode, "robot_use_multi_agent", cfg.RobotUseMultiAgent)
|
||||
setBoolInMap(maNode, "batch_use_multi_agent", cfg.BatchUseMultiAgent)
|
||||
setIntInMap(maNode, "plan_execute_loop_max_iterations", cfg.PlanExecuteLoopMaxIterations)
|
||||
}
|
||||
|
||||
func ensureMap(parent *yaml.Node, path ...string) *yaml.Node {
|
||||
|
||||
@@ -38,19 +38,32 @@ func (h *MarkdownAgentsHandler) safeJoin(filename string) (string, error) {
|
||||
return filepath.Join(h.dir, clean), nil
|
||||
}
|
||||
|
||||
// existingOtherOrchestrator 若目录中已有别的主代理文件,返回其文件名;writingBasename 为当前正在写入的文件名时视为同一文件不冲突。
|
||||
// existingOtherOrchestrator 若目录中已有同槽位的其他主代理文件,返回其文件名;writingBasename 为当前正在写入的文件名时不冲突。
|
||||
func existingOtherOrchestrator(dir, writingBasename string) (other string, err error) {
|
||||
load, err := agents.LoadMarkdownAgentsDir(dir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if load.Orchestrator == nil {
|
||||
return "", nil
|
||||
wb := filepath.Base(strings.TrimSpace(writingBasename))
|
||||
switch agents.OrchestratorMarkdownKind(wb) {
|
||||
case "plan_execute":
|
||||
if load.OrchestratorPlanExecute != nil && !strings.EqualFold(load.OrchestratorPlanExecute.Filename, wb) {
|
||||
return load.OrchestratorPlanExecute.Filename, nil
|
||||
}
|
||||
case "supervisor":
|
||||
if load.OrchestratorSupervisor != nil && !strings.EqualFold(load.OrchestratorSupervisor.Filename, wb) {
|
||||
return load.OrchestratorSupervisor.Filename, nil
|
||||
}
|
||||
case "deep":
|
||||
if load.Orchestrator != nil && !strings.EqualFold(load.Orchestrator.Filename, wb) {
|
||||
return load.Orchestrator.Filename, nil
|
||||
}
|
||||
default:
|
||||
if load.Orchestrator != nil && !strings.EqualFold(load.Orchestrator.Filename, wb) {
|
||||
return load.Orchestrator.Filename, nil
|
||||
}
|
||||
}
|
||||
if strings.EqualFold(load.Orchestrator.Filename, writingBasename) {
|
||||
return "", nil
|
||||
}
|
||||
return load.Orchestrator.Filename, nil
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// ListMarkdownAgents GET /api/multi-agent/markdown-agents
|
||||
@@ -101,7 +114,7 @@ func (h *MarkdownAgentsHandler) GetMarkdownAgent(c *gin.Context) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
isOrch := agents.IsOrchestratorMarkdown(filename, agents.FrontMatter{Kind: sub.Kind})
|
||||
isOrch := agents.IsOrchestratorLikeMarkdown(filename, sub.Kind)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"filename": filename,
|
||||
"raw": string(b),
|
||||
@@ -172,7 +185,10 @@ func (h *MarkdownAgentsHandler) CreateMarkdownAgent(c *gin.Context) {
|
||||
MaxIterations: body.MaxIterations,
|
||||
Kind: strings.TrimSpace(body.Kind),
|
||||
}
|
||||
if strings.EqualFold(filepath.Base(path), agents.OrchestratorMarkdownFilename) && sub.Kind == "" {
|
||||
base := filepath.Base(path)
|
||||
if (strings.EqualFold(base, agents.OrchestratorMarkdownFilename) ||
|
||||
strings.EqualFold(base, agents.OrchestratorPlanExecuteMarkdownFilename) ||
|
||||
strings.EqualFold(base, agents.OrchestratorSupervisorMarkdownFilename)) && sub.Kind == "" {
|
||||
sub.Kind = "orchestrator"
|
||||
}
|
||||
if sub.ID == "" {
|
||||
@@ -237,7 +253,9 @@ func (h *MarkdownAgentsHandler) UpdateMarkdownAgent(c *gin.Context) {
|
||||
MaxIterations: body.MaxIterations,
|
||||
Kind: strings.TrimSpace(body.Kind),
|
||||
}
|
||||
if strings.EqualFold(filename, agents.OrchestratorMarkdownFilename) && sub.Kind == "" {
|
||||
if (strings.EqualFold(filename, agents.OrchestratorMarkdownFilename) ||
|
||||
strings.EqualFold(filename, agents.OrchestratorPlanExecuteMarkdownFilename) ||
|
||||
strings.EqualFold(filename, agents.OrchestratorSupervisorMarkdownFilename)) && sub.Kind == "" {
|
||||
sub.Kind = "orchestrator"
|
||||
}
|
||||
if sub.Name == "" {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"cyberstrike-ai/internal/config"
|
||||
"cyberstrike-ai/internal/multiagent"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -139,7 +140,7 @@ func (h *AgentHandler) MultiAgentLoopStream(c *gin.Context) {
|
||||
taskStatus := "completed"
|
||||
defer h.tasks.FinishTask(conversationID, taskStatus)
|
||||
|
||||
sendEvent("progress", "正在启动 Eino DeepAgent...", map[string]interface{}{
|
||||
sendEvent("progress", "正在启动 Eino 多代理...", map[string]interface{}{
|
||||
"conversationId": conversationID,
|
||||
})
|
||||
|
||||
@@ -159,6 +160,7 @@ func (h *AgentHandler) MultiAgentLoopStream(c *gin.Context) {
|
||||
prep.RoleTools,
|
||||
progressCallback,
|
||||
h.agentsMarkdownDir,
|
||||
strings.TrimSpace(req.Orchestration),
|
||||
)
|
||||
|
||||
if runErr != nil {
|
||||
@@ -215,11 +217,15 @@ func (h *AgentHandler) MultiAgentLoopStream(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
effectiveOrch := config.NormalizeMultiAgentOrchestration(h.config.MultiAgent.Orchestration)
|
||||
if o := strings.TrimSpace(req.Orchestration); o != "" {
|
||||
effectiveOrch = config.NormalizeMultiAgentOrchestration(o)
|
||||
}
|
||||
sendEvent("response", result.Response, map[string]interface{}{
|
||||
"mcpExecutionIds": result.MCPExecutionIDs,
|
||||
"conversationId": conversationID,
|
||||
"messageId": assistantMessageID,
|
||||
"agentMode": "eino_deep",
|
||||
"agentMode": "eino_" + effectiveOrch,
|
||||
})
|
||||
sendEvent("done", "", map[string]interface{}{"conversationId": conversationID})
|
||||
}
|
||||
@@ -258,6 +264,7 @@ func (h *AgentHandler) MultiAgentLoop(c *gin.Context) {
|
||||
prep.RoleTools,
|
||||
nil,
|
||||
h.agentsMarkdownDir,
|
||||
strings.TrimSpace(req.Orchestration),
|
||||
)
|
||||
if runErr != nil {
|
||||
h.logger.Error("Eino DeepAgent 执行失败", zap.Error(runErr))
|
||||
|
||||
@@ -405,8 +405,8 @@ func (h *OpenAPIHandler) GetOpenAPISpec(c *gin.Context) {
|
||||
},
|
||||
"agentMode": map[string]interface{}{
|
||||
"type": "string",
|
||||
"description": "代理模式(single | multi)",
|
||||
"enum": []string{"single", "multi"},
|
||||
"description": "代理模式:single(ReAct)| deep | plan_execute | supervisor(Eino);旧值 multi 按 deep",
|
||||
"enum": []string{"single", "deep", "plan_execute", "supervisor", "multi"},
|
||||
},
|
||||
"scheduleMode": map[string]interface{}{
|
||||
"type": "string",
|
||||
@@ -1502,8 +1502,8 @@ func (h *OpenAPIHandler) GetOpenAPISpec(c *gin.Context) {
|
||||
"/api/multi-agent": map[string]interface{}{
|
||||
"post": map[string]interface{}{
|
||||
"tags": []string{"对话交互"},
|
||||
"summary": "发送消息并获取 AI 回复(Eino DeepAgent,非流式)",
|
||||
"description": "与 `POST /api/agent-loop` 请求体相同,但由 **CloudWeGo Eino DeepAgent** 执行多代理编排。**前提**:`multi_agent.enabled: true`(可在设置页或 `config.yaml` 开启);未启用时返回 404 JSON。请求体支持 `webshellConnectionId`(与单代理 WebShell 助手一致)。",
|
||||
"summary": "发送消息并获取 AI 回复(Eino 多代理,非流式)",
|
||||
"description": "与 `POST /api/agent-loop` 请求体相同,但由 **CloudWeGo Eino** 多代理执行。编排由请求体 `orchestration`(`deep` | `plan_execute` | `supervisor`)指定,缺省为 `deep`。**前提**:`multi_agent.enabled: true`;未启用时返回 404 JSON。支持 `webshellConnectionId`。",
|
||||
"operationId": "sendMessageMultiAgent",
|
||||
"requestBody": map[string]interface{}{
|
||||
"required": true,
|
||||
@@ -1528,6 +1528,11 @@ func (h *OpenAPIHandler) GetOpenAPISpec(c *gin.Context) {
|
||||
"type": "string",
|
||||
"description": "WebShell 连接 ID(可选,与 agent-loop 行为一致)",
|
||||
},
|
||||
"orchestration": map[string]interface{}{
|
||||
"type": "string",
|
||||
"description": "Eino 预置编排:deep | plan_execute | supervisor;缺省 deep",
|
||||
"enum": []string{"deep", "plan_execute", "supervisor"},
|
||||
},
|
||||
},
|
||||
"required": []string{"message"},
|
||||
},
|
||||
@@ -1548,8 +1553,8 @@ func (h *OpenAPIHandler) GetOpenAPISpec(c *gin.Context) {
|
||||
"/api/multi-agent/stream": map[string]interface{}{
|
||||
"post": map[string]interface{}{
|
||||
"tags": []string{"对话交互"},
|
||||
"summary": "发送消息并获取 AI 回复(Eino DeepAgent,SSE)",
|
||||
"description": "与 `POST /api/agent-loop/stream` 类似,事件类型兼容;由 Eino DeepAgent 执行。**前提**:`multi_agent.enabled: true`;路由常注册,未启用时仍返回 200 SSE,流内首条为 `type: error` 后接 `done`。支持 `webshellConnectionId`。",
|
||||
"summary": "发送消息并获取 AI 回复(Eino 多代理,SSE)",
|
||||
"description": "与 `POST /api/agent-loop/stream` 类似;由 Eino 多代理执行。`orchestration` 指定 deep / plan_execute / supervisor,缺省 deep。**前提**:`multi_agent.enabled: true`;未启用时 SSE 内首条为 `type: error` 后接 `done`。支持 `webshellConnectionId`。",
|
||||
"operationId": "sendMessageMultiAgentStream",
|
||||
"requestBody": map[string]interface{}{
|
||||
"required": true,
|
||||
@@ -1562,6 +1567,11 @@ func (h *OpenAPIHandler) GetOpenAPISpec(c *gin.Context) {
|
||||
"conversationId": map[string]interface{}{"type": "string"},
|
||||
"role": map[string]interface{}{"type": "string"},
|
||||
"webshellConnectionId": map[string]interface{}{"type": "string"},
|
||||
"orchestration": map[string]interface{}{
|
||||
"type": "string",
|
||||
"description": "deep | plan_execute | supervisor;缺省 deep",
|
||||
"enum": []string{"deep", "plan_execute", "supervisor"},
|
||||
},
|
||||
},
|
||||
"required": []string{"message"},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user