前言
在基于 Go 语言构建的信息检索类应用中,DuckDuckGo 作为注重隐私保护的搜索引擎,被广泛集成到各类工具链中。
CloudWeGo 生态下的eino-ext组件提供了两套 DuckDuckGo 搜索工具实现
- github.com/cloudwego/eino-ext/components/tool/duckduckgo
- github.com/cloudwego/eino-ext/components/tool/duckduckgo/v2
本文将基于实际业务代码中封装的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 是更优选择。