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 是更优选择。

相关推荐
长栎14 分钟前
写 for 循环写了十年,你却从没用过迭代器模式最狠的那一面
后端
LiaCode18 分钟前
Redis 在生产项目的使用
前端·后端
用户5598224812223 分钟前
Docker Compose Down 导致容器数据误删——ext4 日志恢复全记录
后端
LiaCode23 分钟前
一天学完 redis 的爽翻版核心知识总结
前端·后端
大刚测试开发实战25 分钟前
如何内网穿透访问本地私有化部署的TestHub
前端·后端·github
xiaodaoluanzha44 分钟前
迄今為止,最簡單的編程語言 Nolang
前端·后端
Csvn44 分钟前
Docker 容器管理入门 — 从镜像到容器编排
后端
用户762352425911 小时前
ShardingJDBC
后端
行者全栈架构师1 小时前
IDEA 中 Maven 项目的 15 个红色报错快速解决方法
java·后端
Colin草率地做慢慢地改1 小时前
关于QuickStore这个项目的重构(2)- 数据库建表文件
后端·面试·架构