第9节 自定义工具与callback事件节点处理
在构建智能AI应用时,我们经常需要让AI能够调用外部工具或服务来获取信息或执行操作。例如,查询天气、搜索网页、调用API等。LangChain框架为我们提供了强大的工具机制,允许我们自定义工具并处理回调事件,从而扩展AI的能力。
什么是工具(Tool)
工具是AI代理(Agent)可以调用的函数或服务。每个工具都有一个名称、描述和实际的执行逻辑。当AI在处理用户请求时,如果需要外部信息,它会根据工具的描述选择合适的工具进行调用。
创建自定义工具
让我们通过一个实际的例子来学习如何创建自定义工具——天气查询工具。
1. 定义工具结构
首先,我们需要定义工具的结构:
type Tool struct { client *internal.Client }
这里的Tool结构体包含了一个内部客户端,用于实际调用天气API。
2. 实现工具接口
一个工具需要实现几个关键方法:
Name() 方法
func (t Tool) Name() string { return "weather search" }
这个方法返回工具的名称,AI会根据这个名称来识别和调用工具。
Description() 方法
func (t Tool) Description() string { return ` "a weather query interface. " "use when you answer information about the weather. " "when you need to get city weather information, it is always the first option. " "The input should be {"sheng": xx, "place": xx,"day":"1-7"} which can be parsed by json " "give an example {"sheng": "四川", "place": "绵阳","day":"1"}" "day(非必需) 可传1到7,代表1到7天内天气预报,默认1" "hourtype (非必需) 是否返回时段天气预报,0=不返回,1=返回。默认0。" ` }
这个方法返回工具的详细描述,告诉AI在什么情况下使用这个工具以及如何使用它。描述应该足够详细,让AI能够正确地使用工具。
func (t Tool) Call(ctx context.Context, input string) (string, error) { // 解析输入参数 sheng, place, day, hourtype, err := t.parseInput(input) if err != nil { return "", err } // 调用接口 response, err := t.client.Search(sheng, place, day, hourtype) if err != nil { return "", err } // 解析并格式化输出 return t.parseOutPut(response) }
这是工具的核心方法,负责实际执行工具的功能。它接收输入参数,调用相应的服务,并返回结果。
3. 处理输入和输出
解析输入
func (t Tool) parseInput(input string) (sheng, place, day, hourtype string, err error) { var req Req err = json.Unmarshal([]byte(input), &req) if err != nil { return "", "", "", "", err } return req.Sheng, req.Place, req.Day, req.Place, err }
格式化输出
func (t Tool) parseOutPut(resp *internal.Response) (string, error) { fmt.Println(resp) return fmt.Sprintf("天气信息:\n"+ "天气状况:%s %s\n"+ "温度:%s ~ %s\n"+ "风向:%s ~ %s\n"+ "风力:%s ~ %s\n"+ "当前温度:%.1f°C\n"+ "降水量:%.1f mm\n"+ "湿度:%d%%\n"+ "气压:%d hPa", resp.Weather1, resp.Weather2, resp.Wd1, resp.Wd2, resp.Winddirection1, resp.Winddirection2, resp.Windleve1, resp.Windleve2, resp.NowInfo.Temperature, resp.NowInfo.Precipitation, resp.NowInfo.Humidity, resp.NowInfo.Pressure), nil }
Callback事件节点处理
在工具执行过程中,我们可能需要处理各种事件,比如记录日志、处理错误、监控性能等。Callback机制允许我们在工具执行的不同阶段插入自定义逻辑。
1. 工具调用前的处理
在调用工具之前,我们可以添加预处理逻辑:
func (t Tool) Call(ctx context.Context, input string) (string, error) { // 记录工具调用日志 log.Printf("调用天气工具,输入参数: %s", input) // 解析输入参数 sheng, place, day, hourtype, err := t.parseInput(input) if err != nil { return "", err } // 执行工具逻辑 // ... }
2. 工具调用后的处理
在工具执行完成后,我们可以处理结果或错误
func (t Tool) Call(ctx context.Context, input string) (string, error) { // ... 执行工具逻辑 response, err := t.client.Search(sheng, place, day, hourtype) if err != nil { // 记录错误日志 log.Printf("天气工具调用失败: %v", err) return "", err } result, err := t.parseOutPut(response) if err != nil { // 记录格式化错误 log.Printf("结果格式化失败: %v", err) return "", err } // 记录成功日志 log.Printf("天气工具调用成功,结果: %s", result) return result, nil }
实际应用示例
让我们通过一个完整的示例来展示如何使用自定义工具:
// 创建天气工具实例 weatherTool := weather.NewTool( weather.WithAPIKey("your_api_key"), weather.WithAPIID("your_api_id"), ) // 模拟AI调用工具 result, err := weatherTool.Call(context.Background(), `{"sheng": "北京", "place": "北京"}`) if err != nil { log.Fatal(err) } fmt.Println(result)
最佳实践
1. 清晰的工具描述
确保工具的描述足够详细,让AI能够正确理解和使用工具。
2. 错误处理
始终处理可能出现的错误,并提供有意义的错误信息。
3. 输入验证
在处理输入之前,验证输入的格式和内容是否正确。
4. 日志记录
添加适当的日志记录,方便调试和监控工具的使用情况。
5. 结果格式化
确保工具的输出结果格式清晰、易于理解。
总结
通过本节的学习,我们了解了如何创建自定义工具以及如何处理回调事件。自定义工具是扩展AI能力的重要方式,它允许AI访问外部服务和数据。通过合理的工具设计和事件处理,我们可以构建出功能强大、稳定可靠的AI应用。
在下一节中,我们将学习如何将多个工具组合使用,构建更复杂的AI代理系统。

浙公网安备 33010602011771号