Eino AI 实战:DuckDuckGo 搜索工具 V1 与 V2

前言

在基于 Go 语言构建的信息检索类应用中,DuckDuckGo 作为注重隐私保护的搜索引擎,被广泛集成到各类工具链中。

CloudWeGo 生态下的eino-ext组件提供了两套 DuckDuckGo 搜索工具实现

本文将基于实际业务代码中封装的ToGoSearchV1和ToGoSearchV2函数,从配置设计、返回结果等维度深入剖析两个版本的核心差异,帮助开发者根据场景选择适配的版本。

DuckDuckGo 是什么,可以用来做什么?

DuckDuckGo 是一款主打隐私保护的搜索引擎,与传统搜索引擎不同,它不会追踪用户的搜索行为、存储个人信息,也不会根据用户画像推送个性化结果。 CloudWeGo 的eino-ext组件将 DuckDuckGo 的搜索能力封装为可复用的 Go 语言工具,开发者可通过该工具快速集成网页搜索能力,适用于以下场景:

  • 隐私敏感型应用的信息检索(如企业内部知识库查询、匿名数据采集);
  • 多区域、多时间范围的定向信息抓取;
  • 批量 /web 化的搜索结果聚合与分析;
  • 无需登录、无广告干扰的纯净搜索结果获取。

基于 CloudWeGo 官方文档,该工具核心能力包括多区域限定、结果数量控制、安全搜索级别配置、时间范围筛选等,且 V1 和 V2 版本在这些能力的实现上存在显著差异。

实例代码

go 复制代码
package go_search

import (
        "context""encoding/json""github.com/cloudwego/eino-ext/components/tool/duckduckgo/ddgsearch""log""net/http""time"

        duckduckgoV1 "github.com/cloudwego/eino-ext/components/tool/duckduckgo"
        duckduckgoV2 "github.com/cloudwego/eino-ext/components/tool/duckduckgo/v2"
)

var (
        GO_SEARCH_CONFV1 *duckduckgoV1.Config
        GO_SEARCH_CONFV2 *duckduckgoV2.Config
)

func init() {
        /** // RegionWT 代表全球区域(无特定区域,默认值) RegionWT Region = "wt-wt" // RegionUS 代表美国区域 RegionUS Region = "us-en" // RegionUK 代表英国区域 RegionUK Region = "uk-en" // RegionDE 代表德国区域 RegionDE Region = "de-de" // RegionFR 代表法国区域 RegionFR Region = "fr-fr" // RegionJP 代表日本区域 RegionJP Region = "jp-jp" // RegionCN 代表中国区域 RegionCN Region = "cn-zh" // RegionRU 代表俄罗斯区域 RegionRU Region = "ru-ru" */

        GO_SEARCH_CONFV1 = &duckduckgoV1.Config{
                ToolName:   "duckduckgo_search",                        // 工具名称
                ToolDesc:   "search web for information by duckduckgo", // 工具描述
                Region:     ddgsearch.RegionCN,                         // 搜索地区
                MaxResults: 10,                                         // 每页结果数量
                SafeSearch: ddgsearch.SafeSearchOff,                    // 安全搜索级别
                TimeRange:  ddgsearch.TimeRangeAll,                     // 时间范围
                DDGConfig: &ddgsearch.Config{
                        Timeout:    10 * time.Second,
                        Cache:      true,
                        MaxRetries: 5,
                }, // DuckDuckGo 配置
        }

        GO_SEARCH_CONFV2 = &duckduckgoV2.Config{
                ToolName: "duckduckgo_search",                        // 工具的名称
                ToolDesc: "search web for information by duckduckgo", // 工具的描述信息
                Timeout:  30,                                         // 单次请求的最大耗时, 默认 30 秒// 发送HTTP请求的客户端实例// 若设置了HTTPClient,则Timeout配置项将不再生效// 可选配置。默认值:&http.client{Timeout: Timeout}
                HTTPClient: &http.Client{
                        Timeout: 30 * time.Second,
                },
                MaxResults: 3,                     // 返回结果的数量
                Region:     duckduckgoV2.RegionCN, // 地理区域限定
        }
}

