异常
SDK 抛出的所有异常都继承自 XiangxinError,一个 except XiangxinError 就能兜住全部情况;需要区别处理时,再捕获更具体的类型。所有异常都可以直接从 xiangxin 导入。
XiangxinError 所有 SDK 异常的基类;本地配置错误也直接抛它
├── APIError 服务端返回了非 2xx 响应
│ ├── BadRequestError 400
│ ├── AuthenticationError 401 密钥缺失、无效或已禁用
│ ├── InsufficientBalanceError 402 组织余额不足
│ ├── PermissionDeniedError 403
│ ├── NotFoundError 404 例如模型不存在
│ ├── UnprocessableEntityError 422 请求未通过校验
│ ├── RateLimitError 429 超出速率限制
│ ├── OverloadedError 529 服务过载
│ ├── InternalServerError 其他 5xx
│ └── APIResponseValidationError 2xx,但响应体结构不对
└── APIConnectionError 没拿到 HTTP 响应(DNS、连接被拒、中断)
└── APITimeoutError 超时基础异常
xiangxin.XiangxinError
基类:Exception
所有 SDK 异常的基类。本地的配置或参数错误也直接抛出这个类,例如缺少 API 密钥、密钥格式非法、base_url 或 timeout 非法、state 为 None、questions 为空、问题字典缺少 type 或 criteria。这类错误在发出请求前就会抛出,不会重试。
注意:用问题类构造问题时(如 Choice(criteria={})、拼错字段名),报错来自 pydantic,抛出的是 pydantic.ValidationError,而不是 XiangxinError。
HTTP 错误
xiangxin.APIError
服务端返回了不成功的 HTTP 响应,携带响应体与请求元数据。str(e) 会输出状态码、信息和请求 ID,例如 402 insufficient_balance (request_id=req_8f2c...),可以直接写进日志。
status_code
status_code: intHTTP 状态码。status 是它的只读别名。
message
message: str人类可读的错误信息:取自错误体的 detail,没有时为 HTTP 原因短语。
body
body: Any服务端的 JSON 错误体、纯文本响应,空响应体时为 None。
detail
属性
detail: Any错误体中的 detail 字段:通常是字符串(如 "insufficient_balance"),422 时可能是字段错误列表;没有时为 None。
headers
headers: httpx.HeadersHTTP 响应头。
endpoint
endpoint: str | None请求方法与 URL(不含查询参数和片段),例如 "POST https://api.xiangxinai.cn/v1/systemone"。
request_id
属性
request_id: str | None响应头 x-request-id,没有时为 None。排查问题时请提供给技术支持。
xiangxin.BadRequestError
基类:APIError
请求无效(400)。
xiangxin.AuthenticationError
基类:APIError
身份验证失败(401):API 密钥缺失、无效或已被禁用。
xiangxin.InsufficientBalanceError
基类:APIError
xiangxin.PermissionDeniedError
基类:APIError
无权访问(403)。
xiangxin.NotFoundError
基类:APIError
资源不存在(404),例如 model 写了一个不存在的模型名。
xiangxin.UnprocessableEntityError
基类:APIError
请求未通过服务端校验(422),例如选项超过 255 个、档位数不在 2–10、超出 token 上限。
xiangxin.RateLimitError
基类:APIError
超出速率限制(429)。默认会被自动重试。
retry_after
retry_after: float | None服务端建议等待的秒数,解析自 retry-after-ms 或 retry-after 响应头;没有时为 None。
xiangxin.OverloadedError
基类:APIError
服务过载或模型后端暂不可用(529)。稍后重试通常就能成功,默认会被自动重试。
xiangxin.InternalServerError
基类:APIError
服务端处理失败(529 以外的 5xx)。500、502、503、504 默认会被自动重试。
连接错误
xiangxin.APIConnectionError
基类:XiangxinError、ConnectionError
请求没有拿到 HTTP 响应,例如 DNS 解析失败、连接被拒绝或中断。
xiangxin.APITimeoutError
基类:APIConnectionError、TimeoutError
请求超过了配置的超时。
timeout
timeout: float | httpx.Timeout | None该请求使用的超时设置,单位为秒或 httpx.Timeout。
响应校验
xiangxin.APIResponseValidationError
基类:APIError
HTTP 请求成功,但响应体不是合法 JSON,或者缺少必需字段、结构不符合响应模型(包括你传入的 response_model)。message 中包含出错字段的点分路径,例如 answers.tone.confidence;status_code、body、headers 与普通 APIError 相同。
状态码对照
| 状态码 | 异常类 | 典型 detail | 是否自动重试 | 你该做什么 |
|---|---|---|---|---|
| — | XiangxinError | “缺少 API 密钥” 等 | 否 | 修正配置或参数:设置 XIANGXIN_API_KEY、问题不能为空、Choice / Score 必须有 criteria。 |
| 400 | BadRequestError | 否 | 检查请求体是否是合法 JSON。 | |
| 401 | AuthenticationError | invalid_api_key | 否 | 检查密钥是否正确、是否已在控制台被禁用或删除。 |
| 402 | InsufficientBalanceError | insufficient_balance | 否 | 到控制台充值,或等待下月赠送额度。 |
| 403 | PermissionDeniedError | 否 | 确认该密钥有权访问此资源。 | |
| 404 | NotFoundError | model_not_found | 否 | 检查 model 名称,见模型。 |
| 422 | UnprocessableEntityError | 字段错误列表、Too many choices...、max_tokens_exceeded | 否 | 按 detail 修正请求:选项过多、档位数不在 2–10、state 过长等。 |
| 429 | RateLimitError | 是 | 默认会按 retry-after 自动等待重试;仍失败时降低并发。 | |
| 529 | OverloadedError | overloaded | 是 | 默认自动退避重试;仍失败时稍后再试。 |
| 500/502/503/504 | InternalServerError | 是 | 默认自动重试;持续出现请带上 request_id 联系我们。 | |
| — | APIConnectionError | 是 | 检查网络、代理和 base_url。 | |
| — | APITimeoutError | 是 | 调大 timeout,或缩短 state。 |
“是否自动重试”指默认 RetryPolicy 的行为,可以修改,见重试。完整的 HTTP 错误说明见 HTTP 错误码。
处理示例
最常见的写法
import logging
from xiangxin import (
APIConnectionError,
APIError,
AuthenticationError,
InsufficientBalanceError,
Noul,
UnprocessableEntityError,
XiangxinClient,
)
log = logging.getLogger("app")
client = XiangxinClient()
def is_complaint(text: str) -> float | None:
try:
resp = client.system_one(
state=text,
questions={"complaint": Noul(instructions="这是一条投诉吗?")},
)
except InsufficientBalanceError:
# 余额不足:通知负责人充值,本次降级处理
log.critical("象信余额不足,请到 https://console.xiangxinai.cn 充值")
return None
except AuthenticationError as e:
log.critical("象信 API 密钥无效或已禁用:%s", e)
raise
except UnprocessableEntityError as e:
# 请求本身有问题,重试无用
log.error("请求未通过校验:%s", e.detail)
raise
except APIConnectionError as e:
# 已经按重试策略重试过了,仍然连不上
log.warning("暂时无法连接象信:%s", e)
return None
except APIError as e:
log.error("象信返回错误 %s(request_id=%s)", e.status_code, e.request_id)
return None
return resp.answers["complaint"].noul余额不足时提示用户充值
如果你把象信嵌在自己的产品里,余额耗尽时最好给运营同学一个明确的指引,而不是笼统的“服务异常”:
from xiangxin import InsufficientBalanceError, Noul, XiangxinClient
client = XiangxinClient()
try:
client.system_one(state="测试", questions={"ok": Noul(instructions="这是测试消息吗?")})
except InsufficientBalanceError as e:
print(
"象信账户余额已用完(请求 ID:%s)。\n"
"请登录控制台 → 设置 → 账单 充值,最低 ¥10,充值额度 12 个月内有效。" % e.request_id
)提示
可以在控制台的账单页开启自动充值,避免生产环境因余额耗尽而中断。计费规则见定价与额度。
读取 422 的字段错误
422 的 detail 可能是字符串,也可能是 pydantic 风格的错误列表,每项含 loc(出错字段的路径)和 msg:
from xiangxin import Choice, UnprocessableEntityError, XiangxinClient
client = XiangxinClient()
too_many = Choice(instructions="属于哪个类目?", criteria={f"c{i}": None for i in range(300)})
try:
client.system_one(state="一台二手笔记本", questions={"cat": too_many})
except UnprocessableEntityError as e:
if isinstance(e.detail, list):
for err in e.detail:
print(".".join(str(p) for p in err.get("loc", [])), err.get("msg"))
else:
print(e.detail) # 例如 "Too many choices. Must have at most 255 choices."选项超过 255 个时,可以先用检索或规则缩小候选集,再交给 Choice;或者参考意图路由做分层选择。

