update
This commit is contained in:
1 parent
0690285147
commit
5618cd2738
154 files changed
+5021
-3161
No files matched your search
@@ -116,6 +116,7 @@ type Agent struct {
|
||||
evolver *evolver.EvolverEngine
|
||||
callback EventCallback
|
||||
totalTokens int
|
||||
cache map[string]string
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
@@ -130,7 +131,7 @@ func NewAgent(client llm.Client, registry *tools.Registry, cfg *Config, sc *safe
|
||||
if cfg.MaxContextTokens <= 0 {
|
||||
cfg.MaxContextTokens = 8000
|
||||
}
|
||||
return &Agent{llm: client, registry: registry, config: cfg, safetyCtl: sc, evolver: ev}
|
||||
return &Agent{llm: client, registry: registry, config: cfg, safetyCtl: sc, evolver: ev, cache: make(map[string]string)}
|
||||
}
|
||||
|
||||
// SetCallback 注入事件回调 // Logan.Li
|
||||
@@ -293,6 +294,14 @@ func (a *Agent) processToolCalls(ctx context.Context, calls []llm.ToolCall, hist
|
||||
continue
|
||||
}
|
||||
|
||||
if msg := a.suppressBroadKubernetesGet(tc.Function.Name, args, history); msg != "" {
|
||||
a.emit(Event{Type: EventToolCall, Phase: PhaseToolRunning, Round: round, ToolName: tc.Function.Name, ToolArgs: args, Risk: tools.RiskSafe.Display(), Summary: "工具执行中"})
|
||||
a.emit(Event{Type: EventToolResult, Phase: PhaseObserving, Round: round, ToolName: tc.Function.Name,
|
||||
Output: msg, Success: true, Duration: "0s", OutputSize: len(msg)})
|
||||
obs = append(obs, llm.Message{Role: "tool", ToolCallID: tc.ID, Name: tc.Function.Name, Content: msg})
|
||||
continue
|
||||
}
|
||||
|
||||
risk := a.toolRisk(tool, args)
|
||||
a.emit(Event{Type: EventStatus, Phase: PhaseSafety, Round: round, ToolName: tc.Function.Name, ToolArgs: args, Risk: risk.Display(), Summary: "安全检查"})
|
||||
|
||||
@@ -315,6 +324,16 @@ func (a *Agent) processToolCalls(ctx context.Context, calls []llm.ToolCall, hist
|
||||
|
||||
// 执行
|
||||
a.emit(Event{Type: EventToolCall, Phase: PhaseToolRunning, Round: round, ToolName: tc.Function.Name, ToolArgs: args, Risk: risk.Display(), Summary: "工具执行中"})
|
||||
if cached, ok := a.cachedToolResult(tc.Function.Name, args); ok {
|
||||
a.emit(Event{Type: EventToolResult, Phase: PhaseObserving, Round: round, ToolName: tc.Function.Name,
|
||||
Output: cached, Success: true, Duration: "0s", OutputSize: len(cached)})
|
||||
records = append(records, evolver.ToolCallRecord{
|
||||
ToolName: tc.Function.Name, Args: args, Output: cached,
|
||||
Duration: 0, Success: true, RiskLevel: int(risk),
|
||||
})
|
||||
obs = append(obs, llm.Message{Role: "tool", ToolCallID: tc.ID, Name: tc.Function.Name, Content: cached})
|
||||
continue
|
||||
}
|
||||
toolStart := time.Now()
|
||||
toolCtx, cancel := context.WithTimeout(ctx, a.config.ToolTimeout)
|
||||
result, execErr := tool.Execute(toolCtx, args)
|
||||
@@ -346,6 +365,7 @@ func (a *Agent) processToolCalls(ctx context.Context, calls []llm.ToolCall, hist
|
||||
|
||||
a.emit(Event{Type: EventToolResult, Phase: PhaseObserving, Round: round, ToolName: tc.Function.Name,
|
||||
Output: output, Success: success, Duration: dur.Truncate(time.Millisecond).String(), OutputSize: len(output)})
|
||||
a.storeToolResult(tc.Function.Name, args, output, success)
|
||||
|
||||
records = append(records, evolver.ToolCallRecord{
|
||||
ToolName: tc.Function.Name, Args: args, Output: output,
|
||||
@@ -369,6 +389,82 @@ func (a *Agent) processToolCalls(ctx context.Context, calls []llm.ToolCall, hist
|
||||
return obs, records, failInc
|
||||
}
|
||||
|
||||
// cachedToolResult 返回同一任务内只读工具的缓存结果,避免 LLM 重复消耗 token。 // Logan.Li
|
||||
func (a *Agent) cachedToolResult(toolName string, args map[string]interface{}) (string, bool) {
|
||||
if !cacheableTool(toolName) {
|
||||
return "", false
|
||||
}
|
||||
key := toolCacheKey(toolName, args)
|
||||
if key == "" {
|
||||
return "", false
|
||||
}
|
||||
if out, ok := a.cache[key]; ok {
|
||||
return "已复用本轮任务缓存结果;不要重复查询同一对象,请基于首次结果总结,或改用更具体的 name/status_filter/pod 参数补查。\n\n" + out, true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// storeToolResult 缓存成功的只读工具输出。 // Logan.Li
|
||||
func (a *Agent) storeToolResult(toolName string, args map[string]interface{}, output string, success bool) {
|
||||
if !success || !cacheableTool(toolName) {
|
||||
return
|
||||
}
|
||||
key := toolCacheKey(toolName, args)
|
||||
if key == "" {
|
||||
return
|
||||
}
|
||||
a.cache[key] = output
|
||||
}
|
||||
|
||||
func cacheableTool(toolName string) bool {
|
||||
switch toolName {
|
||||
case "kubernetes_diagnose", "kubernetes_get", "pod_logs", "aiops_asset_search", "aiops_alert_list", "aiops_metric_summary":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func toolCacheKey(toolName string, args map[string]interface{}) string {
|
||||
b, err := json.Marshal(args)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return toolName + ":" + string(b)
|
||||
}
|
||||
|
||||
// suppressBroadKubernetesGet 在聚合诊断后拦截全量 K8s 查询,迫使模型按异常对象下钻。 // Logan.Li
|
||||
func (a *Agent) suppressBroadKubernetesGet(toolName string, args map[string]interface{}, history map[string]int) string {
|
||||
if a.config.Mode != "kubernetes" && a.config.Mode != "k8s" {
|
||||
return ""
|
||||
}
|
||||
if toolName != "kubernetes_get" || !hasDiagnoseHistory(history) {
|
||||
return ""
|
||||
}
|
||||
resource := strings.ToLower(strings.TrimSpace(fmt.Sprint(args["resource"])))
|
||||
name := strings.TrimSpace(fmt.Sprint(args["name"]))
|
||||
labelSelector := strings.TrimSpace(fmt.Sprint(args["label_selector"]))
|
||||
statusFilter := strings.TrimSpace(fmt.Sprint(args["status_filter"]))
|
||||
if name != "" && name != "<nil>" || labelSelector != "" && labelSelector != "<nil>" || statusFilter != "" && statusFilter != "<nil>" {
|
||||
return ""
|
||||
}
|
||||
switch resource {
|
||||
case "pods", "pod", "deployments", "deployment", "deploy", "services", "service", "svc", "nodes", "node", "events", "event":
|
||||
return "已拦截全量 Kubernetes 查询:本轮已经执行过 kubernetes_diagnose,里面包含异常 Workload、异常 Pod、Top Events 和节点摘要。请不要再次拉全量资源;如需补证据,请按诊断结果中的具体对象调用 kubernetes_get(name/status_filter) 或 pod_logs(pod)。"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func hasDiagnoseHistory(history map[string]int) bool {
|
||||
for key, count := range history {
|
||||
if count > 0 && strings.HasPrefix(key, "kubernetes_diagnose:") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// triggerEvolve 异步触发进化(带超时 + panic 恢复,绝不阻塞主循环) // Logan.Li
|
||||
func (a *Agent) triggerEvolve(ctx context.Context, query, serverHost, answer string, records []evolver.ToolCallRecord, success bool, start time.Time) {
|
||||
if a.evolver == nil {
|
||||
@@ -522,34 +618,46 @@ func (a *Agent) trimMessages(msgs []llm.Message) []llm.Message {
|
||||
return ensureToolMessageIntegrity(result)
|
||||
}
|
||||
|
||||
// ensureToolMessageIntegrity 确保 tool 消息前面有对应 assistant(tool_calls) // Logan.Li
|
||||
// ensureToolMessageIntegrity 确保 assistant(tool_calls) 后紧跟完整 tool 结果组 // Logan.Li
|
||||
func ensureToolMessageIntegrity(msgs []llm.Message) []llm.Message {
|
||||
validIDs := make(map[string]bool)
|
||||
for _, m := range msgs {
|
||||
if m.Role == "assistant" {
|
||||
for _, tc := range m.ToolCalls {
|
||||
validIDs[tc.ID] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
out := make([]llm.Message, 0, len(msgs))
|
||||
for i, m := range msgs {
|
||||
for i := 0; i < len(msgs); i++ {
|
||||
m := msgs[i]
|
||||
if m.Role == "tool" {
|
||||
if !validIDs[m.ToolCallID] || i == 0 || msgs[i-1].Role != "assistant" {
|
||||
continue
|
||||
}
|
||||
matched := false
|
||||
for _, tc := range msgs[i-1].ToolCalls {
|
||||
if tc.ID == m.ToolCallID {
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !matched {
|
||||
continue
|
||||
continue
|
||||
}
|
||||
if m.Role != "assistant" || len(m.ToolCalls) == 0 {
|
||||
out = append(out, m)
|
||||
continue
|
||||
}
|
||||
|
||||
required := make(map[string]bool, len(m.ToolCalls))
|
||||
for _, tc := range m.ToolCalls {
|
||||
if tc.ID != "" {
|
||||
required[tc.ID] = true
|
||||
}
|
||||
}
|
||||
out = append(out, m)
|
||||
if len(required) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
seen := make(map[string]bool, len(required))
|
||||
tools := make([]llm.Message, 0, len(required))
|
||||
j := i + 1
|
||||
for ; j < len(msgs) && msgs[j].Role == "tool"; j++ {
|
||||
tm := msgs[j]
|
||||
if !required[tm.ToolCallID] || seen[tm.ToolCallID] {
|
||||
continue
|
||||
}
|
||||
seen[tm.ToolCallID] = true
|
||||
tools = append(tools, tm)
|
||||
}
|
||||
|
||||
if len(seen) == len(required) {
|
||||
out = append(out, m)
|
||||
out = append(out, tools...)
|
||||
}
|
||||
i = j - 1
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user