func ToGoSearchV1(ctx context.Context, searchKey string, page int) ([]*duckduckgoV1.SearchResult, error) {
        // Create search client// Deprecated: use NewTextSearchTool in V2 instead.
        searchTool, err := duckduckgoV1.NewTool(ctx, GO_SEARCH_CONFV1)
        if err != nil {
                log.Printf("NewTool of duckduckgo failed, err=%v", err)
                return nil, err
        }

        // Create search request
        searchReq := &duckduckgoV1.SearchRequest{
                Query: searchKey,
                Page:  page,
        }

        jsonReq, err := json.Marshal(searchReq)
        if err != nil {
                log.Printf("Marshal of search request failed, err=%v", err)
                return nil, err
        }

        // Execute search
        resp, err := searchTool.InvokableRun(ctx, string(jsonReq))
        if err != nil {
                log.Printf("Search of duckduckgo failed, err=%v", err)
                return nil, err
        }

        var searchResp duckduckgoV1.SearchResponse
        if err := json.Unmarshal([]byte(resp), &searchResp); err != nil {
                log.Printf("Unmarshal of search response failed, err=%v", err)
                return nil, err
        }

        return searchResp.Results, nil
}

func ToGoSearchV2(ctx context.Context, searchKey string, timeRange string) ([]*duckduckgoV2.TextSearchResult, error) {
        searchTool, err := duckduckgoV2.NewTextSearchTool(ctx, GO_SEARCH_CONFV2)
        if err != nil {
                return nil, err
        }

        searchReq := &duckduckgoV2.TextSearchRequest{
                Query:     searchKey,
                TimeRange: duckduckgoV2.TimeRange(timeRange),
        }

        jsonReq, err := json.Marshal(searchReq)
        if err != nil {
                return nil, err
        }

        resp, err := searchTool.InvokableRun(ctx, string(jsonReq))
        if err != nil {
                return nil, err
        }

        var result duckduckgoV2.TextSearchResponse
        err = json.Unmarshal([]byte(resp), &result)
        if err != nil {
                return nil, err
        }

        log.Printf("resp: %s", result)

        return result.Results, nil
}

两个代码函数的区别和对比

Config 配置的区别

配置是工具初始化的核心,V1 和 V2 版本的 Config 结构体在设计理念、配置项粒度、可扩展性上差异显著,具体对比如下:

维度 V1 版本(duckduckgo) V2 版本(duckduckgo/v2)
配置层级 双层嵌套结构:外层Config + 内层ddgsearch.Config,拆分通用配置与底层请求配置 单层扁平结构:所有配置项直接定义在Config中,无嵌套,更简洁
核心配置项 基础:ToolName、ToolDesc、Region、MaxResults、SafeSearch、TimeRange- 底层:Timeout、Cache、MaxRetries 基础:ToolName、ToolDesc、Timeout、MaxResults、Region- 扩展:HTTPClient(自定义客户端)
超时配置 底层DDGConfig.Timeout(单位:time.Duration,示例:10*time.Second),仅控制请求超时 顶层Timeout(单位:秒,示例:30),若配置HTTPClient则覆盖 Timeout,支持自定义客户端超时
特殊配置项 包含SafeSearch(安全搜索级别,如 Off/Moderate/Strict)、Cache(缓存开关)、MaxRetries(重试次数) 无 SafeSearch、Cache、MaxRetries 配置,简化底层控制,聚焦核心搜索能力
时间范围配置 初始化时固定在 Config 中(示例:TimeRangeAll),搜索请求仅传页码,无法动态调整 初始化不配置时间范围,在TextSearchRequest中动态传入TimeRange,灵活性更高
区域配置来源 依赖ddgsearch子包的 Region 常量(如 ddgsearch.RegionCN) 内置 Region 常量(如 duckduckgoV2.RegionCN),无需依赖子包,耦合度更低
可用性 已弃用,未移除:// Deprecated: use NewTextSearchTool in V2 instead. 推荐使用

从代码实现看:

  • V1 的 Config 更偏向 "全量控制",覆盖了搜索行为的底层细节(缓存、重试、安全搜索);
  • V2 则做了 "极简设计",剔除了非核心配置,同时开放HTTPClient自定义能力,适配更复杂的网络场景(如代理、自定义超时策略)。

