From 4d81d2be97c24441171fbdf290995b475a34fbe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=AC=E6=98=8E?= <83812544+Ed1s0nZ@users.noreply.github.com> Date: Mon, 6 Jul 2026 16:26:38 +0800 Subject: [PATCH] Add files via upload --- internal/multiagent/plan_execute_executor.go | 25 ++++++++++++++++---- internal/multiagent/runner.go | 9 +++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/internal/multiagent/plan_execute_executor.go b/internal/multiagent/plan_execute_executor.go index 170a99b5..5bb133ae 100644 --- a/internal/multiagent/plan_execute_executor.go +++ b/internal/multiagent/plan_execute_executor.go @@ -8,7 +8,15 @@ import ( "github.com/cloudwego/eino/adk/prebuilt/planexecute" ) -// newPlanExecuteExecutor 与 planexecute.NewExecutor 行为一致,但可为执行器注入 Handlers(例如 summarization 中间件)。 +// newPlanExecuteExecutor builds the Plan-Execute Executor as an Eino ChatModelAgent. +// +// Eino's planexecute.Config accepts any adk.Agent as Executor; this implementation +// keeps the official Executor contract (Plan/UserInput/ExecutedSteps session keys +// and ExecutedStepSessionKey output) while using ChatModelAgentConfig.Handlers so +// the executor can run the same ADK middleware stack as Deep/Supervisor. As of +// Eino v0.9.12/v0.10.0-alpha.10, planexecute.NewExecutor still does not expose a +// Handlers field, so this custom Executor is the best-practice extension point +// that preserves middleware without forking the whole planexecute loop. func newPlanExecuteExecutor(ctx context.Context, cfg *planexecute.ExecutorConfig, handlers []adk.ChatModelAgentMiddleware) (adk.Agent, error) { if cfg == nil { return nil, fmt.Errorf("plan_execute: ExecutorConfig 为空") @@ -25,18 +33,27 @@ func newPlanExecuteExecutor(ctx context.Context, cfg *planexecute.ExecutorConfig if !ok { return nil, fmt.Errorf("plan_execute executor: session value %q missing (possible session corruption)", planexecute.PlanSessionKey) } - plan_ := plan.(planexecute.Plan) + plan_, ok := plan.(planexecute.Plan) + if !ok { + return nil, fmt.Errorf("plan_execute executor: session value %q has invalid type %T", planexecute.PlanSessionKey, plan) + } userInput, ok := adk.GetSessionValue(ctx, planexecute.UserInputSessionKey) if !ok { return nil, fmt.Errorf("plan_execute executor: session value %q missing (possible session corruption)", planexecute.UserInputSessionKey) } - userInput_ := userInput.([]adk.Message) + userInput_, ok := userInput.([]adk.Message) + if !ok { + return nil, fmt.Errorf("plan_execute executor: session value %q has invalid type %T", planexecute.UserInputSessionKey, userInput) + } var executedSteps_ []planexecute.ExecutedStep executedStep, ok := adk.GetSessionValue(ctx, planexecute.ExecutedStepsSessionKey) if ok { - executedSteps_ = executedStep.([]planexecute.ExecutedStep) + executedSteps_, ok = executedStep.([]planexecute.ExecutedStep) + if !ok { + return nil, fmt.Errorf("plan_execute executor: session value %q has invalid type %T", planexecute.ExecutedStepsSessionKey, executedStep) + } } in := &planexecute.ExecutionContext{ diff --git a/internal/multiagent/runner.go b/internal/multiagent/runner.go index e1927dee..3823a7c7 100644 --- a/internal/multiagent/runner.go +++ b/internal/multiagent/runner.go @@ -100,6 +100,14 @@ func RunDeepAgent( if orchMode == "supervisor" && len(effectiveSubs) == 0 { return nil, fmt.Errorf("multi_agent.orchestration=supervisor 时需至少配置一个子代理(sub_agents 或 agents 目录 Markdown)") } + if orchMode == "supervisor" && len(effectiveSubs) == 1 && progress != nil { + progress("progress", "Supervisor 是专家路由模式;当前仅 1 个子代理,专家路由空间有限,仍会继续执行。", map[string]interface{}{ + "conversationId": conversationID, + "source": "eino", + "orchestration": orchMode, + "kind": "supervisor_boundary_hint", + }) + } einoLoc, einoSkillMW, einoFSTools, skillsRoot, einoErr := prepareEinoSkills(ctx, appCfg.SkillsDir, ma, logger) if einoErr != nil { @@ -351,6 +359,7 @@ func RunDeepAgent( sb.WriteString("\n- ") sb.WriteString(sa.Name(ctx)) } + sb.WriteString("\n\nSupervisor 是专家路由模式:仅当任务确实需要不同专家分工时才 transfer;简单查询、单步工具调用或无需专业分流的任务由你直接完成。避免在同一子代理之间反复 transfer;除非有新的、具体的补充目标。专家返回后,你必须自行汇总、裁剪、校验证据,再用 exit 交付最终答案。") sb.WriteString("\n\n当你已完成用户目标或需要将最终结论交付用户时,使用 exit 工具结束。") supInstr = sb.String() }