ScavioScavio
产品定价文档
登录开始使用
  1. 首页
  2. 教程
  3. 如何构建代理搜索重试链
教程

如何构建代理搜索重试链

为人工智能代理构建强大的搜索重试链。处理空结果、超时和查询重新制定。 Python 实现指南。

获取免费API密钥API文档

AI 代理中的搜索工具失败的频率比开发人员预期的要高:过于具体的查询导致空结果、慢速网络超时、突发使用期间的速率限制以及来自 LLM 的格式错误的查询。重试链通过尝试逐渐简单的查询、尝试之间的退避以及在所有重试失败时回退到缓存的结果来优雅地处理这些失败。本教程构建了一个包装任何搜索 API 调用的生产级重试链。该模式增加了几乎为零的成本开销,因为重试仅在失败时触发。

前置条件

  • 已安装 Python 3.9+
  • 请求已安装库
  • 来自 scavio.dev 的 Scavio API 密钥
  • 具有强化搜索工具的代理

操作指南

步骤 1: 构建具有错误处理功能的基本搜索功能

从搜索功能开始,该功能捕获所有故障模式并对其进行分类:HTTP 错误、超时、空结果和格式错误的响应。

Python
import requests, os, time
from enum import Enum

API_KEY = os.environ['SCAVIO_API_KEY']

class SearchError(Enum):
    SUCCESS = 'success'
    EMPTY = 'empty_results'
    TIMEOUT = 'timeout'
    RATE_LIMIT = 'rate_limit'
    AUTH_ERROR = 'auth_error'
    SERVER_ERROR = 'server_error'
    UNKNOWN = 'unknown'

def search_with_status(query: str, timeout: int = 15) -> tuple:
    """Returns (results, error_type)."""
    try:
        resp = requests.post('https://api.scavio.dev/api/v1/search',
            headers={'x-api-key': API_KEY, 'Content-Type': 'application/json'},
            json={'query': query, 'country_code': 'us'},
            timeout=timeout)
        if resp.status_code == 429:
            return [], SearchError.RATE_LIMIT
        if resp.status_code == 401:
            return [], SearchError.AUTH_ERROR
        if resp.status_code >= 500:
            return [], SearchError.SERVER_ERROR
        results = resp.json().get('organic_results', [])
        if not results:
            return [], SearchError.EMPTY
        return results, SearchError.SUCCESS
    except requests.exceptions.Timeout:
        return [], SearchError.TIMEOUT
    except Exception:
        return [], SearchError.UNKNOWN

步骤 2: 构建查询简化链

当查询返回空结果时,请逐渐尝试更简单的版本。删除限定符、缩短查询并扩大范围。

Python
def simplify_query(query: str) -> list:
    """Generate progressively simpler versions of a query."""
    words = query.split()
    variants = [query]  # original first
    # Remove quotes and special chars
    cleaned = query.replace('"', '').replace("'", '')
    if cleaned != query:
        variants.append(cleaned)
    # Remove year qualifiers
    import re
    no_year = re.sub(r'\b202[0-9]\b', '', query).strip()
    if no_year != query:
        variants.append(no_year)
    # First N words (progressively shorter)
    if len(words) > 5:
        variants.append(' '.join(words[:5]))
    if len(words) > 3:
        variants.append(' '.join(words[:3]))
    # Remove filler words
    filler = {'the', 'a', 'an', 'is', 'are', 'was', 'were', 'for', 'and', 'or', 'but'}
    no_filler = ' '.join(w for w in words if w.lower() not in filler)
    if no_filler != query and len(no_filler) > 5:
        variants.append(no_filler)
    return list(dict.fromkeys(variants))  # dedupe preserving order

# Test:
for v in simplify_query('"best enterprise CRM software" for startups 2026'):
    print(f'  {v}')

步骤 3: 构建带回退的重试链

将重试与针对瞬态错误的指数退避和针对空结果的查询简化结合在一起。

Python
import logging

log = logging.getLogger('search_retry')

