公告: GPT Image 2.6 Flare 与 Sunburst 上新限时 8 折,立即体验
HiAPI
博客

教程

如何通过 HiAPI API 调用 kimi-k3.1-pro:curl、Python 与可直接运行的请求

一份实操集成指南,涵盖端点、请求参数、流式、工具调用和错误排查

2026年11月12日
封面图:深墨绿底色上,两组扁平缎带呈经纬交错编织成规整的开放式网格,缎带为克制的黄铜色与暗绿色。
本页内容

为什么要通过聚合 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 体。继续之前确认三件事:

  1. choices 是非空数组。
  2. choices[0].message.content 包含真实答案。
  3. 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] 结束。

四条规则能覆盖几乎所有错误:

  1. 累积,不要替换。 每个 delta 是一个片段。把 delta.content 追加到缓冲区;完整答案是按顺序拼接每个片段。
  2. 跳过空 delta。 首个和最后一个事件经常不带内容。防止追加 None。
  3. 把 [DONE] 当作终止符,而不是数据。 在那里停止读取;其后内容不属于响应。
  4. 从最后一个携带 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 不是装饰——它是模型判断何时以及如何调用工具的唯一指引。像给新同事写说明那样写它。

响应循环

  1. 发送请求。
  2. 如果 choices[0].finish_reason 表明是工具调用,读取 choices[0].message.tool_calls。每个条目有 id、函数 name 和 arguments——一个 JSON 编码的字符串,不是对象。解析它。
  3. 在你的代码中执行函数。对参数做任何处理前先校验;模型可能生成看似合理但缺字段或类型错误的载荷。
  4. 把助手的消息——包括其 tool_calls——追加到你的 messages 数组。
  5. 追加一条新消息,角色为 tool,带上匹配的 tool_call_id,结果作为 content。
  6. 再次发送请求。模型现在用工具输出组织最终答案。

第 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 才算被真正集成,而不仅仅是连上了。

立即开始构建持久 AI 产物

获取 API 密钥,通过一个基于任务(task)的 API 调用图像、视频和音频模型。

阅读 API 文档