教程
如何通过 HiAPI API 调用 kimi-k3.1-pro:curl、Python 与可直接运行的请求
一份实操集成指南,涵盖端点、请求参数、流式、工具调用和错误排查
本页内容
为什么要通过聚合 API 调用 kimi-k3.1-pro
每一次模型集成都带着同样的隐性成本:第一天很有趣,第二个月就是维护。当你把 kimi-k3.1-pro 直接接入应用时,你就一并继承了该提供方的认证方案、错误语义、配额模型和发布节奏。对单模型原型来说这没问题。可一旦你想要第二个模型、第二个环境,或者某个端点出问题时想要一条降级路径,它就会变成摩擦。
聚合 API 把这些差异收敛成一份契约。HiAPI 以 OpenAI 兼容的请求形态对外提供 kimi-k3.1-pro,这意味着你在这里写的客户端代码不是一次性的。换掉模型标识符,同样的请求结构就能沿用到平台上的其他模型——相同的请求头、相同的消息数组、相同的流式封装。
具体到 kimi-k3.1-pro,这有三条实际意义:
- 单一凭证面。 你只需管理一个 API Key 和一个基础 URL,而不是一组不断增多的提供方账号。
- 可预测的载荷。 请求与响应模式在各模型间保持稳定,因此你的解析、重试和日志层只需写一次。
- 迁移成本低。 如果后续某类负载更适合别的模型,改动只是载荷里的一个字符串,而不是重写传输层代码。
本指南的其余部分就是集成本身:请求解剖、可直接复制的 curl 调用、等价的 Python 客户端,然后是生产环境中通常最容易出问题的两个特性——流式与工具调用——最后是一份错误排查清单。
开始之前你需要准备
- 一个 HiAPI API Key,存放在你项目保存密钥的地方。
- 从 HiAPI 控制台获取你账号的 API 基础 URL。下面所有示例都用
https://api.hiapi.ai/v1作为占位基址;上线前请到控制台确认真实主机。 - kimi-k3.1-pro 的模型标识符字符串——从控制台的模型列表中原样复制,因为标识符区分大小写。
- 用于首次测试的
curl,以及装有requests库的 Python 3.9+,用于客户端示例。
请求形态:端点、请求头与模型参数
每次对话调用都遵循相同的解剖结构。一次把每个部分弄明白,之后就不必逐字段调试。
端点
对话补全位于基础 URL 下的单一路径:
POST {BASE_URL}/chat/completions
这就是本指南所有示例所需的唯一端点。流式和工具调用不是独立的端点——它们是同一请求体上的选项。
请求头
真正起作用的只有两个请求头:
Authorization: Bearer $HIAPI_API_KEY
Content-Type: application/json
密钥放在 Authorization 头中,带 Bearer 前缀。不要把它放进 URL、查询参数或请求体中——这三种做法都会让它泄漏到日志和错误报告里。显式设置 Content-Type;有些 HTTP 客户端不会替你推断。
真正重要的请求体字段
一个最小请求体有四个有意义的字段:
model—— kimi-k3.1-pro 标识符。必填。这是首次尝试时最容易写错的字段,因为它必须完全匹配。messages—— 有序的对象数组,每个对象包含role(system、user或assistant)和content。必填且非空。顺序即你的对话历史;模型看到的正是这个数组,别无其他。stream—— 布尔值,默认false。为true时,响应以服务器发送事件的形式到达,而不是单一 JSON 体。下文有专门章节。temperature—— 可选的采样控制。较低的值产生更确定的输出;较高的值产生更多变化。每个用例选一次值就别动它,不要每次请求都调。
随着集成成熟,你可以添加的可选字段:
max_tokens—— 生成长度上限。可用作成本和延迟的护栏。注意它限制的是输出,不是输入。tools—— 工具定义数组,后文详述。tool_choice—— 控制模型是否以及如何调用你提供的工具。stop—— 一个或多个终止生成的序列。
你应该预期的响应
非流式响应返回一个 JSON 对象,其 choices 数组至少包含一个条目。在该条目中你会找到一个 message 对象,含 role 和 content,外加一个 finish_reason,告诉你生成为何停止。始终根据 finish_reason 分支处理,而不要假设内容是完整的——被截断的响应仍有内容,只是不是完整答案。
针对该结构编写解析器,而不是针对字符串化响应。这样无论你在平台上如何更换模型,它都能存活。
一个可运行的 curl 请求
从这里开始。如果它返回有效响应,说明你的密钥、基础 URL 和模型标识符都正确,之后任何问题都在代码里而不是凭证上。
curl -sS "$HIAPI_BASE_URL/chat/completions" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "kimi-k3.1-pro",
"messages": [
{"role": "system", "content": "你是一个简洁的技术助手。"},
{"role": "user", "content": "用两句话解释什么是幂等键。"}
],
"temperature": 0.3,
"max_tokens": 256
}'
各字段说明
-sS让 curl 对进度保持安静,但仍暴露错误。少了第二个S,失败的请求可能静默退出,白白浪费一轮调试。- 环境变量 让密钥不进入 shell 历史,也不留在终端回滚缓冲里。在你的 shell 配置文件或已加载的
.env中导出HIAPI_BASE_URL和HIAPI_API_KEY。 - 用单引号包裹请求体 对 POSIX shell 来说是正确的,只要载荷里不含单引号。一旦需要单引号,就把请求体移到文件里,改用
-d @body.json传入——这样能避免一类看起来像 JSON 格式错误的引号 bug。 system消息 是可选的,但值得包含。它是固定语气和输出格式最省事的地方。max_tokens在此示例中刻意保守。它同时限制冒烟测试的延迟和花费。
解读结果
成功调用返回 HTTP 200 和 JSON 体。继续之前确认三件事:
choices是非空数组。choices[0].message.content包含真实答案。choices[0].finish_reason是正常完成值,而不是长度截断。
如果响应体是错误对象,跳到故障排查章节——错误消息通常会点明具体出错字段。
可重复性小贴士
把那个 curl 调用保存为仓库里的 shell 脚本。一条命令对实时 API 做冒烟测试,是区分“我的集成坏了”和“我的账号或密钥变了”的最快方法。每当看起来不对劲,先跑它。
同样的请求用 Python 写
Python 版应该看起来很无聊——这正是目标。所有有意思的决策(模型标识符、基础 URL、超时)都应放在配置里,而不是散落在各调用点。
import os
import requests
BASE_URL = os.environ["HIAPI_BASE_URL"].rstrip("/")
API_KEY = os.environ["HIAPI_API_KEY"]
MODEL = os.environ.get("HIAPI_MODEL", "kimi-k3.1-pro")
HEADERS = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
def chat(messages, temperature=0.3, max_tokens=512, timeout=60):
payload = {
"model": MODEL,
"messages": messages,
"temperature": temperature,
"max_tokens": max_tokens,
}
resp = requests.post(
f"{BASE_URL}/chat/completions",
headers=HEADERS,
json=payload,
timeout=timeout,
)
resp.raise_for_status()
data = resp.json()
choice = data["choices"][0]
return choice["message"]["content"], choice.get("finish_reason")
if __name__ == "__main__":
text, reason = chat([
{"role": "system", "content": "你是一个简洁的技术助手。"},
{"role": "user", "content": "用两句话解释什么是幂等键。"},
])
print(reason)
print(text)
为什么这样组织
- 用
json=而不是data=——requests库会替你序列化字典并设置内容类型。手工序列化容易引入转义错误。 - 始终
timeout=60—— 未设置超时是工作进程挂起直到被管理器杀掉的最常见原因。服务中的每次网络调用都应设置超时。 - 解析前先
raise_for_status()—— 这会把 HTTP 层失败在调用点转成异常,而不是让格式错误的响应体流向下游。 MODEL来自环境 —— 切换模型时,模型标识符是唯一变化的东西。把它放在配置里意味着切换是部署变量,而不是代码改动。- 返回
finish_reason—— 调用方几乎总是需要知道答案是否被截断。
保持跨模型可复用
chat(messages, ...) 的签名刻意不暴露任何模型特定内容。如果以后把更便宜的负载路由到其他模型,要么传显式模型覆盖参数,要么用不同的 MODEL 构造第二个客户端对象。传输、请求头、重试策略和错误处理都保持不变。
关于重试的说明
不要在 chat() 内部盲目重试。在知道该操作是否可安全重复的层重试——失败的补全通常安全,但已经在下游改变状态的请求则不安全。只对瞬时传输失败和限流响应重试,且只用封顶的指数退避。
流式响应而不弄坏客户端
流式是聊天 UI 感觉即时与空白十秒之间的差别。它也引入了最多的集成 bug,因为响应不再是一个 JSON 文档。
如何开启
在请求体中加入 "stream": true。请求的其他部分不变。
with requests.post(
f"{BASE_URL}/chat/completions",
headers=HEADERS,
json={"model": MODEL, "messages": messages, "stream": True},
stream=True,
timeout=120,
) as resp:
resp.raise_for_status()
for line in resp.iter_lines(decode_unicode=True):
if not line or not line.startswith("data:"):
continue
chunk = line[len("data:"):].strip()
if chunk == "[DONE]":
break
# 将 chunk 解析为 JSON 并读取 choices[0].delta.content
实际到达的内容
响应体是一系列服务器发送事件。每个事件是一行 data:,携带一个小 JSON 对象。choices[0] 里是 delta 对象而不是 message 对象——delta 通常包含 content 的一个片段,也可能什么都不包含。流以哨兵行 data: [DONE] 结束。
四条规则能覆盖几乎所有错误:
- 累积,不要替换。 每个 delta 是一个片段。把
delta.content追加到缓冲区;完整答案是按顺序拼接每个片段。 - 跳过空 delta。 首个和最后一个事件经常不带内容。防止追加
None。 - 把
[DONE]当作终止符,而不是数据。 在那里停止读取;其后内容不属于响应。 - 从最后一个携带
finish_reason的事件读取它。 流式模式下,完成状态出现在较晚的 delta 中,而不是顶层字段。
值得点名的坑
- 忘记在客户端调用中设置
stream=True。 你会把整个流当作一个缓冲体接收,这违背了初衷,且看起来像一个非常慢的非流式请求。 - 超时按非流式设置。 长生成的流式持续时间比短请求长。给流式路径更宽松的读取超时,并考虑一个空闲计时器,在选定间隔内没有字节到达时中止。
- 逐行解析但不检查前缀。 不是每一行都是数据。空行分隔事件;忽略它们。
- 假设 delta 有
content键。 工具调用流式会在 delta 中放入其他键。始终防御性读取。 - 忘记客户端消费者。 服务器把流写入缓冲区且只在结束时输出,技术上算流式,实践上不算。确保下游在片段到达时渲染它们。
何时不该流式
批处理任务、后台摘要,以及任何输出由程序消费而非展示给人类的东西,通常不流式更简单。非流式响应只需一次解析,更易于记录日志,也更容易按模式校验。在延迟对用户可见的地方使用流式;不要默认流式。
向流程中添加工具调用
工具调用让模型请求你的代码执行操作,而不是编造答案。协议是两轮对话:你声明工具,模型可能以调用请求回应,你执行它,然后把结果发回。
声明工具
在请求中加入 tools 数组。每个条目包裹一个函数描述:
{
"model": "kimi-k3.1-pro",
"messages": [{"role": "user", "content": "里斯本天气怎么样?"}],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "返回指定城市的当前天气。",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名"}
},
"required": ["city"]
}
}
}
],
"tool_choice": "auto"
}
函数及其每个参数上的 description 不是装饰——它是模型判断何时以及如何调用工具的唯一指引。像给新同事写说明那样写它。
响应循环
- 发送请求。
- 如果
choices[0].finish_reason表明是工具调用,读取choices[0].message.tool_calls。每个条目有id、函数name和arguments——一个 JSON 编码的字符串,不是对象。解析它。 - 在你的代码中执行函数。对参数做任何处理前先校验;模型可能生成看似合理但缺字段或类型错误的载荷。
- 把助手的消息——包括其
tool_calls——追加到你的messages数组。 - 追加一条新消息,角色为
tool,带上匹配的tool_call_id,结果作为content。 - 再次发送请求。模型现在用工具输出组织最终答案。
第 4 步是大家会跳过的,也正是会坏掉的那一步。如果你只发回工具结果而没有助手的工具调用轮次,对话就有悬空引用,模型没有记录它曾要求什么。
处理多个调用
响应可能在同一轮中包含多个工具调用。逐个执行,并为每个调用返回一条 tool 消息,各自携带自己的 tool_call_id。不要合并它们。
控制调用
tool_choice 是你的策略杠杆:auto 让模型决定,要求特定工具则强制调用一个,禁用工具则告诉它直接回答。保持预算——如果工具持续失败而模型持续重试,工具循环可能跑很久。在你自己的循环里限制迭代次数,达到上限时返回清晰消息,而不是让请求磨下去。
将工具输出视为不可信
模型把工具结果当文本读取。保持它们小、结构化、不含指令,并在你这边校验参数。返回无界文本的工具既是成本问题也是正确性风险。
排查常见错误
大多数集成失败落入五个桶。在改提示词之前先顺着这份清单排查。
1. 认证与授权
- 症状: HTTP 状态在 401/403 范围。
- 检查: 密钥存在、粘贴时没有前后空白,且恰好带
Bearer前缀。 - 检查: 基础 URL 与 HiAPI 控制台显示的一致——过时或粘贴错误的主机会产生令人困惑的认证错误。
- 修复: 重新导出环境变量,然后重跑 curl 冒烟测试,再动代码。
2. 格式错误的载荷
- 症状: 命名某字段的请求校验错误,或无法解析的响应体。
- 检查: 请求体是有效 JSON。手工构建时,用一次快速
json.tool校验;代码构建时,确保字典没有被意外多嵌套一层。 - 检查:
messages非空,且每个条目都有role和content。 - 检查:
role值来自支持的集合。像role: "human"这样的拼写错误在某些客户端里是静默错误而非明显错误。 - 检查:
tools条目有type: "function"和嵌套的function对象。扁平化定义是最常见的工具调用错误。
3. 模型标识符
- 症状: 未找到类错误,或提示该模型对你的账号不可用。
- 检查: 标识符从控制台原样复制。大小写和分隔符都重要。
- 检查: 模型已为你的密钥启用。即使标识符有效,你的账号不能调用的模型仍会失败。
4. 上下文与长度限制
- 症状: 关于请求超过最大上下文的错误,或响应以与长度相关的
finish_reason结束。 - 检查: 你的系统提示词、消息历史以及任何工具定义的总和。工具模式计入预算,且容易被遗忘。
- 修复: 裁剪历史、摘要早期轮次或拆分任务。对于输出截断,如果负载确实需要更多,就提高
max_tokens;否则保持有界。
5. 速率限制与超时
- 症状: 负载下的限流状态,或客户端读取超时。
- 检查: 重试是否在没有任何退避的情况下发起。紧密的重试循环会把短暂限流变成持续限流。
- 修复: 遵从响应指引的限流信号,使用带抖动的封顶指数退避,并控制每个密钥的并发。对于超时,先检查客户端超时值——客户端限制短于模型生成时间正好产生此症状。
通用调试纪律
每次失败都记录四件事:HTTP 状态、响应体、模型标识符,以及是否设置了 stream。这四项能解释绝大多数事故。并保持 curl 冒烟测试是最新的——当它通过而你的应用失败时,你就知道问题在代码里,不在凭证上。
阅读错误消息
聚合层通常明确说明哪个字段导致了拒绝。抵制改写提示词的冲动。读消息,修被点名的字段,重试一次。如果同样错误持续,在深入之前先对照冒烟测试验证请求。
上线之前:生产检查清单
“在我终端里能跑”和“能在生产环境跑”之间的差距,主要是一份没人写下来的清单。这里有一份值得抄走的。
配置与密钥
- API 密钥存放在密钥管理器中,绝不在源码控制里,也绝不在 URL 或日志行中。确认你的 HTTP 客户端无法在调试级别打印
Authorization头。 - 基础 URL 和模型标识符是配置,不是散落在代码库中的常量。对照控制台确认基础 URL。
- 按计划轮换密钥,并在任何团队成员离开后轮换。你的客户端代码应无需改动即可拾取轮换后的密钥。
请求卫生
- 每次出站调用都有显式超时,流式路径有更宽松的超时加空闲中止规则。
- 每条调用路径都设置了
max_tokens。无界生成就是无界账单。 - 采样设置按用例固定并记录在案,这样需要调试时结果可复现。
响应处理
- 你的解析器根据
finish_reason分支,并把截断视为独立结果而非完整答案。 - 流式累积既对短响应测试,也对长响应测试,包括提前结束的响应。
- 工具调用参数在使用前被校验,且助手的工具调用轮次总是回显到消息数组中。
- 工具循环有迭代上限和清晰的失败消息。
失败行为
- 重试有界,使用带抖动的封顶指数退避,且仅适用于可安全重复的操作。
- 限流响应被遵从,而不是被猛砸。
- 失败记录状态、响应体、模型标识符和 stream 标志——这四项字段才能真正解决事故。
- curl 冒烟测试在 CI 中或按计划运行,这样你从仪表盘而不是客户那里得知凭证或模型变更。
成本与容量
- 你清楚用量如何计费——平台按处理的 token 计费,因此长提示词和长生成都重要。到控制台的价格面板查看当前费率。
- 你知道哪类负载是最大消费者,以及它需要完整模型还是更轻量的模型。
- 你有降级路径:如果某次调用反复失败,应用会明显降级而不是静默挂起。
最后一个问题
问一问:不是写这段集成的人,能否观测它。如果你团队里的开发者能读懂冒烟测试、错误日志字段和配置,并理解如何诊断失败,你就完成了。如果不能,清单还没完成。
有了可运行的 curl 调用、可复用的 Python 客户端和这份清单,kimi-k3.1-pro 才算被真正集成,而不仅仅是连上了。