# CallBack 定义方法

## 方法说明

### 1、BeforeModelCallBack
type BeforeModelCallback func(ctx agent.CallbackContext, llmRequest *model.LLMRequest) (*model.LLMResponse, error)

BeforeModelCallback that is called before sending a request to the model.
If it returns non-nil LLMResponse or error, the actual model call is skipped
and the returned response/error is used.

### 2、AfterModelCallback
type AfterModelCallback func(ctx agent.CallbackContext, llmResponse *model.LLMResponse, llmResponseError error) (*model.LLMResponse, error)

AfterModelCallback that is called after receiving a response from the model.
If it returns non-nil LLMResponse or error, the actual model response/error
is replaced with the returned response/error.

### 3、BeforeToolCallback
type BeforeToolCallback func(ctx tool.Context, tool tool.Tool, args map[string]any) (map[string]any, error)

BeforeToolCallback is executed before a tool's Run method.
Callbacks are executed in the order they are provided.
If a callback returns a non-nil result or an error:
- execution of remaining callbacks stops
- the actual tool call is skipped
- the returned result is used as the tool result

To modify tool arguments and still run the tool,
update args in place and return (nil, nil).

### 4、AfterToolCallback
type AfterToolCallback func(ctx tool.Context, tool tool.Tool, args, result map[string]any, err error) (map[string]any, error)
AfterToolCallback is a function type executed after a tool's Run method has completed, 
regardless of whether the tool returned a result or an error.

Callbacks are executed in the order they are provided.
If a callback returns a non-nil result or an error:
- execution of remaining callbacks stops
- the returned result and/or error is used as the final tool output


## callback方法示例

### 1、BeforeModelCallBack 代码示例
何时触发: 在LlmAgent流程中向 LLM 发送请求之前调用。
用途: 允许检查和修改发送给 LLM 的请求。用例包括添加动态指令、基于状态注入少量示例、修改模型配置、实现防护机制 (如亵渎过滤器) 或实现请求级缓存。
返回值效果: 如果回调返回 nil,LLM 继续其正常工作流程。如果回调返回 LlmResponse 对象,则跳过对 LLM 的调用。返回的 LlmResponse 直接使用,就像它来自模型一样。这对于实现防护栏或缓存非常强大。

```go
func onBeforeModel(ctx agent.CallbackContext, req *model.LLMRequest) (*model.LLMResponse, error) {
    log.Printf("[Callback] BeforeModel triggered for agent %q.", ctx.AgentName())

    // Modification Example: Add a prefix to the system instruction.
    if req.Config.SystemInstruction != nil {
        prefix := "[Modified by Callback] "
        // This is a simplified example; production code might need deeper checks.
        if len(req.Config.SystemInstruction.Parts) > 0 {
            req.Config.SystemInstruction.Parts[0].Text = prefix + req.Config.SystemInstruction.Parts[0].Text
        } else {
            req.Config.SystemInstruction.Parts = append(req.Config.SystemInstruction.Parts, &genai.Part{Text: prefix})
        }
        log.Printf("[Callback] Modified system instruction.")
    }

    // Skip Example: Check for "BLOCK" in the user's prompt.
    for _, content := range req.Contents {
        for _, part := range content.Parts {
            if strings.Contains(strings.ToUpper(part.Text), "BLOCK") {
                log.Println("[Callback] 'BLOCK' keyword found. Skipping LLM call.")
                return &model.LLMResponse{
                    Content: &genai.Content{
                        Parts: []*genai.Part{{Text: "LLM call was blocked by before_model_callback."}},
                        Role:  "model",
                    },
                }, nil
            }
        }
    }

    log.Println("[Callback] Proceeding with LLM call.")
    return nil, nil
}

rootAgent, err := veagent.New(&veagent.Config{
		Config: llmagent.Config{
			Name:        "...",
			Description: "...",
			Instruction: "...",
			BeforeModelCallbacks:[]llmagent.BeforeModelCallback{
				onBeforeModel,
			},
		},
	})
```

### 2、AfterModelCallBack 代码示例
何时触发: 在从 LLM 接收到响应 (LlmResponse) 之后,在调用智能体进一步处理之前调用。
用途: 允许检查或修改原始 LLM 响应。用例包括:
记录模型输出,
重新格式化响应,
审查模型生成的敏感信息,
从 LLM 响应中解析结构化数据并将其存储在callback_context.state中
或处理特定错误代码。

