himga.llm — API 设计与使用说明#
版本:基于当前实现(2026-04-22) 模块路径:
src/himga/llm/
目录#
1. 概述#
himga.llm 封装 LLM API 调用,提供统一的 chat 接口。agent、eval/judge 等上层模块只依赖 BaseLLMClient,不感知具体 provider,使替换模型或接入新 provider 无需修改上层代码。
目前提供的实现
实现 |
Provider |
说明 |
|---|---|---|
|
Anthropic |
调用 Anthropic Messages API,默认模型 |
2. 模块结构#
src/himga/llm/
├── __init__.py # 导出 BaseLLMClient, AnthropicClient, get_client
└── client.py # 抽象接口 + 实现 + 工厂函数
公共导入路径
from himga.llm import BaseLLMClient, AnthropicClient, get_client
3. 类型参考#
3.1 BaseLLMClient#
class BaseLLMClient(ABC)
所有 LLM provider 的抽象基类,仅定义一个方法:chat。
方法#
chat(messages, *, model, max_tokens, temperature)#
@abstractmethod
def chat(
self,
messages: list[dict],
*,
model: str | None = None,
max_tokens: int = 1024,
temperature: float = 0.0,
) -> str
发送对话请求,返回 assistant 回复文本。
参数
参数 |
类型 |
默认值 |
说明 |
|---|---|---|---|
|
|
(必填) |
OpenAI 格式消息列表,详见 消息格式说明 |
|
|
|
覆盖 client 默认模型。 |
|
|
|
最大生成 token 数 |
|
|
|
采样温度。评测场景默认 |
返回
str:assistant 回复文本,去除首尾空白。
3.2 AnthropicClient#
class AnthropicClient(BaseLLMClient)
基于 Anthropic Python SDK 的 LLM 客户端。
依赖:anthropic 包(已在 pyproject.toml 中声明),以及环境变量 ANTHROPIC_API_KEY。
构造函数#
def __init__(self, model: str = "claude-sonnet-4-6") -> None
参数 |
类型 |
默认值 |
说明 |
|---|---|---|---|
|
|
|
默认模型 ID,可在调用 |
实现细节#
AnthropicClient 自动处理 OpenAI 格式与 Anthropic API 格式之间的差异:消息列表中 role="system" 的条目被提取并作为 Anthropic Messages API 的顶级 system 参数传入,其余消息保持原顺序。
# 内部处理示例:
# 输入:[{"role": "system", "content": "..."}, {"role": "user", "content": "..."}]
# → Anthropic API 调用:
# system = "..."
# messages = [{"role": "user", "content": "..."}]
可用模型速查#
模型 ID |
说明 |
|---|---|
|
默认,均衡性能与速度 |
|
快速且廉价,适合 judge 批量调用 |
|
最强能力,适合复杂推理任务 |
4. 工厂函数#
4.1 get_client#
def get_client(provider: str | None = None) -> BaseLLMClient
根据 provider 名称返回对应的 LLM client 实例。
参数
参数 |
类型 |
说明 |
|---|---|---|
|
|
Provider 名称。 |
支持的 provider
provider 值 |
返回类型 |
|---|---|
|
|
异常
异常 |
触发条件 |
|---|---|
|
provider 不在支持列表中 |
优先级:显式参数 > LLM_PROVIDER 环境变量 > 默认值 "anthropic"
# 三种等价的获取方式:
client = get_client("anthropic") # 显式指定
os.environ["LLM_PROVIDER"] = "anthropic"
client = get_client() # 读环境变量
client = get_client() # 无环境变量时默认 anthropic
5. 消息格式说明#
chat 接受 OpenAI 格式的消息列表:
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What is the capital of France?"},
{"role": "assistant", "content": "Paris."},
{"role": "user", "content": "And Germany?"},
]
字段说明
字段 |
类型 |
说明 |
|---|---|---|
|
|
|
|
|
消息文本内容 |
注意事项
system消息可放在列表任意位置,AnthropicClient会自动提取并处理若消息列表为空,
AnthropicClient将发送不含任何 user 消息的请求(API 可能报错)BaseAgent._build_messages生成的消息列表始终符合此格式(一条 system + 一条 user)
6. 使用示例#
6.1 基础调用#
from himga.llm import get_client
llm = get_client() # 默认 AnthropicClient
response = llm.chat([
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What is 2 + 2?"},
])
print(response) # "4"
6.2 自定义模型与参数#
from himga.llm import AnthropicClient
# 初始化时设置默认模型
llm = AnthropicClient(model="claude-haiku-4-5-20251001")
# 全局默认参数调用
response = llm.chat([{"role": "user", "content": "Hi"}])
# 单次覆盖参数
response = llm.chat(
[{"role": "user", "content": "Write a poem about memory."}],
model="claude-opus-4-7", # 本次用更强的模型
max_tokens=512,
temperature=0.7, # 适当提高创造性
)
6.3 实现 MockLLMClient(测试用)#
测试中不产生真实 API 调用:
from himga.llm import BaseLLMClient
class MockLLMClient(BaseLLMClient):
"""返回固定响应的测试 stub。"""
def __init__(self, response: str = "mock answer"):
self._response = response
self.call_count = 0
self.last_messages: list[dict] | None = None
def chat(self, messages, *, model=None, max_tokens=1024, temperature=0.0) -> str:
self.call_count += 1
self.last_messages = messages
return self._response
用法:
llm = MockLLMClient(response="Paris")
assert llm.chat([{"role": "user", "content": "Capital of France?"}]) == "Paris"
assert llm.call_count == 1
6.4 实现自定义 LLM 接入#
接入 OpenAI 或本地模型时,继承 BaseLLMClient 并实现 chat:
from himga.llm import BaseLLMClient
class OpenAIClient(BaseLLMClient):
def __init__(self, model: str = "gpt-4o"):
from openai import OpenAI
self._client = OpenAI()
self._default_model = model
def chat(self, messages, *, model=None, max_tokens=1024, temperature=0.0) -> str:
resp = self._client.chat.completions.create(
model=model or self._default_model,
messages=messages,
max_tokens=max_tokens,
temperature=temperature,
)
return resp.choices[0].message.content
注册到 get_client(可在项目内 monkey-patch 或扩展 get_client 函数):
# 无需修改 himga.llm,直接实例化使用
from himga.agent import BaseAgent
from himga.memory import NullMemory
agent = BaseAgent(memory=NullMemory(), llm=OpenAIClient())
7. 设计决策说明#
为什么采用 OpenAI 格式的 messages?#
OpenAI 格式已成为业界通用标准,Anthropic、Cohere、本地模型(Ollama)等均提供兼容适配层。BaseAgent 和 judge.py 生成的 prompt 无需感知 provider 差异,降低了上层代码的耦合度。
为什么 temperature=0.0 为默认值?#
评测场景要求结果可复现(方便与论文数值对比)。judge_answer 在批量调用时也使用 0.0 保证判断一致性。对需要随机性的场景(如采样多个候选答案),调用方可按次覆盖。
为什么不在接口层做重试?#
重试属于基础设施关注点(网络可靠性、退避策略),不应耦合进 LLM 抽象。调用方可用 tenacity 等库在外部包装,或在具体 client 内部按需实现。
为什么 AnthropicClient.__init__ 延迟导入 anthropic?#
若用户使用其他 provider(如未来的 OpenAIClient),不应因未安装 anthropic 包而在 import 阶段报错。延迟导入(在 __init__ 内部 import anthropic)使模块在不安装该包时仍可被其他代码引用。