摘要

随着大语言模型技术的快速发展,越来越多的智能问答系统、推荐系统和辅助决策系统开始集成第三方大模型接口。为了提升系统的可扩展性与可维护性,本文设计并实现了一个统一的大语言模型客户端 LLMClient。该客户端采用面向对象思想,对 Anthropic 与 OpenRouter 两类模型服务进行统一封装,实现了模型选择、API Key 解析、消息请求发送以及异常处理等功能。该设计降低了业务层与底层模型接口之间的耦合度,提高了系统在不同运行环境下的适配能力,为后续扩展更多模型提供了良好的基础。

一. 设计背景

在基于大语言模型的应用开发过程中,不同模型服务商通常具有不同的接口地址、认证方式、请求格式与响应结构。如果在业务代码中直接编写各个平台的请求逻辑,会导致以下问题:

  1. 系统耦合度高,后期切换模型平台成本较大。
  2. 请求逻辑分散,不利于代码维护。
  3. 错误处理方式不统一,影响用户体验。
  4. 在命令行环境与 Web 环境下,API Key 的处理方式难以兼顾。

因此,有必要设计一个统一的大语言模型访问模块,对不同平台的调用细节进行抽象和封装,从而为上层业务提供一致的调用接口。

二. 功能需求分析

根据系统设计目标,该模块需要满足以下功能需求:

  1. 支持多种大模型服务提供方的统一接入。
  2. 能够根据配置自动选择当前使用的模型平台。
  3. 支持从配置项中读取 API Key。
  4. 在命令行交互环境下,允许用户手动输入 API Key。
  5. 对外提供统一的对话接口,简化业务层调用。
  6. 能够对常见 HTTP 错误进行识别并返回友好的提示信息。

三. 模块总体设计

本模块采用类封装方式实现,核心类为 LLMClient。其总体职责如下:

  1. 保存不同平台的接口地址常量
  2. 根据配置确定当前模型服务提供方
  3. 解析并获取 API Key
  4. 提供统一的 chat() 对话方法
  5. 分别实现 Anthropic 和 OpenRouter 的请求逻辑
  6. 对异常情况进行统一处理

该设计遵循“高内聚、低耦合”的原则,将与模型调用相关的逻辑集中在一个类中,便于维护与扩展。

四. 关键实现分析

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 获取逻辑。其主要设计思路如下:

  1. 优先从系统配置 CFG 中读取 API Key。
  2. 若当前为非交互环境,则记录警告日志并返回空字符串,避免程序阻塞。
  3. 若当前为命令行交互环境,则提示用户输入 Key 或选择跳过。
  4. 用户输入成功后,将 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() 方法是该模块的统一对外入口。其主要优点如下:

  1. 对调用者屏蔽底层平台差异
  2. 统一参数形式,简化业务层调用
  3. 在未配置 API Key 时能够直接返回提示信息
  4. 根据当前 provider 自动分发到不同的平台请求函数

6. Anthropic 平台请求实现

在 _call_anthropic() 方法中,系统通过 requests.post() 向 Anthropic 接口发送请求,并设置请求头、模型名称、最大输出长度以及消息内容。

其特点包括:

  1. 使用 x-api-key 进行身份认证
  2. 将 system 作为独立字段传递
  3. 设置 timeout=90,避免网络阻塞
  4. 调用 raise_for_status() 检查 HTTP 状态码

该实现符合 Anthropic 官方接口的调用规范。

7. OpenRouter 平台请求实现

_call_openrouter() 方法与前者类似,但针对 OpenRouter 的接口要求进行了适配,主要区别如下:

  1. 采用 Authorization: Bearer 方式认证
  2. 请求消息以 messages 列表形式传递,其中包含 system 和 user
  3. 添加 HTTP-Referer 与 X-Title 头信息,便于平台识别调用来源
  4. 在解析 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}"

这种方式可以避免程序因网络异常、解析异常等问题直接崩溃,使系统具备基本的容错能力。

六. 模块设计特点

该模块具有以下几个方面的优点:

  1. 结构清晰,职责划分明确。
  2. 通过统一接口降低了业务层调用复杂度。
  3. 同时兼容交互式环境和非交互式环境。
  4. 支持不同模型平台的灵活切换。
  5. 错误提示信息明确,便于用户理解和维护人员排查问题。
  6. 具有较好的扩展性,后续可继续增加 OpenAI、Gemini、DeepSeek 等模型平台支持。

七. 可扩展性分析

从软件工程角度来看,该类的扩展性较强。若后续需要新增其他模型平台,仅需完成以下工作:

  1. 增加新的接口地址常量。
  2. 在配置项中加入新的 provider 与模型名称。
  3. 编写对应的请求方法。
  4. 在 chat() 方法中添加分发逻辑。

因此,该设计具有较好的复用价值和工程实践意义。

八. 结论

本文围绕一个统一的大语言模型客户端 LLMClient 的实现过程,介绍了其设计目标、功能需求、实现原理及关键技术细节。该模块通过封装 Anthropic 与 OpenRouter 两类模型平台的调用逻辑,实现了统一接口访问、配置化模型切换、灵活的 Key 获取方式以及友好的异常处理机制。实践表明,该设计能够有效降低系统耦合度,提高代码可维护性,为智能问答、推荐分析和 RAG 等应用场景提供可靠的模型调用基础。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