设计与实现一个通用 LLM 客户端:Anthropic 与 OpenRouter 双通道设计
摘要
随着大语言模型技术的快速发展,越来越多的智能问答系统、推荐系统和辅助决策系统开始集成第三方大模型接口。为了提升系统的可扩展性与可维护性,本文设计并实现了一个统一的大语言模型客户端 LLMClient。该客户端采用面向对象思想,对 Anthropic 与 OpenRouter 两类模型服务进行统一封装,实现了模型选择、API Key 解析、消息请求发送以及异常处理等功能。该设计降低了业务层与底层模型接口之间的耦合度,提高了系统在不同运行环境下的适配能力,为后续扩展更多模型提供了良好的基础。
一. 设计背景
在基于大语言模型的应用开发过程中,不同模型服务商通常具有不同的接口地址、认证方式、请求格式与响应结构。如果在业务代码中直接编写各个平台的请求逻辑,会导致以下问题:
- 系统耦合度高,后期切换模型平台成本较大。
- 请求逻辑分散,不利于代码维护。
- 错误处理方式不统一,影响用户体验。
- 在命令行环境与 Web 环境下,API Key 的处理方式难以兼顾。
因此,有必要设计一个统一的大语言模型访问模块,对不同平台的调用细节进行抽象和封装,从而为上层业务提供一致的调用接口。
二. 功能需求分析
根据系统设计目标,该模块需要满足以下功能需求:
- 支持多种大模型服务提供方的统一接入。
- 能够根据配置自动选择当前使用的模型平台。
- 支持从配置项中读取 API Key。
- 在命令行交互环境下,允许用户手动输入 API Key。
- 对外提供统一的对话接口,简化业务层调用。
- 能够对常见 HTTP 错误进行识别并返回友好的提示信息。
三. 模块总体设计
本模块采用类封装方式实现,核心类为 LLMClient。其总体职责如下:
- 保存不同平台的接口地址常量
- 根据配置确定当前模型服务提供方
- 解析并获取 API Key
- 提供统一的 chat() 对话方法
- 分别实现 Anthropic 和 OpenRouter 的请求逻辑
- 对异常情况进行统一处理
该设计遵循“高内聚、低耦合”的原则,将与模型调用相关的逻辑集中在一个类中,便于维护与扩展。
四. 关键实现分析
1. 接口地址定义
在类中首先定义了两个平台的请求地址:
class LLMClient:
ANTHROPIC_URL = "https://api.anthropic.com/v1/messages"
OPENROUTER_URL = "https://openrouter.ai/api/v1/chat/completions"
这种写法将平台相关常量集中管理,便于后续修改和统一维护。
2. 初始化方法设计
def __init__(self):
self.provider = CFG["llm_provider"]
self.api_key = self._resolve_key()
初始化阶段完成两个核心任务:一是确定当前采用的模型服务商,二是获取对应的 API Key。这样可以保证对象创建后立即具备完整的调用能力。
3. API Key 解析机制
_resolve_key() 方法用于处理 API Key 获取逻辑。其主要设计思路如下:
- 优先从系统配置 CFG 中读取 API Key。
- 若当前为非交互环境,则记录警告日志并返回空字符串,避免程序阻塞。
- 若当前为命令行交互环境,则提示用户输入 Key 或选择跳过。
- 用户输入成功后,将 Key 同步写入环境变量与配置项中,供当前程序继续使用。
该设计兼顾了自动化部署环境与本地调试环境的不同需求,具有较强的实用性。
4. 模型名称选择
@property
def model(self) -> str:
return CFG["claude_model"] if self.provider == "anthropic" else CFG["openrouter_model"]
这里通过属性方法 model 对不同平台的模型名称进行统一封装,简化了后续请求代码的书写,也增强了可读性。
5. 统一对话接口设计
def chat(self, system: str, user: str) -> str:
if not self.api_key:
return "ℹ LLM 未配置,请在 Settings 中填写 API Key。"
return (self._call_anthropic if self.provider == "anthropic" else self._call_openrouter)(system, user)
chat() 方法是该模块的统一对外入口。其主要优点如下:
- 对调用者屏蔽底层平台差异
- 统一参数形式,简化业务层调用
- 在未配置 API Key 时能够直接返回提示信息
- 根据当前 provider 自动分发到不同的平台请求函数
6. Anthropic 平台请求实现
在 _call_anthropic() 方法中,系统通过 requests.post() 向 Anthropic 接口发送请求,并设置请求头、模型名称、最大输出长度以及消息内容。
其特点包括:
- 使用 x-api-key 进行身份认证
- 将 system 作为独立字段传递
- 设置 timeout=90,避免网络阻塞
- 调用 raise_for_status() 检查 HTTP 状态码
该实现符合 Anthropic 官方接口的调用规范。
7. OpenRouter 平台请求实现
_call_openrouter() 方法与前者类似,但针对 OpenRouter 的接口要求进行了适配,主要区别如下:
- 采用 Authorization: Bearer 方式认证
- 请求消息以 messages 列表形式传递,其中包含 system 和 user
- 添加 HTTP-Referer 与 X-Title 头信息,便于平台识别调用来源
- 在解析 JSON 返回结果后,进一步判断是否存在业务层错误字段 error
该方法体现了对不同平台接口差异的封装思想。
五. 异常处理设计
为了提升系统健壮性,代码中对异常进行了分类处理:
1. HTTP 异常处理
@staticmethod
def _http_err(e: requests.HTTPError) -> str:
code = e.response.status_code
return {
401: "❌ API Key 无效,请检查",
402: "❌ 余额不足,请充值或换用免费模型",
429: "❌ 请求频率超限,请稍后再试",
}.get(code, f"❌ HTTP {code}: {e.response.text[:200]}")
该方法将常见错误码转化为用户更容易理解的提示信息,既提高了交互友好性,也降低了排查难度。
2. 通用异常处理
在两个请求方法中,还对其他异常进行了捕获:
except Exception as e:
return f"❌ 请求失败: {e}"
这种方式可以避免程序因网络异常、解析异常等问题直接崩溃,使系统具备基本的容错能力。
六. 模块设计特点
该模块具有以下几个方面的优点:
- 结构清晰,职责划分明确。
- 通过统一接口降低了业务层调用复杂度。
- 同时兼容交互式环境和非交互式环境。
- 支持不同模型平台的灵活切换。
- 错误提示信息明确,便于用户理解和维护人员排查问题。
- 具有较好的扩展性,后续可继续增加 OpenAI、Gemini、DeepSeek 等模型平台支持。
七. 可扩展性分析
从软件工程角度来看,该类的扩展性较强。若后续需要新增其他模型平台,仅需完成以下工作:
- 增加新的接口地址常量。
- 在配置项中加入新的 provider 与模型名称。
- 编写对应的请求方法。
- 在 chat() 方法中添加分发逻辑。
因此,该设计具有较好的复用价值和工程实践意义。
八. 结论
本文围绕一个统一的大语言模型客户端 LLMClient 的实现过程,介绍了其设计目标、功能需求、实现原理及关键技术细节。该模块通过封装 Anthropic 与 OpenRouter 两类模型平台的调用逻辑,实现了统一接口访问、配置化模型切换、灵活的 Key 获取方式以及友好的异常处理机制。实践表明,该设计能够有效降低系统耦合度,提高代码可维护性,为智能问答、推荐分析和 RAG 等应用场景提供可靠的模型调用基础。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)