Hermes Agent API 调用与错误处理机制分析

3 阅读5分钟

Hermes Agent API 调用是 Hermes Agent 的核心部分,主要负责处理与 LLM 提供商的 API 交互、错误处理和恢复策略。这部分代码实现了一个健壮的 API 调用系统,确保在各种错误情况下能够优雅地处理和恢复。

核心功能

1. API 调用与重试机制

while retry_count < max_retries:
    try:
        self._reset_stream_delivery_tracking()
        api_kwargs = self._build_api_kwargs(api_messages)
        # ...
        if _use_streaming:
            response = self._interruptible_streaming_api_call(
                api_kwargs, on_first_delta=_stop_spinner
            )
        else:
            response = self._interruptible_api_call(api_kwargs)
        # ...
  • 流式 API 调用:优先使用流式 API 调用,即使没有流式消费者,因为它提供更好的健康检查和超时检测
  • 重试机制:实现了带有指数退避和抖动的重试策略,处理临时错误

2. 错误处理与分类

classified = classify_api_error(
    api_error,
    provider=getattr(self, "provider", "") or "",
    model=getattr(self, "model", "") or "",
    approx_tokens=approx_tokens,
    context_length=_ctx_len,
    num_messages=len(api_messages) if api_messages else 0,
)
  • 错误分类:将 API 错误分类为不同类型,如速率限制、上下文溢出、有效载荷过大等
  • 针对性处理:根据错误类型采取不同的恢复策略

3. 恢复策略

3.1 上下文压缩

if is_payload_too_large:
    compression_attempts += 1
    if compression_attempts > max_compression_attempts:
        # ...
    original_len = len(messages)
    messages, active_system_prompt = self._compress_context(
        messages, system_message, approx_tokens=approx_tokens,
        task_id=effective_task_id,
    )
    # ...
  • 当遇到有效载荷过大或上下文长度超限错误时,自动压缩对话历史
  • 压缩后创建新会话,确保历史记录的连续性

3.2 模型回退

if is_rate_limited and self._fallback_index < len(self._fallback_chain):
    # ...
    self._emit_status("⚠️ Rate limited — switching to fallback provider...")
    if self._try_activate_fallback():
        retry_count = 0
        compression_attempts = 0
        primary_recovery_attempted = False
        continue
  • 当遇到速率限制或其他无法恢复的错误时,切换到备用模型
  • 实现了模型回退链,确保服务的连续性

3.3 凭证池轮换

recovered_with_pool, has_retried_429 = self._recover_with_credential_pool(
    status_code=status_code,
    has_retried_429=has_retried_429,
    classified_reason=classified.reason,
    error_context=error_context,
)
if recovered_with_pool:
    continue
  • 当遇到认证错误或速率限制时,尝试使用凭证池中的其他凭证

4. 令牌使用跟踪与会话管理

if hasattr(response, 'usage') and response.usage:
    canonical_usage = normalize_usage(
        response.usage,
        provider=self.provider,
        api_mode=self.api_mode,
    )
    prompt_tokens = canonical_usage.prompt_tokens
    completion_tokens = canonical_usage.output_tokens
    total_tokens = canonical_usage.total_tokens
    # ...
    self.session_prompt_tokens += prompt_tokens
    self.session_completion_tokens += completion_tokens
    self.session_total_tokens += total_tokens
    # ...
  • 跟踪 API 调用的令牌使用情况
  • 持久化令牌计数到会话数据库
  • 估算 API 调用成本

5. 特殊错误处理

5.1 推理预算耗尽

_thinking_exhausted = (
    not _trunc_has_tool_calls
    and _has_think_tags
    and (
        (_trunc_content is not None and not self._has_content_after_think_block(_trunc_content))
        or _trunc_content is None
    )
)
  • 检测模型是否将所有输出令牌都用于推理,没有留下令牌用于实际响应
  • 提供用户友好的错误消息和解决方案

5.2 思维块签名错误

if (
    classified.reason == FailoverReason.thinking_signature
    and not thinking_sig_retry_attempted
):
    thinking_sig_retry_attempted = True
    for _m in messages:
        if isinstance(_m, dict):
            _m.pop("reasoning_details", None)
    # ...
  • 处理 Anthropic 思维块签名无效的情况
  • 移除所有思维块,然后重试

技术亮点

  1. 多层错误处理:实现了精细的错误分类和针对性处理策略
  2. 自动上下文管理:当上下文过大时自动压缩,确保对话能够继续
  3. 弹性恢复机制:通过凭证池、模型回退等多种方式提高系统可靠性
  4. 用户体验优先:提供详细的错误信息和恢复建议,增强用户体验
  5. 资源使用跟踪:详细记录令牌使用和成本估算,帮助用户了解资源消耗
  6. 中断处理:在长时间操作中响应用户中断请求,提高交互性

代码优化建议

  1. 错误处理模块化:将错误处理逻辑进一步模块化,提高代码可维护性
  2. 异步错误处理:对于某些长时间运行的恢复操作,考虑使用异步处理
  3. 自适应重试策略:根据错误类型和历史成功率调整重试策略
  4. 错误预测:基于历史数据预测可能的错误类型,提前采取预防措施
  5. 监控与告警:添加更详细的监控和告警机制,及时发现和解决问题

输入输出示例

输入:用户发送了一个复杂的请求,导致上下文长度超过模型限制

messages = [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "I need help with a complex Python project. Here's the code... [very long code]"},
    # ... 更多对话内容,导致上下文长度超限 ...
    {"role": "user", "content": "Now I want to optimize this code."}
]

输出:系统自动压缩上下文并继续对话

⚠️  Context length exceeded — stepping down: 1000000200000 tokens
🗜️ Context too large (~250000 tokens) — compressing (1/3)...
🗜️ Compressed 5010 messages, retrying...
⏱️  API call completed in 4.2s

最终响应

I've analyzed your code and identified several optimization opportunities:

1. **Algorithm optimization**: The nested loops in your `process_data` function can be replaced with vectorized operations using NumPy.

2. **Memory usage**: You're loading the entire dataset into memory. Consider using generators or batch processing for large files.

3. **Caching**: Implement memoization for frequently called functions with expensive computations.

Would you like me to implement any of these optimizations?

总结

这段代码是 Hermes Agent 的核心部分,实现了一个健壮的 API 调用和错误处理系统。它通过多种恢复策略(如上下文压缩、模型回退、凭证池轮换)确保在各种错误情况下能够继续提供服务,同时通过详细的错误信息和用户友好的提示提高用户体验。

这种设计使得 Hermes Agent 能够处理复杂的对话场景,即使在遇到 API 错误或资源限制时也能保持服务的连续性和可靠性。这是一个精心设计的系统,展示了如何构建一个健壮的 AI 代理,能够应对各种挑战并提供一致的用户体验。