凌晨三点,我的生产环境 AI Agent 突然疯狂报错:401 Unauthorized。用户对话全部卡死,队列堆积超过 2000 条请求。更糟糕的是,重启服务后状态丢失,用户被迫重新开始对话流程——那是我第一次意识到:错误处理不是"锦上添花",而是 AI Agent 的生命线。

本文将分享我设计 AI Agent 错误处理与状态恢复机制的全部经验,涵盖 401/403 认证错误429 限流处理连接超时恢复消息状态持久化 等核心场景。所有代码基于 HolySheep AI API 编写,国内直连延迟<50ms,价格比官方渠道低 85% 以上。

为什么 AI Agent 必须重视错误处理

与普通 API 调用不同,AI Agent 具有以下特点:

我曾见过一个 AI Agent 因为没有处理 429 限流错误,在 10 分钟内产生 $300 的无效请求。所以今天这套错误处理框架,是我用真金白银"买"来的教训。

错误分类与处理策略

1. 认证与授权错误(4xx 客户端错误)

# holysheep_error_handler.py
import httpx
import asyncio
from typing import Optional, Dict, Any
from dataclasses import dataclass
from enum import Enum

class ErrorSeverity(Enum):
    RETRY_IMMEDIATE = "retry_immediate"      # 网络波动,可立即重试
    RETRY_WITH_BACKOFF = "retry_backoff"     # 限流,需指数退避
    AUTH_ERROR = "auth_error"                 # 认证错误,需检查配置
    FATAL = "fatal"                           # 无解,需人工介入

@dataclass
class APIError(Exception):
    status_code: int
    message: str
    severity: ErrorSeverity
    retry_after: Optional[int] = None  # 秒

def classify_error(response: httpx.Response) -> APIError:
    """根据 HTTP 状态码分类错误类型"""
    status = response.status_code
    
    if status == 401:
        return APIError(
            status_code=401,
            message="API Key 无效或已过期,请检查 https://www.holysheep.ai/register 的密钥配置",
            severity=ErrorSeverity.AUTH_ERROR
        )
    elif status == 403:
        return APIError(
            status_code=403,
            message="权限不足,可能账户余额不足或未开通对应模型",
            severity=ErrorSeverity.AUTH_ERROR
        )
    elif status == 429:
        retry_after = int(response.headers.get("Retry-After", 60))
        return APIError(
            status_code=429,
            message=f"请求过于频繁,请等待 {retry_after} 秒后重试",
            severity=ErrorSeverity.RETRY_WITH_BACKOFF,
            retry_after=retry_after
        )
    elif status >= 500:
        return APIError(
            status_code=status,
            message=f"服务端错误 ({status}),通常可重试解决",
            severity=ErrorSeverity.RETRY_IMMEDIATE
        )
    else:
        return APIError(
            status_code=status,
            message=f"未知错误: {response.text[:200]}",
            severity=ErrorSeverity.FATAL
        )

2. HolySheep API 调用封装(含自动重试)

# holysheep_agent.py
import httpx
import asyncio
import time
import json
from typing import List, Dict, Any, Optional
from .holysheep_error_handler import APIError, ErrorSeverity, classify_error

