221 lines
9.2 KiB
Markdown
221 lines
9.2 KiB
Markdown
|
|
# 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
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
|
|||
|
|
|