Grok 四个域名别点错
把密钥贴进代码、把 base_url 改一行、按下运行——这是很多人接大模型 API 的全部准备工作。然后报错来了:返回体不是 JSON,而是一整页 HTML;或者终端里明晃晃写着 401,可密钥刚刚才复制过。这类事故的起点往往不在代码,而在请求根本没发到该去的地址。
这一节不急着发请求。我们先用一段不联网的自检脚本,把「入口选错」这一类问题挡在第一次调用之前。
一、一次真实的误配
设想一个很常见的交接场景:同事把控制台地址发在群里,说「密钥在这个页面创建」。新接手的人顺手把这个地址复制进了配置:
XAI_BASE_URL=https://console.x.ai/team/default/api-keys
后面无论 SDK 怎么调,请求都会打到一个网页后台,而不是推理端点。返回的可能是一个登录页的 HTML、一次重定向,或者一个跟 API 毫无关系的 404。此时去查密钥、查模型名、查网络,全是白费力气——地址层就错了。
另一类误配更隐蔽:地址域名对,路径少了 /v1。SDK 有时会把路径拼成正确的形式,有时不会,取决于初始化方式,于是同一份代码在不同封装下表现不一致。
所以本节分两步:配置阶段一次性核对域名、路径与密钥(脚本会列出全部问题);排障阶段按 2.4 的分层顺序走,先判请求有没有打到推理端点,再看身份有没有被识别,最后才纠路径细节与请求体。前者是静态检查,后者按返回形态定位,两者不冲突。
二、先做一次入口自检
下面这段脚本不发起任何网络请求,只检查配置层。把它放在项目里,作为接入前的第一道闸。
"""入口自检:不联网,只把配置层的错误挡在第一次调用之前。"""
import os
from urllib.parse import urlparse
API_HOST = "api.x.ai"
API_PATH = "/v1"
def read_config() -> dict:
# 密钥只从环境变量读取,代码库里永远不出现真实值
return {
"api_key": os.environ.get("XAI_API_KEY", ""),
"base_url": os.environ.get("XAI_BASE_URL", "https://api.x.ai/v1"),
}
def check(cfg: dict) -> list[str]:
problems: list[str] = []
parsed = urlparse(cfg["base_url"])
if parsed.scheme != "https":
problems.append("base_url 必须是 https 地址")
if parsed.hostname != API_HOST:
problems.append(
"请求应发往 " + API_HOST + ",当前是 " + str(parsed.hostname)
)
if parsed.path.rstrip("/") != API_PATH:
problems.append("路径应为 /v1,当前是 " + (parsed.path or "空"))
key = cfg["api_key"]
if not key:
problems.append("XAI_API_KEY 未设置")
elif key.startswith("YOUR_"):
problems.append("XAI_API_KEY 仍是占位符,没有换成环境变量里的值")
return problems
DEMO_CASES = [
("配置正确", {"api_key": "not-a-real-key", "base_url": "https://api.x.ai/v1"}),
("误填控制台", {"api_key": "not-a-real-key", "base_url": "https://console.x.ai/v1"}),
("漏写路径", {"api_key": "not-a-real-key", "base_url": "https://api.x.ai"}),
("密钥是占位符", {"api_key": "YOUR_XAI_API_KEY", "base_url": "https://api.x.ai/v1"}),
]
if __name__ == "__main__":
for name, cfg in DEMO_CASES:
issues = check(cfg)
print(name + ":" + ("通过" if not issues else " / ".join(issues)))
DEMO_CASES 里的 not-a-real-key 只是为了让自检逻辑跑起来,不是密钥,也不要把它写进任何配置文件。真实项目里只保留 read_config() 与 check(),把 check() 的结果接到启动日志或 CI 里。
三、四项配置检查与域名分工
四个域名在社区教程里经常被整理成一张表,但它们的核验状态并不相同。下表把「官方页直接写到」与「社区在传」分开标,避免把「大家都这么说」当成官方结论。
| 域名 | 用途 | 核验状态 | 常见误用 |
|---|---|---|---|
console.x.ai |
注册账号、充值、创建密钥、管理 API 账单 | 官方 Quickstart 与账号 FAQ 出现 | 当成 base_url 填进代码 |
api.x.ai |
真正接收推理请求的端点 | 官方 Quickstart 出现 | 漏写 /v1 路径 |
accounts.x.ai |
管理账号资料、登录方式与 MFA | 官方账号 FAQ 出现 | 拿来找 API Keys 页面 |
grok.com |
面向普通用户的对话产品,账单在产品设置里另管 | 官方账号 FAQ 出现 | 在产品站里找开发者密钥 |
脚本的三段判断分别对应三种独立风险:
scheme:只接受https。写成http时,问题会表现为连接被拒或跳转,而不是一条清晰的报错。hostname:把控制台、产品站、聚合服务都挡掉。这一条直接消灭本节开头那类事故。path:固定检查/v1。SDK 版本不同,路径拼接行为可能不同,显式写全比依赖默认行为稳妥。
密钥检查单独放在最后,是为了让报错顺序有意义:地址错了就先改地址,不要一上来就轮换密钥,那只会把问题搅得更乱。
这一节只处理「入口地址」。本教程不提供任何中转、代理、镜像站或网络绕行方案,也不对某个地区的直连可用性下结论;相关前提请在自己的网络与合规流程里确认。
四、预期输出与异常分支
运行上面的 __main__,四组样本应输出:
配置正确:通过
误填控制台:请求应发往 api.x.ai,当前是 console.x.ai
漏写路径:路径应为 /v1,当前是 空
密钥是占位符:XAI_API_KEY 仍是占位符,没有换成环境变量里的值
自检通过之后再发第一条请求,最值得记住的一条现象是返回一整页 HTML:JSON 接口不会返回登录界面,看到它就先别往模型、额度、限流上猜,回头核对 base_url 的 hostname 与 path。其余状态码(401、404、429 等)按 2.4 的分诊表逐层定位,错误码该不该重试的规则见 10.1。本节只需记住一句:先分层,再排查。
五、判据卡
| 判断项 | 该这么做 | 不该这么做 |
|---|---|---|
初始化 base_url |
显式写 https://api.x.ai/v1 |
复制控制台或产品站地址 |
| 密钥来源 | 环境变量 + .env,代码里只留占位符 |
把密钥字面量提交进仓库 |
| 首次失败 | 按「地址层 → 认证层 → 路径层 → 请求体层」定位(见 2.4) | 一报错就轮换密钥或改模型名 |
| 入口分工不明 | 到官方页与控制台当前页面复核 | 照抄第三方教程里的域名清单 |
这四条可以单独抽出来贴在项目 README 里。它们约束的是接入前的动作顺序,而不是某个 SDK 的写法,换语言、换封装都成立。
六、来源与核验
- 官方 Quickstart:
https://docs.x.ai/developers/quickstart(核验日期 2026-11-04)。该页给出账号、控制台、密钥与首请求的路径,api.x.ai/v1作为base_url的写法也来自这里。 - 官方账号 FAQ:
https://docs.x.ai/developers/faq/accounts(核验日期 2026-11-04,页面本次实测可达)。accounts.x.ai用于管理账号资料、登录方式与 MFA,grok.com是普通用户产品、其账单在产品设置里单独管理,这两点来自该页。 - 价格、模型名、限额等信息不在本节范围内,统一见
官方事实基线表.md。模型与端点变化较快,本节的核验日期不代表长期有效。
七、动手试
- 把
read_config()/check()放进你的项目,用四个DEMO_CASES跑一遍,确认四类问题都能被挡住。 - 故意把
XAI_BASE_URL改成控制台地址,观察自检输出;再把它改回来,确认不影响后续代码。 - 想一想:如果团队用的是某个封装好的 SDK,
base_url由框架自动拼接,这道自检该放在哪一层才不会被绕过?把答案写成一句不超过 40 字的判据,补进你自己的接入清单。
把入口这一层先确认干净,后面排查请求体、模型与账单时,链条会短很多:地址没选错,剩下的变量才值得逐个查。