mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-05-15 12:58:01 +02:00
83 lines
3.6 KiB
Go
83 lines
3.6 KiB
Go
package multiagent
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/cloudwego/eino/components/tool"
|
|
)
|
|
|
|
// injectToolNamesOnlyInstruction prepends a compact tool-name-only section into
|
|
// the system instruction so the model can reference current callable names.
|
|
// 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)
|
|
if len(names) == 0 {
|
|
return strings.TrimSpace(instruction)
|
|
}
|
|
hasToolSearch := toolSearchMiddlewareActive
|
|
if !hasToolSearch {
|
|
for _, n := range names {
|
|
if strings.EqualFold(strings.TrimSpace(n), "tool_search") {
|
|
hasToolSearch = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
var sb strings.Builder
|
|
sb.WriteString("以下是当前会话绑定的工具名称索引(仅名称,无参数 JSON Schema)。\n")
|
|
sb.WriteString("说明:若启用了 tool_search,则列表里可能含「非常驻」工具——它们不一定出现在当前轮次下发给模型的工具定义中;在未看到该工具的完整 schema 前,禁止凭名称臆测参数。\n")
|
|
for _, name := range names {
|
|
sb.WriteString("- ")
|
|
sb.WriteString(name)
|
|
sb.WriteByte('\n')
|
|
}
|
|
sb.WriteString("\n使用规则:\n")
|
|
sb.WriteString("1) 上表仅为名称索引,不含参数定义。禁止猜测参数名、类型、枚举取值或是否必填。\n")
|
|
if hasToolSearch {
|
|
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 {
|
|
sb.WriteString("2) 调用具体工具前,请先确认该工具的参数要求(以当前请求中的工具定义为准);不确定时先澄清再调用。\n")
|
|
sb.WriteString("3) 不要臆造不存在的工具名。\n\n")
|
|
}
|
|
if s := strings.TrimSpace(instruction); s != "" {
|
|
sb.WriteString(s)
|
|
}
|
|
return sb.String()
|
|
}
|
|
|
|
func collectToolNames(ctx context.Context, tools []tool.BaseTool) []string {
|
|
if len(tools) == 0 {
|
|
return nil
|
|
}
|
|
seen := make(map[string]struct{}, len(tools))
|
|
out := make([]string, 0, len(tools))
|
|
for _, t := range tools {
|
|
if t == nil {
|
|
continue
|
|
}
|
|
info, err := t.Info(ctx)
|
|
if err != nil || info == nil {
|
|
continue
|
|
}
|
|
name := strings.TrimSpace(info.Name)
|
|
if name == "" {
|
|
continue
|
|
}
|
|
key := strings.ToLower(name)
|
|
if _, ok := seen[key]; ok {
|
|
continue
|
|
}
|
|
seen[key] = struct{}{}
|
|
out = append(out, name)
|
|
}
|
|
return out
|
|
}
|
|
|