class HolySheepAIAgent:
    """HolySheep AI Agent 封装,包含完整错误处理"""
    
    def __init__(
        self,
        api_key: str,
        base_url: str = "https://api.holysheep.ai/v1",
        max_retries: int = 3,
        timeout: float = 60.0
    ):
        self.api_key = api_key
        self.base_url = base_url
        self.max_retries = max_retries
        self.timeout = timeout
        self.conversation_history: List[Dict[str, str]] = []
        
        # HolySheep 国内直连,延迟 < 50ms
        self.client = httpx.AsyncClient(
            timeout=httpx.Timeout(timeout),
            limits=httpx.Limits(max_keepalive_connections=20, max_connections=100)
        )
    
    async def chat(
        self,
        message: str,
        model: str = "gpt-4.1",
        temperature: float = 0.7,
        conversation_id: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        发送消息到 HolySheep AI,带完整错误处理
        
        Args:
            message: 用户消息
            model: 模型名称(支持 gpt-4.1、claude-sonnet-4.5 等)
            temperature: 温度参数
            conversation_id: 对话 ID(用于状态追踪)
        
        Returns:
            包含 assistant 回复和元数据的字典
        """
        # 构建请求
        messages = self.conversation_history + [{"role": "user", "content": message}]
        payload = {
            "model": model,
            "messages": messages,
            "temperature": temperature
        }
        
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }
        
        # 重试循环
        last_error = None
        for attempt in range(self.max_retries + 1):
            try:
                response = await self.client.post(
                    f"{self.base_url}/chat/completions",
                    json=payload,
                    headers=headers
                )
                
                if response.status_code == 200:
                    result = response.json()
                    assistant_message = result["choices"][0]["message"]["content"]
                    
                    # 更新对话历史(状态持久化关键步骤)
                    self.conversation_history.append({"role": "user", "content": message})
                    self.conversation_history.append({"role": "assistant", "content": assistant_message})
                    
                    return {
                        "content": assistant_message,
                        "model": result.get("model"),
                        "usage": result.get("usage", {}),
                        "conversation_id": conversation_id or self._generate_id(),
                        "total_tokens": result.get("usage", {}).get("total_tokens", 0)
                    }
                
                # 非 200 响应,分类处理
                error = classify_error(response)
                
                if error.severity == ErrorSeverity.AUTH_ERROR:
                    # 认证错误不重试,立即抛出
                    raise error
                elif error.severity == ErrorSeverity.RETRY_WITH_BACKOFF:
                    # 限流错误,指数退避重试
                    wait_time = error.retry_after or (2 ** attempt)
                    print(f"[警告] 触发限流,等待 {wait_time} 秒后重试 (第 {attempt + 1} 次)")
                    await asyncio.sleep(wait_time)
                    continue
                elif error.severity == ErrorSeverity.RETRY_IMMEDIATE:
                    # 服务端错误,等待后重试
                    wait_time = 2 ** attempt
                    print(f"[提示] 服务端错误 {error.status_code},{wait_time} 秒后重试")
                    await asyncio.sleep(wait_time)
                    continue
                else:
                    raise error
                    
            except httpx.TimeoutException as e:
                last_error = e
                wait_time = 2 ** attempt
                print(f"[警告] 请求超时,{wait_time} 秒后重试 (第 {attempt + 1} 次)")
                await asyncio.sleep(wait_time)
                continue
            except httpx.ConnectError as e:
                last_error = e
                # 连接错误可能网络问题,短暂等待后重试
                await asyncio.sleep(1)
                continue
        
        # 所有重试都失败
        raise RuntimeError(f"经过 {self.max_retries} 次重试后仍然失败: {last_error}")
    
    def _generate_id(self) -> str:
        """生成唯一对话 ID"""
        import uuid
        return str(uuid.uuid4())
    
    async def close(self):
        await self.client.aclose()

状态恢复机制设计

错误处理的下半篇文章是状态恢复。我踩过的坑包括:重启后对话历史丢失、重试后生成重复内容、状态机状态不一致。

1. 对话状态持久化

# state_persistence.py
import json
import redis
import asyncio
from typing import Optional, List, Dict
from datetime import datetime, timedelta

class ConversationStateManager:
    """对话状态管理器,支持 Redis 持久化"""
    
    def __init__(self, redis_url: str = "redis://localhost:6379"):
        self.redis = redis.from_url(redis_url)
        self.default_ttl = timedelta(hours=24)  # 对话状态 24 小时过期
    
    def save_conversation(
        self,
        conversation_id: str,
        history: List[Dict[str, str]],
        metadata: Optional[Dict] = None
    ) -> bool:
        """保存对话状态到 Redis"""
        key = f"conversation:{conversation_id}"
        state = {
            "history": history,
            "metadata": metadata or {},
            "updated_at": datetime.now().isoformat(),
            "message_count": len(history)
        }
        return self.redis.setex(
            key,
            self.default_ttl,
            json.dumps(state)
        )
    
    def load_conversation(self, conversation_id: str) -> Optional[Dict]:
        """从 Redis 加载对话状态"""
        key = f"conversation:{conversation_id}"
        data = self.redis.get(key)
        if data:
            return json.loads(data)
        return None
    
    def update_last_message_id(
        self,
        conversation_id: str,
        message_id: str
    ) -> bool:
        """记录最后处理的消息 ID,用于幂等性检查"""
        key = f"last_message:{conversation_id}"
        return self.redis.set(key, message_id, ex=self.default_ttl)
    
    def get_last_message_id(self, conversation_id: str) -> Optional[str]:
        """获取最后处理的消息 ID"""
        key = f"last_message:{conversation_id}"
        return self.redis.get(key)
    
    def is_message_processed(self, conversation_id: str, message_id: str) -> bool:
        """检查消息是否已处理(幂等性保证)"""
        last_id = self.get_last_message_id(conversation_id)
        return last_id == message_id


class ResilientAgent:
    """具备状态恢复能力的 AI Agent"""
    
    def __init__(self, api_key: str, state_manager: ConversationStateManager):
        self.agent = HolySheepAIAgent(api_key)
        self.state_manager = state_manager
    
    async def process_message(
        self,
        message: str,
        conversation_id: str,
        message_id: str  # 消息唯一 ID
    ) -> Dict[str, Any]:
        """
        处理消息,包含幂等性检查和状态恢复
        
        幂等性保证:
        - 每个 message_id 只处理一次
        - 即使重试也不会产生重复内容
        """
        # 1. 幂等性检查
        if self.state_manager.is_message_processed(conversation_id, message_id):
            print(f"[跳过] 消息 {message_id} 已处理,直接返回历史结果")
            return self.state_manager.load_conversation(conversation_id)
        
        # 2. 尝试加载历史状态
        saved_state = self.state_manager.load_conversation(conversation_id)
        if saved_state:
            print(f"[恢复] 从断点恢复对话 {conversation_id},历史消息数: {saved_state['message_count']}")
            self.agent.conversation_history = saved_state["history"]
        
        # 3. 发送消息
        try:
            result = await self.agent.chat(
                message=message,
                conversation_id=conversation_id
            )
            
            # 4. 保存状态(双重保证)
            self.state_manager.save_conversation(
                conversation_id=conversation_id,
                history=self.agent.conversation_history,
                metadata={
                    "last_model": result["model"],
                    "last_message_id": message_id
                }
            )
            
            # 5. 标记消息已处理
            self.state_manager.update_last_message_id(conversation_id, message_id)
            
            return result
            
        except Exception as e:
            # 即使失败也要保存当前状态
            self.state_manager.save_conversation(
                conversation_id=conversation_id,
                history=self.agent.conversation_history,
                metadata={"last_error": str(e)}
            )
            raise

2. 批量处理与断点续传

# batch_processor.py
import asyncio
from typing import List, Dict, Any
from dataclasses import dataclass
import json

@dataclass
class BatchTask:
    task_id: str
    messages: List[str]
    priority: int = 0

class BatchProcessor:
    """批量任务处理器,支持断点续传"""
    
    def __init__(self, agent: HolySheepAIAgent, checkpoint_file: str = "checkpoint.json"):
        self.agent = agent
        self.checkpoint_file = checkpoint_file
        self.checkpoints: Dict[str, int] = self._load_checkpoints()
    
    def _load_checkpoints(self) -> Dict[str, int]:
        """加载断点"""
        try:
            with open(self.checkpoint_file, 'r') as f:
                return json.load(f)
        except FileNotFoundError:
            return {}
    
    def _save_checkpoint(self, task_id: str, completed_count: int):
        """保存断点"""
        self.checkpoints[task_id] = completed_count
        with open(self.checkpoint_file, 'w') as f:
            json.dump(self.checkpoints, f)
    
    async def process_batch(
        self,
        tasks: List[BatchTask],
        concurrency: int = 5
    ) -> List[Dict[str, Any]]:
        """
        并发处理批量任务,自动跳过已完成部分
        
        使用信号量控制并发数,避免触发 HolySheep API 的 429 限流
        """
        semaphore = asyncio.Semaphore(concurrency)
        results = []
        
        async def process_single(task: BatchTask) -> Dict[str, Any]:
            async with semaphore:
                # 从断点恢复
                start_index = self.checkpoints.get(task.task_id, 0)
                task_results = []
                
                for i, message in enumerate(task.messages[start_index:], start=start_index):
                    try:
                        result = await self.agent.chat(message)
                        task_results.append(result)
                        
                        # 每完成一条消息就保存断点
                        self._save_checkpoint(task.task_id, i + 1)
                        
                    except Exception as e:
                        print(f"[错误] 任务 {task.task_id} 消息 {i} 失败: {e}")
                        # 决定是跳过还是停止
                        task_results.append({"error": str(e), "index": i})
                
                return {
                    "task_id": task.task_id,
                    "results": task_results,
                    "completed": len(task_results),
                    "total": len(task.messages)
                }
        
        # 并发执行所有任务
        task_coroutines = [process_single(task) for task in tasks]
        results = await asyncio.gather(*task_coroutines, return_exceptions=True)
        
        return results

常见报错排查

根据我和团队在实际项目中的经验,整理了 AI Agent 开发中最常见的 5 类错误及其解决方案:

报错 1:401 Unauthorized - API Key 无效

错误信息{"error": {"message": "Incorrect API key provided", "type": "invalid_request_error", "code": "invalid_api_key"}}

原因

解决方案

# 1. 检查 Key 格式(去掉首尾空格)
api_key = os.getenv("HOLYSHEEP_API_KEY", "").strip()

2. 验证 Key 是否有效

async def verify_api_key(api_key: str) -> bool: client = httpx.AsyncClient() try: response = await client.get( "https://api.holysheep.ai/v1/models", headers={"Authorization": f"Bearer {api_key}"} ) return response.status_code == 200 except: return False

3. 建议在 HolySheep 控制台重新生成 Key

https://www.holysheep.ai/register → API Keys → Create New Key

报错 2:429 Rate Limit Exceeded - 请求被限流

错误信息{"error": {"message": "Rate limit reached for requests", "type": "requests", "param": null, "code": "rate_limit_exceeded"}}

原因

解决方案

# 1. 读取 Retry-After 头并等待
async def handle_rate_limit(response: httpx.Response):
    retry_after = int(response.headers.get("Retry-After", 60))
    print(f"限流触发,等待 {retry_after} 秒")
    await asyncio.sleep(retry_after)

2. 实现令牌桶算法控制请求速率

import time class TokenBucket: """令牌桶限流器""" def __init__(self, rate: float, capacity: int): self.rate = rate # 每秒添加的令牌数 self.capacity = capacity self.tokens = capacity self.last_update = time.time() async def acquire(self): while True: now = time.time() elapsed = now - self.last_update self.tokens = min(self.capacity, self.tokens + elapsed * self.rate) self.last_update = now if self.tokens >= 1: self.tokens -= 1 return else: await asyncio.sleep(0.1)

3. 升级到更高配额(HolySheep 支持按需扩容)

https://www.holysheep.ai/register → Billing → 升级套餐

报错 3:ConnectionError: timeout - 连接超时

错误信息httpx.ConnectTimeout: Connection timeout after 60.0s

原因

解决方案

# 1. 增加超时时间并配置重试
client = httpx.AsyncClient(
    timeout=httpx.Timeout(
        connect=10.0,      # 连接超时 10 秒
        read=120.0,        # 读取超时 120 秒
        write=30.0,        # 写入超时 30 秒
        pool=30.0          # 连接池超时 30 秒
    )
)

2. 使用指数退避重试

async def retry_with_backoff(func, max_retries=3, base_delay=2): for i in range(max_retries): try: return await func() except (httpx.ConnectTimeout, httpx.ReadTimeout) as e: if i == max_retries - 1: raise delay = base_delay * (2 ** i) + random.uniform(0, 1) print(f"超时,第 {i+1} 次重试,等待 {delay:.2f} 秒") await asyncio.sleep(delay)

3. 检查是否是 HolySheep 国内节点问题

HolySheep API 国内直连延迟 < 50ms,如延迟异常请检查网络

常见错误与解决方案

错误 1:对话历史无限膨胀导致 400 Bad Request

问题描述:运行几天后突然出现 400 Invalid request 错误,提示消息内容超出限制。

根本原因:对话历史没有限制,累积超过模型的上下文窗口(GPT-4.1 支持 128k tokens)。

# 解决方案:实现消息历史截断
MAX_CONTEXT_TOKENS = 100000  # 留 20% 余量
SAFETY_MARGIN = 0.8

def truncate_history(history: List[Dict], max_tokens: int = MAX_CONTEXT_TOKENS):
    """
    智能截断对话历史,保留最近的对话和系统提示
    """
    # 估算 token 数(中文约 1.5 tokens/字,英文约 4 chars/token)
    def estimate_tokens(text: str) -> int:
        chinese_chars = sum(1 for c in text if '\u4e00' <= c <= '\u9fff')
        other_chars = len(text) - chinese_chars
        return int(chinese_chars * 1.5 + other_chars / 4)
    
    truncated = []
    total_tokens = 0
    
    # 从最新消息往前保留
    for msg in reversed(history):
        msg_tokens = estimate_tokens(msg.get("content", ""))
        if total_tokens + msg_tokens <= max_tokens * SAFETY_MARGIN:
            truncated.insert(0, msg)
            total_tokens += msg_tokens
        else:
            break
    
    return truncated

使用示例

agent.conversation_history = truncate_history(agent.conversation_history)

错误 2:重试导致重复执行副作用操作

问题描述:AI Agent 在执行操作(如发送邮件、转账)后超时,重试导致操作被执行多次。

根本原因:没有实现幂等性机制,HTTP 请求天然不可幂等。

# 解决方案:实现幂等性操作包装器
import hashlib
import json
from typing import Callable, Any

class IdempotentOperation:
    """幂等性操作包装器"""
    
    def __init__(self, storage: ConversationStateManager):
        self.storage = storage
    
    async def execute_once(
        self,
        operation_id: str,
        func: Callable,
        *args, **kwargs
    ) -> Any:
        """
        确保操作只执行一次
        
        Args:
            operation_id: 操作唯一标识(如订单号+操作类型)
            func: 要执行的操作函数
        """
        # 检查是否已执行
        cache_key = f"operation:{operation_id}"
        cached_result = self.storage.redis.get(cache_key)
        
        if cached_result:
            print(f"[幂等] 操作 {operation_id} 已执行,直接返回缓存结果")
            return json.loads(cached_result)
        
        # 执行操作
        result = await func(*args, **kwargs)
        
        # 缓存结果
        self.storage.redis.setex(
            cache_key,
            timedelta(hours=24),
            json.dumps(result)
        )
        
        return result

使用示例

idempotent = IdempotentOperation(state_manager) async def send_notification(email: str, content: str): # 实际发送邮件逻辑 return {"status": "sent", "email": email}

即使重试也不会重复发送

result = await idempotent.execute_once( operation_id=f"email:{order_id}", func=send_notification, email="[email protected]", content="Your order has been shipped" )

错误 3:并发请求导致 Token 消耗异常

问题描述:同一对话被多个并发请求调用,导致 Token 消耗是预期的 2-3 倍。

根本原因:缺少请求互斥机制,多个请求同时读取历史、同时发送、重复累积。

# 解决方案:使用锁保护对话状态
import asyncio
from contextlib import asynccontextmanager

class ConversationLock:
    """对话级别锁"""
    
    def __init__(self):
        self._locks: Dict[str, asyncio.Lock] = {}
        self._lock = asyncio.Lock()  # 保护 _locks 字典本身
    
    async def get_lock(self, conversation_id: str) -> asyncio.Lock:
        async with self._lock:
            if conversation_id not in self._locks:
                self._locks[conversation_id] = asyncio.Lock()
            return self._locks[conversation_id]
    
    @asynccontextmanager
    async def acquire(self, conversation_id: str):
        lock = await self.get_lock(conversation_id)
        await lock.acquire()
        try:
            yield
        finally:
            lock.release()

使用示例

lock = ConversationLock() async def safe_chat(agent, message, conversation_id): async with lock.acquire(conversation_id): # 在锁内执行所有操作 history = state_manager.load_conversation(conversation_id) agent.conversation_history = history result = await agent.chat(message) state_manager.save_conversation(conversation_id, agent.conversation_history) return result

实战经验总结

我在多个生产项目中应用了以上错误处理框架,总结出几条核心经验:

  1. 错误分类是基础:必须区分"可重试错误"和"不可重试错误",401/403 无限重试只会浪费资源并加剧问题。
  2. 状态持久化是生命线:Redis 是最佳选择,checkpoint 文件是兜底方案。我曾因为只依赖内存状态,在服务重启后丢失了 200+ 用户的对话上下文。
  3. 幂等性设计要前置:事后补救的幂等性方案往往漏洞百出。从一开始就要给每个操作分配唯一 ID。
  4. 监控比处理更重要:我目前在生产环境配置了 Prometheus 告警,当 429 错误率超过 5% 时自动触发告警,比用户投诉早 10 分钟发现问题。
  5. 合理选择模型:根据任务复杂度选择模型,不是所有场景都需要 GPT-4.1。像 HolySheep AI 的 DeepSeek V3.2 价格仅 $0.42/MTok,日常对话任务完全够用。

总结

AI Agent 的错误处理与状态恢复机制设计,本质上是在三个维度上做权衡:

本文提供的代码框架经过生产环境验证,可以直接集成到你的项目中。如果在实施过程中遇到具体问题,欢迎在评论区交流。

👉 免费注册 HolySheep AI,获取首月赠额度,体验国内直连<50ms 的低延迟 API 和高达 85% 的成本节省。