Add files via upload

This commit is contained in:
公明
2026-05-13 12:05:12 +08:00
committed by GitHub
parent 4a11ba8f14
commit 762f778e1e
4 changed files with 40 additions and 49 deletions
+11 -8
View File
@@ -161,6 +161,8 @@ func buildReductionMiddleware(ctx context.Context, mw config.MultiAgentEinoMiddl
} }
// prependEinoMiddlewares returns handlers to prepend (outermost first) and optionally replaces tools when tool_search is used. // prependEinoMiddlewares returns handlers to prepend (outermost first) and optionally replaces tools when tool_search is used.
// toolSearchActive is true when the toolsearch middleware was mounted (dynamic tools split off); callers should pass this to
// injectToolNamesOnlyInstruction — tool_search is not part of the pre-middleware tools list, so name-scanning alone cannot detect it.
func prependEinoMiddlewares( func prependEinoMiddlewares(
ctx context.Context, ctx context.Context,
mw *config.MultiAgentEinoMiddlewareConfig, mw *config.MultiAgentEinoMiddlewareConfig,
@@ -170,16 +172,16 @@ func prependEinoMiddlewares(
skillsRoot string, skillsRoot string,
conversationID string, conversationID string,
logger *zap.Logger, logger *zap.Logger,
) (outTools []tool.BaseTool, extraHandlers []adk.ChatModelAgentMiddleware, err error) { ) (outTools []tool.BaseTool, extraHandlers []adk.ChatModelAgentMiddleware, toolSearchActive bool, err error) {
if mw == nil { if mw == nil {
return tools, nil, nil return tools, nil, false, nil
} }
outTools = tools outTools = tools
if mw.PatchToolCallsEffective() { if mw.PatchToolCallsEffective() {
patchMW, perr := patchtoolcalls.New(ctx, &patchtoolcalls.Config{}) patchMW, perr := patchtoolcalls.New(ctx, &patchtoolcalls.Config{})
if perr != nil { if perr != nil {
return nil, nil, fmt.Errorf("patchtoolcalls: %w", perr) return nil, nil, false, fmt.Errorf("patchtoolcalls: %w", perr)
} }
extraHandlers = append(extraHandlers, patchMW) extraHandlers = append(extraHandlers, patchMW)
} }
@@ -190,7 +192,7 @@ func prependEinoMiddlewares(
} else { } else {
redMW, rerr := buildReductionMiddleware(ctx, *mw, conversationID, einoLoc, logger) redMW, rerr := buildReductionMiddleware(ctx, *mw, conversationID, einoLoc, logger)
if rerr != nil { if rerr != nil {
return nil, nil, rerr return nil, nil, false, rerr
} }
extraHandlers = append(extraHandlers, redMW) extraHandlers = append(extraHandlers, redMW)
} }
@@ -209,10 +211,11 @@ func prependEinoMiddlewares(
if split && len(dynamic) > 0 { if split && len(dynamic) > 0 {
ts, terr := toolsearch.New(ctx, &toolsearch.Config{DynamicTools: dynamic}) ts, terr := toolsearch.New(ctx, &toolsearch.Config{DynamicTools: dynamic})
if terr != nil { if terr != nil {
return nil, nil, fmt.Errorf("toolsearch: %w", terr) return nil, nil, false, fmt.Errorf("toolsearch: %w", terr)
} }
extraHandlers = append(extraHandlers, ts) extraHandlers = append(extraHandlers, ts)
outTools = static outTools = static
toolSearchActive = true
if logger != nil { if logger != nil {
logger.Info("eino middleware: tool_search enabled", logger.Info("eino middleware: tool_search enabled",
zap.Int("static_tools", len(static)), zap.Int("static_tools", len(static)),
@@ -233,12 +236,12 @@ func prependEinoMiddlewares(
} }
baseDir := filepath.Join(skillsRoot, rel, sanitizeEinoPathSegment(conversationID)) baseDir := filepath.Join(skillsRoot, rel, sanitizeEinoPathSegment(conversationID))
if mk := os.MkdirAll(baseDir, 0o755); mk != nil { if mk := os.MkdirAll(baseDir, 0o755); mk != nil {
return nil, nil, fmt.Errorf("plantask mkdir: %w", mk) return nil, nil, toolSearchActive, fmt.Errorf("plantask mkdir: %w", mk)
} }
ptBE := &localPlantaskBackend{Local: einoLoc} ptBE := &localPlantaskBackend{Local: einoLoc}
pt, perr := plantask.New(ctx, &plantask.Config{Backend: ptBE, BaseDir: baseDir}) pt, perr := plantask.New(ctx, &plantask.Config{Backend: ptBE, BaseDir: baseDir})
if perr != nil { if perr != nil {
return nil, nil, fmt.Errorf("plantask: %w", perr) return nil, nil, toolSearchActive, fmt.Errorf("plantask: %w", perr)
} }
extraHandlers = append(extraHandlers, pt) extraHandlers = append(extraHandlers, pt)
if logger != nil { if logger != nil {
@@ -247,7 +250,7 @@ func prependEinoMiddlewares(
} }
} }
return outTools, extraHandlers, nil return outTools, extraHandlers, toolSearchActive, nil
} }
func deepExtrasFromConfig(ma *config.MultiAgentConfig) (outputKey string, retry *adk.ModelRetryConfig, taskDesc func(context.Context, []adk.Agent) (string, error)) { func deepExtrasFromConfig(ma *config.MultiAgentConfig) (outputKey string, retry *adk.ModelRetryConfig, taskDesc func(context.Context, []adk.Agent) (string, error)) {
+3 -10
View File
@@ -96,7 +96,7 @@ func RunEinoSingleChatModelAgent(
return nil, err return nil, err
} }
mainToolsForCfg, mainOrchestratorPre, err := prependEinoMiddlewares(ctx, &ma.EinoMiddleware, einoMWMain, mainTools, einoLoc, skillsRoot, conversationID, logger) mainToolsForCfg, mainOrchestratorPre, singleToolSearchActive, err := prependEinoMiddlewares(ctx, &ma.EinoMiddleware, einoMWMain, mainTools, einoLoc, skillsRoot, conversationID, logger)
if err != nil { if err != nil {
return nil, fmt.Errorf("eino single eino 中间件: %w", err) return nil, fmt.Errorf("eino single eino 中间件: %w", err)
} }
@@ -178,22 +178,15 @@ func RunEinoSingleChatModelAgent(
}, },
EmitInternalEvents: true, EmitInternalEvents: true,
} }
ins := injectToolNamesOnlyInstruction(ctx, ag.EinoSingleAgentSystemInstruction(), mainTools) ins := injectToolNamesOnlyInstruction(ctx, ag.EinoSingleAgentSystemInstruction(), mainTools, singleToolSearchActive)
if logger != nil { if logger != nil {
names := collectToolNames(ctx, mainTools) names := collectToolNames(ctx, mainTools)
mountedNames := collectToolNames(ctx, mainToolsForCfg) mountedNames := collectToolNames(ctx, mainToolsForCfg)
hasToolSearch := false
for _, n := range names {
if strings.EqualFold(strings.TrimSpace(n), "tool_search") {
hasToolSearch = true
break
}
}
logger.Info("eino tool-name injection", logger.Info("eino tool-name injection",
zap.String("scope", "eino_single"), zap.String("scope", "eino_single"),
zap.Int("tool_names", len(names)), zap.Int("tool_names", len(names)),
zap.Int("mounted_tool_names", len(mountedNames)), zap.Int("mounted_tool_names", len(mountedNames)),
zap.Bool("has_tool_search", hasToolSearch), zap.Bool("tool_search_middleware", singleToolSearchActive),
) )
} }
+20 -11
View File
@@ -9,34 +9,43 @@ import (
// injectToolNamesOnlyInstruction prepends a compact tool-name-only section into // injectToolNamesOnlyInstruction prepends a compact tool-name-only section into
// the system instruction so the model can reference current callable names. // the system instruction so the model can reference current callable names.
func injectToolNamesOnlyInstruction(ctx context.Context, instruction string, tools []tool.BaseTool) string { // toolSearchMiddlewareActive must be true when prependEinoMiddlewares mounted toolsearch (dynamic tools); do not infer this
// by scanning tool names — tool_search is injected by middleware and is usually absent from the pre-split tools list.
func injectToolNamesOnlyInstruction(ctx context.Context, instruction string, tools []tool.BaseTool, toolSearchMiddlewareActive bool) string {
names := collectToolNames(ctx, tools) names := collectToolNames(ctx, tools)
if len(names) == 0 { if len(names) == 0 {
return strings.TrimSpace(instruction) return strings.TrimSpace(instruction)
} }
hasToolSearch := false hasToolSearch := toolSearchMiddlewareActive
for _, n := range names { if !hasToolSearch {
if strings.EqualFold(strings.TrimSpace(n), "tool_search") { for _, n := range names {
hasToolSearch = true if strings.EqualFold(strings.TrimSpace(n), "tool_search") {
break hasToolSearch = true
break
}
} }
} }
var sb strings.Builder var sb strings.Builder
sb.WriteString("以下是当前会话中可调用的工具名称列表(仅名称,无参数定义):\n") sb.WriteString("以下是当前会话绑定的工具名称索引(仅名称,无参数 JSON Schema)。\n")
sb.WriteString("说明:若启用了 tool_search,则列表里可能含「非常驻」工具——它们不一定出现在当前轮次下发给模型的工具定义中;在未看到该工具的完整 schema 前,禁止凭名称臆测参数。\n")
for _, name := range names { for _, name := range names {
sb.WriteString("- ") sb.WriteString("- ")
sb.WriteString(name) sb.WriteString(name)
sb.WriteByte('\n') sb.WriteByte('\n')
} }
sb.WriteString("\n使用规则:\n") sb.WriteString("\n使用规则:\n")
sb.WriteString("1) 上仅为名称列表,不含参数定义。\n") sb.WriteString("1) 上仅为名称索引,不含参数定义。禁止猜测参数名、类型、枚举取值或是否必填。\n")
if hasToolSearch { if hasToolSearch {
sb.WriteString("2) 在调用具体工具前,应先使用 tool_search 查看工具详情与参数要求,再发起调用。\n") sb.WriteString("【强制 / 最高优先级】本会话已启用 tool_search(动态工具池)。凡名称索引里出现、但你在「当前请求所附 tools 定义」中看不到其完整参数 schema 的工具,一律必须先调用 tool_search;为省 token 或赶进度而跳过 tool_search、直接调用业务工具,属于明确禁止的错误流程。\n")
sb.WriteString("2) 默认策略:只要对目标工具的参数定义有任何不确定,就先 tool_search;宁可多一次 tool_search,也不要在未见 schema 时盲调业务工具。\n")
sb.WriteString("3) 调用顺序:先 tool_search(唯一必填参数 regex_pattern:按工具名匹配的正则,如子串 nuclei 或 ^exact_tool_name$)→ 在后续轮次确认目标工具已出现在 tools 列表且已阅读其 schema → 再发起对该工具的真实调用。\n")
sb.WriteString("4) tool_search 的返回仅为匹配到的工具名列表;schema 在解锁后的下一轮才会下发。禁止在 schema 未出现时编造 JSON 参数。\n")
sb.WriteString("5) 不要臆造不存在的工具名。\n\n")
} else { } else {
sb.WriteString("2) 调用具体工具前,请先确认该工具的参数要求;不确定时先澄清再调用。\n") sb.WriteString("2) 调用具体工具前,请先确认该工具的参数要求(以当前请求中的工具定义为准);不确定时先澄清再调用。\n")
sb.WriteString("3) 不要臆造不存在的工具名。\n\n")
} }
sb.WriteString("3) 不要臆造不存在的工具名。\n\n")
if s := strings.TrimSpace(instruction); s != "" { if s := strings.TrimSpace(instruction); s != "" {
sb.WriteString(s) sb.WriteString(s)
} }
+6 -20
View File
@@ -223,7 +223,7 @@ func RunDeepAgent(
return nil, fmt.Errorf("子代理 %q 工具: %w", id, err) return nil, fmt.Errorf("子代理 %q 工具: %w", id, err)
} }
subToolsForCfg, subPre, err := prependEinoMiddlewares(ctx, &ma.EinoMiddleware, einoMWSub, subTools, einoLoc, skillsRoot, conversationID, logger) subToolsForCfg, subPre, subToolSearchActive, err := prependEinoMiddlewares(ctx, &ma.EinoMiddleware, einoMWSub, subTools, einoLoc, skillsRoot, conversationID, logger)
if err != nil { if err != nil {
return nil, fmt.Errorf("子代理 %q eino 中间件: %w", id, err) return nil, fmt.Errorf("子代理 %q eino 中间件: %w", id, err)
} }
@@ -260,23 +260,16 @@ func RunDeepAgent(
subHandlers = append(subHandlers, teleMw) subHandlers = append(subHandlers, teleMw)
} }
subInstrFinal := injectToolNamesOnlyInstruction(ctx, instr, subTools) subInstrFinal := injectToolNamesOnlyInstruction(ctx, instr, subTools, subToolSearchActive)
if logger != nil { if logger != nil {
subNames := collectToolNames(ctx, subTools) subNames := collectToolNames(ctx, subTools)
mountedNames := collectToolNames(ctx, subToolsForCfg) mountedNames := collectToolNames(ctx, subToolsForCfg)
hasToolSearch := false
for _, n := range subNames {
if strings.EqualFold(strings.TrimSpace(n), "tool_search") {
hasToolSearch = true
break
}
}
logger.Info("eino tool-name injection", logger.Info("eino tool-name injection",
zap.String("scope", "sub_agent"), zap.String("scope", "sub_agent"),
zap.String("agent", id), zap.String("agent", id),
zap.Int("tool_names", len(subNames)), zap.Int("tool_names", len(subNames)),
zap.Int("mounted_tool_names", len(mountedNames)), zap.Int("mounted_tool_names", len(mountedNames)),
zap.Bool("has_tool_search", hasToolSearch), zap.Bool("tool_search_middleware", subToolSearchActive),
) )
} }
sa, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{ sa, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
@@ -341,28 +334,21 @@ func RunDeepAgent(
if err != nil { if err != nil {
return nil, err return nil, err
} }
mainToolsForCfg, mainOrchestratorPre, err := prependEinoMiddlewares(ctx, &ma.EinoMiddleware, einoMWMain, mainTools, einoLoc, skillsRoot, conversationID, logger) mainToolsForCfg, mainOrchestratorPre, mainToolSearchActive, err := prependEinoMiddlewares(ctx, &ma.EinoMiddleware, einoMWMain, mainTools, einoLoc, skillsRoot, conversationID, logger)
if err != nil { if err != nil {
return nil, err return nil, err
} }
orchInstruction = injectToolNamesOnlyInstruction(ctx, orchInstruction, mainTools) orchInstruction = injectToolNamesOnlyInstruction(ctx, orchInstruction, mainTools, mainToolSearchActive)
if logger != nil { if logger != nil {
mainNames := collectToolNames(ctx, mainTools) mainNames := collectToolNames(ctx, mainTools)
mountedNames := collectToolNames(ctx, mainToolsForCfg) mountedNames := collectToolNames(ctx, mainToolsForCfg)
hasToolSearch := false
for _, n := range mainNames {
if strings.EqualFold(strings.TrimSpace(n), "tool_search") {
hasToolSearch = true
break
}
}
logger.Info("eino tool-name injection", logger.Info("eino tool-name injection",
zap.String("scope", "orchestrator"), zap.String("scope", "orchestrator"),
zap.String("orchestration", orchMode), zap.String("orchestration", orchMode),
zap.Int("tool_names", len(mainNames)), zap.Int("tool_names", len(mainNames)),
zap.Int("mounted_tool_names", len(mountedNames)), zap.Int("mounted_tool_names", len(mountedNames)),
zap.Bool("has_tool_search", hasToolSearch), zap.Bool("tool_search_middleware", mainToolSearchActive),
) )
} }