def search_retry_chain(query: str, max_retries: int = 3) -> dict:
    """Retry chain: try original, then simplified queries, with backoff."""
    queries = simplify_query(query)
    all_attempts = []
    for attempt, q in enumerate(queries[:max_retries]):
        results, error = search_with_status(q)
        all_attempts.append({'query': q, 'error': error.value, 'result_count': len(results)})
        if error == SearchError.SUCCESS:
            return {
                'results': results,
                'query_used': q,
                'attempts': len(all_attempts),
                'all_attempts': all_attempts
            }
        if error == SearchError.AUTH_ERROR:
            log.error('Authentication failed. Check your API key.')
            break
        if error in (SearchError.RATE_LIMIT, SearchError.SERVER_ERROR, SearchError.TIMEOUT):
            wait = 2 ** attempt
            log.warning(f'Transient error ({error.value}), retrying in {wait}s...')
            time.sleep(wait)
            # Retry same query for transient errors
            results, error = search_with_status(q)
            if error == SearchError.SUCCESS:
                all_attempts.append({'query': q, 'error': 'retry_success', 'result_count': len(results)})
                return {'results': results, 'query_used': q,
                        'attempts': len(all_attempts), 'all_attempts': all_attempts}
        # For EMPTY, move to next simplified query
        log.info(f'Attempt {attempt + 1}: "{q}" -> {error.value}')
    return {'results': [], 'query_used': query,
            'attempts': len(all_attempts), 'all_attempts': all_attempts}

步骤 4: 添加结果缓存以供回退

缓存成功的搜索结果,以便代理在所有重试失败时都有回退。基于 TTL 的缓存可防止提供非常陈旧的数据。

Python
import hashlib

_cache = {}
CACHE_TTL = 3600  # 1 hour

def cached_search(query: str) -> dict:
    key = hashlib.md5(query.lower().strip().encode()).hexdigest()
    now = time.time()
    # Check cache first
    if key in _cache:
        cached, timestamp = _cache[key]
        if now - timestamp < CACHE_TTL:
            return {**cached, 'from_cache': True, 'cache_age_s': int(now - timestamp)}
    # Run retry chain
    result = search_retry_chain(query)
    # Cache successful results
    if result['results']:
        _cache[key] = (result, now)
    elif key in _cache:
        # All retries failed, return stale cache with warning
        cached, timestamp = _cache[key]
        return {**cached, 'from_cache': True,
                'cache_age_s': int(now - timestamp),
                'warning': 'Serving stale cached results (all retries failed)'}
    return {**result, 'from_cache': False}

# Test:
result = cached_search('best crm software 2026')
print(f'Results: {len(result["results"])}, cache: {result["from_cache"]}, '
      f'attempts: {result["attempts"]}')

步骤 5: 作为主要搜索工具集成到您的代理中

用重试链替换代理的基本搜索功能。代理调用相同的接口,但会自动重试和缓存。

Python
def agent_search_tool(query: str) -> str:
    """Production-ready search tool for agents.
    Handles retries, query simplification, and caching automatically."""
    result = cached_search(query)
    if not result['results']:
        return f'Search returned no results after {result["attempts"]} attempts. Please rephrase your query.'
    formatted = []
    for r in result['results'][:5]:
        formatted.append(f'Title: {r["title"]}\nURL: {r["link"]}\nSnippet: {r.get("snippet", "")}')
    header = f'Found {len(result["results"])} results'
    if result.get('from_cache'):
        header += f' (cached, {result["cache_age_s"]}s old)'
    if result['query_used'] != query:
        header += f' (simplified query: "{result["query_used"]}")'     
    return f'{header}\n\n' + '\n\n'.join(formatted)

# Use in any agent framework:
# @tool
# def web_search(query: str) -> str:
#     return agent_search_tool(query)

print(agent_search_tool('best enterprise CRM software pricing comparison 2026'))

Python 示例

Python
import os, requests, time, hashlib

API_KEY = os.environ['SCAVIO_API_KEY']
_cache = {}

