教程
如何通过 HiAPI API 使用 grok-4.7:curl、Python 与一次可用的请求
从首次请求到验证响应的实操指南
首次请求前你需要准备什么
在向 API 发送任何内容之前,请确保四件事已就绪。跳过其中任何一项,是首次调用失败的最常见原因,而失败原因往往与模型本身毫无关系。
一个 HiAPI 账户
你需要一个有效的 HiAPI.ai 账户。登录仪表盘以管理项目、密钥和用量。本教程中的所有操作都通过同一个统一 API 界面完成,因此你在此处创建的密钥可在平台提供的各个模型间通用。
一个 API 密钥
在仪表盘中创建 API 密钥,并将其存放在安全之处——测试时可用 shell 环境变量,任何将投入生产运行的东西则用密钥管理器。切勿将密钥提交到代码仓库,也不要粘贴到客户端应用中。
对于本地测试,每个 shell 会话导出一次即可:
export HIAPI_API_KEY="your-api-key-here"
基础 URL
基础 URL 是 HiAPI API 的根地址。本教程中的所有请求都相对于它。请查阅你的仪表盘,确认你的账户应使用的确切基础 URL,并将其存入变量,以便脚本和应用代码引用同一个值:
export HIAPI_BASE_URL="https://api.hiapi.ai"
模型标识符
本教程使用的模型标识符是 grok-4.7。它以纯字符串形式在请求体中传递。如果标识符有误,你通常会收到明确的错误返回,而不是静默回退,因此拼写错误很容易被发现。
一旦这四项——账户、密钥、基础 URL、模型 ID——齐备,请求形态所需的一切就都有了。curl 方式无需安装任何 SDK,Python 方式也只需要你大概率已具备的 HTTP 客户端。
快速环境检查
确认密钥和基础 URL 对你即将使用的 shell 可见:
[ -n "$HIAPI_API_KEY" ] && echo "key set"
[ -n "$HIAPI_BASE_URL" ] && echo "base url set"
如果两行都打印出来,你就可以进入下一节了。
HiAPI 上 grok-4.7 请求的形态
本教程中的每个请求都共享同样的四个要素:端点、认证头、模型字段和 messages 数组。分别理解它们,会让之后理解流式传输和工具调用容易得多,因为这些功能只改变此形态的一部分。
端点
该调用是向基础 URL 下的聊天补全端点发送 POST 请求。本教程中我们将其表示为:
POST {HIAPI_BASE_URL}/v1/chat/completions
如果你的仪表盘显示的路径不同,请使用仪表盘中的路径;无论哪种方式,请求体都相同。
认证头
认证方式是 Authorization 头中的 bearer token:
Authorization: Bearer $HIAPI_API_KEY
由于它是请求头而非查询参数,密钥永远不会出现在 URL、访问日志或浏览器历史中。
内容类型
请求体是 JSON,因此发送 Content-Type: application/json。curl 和任何 Python HTTP 客户端都能为你设置它,但明确写出这一点是值得的,这样请求头集合就没有歧义。
请求体:model 与 messages
请求体是一个至少包含两个字段的 JSON 对象:
model— 模型标识符字符串,此处为grok-4.7。messages— 有序的消息对象数组。每个对象有role和content。常用角色有:system用于框定对话的指令,user用于请求本身,assistant用于你在延续对话时提供先前的模型轮次。
最小请求体如下所示:
{
"model": "grok-4.7",
"messages": [
{ "role": "system", "content": "You are a concise technical assistant." },
{ "role": "user", "content": "Explain what an idempotent HTTP method is in two sentences." }
]
}
这就是基本调用的全部约定。其他任何内容——流式传输、工具定义——都是同一个对象上的附加字段,这正是前面几节重要的原因:一旦此形态正确,其余都是附加的。
响应长什么样
响应是一个 JSON 对象。你几乎总是想要的部分在 choices 下,它是一个数组;每个条目有一个包含 role 和 content 的 message。此外还有元数据,例如处理请求的模型以及描述 token 数量的 usage 块。读取 choices[0].message.content,你就得到了答案文本。其他字段对日志记录和成本跟踪有用,但入门并不需要它们。
一个可用的 curl 请求
密钥已导出、请求形态已理解之后,curl 调用就很简短。当你想把问题从应用代码中隔离出来时,这就是该复制的版本。
curl -sS "$HIAPI_BASE_URL/v1/chat/completions" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "grok-4.7",
"messages": [
{ "role": "system", "content": "You are a concise technical assistant." },
{ "role": "user", "content": "In one sentence, what is a bearer token?" }
]
}'
每个标志的作用
-sS保持输出安静,但仍会打印错误。没有它,失败的请求可能看起来像空响应。- URL 由基础 URL 变量加上聊天补全路径构成,因此你永远不用在脚本中硬编码主机。
-H "Authorization: ..."发送 bearer token。变量由 shell 展开,这就是之前导出它很重要的原因。-H "Content-Type: application/json"告诉服务器请求体是 JSON。-d '...'是请求体。JSON 外围的单引号可防止 shell 解释其中的大括号和引号。
如果你更愿意把请求体放在文件中,将其写入 body.json,并将 -d 参数替换为 -d @body.json。一旦 messages 数组变长,这通常更清晰。
读取原始 JSON
响应以 JSON 对象形式到达标准输出。当你想用肉眼检查时,通过格式化工具管道处理:
curl -sS "$HIAPI_BASE_URL/v1/chat/completions" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d @body.json | python -m json.tool
你应能看到一个 choices 数组,其第一个元素包含一个带有 content 字符串的 message 对象——该字符串就是模型的答案。与之并列的通常还有响应的标识符、回显的模型名称,以及包含提示和补全 token 计数的 usage 对象。现在就把一次成功响应记录下来:当你验证 Python 客户端时,它就是你要对比的参照。
此阶段的常见失败
401 几乎总是意味着 bearer token 缺失、格式错误,或未真正在当前 shell 中导出。400 若伴随关于请求体的消息,通常意味着 JSON 格式错误——尾随逗号或从文档中粘贴的智能引号是常见元凶。在查看其他任何东西之前,先修复这两项。
同样的请求用 Python 实现
Python 版本用相同的请求体做同样的事。重写它的意义在于,从 shell 测试转为可放入应用的代码,请求体是数据结构而非带引号的字符串。
使用标准库
如果你不想添加依赖,urllib 就足够了:
import json
import os
import urllib.request
api_key = os.environ["HIAPI_API_KEY"]
base_url = os.environ["HIAPI_BASE_URL"]
payload = {
"model": "grok-4.7",
"messages": [
{"role": "system", "content": "You are a concise technical assistant."},
{"role": "user", "content": "In one sentence, what is a bearer token?"},
],
}
request = urllib.request.Request(
f"{base_url}/v1/chat/completions",
data=json.dumps(payload).encode("utf-8"),
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
method="POST",
)
with urllib.request.urlopen(request) as response:
body = json.loads(response.read().decode("utf-8"))
print(body["choices"][0]["message"]["content"])
使用 requests
如果 requests 已在你的环境中,调用更简短:
import os
import requests
response = requests.post(
f"{os.environ['HIAPI_BASE_URL']}/v1/chat/completions",
headers={"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}"},
json={
"model": "grok-4.7",
"messages": [
{"role": "system", "content": "You are a concise technical assistant."},
{"role": "user", "content": "In one sentence, what is a bearer token?"},
],
},
timeout=60,
)
response.raise_for_status()
print(response.json()["choices"][0]["message"]["content"])
为什么这个版本对应用更安全
请注意三个刻意的选择。密钥和基础 URL 来自环境变量,绝不来自源码中的字面量。请求体是 Python 字典,因此放错位置的引号会立即产生 SyntaxError,而不是令人困惑的服务器端拒绝。并且调用设置了超时:没有超时,停滞的连接可能无限期挂起一个工作进程。
用你在 curl 中使用的相同提示词运行 Python 版本。答案的措辞在多次运行间可能略有不同——模型并非确定性的——但响应的结构应完全一致:一个 choices 数组、一个带 content 的 message,以及一个 usage 块。如果结构匹配,你的客户端就连接正确,其余各节都是可选的增强。
开启流式传输
流式传输改变的是 token 到达的方式,而不是你请求的内容。你不必等待完整答案,而是逐步收到它,这正是聊天界面感觉响应迅速的原因。
请求的改动
在你已有的请求体中添加一个字段:
{
"model": "grok-4.7",
"stream": true,
"messages": [
{ "role": "user", "content": "Count from one to five, one number per line." }
]
}
端点、请求头或 messages 的其他部分都没有变化。这正是先学习基础形态的全部意义:流式传输是一个附加标志,而不是第二个 API。
返回什么
启用 stream 后,响应不再是单个 JSON 对象。它是一系列服务器发送事件(SSE)。每个事件携带一行 data:,其载荷是一个 JSON 片段,形态与普通响应相同,只是 choices[0] 中包含的是 delta 对象而非完整的 message。早期片段可能只包含角色;中间片段携带内容片段;流以哨兵行 data: [DONE] 结束。
作为客户端,你的任务是将 delta.content 字符串按顺序拼接起来,并在哨兵处停止。将任何缺少 content 的片段视为无操作而非错误。
在 Python 中消费数据块
import json
import os
import requests
with requests.post(
f"{os.environ['HIAPI_BASE_URL']}/v1/chat/completions",
headers={"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}"},
json={
"model": "grok-4.7",
"stream": True,
"messages": [{"role": "user", "content": "Count from one to five."}],
},
stream=True,
timeout=60,
) as response:
response.raise_for_status()
for line in response.iter_lines():
if not line:
continue
line = line.decode("utf-8")
if not line.startswith("data: "):
continue
data = line[len("data: "):]
if data == "[DONE]":
break
delta = json.loads(data)["choices"][0].get("delta", {})
text = delta.get("content")
if text:
print(text, end="", flush=True)
两个细节决定了流式传输是正常工作还是看似挂起。向 HTTP 客户端传入 stream=True,这样它不会缓冲整个响应体,并逐行迭代而非一次性读取响应。print 上的 flush=True 只关乎你在屏幕上看到的内容;没有它,即使数据块是随到随处理的,输出也可能看起来在最后突然爆发。
单独验证流式传输
不要在同一测试中同时验证流式传输和工具调用。先确认拼接后的文本对简单提示词给出了合理答案,然后逐个添加功能。如果流结束时没有哨兵,那是检查连接的信号,而不是检查模型。
工具调用选项
工具调用让模型可以请求你代码中定义的函数,而不只是生成文本。请求侧的附加方式与流式传输完全相同。
选项放在哪里
工具定义是请求体上的 tools 数组。每个条目描述一个可调用项:名称、何时使用它的描述,以及其参数的 JSON schema 描述。描述比看上去更重要——它是模型判断工具何时适用的唯一指引。
{
"model": "grok-4.7",
"messages": [
{ "role": "user", "content": "What is the weather in Lisbon right now?" }
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Return the current weather for a named city.",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "City name, e.g. Lisbon" }
},
"required": ["city"]
}
}
}
]
}
要记住的形态是 type 加 function,其中嵌套 name、description 和 parameters。如果你已有 JSON schema 定义,parameters 块就是它们该放的地方。
模型如何回复
当模型决定需要工具时,choices[0] 中的 assistant 消息不是普通文本。其 content 通常为空,并携带一个 tool_calls 数组。每个条目有一个标识符、一个与你某个定义匹配的函数名,以及一个包含 JSON 的 arguments 字符串。请解析该 arguments 字符串——它是字符串而非对象,把它当作对象是常见的错误来源。
你的代码随后运行该函数,并发送一个后续请求:原始对话、带 tool_calls 的 assistant 消息,以及一条角色为 tool 的新消息,包含调用标识符和结果。模型使用该结果生成最终答案。tool_call_id 必须与 assistant 消息中的标识符匹配;没有它,后续请求就是含糊的。
实用防护措施
- 用你发送的同一 schema 验证解析出的参数。模型受 schema 引导,但并不受其约束。
- 将返回的函数名视为不可信输入,只路由到明确的函数允许列表。
- 不要在首次测试时同时启用
stream和工具。先端到端确认非流式工具往返,然后如果你的用例需要两者,再组合使用。
上线前验证响应
在终端中能工作的一次调用,不等同于已验证的集成。请完整走一遍这份清单,然后保留脚本作为冒烟测试。
确认传输层
在解析任何内容之前,检查 HTTP 状态为 200。先解析的客户端会在真正问题为 401 时产生令人困惑的 KeyError。显式断言状态,失败时连同响应体一起记录——服务器通常会说明哪里出错了。
确认响应结构
要断言,不要假设。在成功的非流式调用中,验证三件事:choices 存在且非空,choices[0].message.content 是非空字符串,响应中回显的模型与你发送的一致。最后这项检查能捕获数量惊人的配置错误——默认模型覆盖了你请求的模型。
刻意演练错误路径
不要等到故障发生才了解代码的行为。运行三个快速负面测试:
- 移除认证头后调用,确认你的代码暴露出明确的认证错误,而不是无限重试。
- 发送缺少
model字段的请求体,确认你得到客户端错误,而不是挂起。 - 发送一个刻意很长的提示词,确认你的超时和截断处理行为符合预期。
每一项都应产生具体的、已记录的失败——而不是笼统的“出了点问题”。
单独验证增强功能
如果你启用了流式传输,确认拼接后的数据块形成连贯的答案,且哨兵结束了循环。如果你启用了工具,确认往返完成:工具调用被解析、函数被执行、结果带着匹配的标识符返回,并产生最终文本答案。将每项验证放在各自的小脚本中,这样失败会指向某一个功能。
让检查可重复
将整个序列放入一个文件,任何断言失败即以非零码退出,并在每次部署前针对你的暂存配置运行它。它应该只需几秒钟,覆盖基本调用、一次流式调用,如果你使用工具,再覆盖一次工具往返。当它通过时,你就可以相信生产中的失败在于你发送的请求,而非底层管道。
检查通过后,再运行一次 curl 调用。如果原始 JSON 与你的应用输出一致,集成就完成了。