返回结果的区别

返回结果的结构体设计、数据粒度、使用方式是两个版本的核心差异点,具体如下:

结果结构体定义
  • V1 版本 :返回[]*duckduckgoV1.SearchResult,依赖SearchResponse统一封装结果,结构体核心字段包括:
c 复制代码
type SearchResult struct {
        Title       string `json:"title" jsonschema_description:"The title of the search result"`
        Description string `json:"description" jsonschema_description:"The description of the search result"`
        Link        string `json:"link" jsonschema_description:"The link of the search result"`
}
type SearchResponse struct {
        Results []*SearchResult `json:"results" jsonschema_description:"The results of the search"`
}
  • V2 版本 :返回[]*duckduckgoV2.TextSearchResult,依赖TextSearchResponse封装,结构体适配大模型的状态提示:
go 复制代码
type TextSearchResult struct {
        // Title is the title of the search result
        Title string `json:"title"` // URL is the web address of the result
        URL string `json:"url"` // Summary is the summary of the result content
        Summary string `json:"summary"`
}
type TextSearchResponse struct {
        // Message is a brief status message for the model
        Message string `json:"message"` // Results contains the list of search results
        Results []*TextSearchResult `json:"results,omitempty"`
}
结果获取逻辑
维度 V1 版本 V2 版本
请求参数 搜索请求传Query+Page(页码),支持分页获取结果 搜索请求传Query+TimeRange(时间范围),无分页参数,仅返回固定数量结果
结果数量控制 初始化 Config 时配置MaxResults(每页数量),结合 Page 实现分页 初始化 Config 时配置MaxResults(总返回数量),无分页能力
元数据丰富度 仅保留核心的 Title/Link/Description,轻量化设计 结果包含 Message 大模型适配数据,信息更完整
业务适配场景
  • V1 版本适合需要分页获取、元数据完整、底层可控的场景(如批量数据采集、结果统计分析),但配置和解析复杂度较高;
  • V2 版本适合快速检索、动态时间范围筛选、自定义网络请求的场景(如实时搜索、轻量级检索工具),简化了配置和解析成本,但失去了分页、缓存、重试等能力。

总结

CloudWeGo 的 DuckDuckGo 搜索工具 V1 和 V2 版本本质是 "全功能版" 与 "轻量版" 的区分:

  • V1 版本侧重底层控制与完整能力,通过嵌套配置覆盖缓存、重试、安全搜索等细节,返回结果包含丰富元数据,支持分页,适合对搜索过程和结果完整性要求高的场景;
  • V2 版本侧重简洁易用与灵活扩展,扁平配置降低使用成本,开放自定义 HTTPClient,支持动态时间范围筛选,适合轻量级、高灵活性的检索需求。

开发者在选择时,若需精细化控制搜索行为(如重试、缓存)、分页获取结果,优先选择 V1;若追求极简集成、动态时间筛选、自定义网络配置,V2 是更优选择。

相关推荐
码一行1 小时前
Eino AI 实战: Eino 的文档加载与解析
后端·go
未秃头的程序猿1 小时前
🚀 设计模式在复杂支付系统中的应用:策略+工厂+模板方法模式实战
后端·设计模式
踏浪无痕1 小时前
@Transactional的5种失效场景和自检清单
spring boot·后端·spring cloud
6***v4171 小时前
搭建Golang gRPC环境:protoc、protoc-gen-go 和 protoc-gen-go-grpc 工具安装教程
开发语言·后端·golang
水痕011 小时前
go使用cobra来启动项目
开发语言·后端·golang
用户345848285051 小时前
python在使用synchronized关键字时,需要注意哪些细节问题?
后端
代码扳手1 小时前
Golang 高效内网文件传输实战:零拷贝、断点续传与 Protobuf 指令解析(含完整源码)
后端·go
银河邮差2 小时前
python实战-用海外代理IP抓LinkedIn热门岗位数据
后端·python
undsky2 小时前
【RuoYi-Eggjs】:让 MySQL 更简单
后端·node.js