def search(query, timeout=10):
    resp = requests.post('https://api.scavio.dev/api/v1/search',
        headers={'x-api-key': API_KEY, 'Content-Type': 'application/json'},
        json={'query': query, 'country_code': 'us'}, timeout=timeout)
    return resp.json().get('organic_results', [])

def search_with_retry(query, max_retries=3):
    queries = [query] + [' '.join(query.split()[:n]) for n in [5, 3]]
    for q in queries[:max_retries]:
        results = search(q)
        if results:
            key = hashlib.md5(query.encode()).hexdigest()
            _cache[key] = results
            return results
        time.sleep(1)
    return _cache.get(hashlib.md5(query.encode()).hexdigest(), [])

results = search_with_retry('best enterprise CRM pricing comparison 2026')
print(f'{len(results)} results')

JavaScript 示例

JavaScript
const API_KEY = process.env.SCAVIO_API_KEY;
const cache = new Map();

async function search(query) {
  const resp = await fetch('https://api.scavio.dev/api/v1/search', {
    method: 'POST',
    headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({ query, country_code: 'us' })
  });
  return (await resp.json()).organic_results || [];
}

async function searchWithRetry(query, maxRetries = 3) {
  const words = query.split(' ');
  const queries = [query, words.slice(0, 5).join(' '), words.slice(0, 3).join(' ')];
  for (const q of queries.slice(0, maxRetries)) {
    const results = await search(q);
    if (results.length) { cache.set(query, results); return results; }
    await new Promise(r => setTimeout(r, 1000));
  }
  return cache.get(query) || [];
}

searchWithRetry('best enterprise CRM pricing 2026')
  .then(r => console.log(`${r.length} results`));

预期输出

JSON
  "best enterprise CRM software" for startups 2026
  best enterprise CRM software for startups 2026
  best enterprise CRM software for startups
  best enterprise CRM
  enterprise CRM software startups 2026

Attempt 1: "best enterprise CRM software..." -> empty_results
Attempt 2: "best enterprise CRM software for startups" -> success

Results: 10, cache: False, attempts: 2
Found 10 results (simplified query: "best enterprise CRM software for startups")

相关教程

  • 2026 年如何调试代理搜索失败
  • 如何构建具有实时搜索回退功能的 RAG 管道
  • 如何使用搜索 API 修复 Hermes v0.13.0 损坏的搜索

常见问题

大多数开发者在15到30分钟内完成本教程。您需要一个Scavio API密钥(免费套餐即可)和可用的Python或JavaScript环境。

已安装 Python 3.9+. 请求已安装库. 来自 scavio.dev 的 Scavio API 密钥. 具有强化搜索工具的代理. Scavio API密钥注册即送50个免费积分。

可以。免费套餐注册即送50个积分,完全足够完成本教程并构建一个可运行的原型解决方案。

Scavio提供原生LangChain包(langchain-scavio)、MCP服务器以及适用于任何HTTP客户端的REST API。本教程使用 the raw REST API, 但您可以根据需要适配您选择的框架。

相关资源

Best Of

2026 年最佳 AI 生产力应用程序

Read more
Best Of

2026年最佳SerpAPI替代方案

Read more
Glossary

搜索 API 供应商格局(2026)

Read more
Glossary

搜索付费墙时代(2026)

Read more
Use Case

2026 开源 AI 网络访问方案

Read more
Comparison

DataForSEO vs Serper

Read more

开始构建

为人工智能代理构建强大的搜索重试链。处理空结果、超时和查询重新制定。 Python 实现指南。

获取免费API密钥阅读文档
ScavioScavio

面向AI智能体的实时搜索API。搜索所有平台,不仅仅是Google。

产品

  • 功能
  • 定价
  • 控制台
  • 联盟计划

开发者

  • 文档
  • API参考
  • 快速开始
  • MCP集成
  • Python SDK

替代方案

  • Tavily替代方案
  • SerpAPI替代方案
  • Firecrawl替代方案
  • Exa替代方案

工具

  • JSON格式化
  • cURL转代码
  • Token计数器
  • 全部工具

© 2026 Scavio. 保留所有权利。

Featured on TAAFT
服务条款隐私政策