```go
func onAfterModel(ctx agent.CallbackContext, resp *model.LLMResponse, respErr error) (*model.LLMResponse, error) {
    log.Printf("[Callback] AfterModel triggered for agent %q.", ctx.AgentName())
    if respErr != nil {
        log.Printf("[Callback] Model returned an error: %v. Passing it through.", respErr)
        return nil, respErr
    }
    if resp == nil || resp.Content == nil || len(resp.Content.Parts) == 0 {
        log.Println("[Callback] Response is nil or has no parts, nothing to process.")
        return nil, nil
    }
    // Check for function calls and pass them through without modification.
    if resp.Content.Parts[0].FunctionCall != nil {
        log.Println("[Callback] Response is a function call. No modification.")
        return nil, nil
    }

    originalText := resp.Content.Parts[0].Text

    // Use a case-insensitive regex with word boundaries to find "joke".
    re := regexp.MustCompile(`(?i)\bjoke\b`)
    if !re.MatchString(originalText) {
        log.Println("[Callback] 'joke' not found. Passing original response through.")
        return nil, nil
    }

    log.Println("[Callback] 'joke' found. Modifying response.")
    // Use a replacer function to handle capitalization.
    modifiedText := re.ReplaceAllStringFunc(originalText, func(s string) string {
        if strings.ToUpper(s) == "JOKE" {
            if s == "Joke" {
                return "Funny story"
            }
            return "funny story"
        }
        return s // Should not be reached with this regex, but it's safe.
    })

    resp.Content.Parts[0].Text = modifiedText
    return resp, nil
}
```

### 3、BeforeToolCallback 代码示例

何时触发: 在调用特定工具的run_async方法之前,在 LLM 为其生成函数调用之后调用。

用途: 允许检查和修改工具参数,在执行前执行授权检查,记录工具使用尝试,或实现工具级缓存。

返回值效果:

如果回调返回 nil,工具方法将使用(可能修改的)args 执行。
如果返回map,工具方法将被跳过。返回的字典直接用作工具调用的结果。这对于缓存或覆盖工具行为很有用。

```go
func onBeforeTool(ctx tool.Context, t tool.Tool, args map[string]any) (map[string]any, error) {
    log.Printf("[Callback] BeforeTool triggered for tool %q in agent %q.", t.Name(), ctx.AgentName())
    log.Printf("[Callback] Original args: %v", args)

    if t.Name() == "getCapitalCity" {
        if country, ok := args["country"].(string); ok {
            if strings.ToLower(country) == "canada" {
                log.Println("[Callback] Detected 'Canada'. Modifying args to 'France'.")
                args["country"] = "France"
                return args, nil // Proceed with modified args
            } else if strings.ToUpper(country) == "BLOCK" {
                log.Println("[Callback] Detected 'BLOCK'. Skipping tool execution.")
                // Skip tool and return a custom result.
                return map[string]any{"result": "Tool execution was blocked by before_tool_callback."}, nil
            }
        }
    }
    log.Println("[Callback] Proceeding with original or previously modified args.")
    return nil, nil // Proceed with original args
}
```

### 4、AfterToolCallback 代码示例
何时触发: 在工具的执行方法成功完成后立即调用。
用途: 允许在将工具结果发送回 LLM(可能在摘要后) 之前对其进行检查和修改。适用于记录工具结果、后处理或格式化结果,或将结果的特定部分保存到会话状态。

返回值效果:
如果回调返回 nil,使用原始的 tool_response。
如果返回新map,它替换原始的 tool_response。这允许修改或过滤 LLM 看到的结果。

```go
func onAfterTool(ctx tool.Context, t tool.Tool, args map[string]any, result map[string]any, err error) (map[string]any, error) {
    log.Printf("[Callback] AfterTool triggered for tool %q in agent %q.", t.Name(), ctx.AgentName())
    log.Printf("[Callback] Original result: %v", result)

    if err != nil {
        log.Printf("[Callback] Tool run produced an error: %v. Passing through.", err)
        return nil, err
    }

    if t.Name() == "getCapitalCity" {
        if originalResult, ok := result["result"].(string); ok && originalResult == "Washington, D.C." {
            log.Println("[Callback] Detected 'Washington, D.C.'. Modifying tool response.")
            modifiedResult := make(map[string]any)
            for k, v := range result {
                modifiedResult[k] = v
            }
            modifiedResult["result"] = fmt.Sprintf("%s (Note: This is the capital of the USA).", originalResult)
            modifiedResult["note_added_by_callback"] = true
            return modifiedResult, nil
        }
    }

    log.Println("[Callback] Passing original tool response through.")
    return nil, nil
}
```