mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-08-15 23:50:32 +02:00
Add files via upload
This commit is contained in:
+199
-167
@@ -5,13 +5,10 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"cyberstrike-ai/internal/agent"
|
||||
@@ -19,16 +16,16 @@ import (
|
||||
"cyberstrike-ai/internal/config"
|
||||
"cyberstrike-ai/internal/database"
|
||||
"cyberstrike-ai/internal/einomcp"
|
||||
"cyberstrike-ai/internal/openai"
|
||||
"cyberstrike-ai/internal/project"
|
||||
"cyberstrike-ai/internal/reasoning"
|
||||
"cyberstrike-ai/internal/security"
|
||||
|
||||
einoopenai "github.com/cloudwego/eino-ext/components/model/openai"
|
||||
"github.com/cloudwego/eino/adk"
|
||||
"github.com/cloudwego/eino/adk/filesystem"
|
||||
"github.com/cloudwego/eino/adk/prebuilt/deep"
|
||||
"github.com/cloudwego/eino/adk/prebuilt/supervisor"
|
||||
"github.com/cloudwego/eino/components/model"
|
||||
"github.com/cloudwego/eino/components/tool"
|
||||
"github.com/cloudwego/eino/compose"
|
||||
"github.com/cloudwego/eino/schema"
|
||||
"go.uber.org/zap"
|
||||
@@ -122,7 +119,7 @@ func RunDeepAgent(
|
||||
})
|
||||
}
|
||||
|
||||
einoLoc, einoSkillMW, einoFSTools, skillsRoot, einoErr := prepareEinoSkills(ctx, appCfg.SkillsDir, ma, logger)
|
||||
agenticLoc, agenticSkillMW, agenticFSTools, agenticSkillsRoot, einoErr := prepareEinoAgenticSkills(ctx, appCfg.SkillsDir, ma, logger)
|
||||
if einoErr != nil {
|
||||
return nil, einoErr
|
||||
}
|
||||
@@ -156,40 +153,28 @@ func RunDeepAgent(
|
||||
toolInvokeNotify := einomcp.NewToolInvokeNotifyHolder()
|
||||
mainDefs := ag.ToolsForRole(roleTools)
|
||||
|
||||
httpClient := &http.Client{
|
||||
Timeout: 30 * time.Minute,
|
||||
Transport: &http.Transport{
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 300 * time.Second,
|
||||
KeepAlive: 300 * time.Second,
|
||||
}).DialContext,
|
||||
MaxIdleConns: 100,
|
||||
MaxIdleConnsPerHost: 10,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 30 * time.Second,
|
||||
ResponseHeaderTimeout: 60 * time.Minute,
|
||||
},
|
||||
baseHTTPClient := newEinoBaseHTTPClient()
|
||||
modelFactory := newEinoOpenAIChatModelFactory(baseHTTPClient, reasoningClient, logger)
|
||||
agenticModelFactory := newEinoOpenAIAgenticChatModelFactory(baseHTTPClient, reasoningClient, logger)
|
||||
agenticModelRetryCfg := newEinoAgenticModelRetryConfig(&ma.EinoMiddleware, logger, "multiagent")
|
||||
agenticModelFailoverCfg, err := newEinoAgenticModelFailoverConfig(ctx, appCfg, &ma.EinoMiddleware, einoModelModeNormal, agenticModelFactory, logger, "multiagent", progress, orchMode, conversationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 若配置为 Claude provider,注入自动桥接 transport,对 Eino 透明走 Anthropic Messages API
|
||||
httpClient = openai.NewEinoHTTPClient(&appCfg.OpenAI, httpClient)
|
||||
openai.AttachSummarizationDiagTransport(httpClient, logger)
|
||||
|
||||
maxCompletionTokens := appCfg.OpenAI.MaxCompletionTokensEffective()
|
||||
baseModelCfg := &einoopenai.ChatModelConfig{
|
||||
APIKey: appCfg.OpenAI.APIKey,
|
||||
BaseURL: strings.TrimSuffix(appCfg.OpenAI.BaseURL, "/"),
|
||||
Model: appCfg.OpenAI.Model,
|
||||
HTTPClient: httpClient,
|
||||
MaxCompletionTokens: &maxCompletionTokens,
|
||||
}
|
||||
reasoning.ApplyToEinoChatModelConfig(baseModelCfg, &appCfg.OpenAI, reasoningClient)
|
||||
logEinoAgenticModelGate(
|
||||
logger,
|
||||
"multiagent",
|
||||
orchMode,
|
||||
evaluateEinoAgenticModelGate(agenticModelGateFactory(agenticModelFactory, appCfg.OpenAI, einoModelModeNormal), einoAgenticRuntimeSupportV0914()),
|
||||
)
|
||||
|
||||
deepMaxIter := agentMaxIterations(appCfg)
|
||||
|
||||
var subAgents []adk.Agent
|
||||
var subAgents []adk.TypedAgent[*schema.AgenticMessage]
|
||||
var supervisorSubAgents []adk.Agent
|
||||
if orchMode != "plan_execute" {
|
||||
subAgents = make([]adk.Agent, 0, len(effectiveSubs))
|
||||
subAgents = make([]adk.TypedAgent[*schema.AgenticMessage], 0, len(effectiveSubs))
|
||||
supervisorSubAgents = make([]adk.Agent, 0, len(effectiveSubs))
|
||||
for _, sub := range effectiveSubs {
|
||||
id := strings.TrimSpace(sub.ID)
|
||||
if id == "" {
|
||||
@@ -218,11 +203,10 @@ func RunDeepAgent(
|
||||
}
|
||||
}
|
||||
|
||||
baseSubModel, err := einoopenai.NewChatModel(ctx, baseModelCfg)
|
||||
subModel, err := agenticModelFactory(ctx, appCfg.OpenAI, einoModelModeNormal)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("子代理 %q ChatModel: %w", id, err)
|
||||
return nil, fmt.Errorf("子代理 %q AgenticModel: %w", id, err)
|
||||
}
|
||||
subModel := newStreamToolCallIndexRepairModel(baseSubModel)
|
||||
|
||||
subDefs := ag.ToolsForRole(roleTools)
|
||||
subTools, err := einomcp.ToolsFromDefinitions(ag, holder, subDefs, recorder, nil, toolInvokeNotify, id)
|
||||
@@ -230,41 +214,41 @@ func RunDeepAgent(
|
||||
return nil, fmt.Errorf("子代理 %q 工具: %w", id, err)
|
||||
}
|
||||
|
||||
subToolsForCfg, subPre, subToolSearchActive, err := prependEinoMiddlewares(ctx, &ma.EinoMiddleware, einoMWSub, subTools, einoLoc, skillsRoot, conversationID, projectID, logger)
|
||||
subToolsForCfg, subPre, subToolSearchActive, err := prependEinoAgenticMiddlewares(ctx, &ma.EinoMiddleware, einoMWSub, subTools, agenticLoc, agenticSkillsRoot, conversationID, projectID, logger)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("子代理 %q eino 中间件: %w", id, err)
|
||||
}
|
||||
|
||||
subMax := resolveMaxIterations(appCfg, sub.MaxIterations)
|
||||
|
||||
subSumMw, err := newEinoSummarizationMiddleware(ctx, subModel, appCfg, &ma.EinoMiddleware, conversationID, db, projectID, logger)
|
||||
subSumMw, err := newEinoAgenticSummarizationMiddleware(ctx, subModel, appCfg, &ma.EinoMiddleware, conversationID, db, projectID, logger)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("子代理 %q summarization 中间件: %w", id, err)
|
||||
return nil, fmt.Errorf("子代理 %q agentic summarization 中间件: %w", id, err)
|
||||
}
|
||||
|
||||
var subHandlers []adk.ChatModelAgentMiddleware
|
||||
var subHandlers []adk.TypedChatModelAgentMiddleware[*schema.AgenticMessage]
|
||||
if len(subPre) > 0 {
|
||||
subHandlers = append(subHandlers, subPre...)
|
||||
}
|
||||
if einoSkillMW != nil {
|
||||
if einoFSTools && einoLoc != nil {
|
||||
subFs, fsErr := subAgentFilesystemMiddleware(ctx, einoLoc, toolInvokeNotify, id, einoExecBegin, einoExecAppendPartial, einoExecRegisterCancel, einoExecUnregisterCancel, einoExecFinish, agentToolTimeoutMinutes(appCfg), agentToolWaitTimeoutSeconds(appCfg), agentShellNoOutputTimeoutSeconds(appCfg), nil)
|
||||
if agenticSkillMW != nil {
|
||||
if agenticFSTools && agenticLoc != nil {
|
||||
subFs, fsErr := subAgentAgenticFilesystemMiddleware(ctx, agenticLoc, toolInvokeNotify, id, einoExecBegin, einoExecAppendPartial, einoExecRegisterCancel, einoExecUnregisterCancel, einoExecFinish, agentToolTimeoutMinutes(appCfg), agentToolWaitTimeoutSeconds(appCfg), agentShellNoOutputTimeoutSeconds(appCfg), nil)
|
||||
if fsErr != nil {
|
||||
return nil, fmt.Errorf("子代理 %q filesystem 中间件: %w", id, fsErr)
|
||||
}
|
||||
subHandlers = append(subHandlers, subFs)
|
||||
}
|
||||
subHandlers = append(subHandlers, einoSkillMW)
|
||||
subHandlers = append(subHandlers, agenticSkillMW)
|
||||
}
|
||||
subHandlers = appendEinoChatModelTailMiddlewares(subHandlers, einoChatModelTailConfig{
|
||||
logger: logger,
|
||||
phase: "sub_agent:" + id,
|
||||
summarization: subSumMw,
|
||||
modelName: appCfg.OpenAI.Model,
|
||||
maxTotalTokens: appCfg.OpenAI.MaxTotalTokens,
|
||||
toolMaxBytes: toolMaxBytesFromMW(&ma.EinoMiddleware),
|
||||
conversationID: conversationID,
|
||||
middlewareConfig: &ma.EinoMiddleware,
|
||||
subHandlers = appendEinoAgenticChatModelTailMiddlewares(subHandlers, einoChatModelTailConfig{
|
||||
logger: logger,
|
||||
phase: "sub_agent:" + id,
|
||||
agenticSummarization: subSumMw,
|
||||
modelName: appCfg.OpenAI.Model,
|
||||
maxTotalTokens: appCfg.OpenAI.MaxTotalTokens,
|
||||
toolMaxBytes: toolMaxBytesFromMW(&ma.EinoMiddleware),
|
||||
conversationID: conversationID,
|
||||
middlewareConfig: &ma.EinoMiddleware,
|
||||
})
|
||||
|
||||
subInstrFinal := project.AppendVisionImageAnalysisIfReady(instr, appCfg.Vision.Ready())
|
||||
@@ -280,11 +264,11 @@ func RunDeepAgent(
|
||||
zap.Bool("tool_search_middleware", subToolSearchActive),
|
||||
)
|
||||
}
|
||||
sa, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
|
||||
sa, err := newEinoAgenticChatModelAgent(ctx, einoAgenticChatModelAgentConfig{
|
||||
Name: id,
|
||||
Description: desc,
|
||||
Instruction: subInstrFinal,
|
||||
GenModelInput: literalInstructionGenModelInput,
|
||||
GenModelInput: literalAgenticInstructionGenModelInput,
|
||||
Model: subModel,
|
||||
ToolsConfig: adk.ToolsConfig{
|
||||
ToolsNodeConfig: compose.ToolsNodeConfig{
|
||||
@@ -299,27 +283,21 @@ func RunDeepAgent(
|
||||
},
|
||||
EmitInternalEvents: true,
|
||||
},
|
||||
MaxIterations: subMax,
|
||||
Handlers: subHandlers,
|
||||
MaxIterations: subMax,
|
||||
Handlers: subHandlers,
|
||||
ModelRetryConfig: agenticModelRetryCfg,
|
||||
ModelFailoverConfig: agenticModelFailoverCfg,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("子代理 %q: %w", id, err)
|
||||
}
|
||||
subAgents = append(subAgents, sa)
|
||||
if adapted := newEinoAgenticMessageAgentAdapter(sa); adapted != nil {
|
||||
supervisorSubAgents = append(supervisorSubAgents, adapted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
baseMainModel, err := einoopenai.NewChatModel(ctx, baseModelCfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("多代理主模型: %w", err)
|
||||
}
|
||||
mainModel := newStreamToolCallIndexRepairModel(baseMainModel)
|
||||
|
||||
mainSumMw, err := newEinoSummarizationMiddleware(ctx, mainModel, appCfg, &ma.EinoMiddleware, conversationID, db, projectID, logger)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("多代理主 summarization 中间件: %w", err)
|
||||
}
|
||||
|
||||
modelFacingTrace := newModelFacingTraceHolder()
|
||||
|
||||
// 与 deep.Config.Name / supervisor 主代理 Name 一致。
|
||||
@@ -346,7 +324,10 @@ func RunDeepAgent(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mainToolsForCfg, mainOrchestratorPre, mainToolSearchActive, err := prependEinoMiddlewares(ctx, &ma.EinoMiddleware, einoMWMain, mainTools, einoLoc, skillsRoot, conversationID, projectID, logger)
|
||||
var mainToolsForCfg []tool.BaseTool
|
||||
var mainToolSearchActive bool
|
||||
var mainAgenticOrchestratorPre []adk.TypedChatModelAgentMiddleware[*schema.AgenticMessage]
|
||||
mainToolsForCfg, mainAgenticOrchestratorPre, mainToolSearchActive, err = prependEinoAgenticMiddlewares(ctx, &ma.EinoMiddleware, einoMWMain, mainTools, agenticLoc, agenticSkillsRoot, conversationID, projectID, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -388,8 +369,8 @@ func RunDeepAgent(
|
||||
|
||||
var deepBackend filesystem.Backend
|
||||
var deepShell filesystem.StreamingShell
|
||||
if einoLoc != nil && einoFSTools {
|
||||
deepBackend = einoLoc
|
||||
if agenticLoc != nil && agenticFSTools {
|
||||
deepBackend = agenticLoc
|
||||
deepShell = &einoStreamingShellWrap{
|
||||
inner: security.NewEinoStreamingShell(),
|
||||
invokeNotify: toolInvokeNotify,
|
||||
@@ -406,8 +387,21 @@ func RunDeepAgent(
|
||||
}
|
||||
}
|
||||
|
||||
var mainModel model.AgenticModel
|
||||
var mainSumMw adk.TypedChatModelAgentMiddleware[*schema.AgenticMessage]
|
||||
if orchMode != "plan_execute" {
|
||||
mainModel, err = agenticModelFactory(ctx, appCfg.OpenAI, einoModelModeNormal)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("多代理主 AgenticModel: %w", err)
|
||||
}
|
||||
mainSumMw, err = newEinoAgenticSummarizationMiddleware(ctx, mainModel, appCfg, &ma.EinoMiddleware, conversationID, db, projectID, logger)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("多代理主 agentic summarization 中间件: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// noNestedTaskMiddleware 必须在最外层(最先拦截),防止 skill 或其他中间件内部触发 task 调用绕过检测。
|
||||
deepHandlers := []adk.ChatModelAgentMiddleware{newNoNestedTaskMiddleware()}
|
||||
deepHandlers := []adk.TypedChatModelAgentMiddleware[*schema.AgenticMessage]{newNoNestedAgenticTaskMiddleware()}
|
||||
var taskBlackboardSupplement string
|
||||
if appCfg.Project.Enabled && db != nil {
|
||||
if pid := strings.TrimSpace(projectID); pid != "" {
|
||||
@@ -416,44 +410,44 @@ func RunDeepAgent(
|
||||
}
|
||||
}
|
||||
}
|
||||
if mw := newTaskContextEnrichMiddleware(runtimeUserMessage, history, ma.SubAgentUserContextMaxRunesEffective(), taskBlackboardSupplement); mw != nil {
|
||||
if mw := newAgenticTaskContextEnrichMiddleware(runtimeUserMessage, history, ma.SubAgentUserContextMaxRunesEffective(), taskBlackboardSupplement); mw != nil {
|
||||
deepHandlers = append(deepHandlers, mw)
|
||||
}
|
||||
if len(mainOrchestratorPre) > 0 {
|
||||
deepHandlers = append(deepHandlers, mainOrchestratorPre...)
|
||||
if len(mainAgenticOrchestratorPre) > 0 {
|
||||
deepHandlers = append(deepHandlers, mainAgenticOrchestratorPre...)
|
||||
}
|
||||
if einoSkillMW != nil {
|
||||
deepHandlers = append(deepHandlers, einoSkillMW)
|
||||
if agenticSkillMW != nil {
|
||||
deepHandlers = append(deepHandlers, agenticSkillMW)
|
||||
}
|
||||
deepHandlers = appendEinoChatModelTailMiddlewares(deepHandlers, einoChatModelTailConfig{
|
||||
logger: logger,
|
||||
phase: "deep_orchestrator",
|
||||
summarization: mainSumMw,
|
||||
modelName: appCfg.OpenAI.Model,
|
||||
maxTotalTokens: appCfg.OpenAI.MaxTotalTokens,
|
||||
toolMaxBytes: toolMaxBytesFromMW(&ma.EinoMiddleware),
|
||||
conversationID: conversationID,
|
||||
trace: modelFacingTrace,
|
||||
middlewareConfig: &ma.EinoMiddleware,
|
||||
deepHandlers = appendEinoAgenticChatModelTailMiddlewares(deepHandlers, einoChatModelTailConfig{
|
||||
logger: logger,
|
||||
phase: "deep_orchestrator",
|
||||
agenticSummarization: mainSumMw,
|
||||
modelName: appCfg.OpenAI.Model,
|
||||
maxTotalTokens: appCfg.OpenAI.MaxTotalTokens,
|
||||
toolMaxBytes: toolMaxBytesFromMW(&ma.EinoMiddleware),
|
||||
conversationID: conversationID,
|
||||
trace: modelFacingTrace,
|
||||
middlewareConfig: &ma.EinoMiddleware,
|
||||
})
|
||||
|
||||
supHandlers := []adk.ChatModelAgentMiddleware{}
|
||||
if len(mainOrchestratorPre) > 0 {
|
||||
supHandlers = append(supHandlers, mainOrchestratorPre...)
|
||||
supHandlers := []adk.TypedChatModelAgentMiddleware[*schema.AgenticMessage]{}
|
||||
if len(mainAgenticOrchestratorPre) > 0 {
|
||||
supHandlers = append(supHandlers, mainAgenticOrchestratorPre...)
|
||||
}
|
||||
if einoSkillMW != nil {
|
||||
supHandlers = append(supHandlers, einoSkillMW)
|
||||
if agenticSkillMW != nil {
|
||||
supHandlers = append(supHandlers, agenticSkillMW)
|
||||
}
|
||||
supHandlers = appendEinoChatModelTailMiddlewares(supHandlers, einoChatModelTailConfig{
|
||||
logger: logger,
|
||||
phase: "supervisor_orchestrator",
|
||||
summarization: mainSumMw,
|
||||
modelName: appCfg.OpenAI.Model,
|
||||
maxTotalTokens: appCfg.OpenAI.MaxTotalTokens,
|
||||
toolMaxBytes: toolMaxBytesFromMW(&ma.EinoMiddleware),
|
||||
conversationID: conversationID,
|
||||
trace: modelFacingTrace,
|
||||
middlewareConfig: &ma.EinoMiddleware,
|
||||
supHandlers = appendEinoAgenticChatModelTailMiddlewares(supHandlers, einoChatModelTailConfig{
|
||||
logger: logger,
|
||||
phase: "supervisor_orchestrator",
|
||||
agenticSummarization: mainSumMw,
|
||||
modelName: appCfg.OpenAI.Model,
|
||||
maxTotalTokens: appCfg.OpenAI.MaxTotalTokens,
|
||||
toolMaxBytes: toolMaxBytesFromMW(&ma.EinoMiddleware),
|
||||
conversationID: conversationID,
|
||||
trace: modelFacingTrace,
|
||||
middlewareConfig: &ma.EinoMiddleware,
|
||||
})
|
||||
|
||||
mainToolsCfg := adk.ToolsConfig{
|
||||
@@ -470,45 +464,42 @@ func RunDeepAgent(
|
||||
EmitInternalEvents: true,
|
||||
}
|
||||
|
||||
deepOutKey, taskGen := deepExtrasFromConfig(ma)
|
||||
deepAgenticOutKey, agenticTaskGen := deepAgenticExtrasFromConfig(ma)
|
||||
|
||||
var da adk.Agent
|
||||
switch orchMode {
|
||||
case "plan_execute":
|
||||
plannerModelCfg := &einoopenai.ChatModelConfig{
|
||||
APIKey: appCfg.OpenAI.APIKey,
|
||||
BaseURL: strings.TrimSuffix(appCfg.OpenAI.BaseURL, "/"),
|
||||
Model: appCfg.OpenAI.Model,
|
||||
HTTPClient: httpClient,
|
||||
MaxCompletionTokens: &maxCompletionTokens,
|
||||
}
|
||||
reasoning.ApplyPlanExecutePlannerModelConfig(plannerModelCfg, &appCfg.OpenAI)
|
||||
basePEMainModel, perr := einoopenai.NewChatModel(ctx, plannerModelCfg)
|
||||
peMainModel, perr := modelFactory(ctx, appCfg.OpenAI, einoModelModePlanner)
|
||||
if perr != nil {
|
||||
return nil, fmt.Errorf("plan_execute 规划模型: %w", perr)
|
||||
}
|
||||
peMainModel := newStreamToolCallIndexRepairModel(basePEMainModel)
|
||||
if logger != nil {
|
||||
logger.Info("plan_execute: planner/replanner 使用无 reasoning 的独立 ChatModel(ToolChoiceForced 兼容)",
|
||||
zap.String("model", appCfg.OpenAI.Model),
|
||||
)
|
||||
}
|
||||
baseExecModel, perr := einoopenai.NewChatModel(ctx, baseModelCfg)
|
||||
execModel, perr := modelFactory(ctx, appCfg.OpenAI, einoModelModeNormal)
|
||||
if perr != nil {
|
||||
return nil, fmt.Errorf("plan_execute 执行器模型: %w", perr)
|
||||
}
|
||||
execModel := newStreamToolCallIndexRepairModel(baseExecModel)
|
||||
// 构建 filesystem 中间件(与 Deep sub-agent 一致)
|
||||
var peFsMw adk.ChatModelAgentMiddleware
|
||||
if einoSkillMW != nil && einoFSTools && einoLoc != nil {
|
||||
peFsMw, err = subAgentFilesystemMiddleware(ctx, einoLoc, toolInvokeNotify, "executor", einoExecBegin, einoExecAppendPartial, einoExecRegisterCancel, einoExecUnregisterCancel, einoExecFinish, agentToolTimeoutMinutes(appCfg), agentToolWaitTimeoutSeconds(appCfg), agentShellNoOutputTimeoutSeconds(appCfg), nil)
|
||||
agenticExecModel, perr := agenticModelFactory(ctx, appCfg.OpenAI, einoModelModeNormal)
|
||||
if perr != nil {
|
||||
return nil, fmt.Errorf("plan_execute 执行器 AgenticModel: %w", perr)
|
||||
}
|
||||
planRewriteSumMw, perr := newEinoSummarizationMiddleware(ctx, execModel, appCfg, &ma.EinoMiddleware, conversationID, db, projectID, logger)
|
||||
if perr != nil {
|
||||
return nil, fmt.Errorf("plan_execute planner/replanner summarization: %w", perr)
|
||||
}
|
||||
var peFsMw adk.TypedChatModelAgentMiddleware[*schema.AgenticMessage]
|
||||
if agenticSkillMW != nil && agenticFSTools && agenticLoc != nil {
|
||||
peFsMw, err = subAgentAgenticFilesystemMiddleware(ctx, agenticLoc, toolInvokeNotify, "executor", einoExecBegin, einoExecAppendPartial, einoExecRegisterCancel, einoExecUnregisterCancel, einoExecFinish, agentToolTimeoutMinutes(appCfg), agentToolWaitTimeoutSeconds(appCfg), agentShellNoOutputTimeoutSeconds(appCfg), nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("plan_execute filesystem 中间件: %w", err)
|
||||
return nil, fmt.Errorf("plan_execute agentic filesystem 中间件: %w", err)
|
||||
}
|
||||
}
|
||||
peRoot, perr := NewPlanExecuteRoot(ctx, &PlanExecuteRootArgs{
|
||||
MainToolCallingModel: peMainModel,
|
||||
ExecModel: execModel,
|
||||
AgenticExecModel: agenticExecModel,
|
||||
OrchInstruction: orchInstruction,
|
||||
ToolsCfg: mainToolsCfg,
|
||||
ExecMaxIter: deepMaxIter,
|
||||
@@ -520,15 +511,15 @@ func RunDeepAgent(
|
||||
ProjectID: projectID,
|
||||
Logger: logger,
|
||||
ModelName: appCfg.OpenAI.Model,
|
||||
// 与 Deep/Supervisor 主代理同源:patch / reduction / toolsearch / plantask(见 buildPlanExecuteExecutorHandlers)。
|
||||
ExecPreMiddlewares: mainOrchestratorPre,
|
||||
SkillMiddleware: einoSkillMW,
|
||||
FilesystemMiddleware: peFsMw,
|
||||
ModelFacingTrace: modelFacingTrace,
|
||||
// 与 Deep/Supervisor 主代理同源:typed patch / reduction / toolsearch / plantask(见 buildPlanExecuteAgenticExecutorHandlers)。
|
||||
AgenticExecPreMiddlewares: mainAgenticOrchestratorPre,
|
||||
AgenticSkillMiddleware: agenticSkillMW,
|
||||
AgenticFilesystemMiddleware: peFsMw,
|
||||
ModelFacingTrace: modelFacingTrace,
|
||||
PlannerReplannerRewriteHandlers: appendEinoChatModelTailMiddlewares(nil, einoChatModelTailConfig{
|
||||
logger: logger,
|
||||
phase: "plan_execute_planner_replanner",
|
||||
summarization: mainSumMw,
|
||||
summarization: planRewriteSumMw,
|
||||
modelName: appCfg.OpenAI.Model,
|
||||
maxTotalTokens: appCfg.OpenAI.MaxTotalTokens,
|
||||
toolMaxBytes: toolMaxBytesFromMW(&ma.EinoMiddleware),
|
||||
@@ -536,40 +527,44 @@ func RunDeepAgent(
|
||||
skipTrace: true,
|
||||
middlewareConfig: &ma.EinoMiddleware,
|
||||
}),
|
||||
AgenticModelRetryConfig: agenticModelRetryCfg,
|
||||
AgenticModelFailoverConfig: agenticModelFailoverCfg,
|
||||
})
|
||||
if perr != nil {
|
||||
return nil, perr
|
||||
}
|
||||
da = peRoot
|
||||
case "supervisor":
|
||||
supCfg := &adk.ChatModelAgentConfig{
|
||||
Name: orchestratorName,
|
||||
Description: orchDescription,
|
||||
Instruction: supInstr,
|
||||
GenModelInput: literalInstructionGenModelInput,
|
||||
Model: mainModel,
|
||||
ToolsConfig: mainToolsCfg,
|
||||
MaxIterations: deepMaxIter,
|
||||
Handlers: supHandlers,
|
||||
Exit: &adk.ExitTool{},
|
||||
supCfg := einoAgenticChatModelAgentConfig{
|
||||
Name: orchestratorName,
|
||||
Description: orchDescription,
|
||||
Instruction: supInstr,
|
||||
GenModelInput: literalAgenticInstructionGenModelInput,
|
||||
Model: mainModel,
|
||||
ToolsConfig: mainToolsCfg,
|
||||
MaxIterations: deepMaxIter,
|
||||
Handlers: supHandlers,
|
||||
Exit: &adk.ExitTool{},
|
||||
ModelRetryConfig: agenticModelRetryCfg,
|
||||
ModelFailoverConfig: agenticModelFailoverCfg,
|
||||
}
|
||||
if deepOutKey != "" {
|
||||
supCfg.OutputKey = deepOutKey
|
||||
if deepAgenticOutKey != "" {
|
||||
supCfg.OutputKey = deepAgenticOutKey
|
||||
}
|
||||
superChat, serr := adk.NewChatModelAgent(ctx, supCfg)
|
||||
superChat, serr := newEinoAgenticChatModelAgentAdapter(ctx, supCfg)
|
||||
if serr != nil {
|
||||
return nil, fmt.Errorf("supervisor 主代理: %w", serr)
|
||||
return nil, fmt.Errorf("supervisor agentic 主代理: %w", serr)
|
||||
}
|
||||
supRoot, serr := supervisor.New(ctx, &supervisor.Config{
|
||||
Supervisor: superChat,
|
||||
SubAgents: subAgents,
|
||||
SubAgents: supervisorSubAgents,
|
||||
})
|
||||
if serr != nil {
|
||||
return nil, fmt.Errorf("supervisor.New: %w", serr)
|
||||
}
|
||||
da = supRoot
|
||||
default:
|
||||
dcfg := &deep.Config{
|
||||
dcfg := &deep.TypedConfig[*schema.AgenticMessage]{
|
||||
Name: orchestratorName,
|
||||
Description: orchDescription,
|
||||
ChatModel: mainModel,
|
||||
@@ -582,18 +577,20 @@ func RunDeepAgent(
|
||||
StreamingShell: deepShell,
|
||||
Handlers: deepHandlers,
|
||||
ToolsConfig: mainToolsCfg,
|
||||
ModelRetryConfig: agenticModelRetryCfg,
|
||||
ModelFailoverConfig: agenticModelFailoverCfg,
|
||||
}
|
||||
if deepOutKey != "" {
|
||||
dcfg.OutputKey = deepOutKey
|
||||
if deepAgenticOutKey != "" {
|
||||
dcfg.OutputKey = deepAgenticOutKey
|
||||
}
|
||||
if taskGen != nil {
|
||||
dcfg.TaskToolDescriptionGenerator = taskGen
|
||||
if agenticTaskGen != nil {
|
||||
dcfg.TaskToolDescriptionGenerator = agenticTaskGen
|
||||
}
|
||||
dDeep, derr := deep.New(ctx, dcfg)
|
||||
dDeep, derr := deep.NewTyped[*schema.AgenticMessage](ctx, dcfg)
|
||||
if derr != nil {
|
||||
return nil, fmt.Errorf("deep.New: %w", derr)
|
||||
return nil, fmt.Errorf("deep.NewTyped[AgenticMessage]: %w", derr)
|
||||
}
|
||||
da = dDeep
|
||||
da = newEinoAgenticMessageAgentAdapter(dDeep)
|
||||
}
|
||||
|
||||
baseMsgs := historyToMessages(history, appCfg, &ma.EinoMiddleware)
|
||||
@@ -625,8 +622,8 @@ func RunDeepAgent(
|
||||
StreamsMainAssistant: streamsMainAssistant,
|
||||
EinoRoleTag: einoRoleTag,
|
||||
CheckpointDir: ma.EinoMiddleware.CheckpointDir,
|
||||
RunRetryMaxAttempts: ma.EinoMiddleware.RunRetryMaxAttempts,
|
||||
RunRetryMaxBackoffSec: ma.EinoMiddleware.RunRetryMaxBackoffSec,
|
||||
RunRetryMaxAttempts: RunRetryMaxAttemptsFromConfig(&ma.EinoMiddleware),
|
||||
RunRetryMaxBackoffSec: int(einoRunRetryMaxBackoffFromConfig(&ma.EinoMiddleware).Seconds()),
|
||||
McpIDsMu: &mcpIDsMu,
|
||||
McpIDs: &mcpIDs,
|
||||
FilesystemMonitorAgent: ag,
|
||||
@@ -639,6 +636,7 @@ func RunDeepAgent(
|
||||
MaxTotalTokens: appCfg.OpenAI.MaxTotalTokens,
|
||||
ToolMaxBytes: toolMaxBytesFromMW(&ma.EinoMiddleware),
|
||||
ModelName: appCfg.OpenAI.Model,
|
||||
MiddlewareConfig: &ma.EinoMiddleware,
|
||||
EmptyResponseMessage: "(Eino multi-agent orchestration completed but no assistant text was captured. Check process details or logs.) " +
|
||||
"(Eino 多代理编排已完成,但未捕获到助手文本输出。请查看过程详情或日志。)",
|
||||
}, baseMsgs)
|
||||
@@ -819,7 +817,9 @@ func toolCallStableID(tc schema.ToolCall) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// toolCallDisplayName 避免前端「未知工具」:DeepAgent 内置 task 等可能延迟写入 function.name。
|
||||
// toolCallDisplayName returns the visible tool name once the model stream has
|
||||
// produced a concrete function name. Anonymous stream fragments are filtered
|
||||
// before progress emission instead of being guessed as task calls.
|
||||
func toolCallDisplayName(tc schema.ToolCall) string {
|
||||
if n := strings.TrimSpace(tc.Function.Name); n != "" {
|
||||
return n
|
||||
@@ -827,7 +827,7 @@ func toolCallDisplayName(tc schema.ToolCall) string {
|
||||
if n := strings.TrimSpace(tc.Type); n != "" && !strings.EqualFold(n, "function") {
|
||||
return n
|
||||
}
|
||||
return "task"
|
||||
return ""
|
||||
}
|
||||
|
||||
// toolCallsSignatureFlush 用于去重键;无 id/index 时用占位 pos,避免流末帧缺 id 时整条工具事件丢失。
|
||||
@@ -835,13 +835,24 @@ func toolCallsSignatureFlush(msg *schema.Message) string {
|
||||
if msg == nil || len(msg.ToolCalls) == 0 {
|
||||
return ""
|
||||
}
|
||||
parts := make([]string, 0, len(msg.ToolCalls))
|
||||
for i, tc := range msg.ToolCalls {
|
||||
visible := filterVisibleToolCallsForProgress(msg.ToolCalls)
|
||||
if len(visible) == 0 {
|
||||
return ""
|
||||
}
|
||||
parts := make([]string, 0, len(visible))
|
||||
for i, tc := range visible {
|
||||
id := toolCallStableID(tc)
|
||||
if id == "" {
|
||||
id = fmt.Sprintf("pos:%d", i)
|
||||
}
|
||||
parts = append(parts, id+"|"+toolCallDisplayName(tc))
|
||||
name := toolCallDisplayName(tc)
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
parts = append(parts, id+"|"+name)
|
||||
}
|
||||
if len(parts) == 0 {
|
||||
return ""
|
||||
}
|
||||
sort.Strings(parts)
|
||||
return strings.Join(parts, ";")
|
||||
@@ -853,8 +864,9 @@ func toolCallsRichSignature(msg *schema.Message) string {
|
||||
if base == "" {
|
||||
return ""
|
||||
}
|
||||
parts := make([]string, 0, len(msg.ToolCalls))
|
||||
for _, tc := range msg.ToolCalls {
|
||||
visible := filterVisibleToolCallsForProgress(msg.ToolCalls)
|
||||
parts := make([]string, 0, len(visible))
|
||||
for _, tc := range visible {
|
||||
id := toolCallStableID(tc)
|
||||
arg := tc.Function.Arguments
|
||||
if len(arg) > 240 {
|
||||
@@ -909,6 +921,10 @@ func emitToolCallsFromMessage(
|
||||
if msg == nil || len(msg.ToolCalls) == 0 || progress == nil {
|
||||
return
|
||||
}
|
||||
visibleToolCalls := filterVisibleToolCallsForProgress(msg.ToolCalls)
|
||||
if len(visibleToolCalls) == 0 {
|
||||
return
|
||||
}
|
||||
if subAgentToolStep == nil {
|
||||
subAgentToolStep = make(map[string]int)
|
||||
}
|
||||
@@ -945,14 +961,14 @@ func emitToolCallsFromMessage(
|
||||
if isSubToolRound {
|
||||
role = "sub"
|
||||
}
|
||||
progress("tool_calls_detected", fmt.Sprintf("检测到 %d 个工具调用", len(msg.ToolCalls)), map[string]interface{}{
|
||||
"count": len(msg.ToolCalls),
|
||||
progress("tool_calls_detected", fmt.Sprintf("检测到 %d 个工具调用", len(visibleToolCalls)), map[string]interface{}{
|
||||
"count": len(visibleToolCalls),
|
||||
"conversationId": conversationID,
|
||||
"source": "eino",
|
||||
"einoAgent": agentName,
|
||||
"einoRole": role,
|
||||
})
|
||||
for idx, tc := range msg.ToolCalls {
|
||||
for idx, tc := range visibleToolCalls {
|
||||
argStr := strings.TrimSpace(tc.Function.Arguments)
|
||||
if argStr == "" && len(tc.Extra) > 0 {
|
||||
if b, mErr := json.Marshal(tc.Extra); mErr == nil {
|
||||
@@ -973,8 +989,7 @@ func emitToolCallsFromMessage(
|
||||
// with an earlier batch in the same agent run.
|
||||
toolCallID = fmt.Sprintf("eino-stream-%d-%d", fallbackToolCallSequence.Add(1), *tc.Index)
|
||||
}
|
||||
// Record pending tool calls for later tool_result correlation / recovery flushing.
|
||||
// We intentionally record even for unknown tools to avoid "running" badge getting stuck.
|
||||
// Record visible pending tool calls for later tool_result correlation / recovery flushing.
|
||||
if markPending != nil && toolCallID != "" {
|
||||
markPending(toolCallPendingInfo{
|
||||
ToolCallID: toolCallID,
|
||||
@@ -989,7 +1004,7 @@ func emitToolCallsFromMessage(
|
||||
"argumentsObj": argsObj,
|
||||
"toolCallId": toolCallID,
|
||||
"index": idx + 1,
|
||||
"total": len(msg.ToolCalls),
|
||||
"total": len(visibleToolCalls),
|
||||
"conversationId": conversationID,
|
||||
"source": "eino",
|
||||
"einoAgent": agentName,
|
||||
@@ -998,6 +1013,23 @@ func emitToolCallsFromMessage(
|
||||
}
|
||||
}
|
||||
|
||||
func filterVisibleToolCallsForProgress(calls []schema.ToolCall) []schema.ToolCall {
|
||||
if len(calls) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]schema.ToolCall, 0, len(calls))
|
||||
for _, tc := range calls {
|
||||
if _, ok := modelOutputRecoveryFromToolCall(tc); ok {
|
||||
continue
|
||||
}
|
||||
if toolCallDisplayName(tc) == "" {
|
||||
continue
|
||||
}
|
||||
out = append(out, tc)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// dedupeRepeatedParagraphs 去掉完全相同的连续/重复段落,缓解多代理各自复述同一列表。
|
||||
func dedupeRepeatedParagraphs(s string, minLen int) string {
|
||||
if s == "" || minLen <= 0 {
|
||||
|
||||
Reference in New Issue
Block a user