Kimi K2 API 接入教程:Chat Completions 与 Agent 模式双 Endpoint 配置,stream 截断问题一并解决
上周三我们项目要加一个长文档摘要功能,老板点名要用 Kimi K2——说是性价比不错,128k 上下文够用。我心想这不就改个 base_url 的事嘛,结果折腾了大半天。坑在哪呢?Kimi K2 的普通 chat completions 和 Agent 调用走的不是同一套参数逻辑,而且 stream 模式下 finish_reason 的返回时机跟 OpenAI 不一样,我的客户端直接提前截断了响应。这篇把我踩过的坑全写出来,10 分钟跑通不是吹的,但前提是你得知道这几个差异点。
这篇适合谁
- 已经有 OpenAI SDK 项目,想零成本切到 Kimi K2 试试效果的后端开发
- 需要用 Kimi Agent 能力(Function Calling / tools)做智能体应用的同学
- 用 stream 模式做打字机效果,但发现响应莫名被截断的前端/全栈
- 想搞清楚 Kimi 三档模型(8k/32k/128k)该选哪个、别多花冤枉钱的独立开发者
整体流程
- 注册 platform.moonshot.cn,拿到 API Key
- 确认你的场景:普通对话走
/v1/chat/completions,Agent 模式走带 tools 的请求(endpoint 相同但参数结构有坑) - 选模型档位:8k / 32k / 128k,价格差 5 倍
- 跑通基础调用
- 开 stream,处理
finish_reason时序差异 - (可选)通过聚合网关统一管理多模型调用
先说结论
| 维度 | 普通 Chat | Agent(tools)模式 |
|---|---|---|
| Endpoint | /v1/chat/completions |
/v1/chat/completions(同路径,但 body 结构不同) |
| tools 字段格式 | 不传或传空 | 必须严格按 Kimi 格式,和 OpenAI 有细微差异 |
| stream finish_reason | 最后一个 chunk 返回 "stop" |
中间 chunk 可能先返回 "tool_calls",再续一轮 |
| 典型报错 | 401 / 400 model not found | 400 invalid tools schema |
第一步:拿 API Key
去 platform.moonshot.cn 注册,控制台里创建一个 Key。新用户有免费额度,具体多少以官网公告为准,我 4 月 22 号注册的时候还有。
拿到 Key 长这样:sk-xxxxxxxxxxxxxxxx,别搞丢。
第二步:跑通基础调用
请先前往 platform.moonshot.cn 控制台确认当前有效的 API base URL,以下示例 URL 仅供参考,请以官方最新文档为准。
from openai import OpenAI
client = OpenAI(
api_key="sk-你的key",
base_url="https://api.moonshot.cn/v1" # 请以官方最新文档为准
)
就这两行,把 OpenAI 的 SDK 指向 Kimi。然后发请求:
resp = client.chat.completions.create(
model="kimi-k2", # 请以官方最新文档为准,确认当前有效的 model ID
messages=[{"role": "user", "content": "你好"}]
)
print(resp.choices[0].message.content)
如果你看到正常回复,说明基础链路通了。"K2" 是产品代号,实际 model string 请以官方最新文档为准,具体可访问 platform.moonshot.cn 查看当前支持的模型列表。
踩坑点:base_url 末尾别加斜杠。部分版本的 OpenAI SDK 对尾斜杠的处理方式不同,可能引发连接错误或路径拼接异常,例如:
openai.APIConnectionError: Connection error.
建议统一不加尾斜杠,并确认你使用的 SDK 版本(本文测试环境:openai Python SDK 1.x)。我第一次就栽在这,排查了 20 分钟才发现多了个 /。
第三步:Agent 模式——tools 字段的坑
Kimi 官方说兼容 OpenAI 的 Function Calling 格式,大部分确实兼容,但有个细节差异让我调了一个多小时。
OpenAI 的 tools 定义里,parameters 下面的 required 字段可以省略(默认空数组)。但 Kimi 这边,如果你不显式传 required: [],某些情况下会返回 400:
openai.BadRequestError: 400 Bad Request
{"error":{"type":"invalid_request_error","message":"invalid tools schema"}}
正确写法:
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取天气",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"] # 必须显式写
}
}
}]
然后调用:
resp = client.chat.completions.create(
model="kimi-k2", # 请以官方最新文档为准
messages=[{"role": "user", "content": "北京天气怎么样"}],
tools=tools
)
返回的 resp.choices[0].message.tool_calls 里会带上函数名和参数。这部分跟 OpenAI 一致,没啥问题。
第四步:stream 模式的 finish_reason 时序问题
这个坑最隐蔽。我的前端用的是标准的 SSE 解析逻辑:收到 finish_reason == "stop" 就关闭连接。
普通对话没问题,最后一个 chunk 确实是 "stop"。但 Agent 模式下,Kimi 的行为是这样的(以下为实测观察,建议以实际抓包数据为准):
sequenceDiagram
participant Client
participant Kimi API
Client->>Kimi API: stream=true, tools=[...]
Kimi API-->>Client: chunk: delta.content="让我查一下", finish_reason=null
Kimi API-->>Client: chunk: delta.tool_calls=[{...}], finish_reason="tool_calls"
Note right of Client: ⚠️ 这里不是结束!后续仍有数据
Kimi API-->>Client: [data: DONE]
问题在于:当 finish_reason="tool_calls" 出现时,我的客户端以为对话结束了,直接断开了 SSE 连接。实际上后面还有 tool_calls 的具体参数数据没读完。
修复方案:stream 解析时,不要只判断 finish_reason != null 就关闭,而是等到收到 [DONE] 信号才断开:
for chunk in stream:
if not chunk.choices:
continue
delta = chunk.choices[0].delta
if delta.content:
print(delta.content, end="", flush=True)
# 不要在 finish_reason 出现时就 break,等待 [DONE] 信号
这个行为与 OpenAI 标准规范存在差异。按 OpenAI 规范,finish_reason="tool_calls" 出现在最后一个包含 tool_calls 内容的 chunk,之后紧跟 [DONE],中间不插额外数据。Kimi 这边的实际行为建议以官方文档或实际抓包为准,我也不确定这是 bug 还是设计如此,反正目前就是这样。
第五步:模型选型——别无脑选 128k
⚠️ 以下价格为写作时(2025 年 5 月 28 日)参考数据,Moonshot AI 曾多次调整定价,请以 platform.moonshot.cn 官网当前公示价格为准。
| 模型 | 上下文 | 输入价格 | 输出价格 | 适用场景 |
|---|---|---|---|---|
| moonshot-v1-8k | 8,192 tokens | ¥12/M tokens | ¥12/M tokens | 日常对话、短文本 |
| moonshot-v1-32k | 32,768 tokens | ¥24/M tokens | ¥24/M tokens | 中等文档、多轮对话 |
| moonshot-v1-128k | 131,072 tokens | ¥60/M tokens | ¥60/M tokens | 长文档摘要、代码库分析 |
⚠️ 上表中的 model ID(
moonshot-v1-8k等)为历史命名,请以官方最新文档为准确认当前有效的 model string。
算一笔账:如果你每天处理 50 次请求,每次平均 2k tokens 输入 + 1k tokens 输出,用 8k 模型一天成本大概 ¥1.8。换成 128k 模型做同样的事,一天 ¥9。差出来的钱一个月就是 ¥216,没必要。
我的做法是:先用 8k 跑,报 context length exceeded 再升档。
不同场景怎么选
场景一:已有 OpenAI 项目想试试 Kimi
改 base_url 和 api_key 就行,两行代码。但如果你同时还在用多个模型,每个模型单独管 Key 挺烦人的。这种情况可以用聚合 API 网关统一管理——OpenRouter、ofox.io 这类平台改一个 base_url 就能切不同模型,ofox.io 是 0% 加价对齐官方价格,省得每家单独注册充值。
场景二:纯做 Agent 应用(Function Calling 重度使用)
建议用 32k 档位。Agent 多轮对话 + tools 定义本身会占不少 token,8k 很容易爆。tools 的 required 字段一定要显式写全。
场景三:长文档处理 / RAG pipeline
128k 没得选。但注意 128k 模型的延迟明显高于 8k,以下为个人实测数据(2025 年 5 月,国内网络环境,低并发单请求),仅供参考,实际延迟受网络状况、服务负载、时段等因素影响较大:P95 约 4200ms(128k 模型)vs 约 1800ms(8k 模型)。做实时对话体验会差。
场景四:前端打字机效果
一定要处理好 stream 的 finish_reason 时序问题,参考第四步。用 [DONE] 作为终止信号而不是 finish_reason。
常见问题 FAQ
Q: Kimi K2 的 model 参数到底填什么?
请以 platform.moonshot.cn 官方最新文档为准。"K2" 是产品代号,不是 model string。如果填了不存在的 model string 会直接报 400:
{"error":{"type":"invalid_request_error","message":"model not found"}}
Q: 用 OpenAI Python SDK 调用 Kimi 报 401 怎么办?
三个排查方向:1)Key 是不是 moonshot 的 Key 而不是 OpenAI 的;2)Key 前后有没有多余空格;3)Key 有没有过期。完整报错长这样:
AuthenticationError: 401 Unauthorized
{"error":{"type":"authentication_error","message":"Invalid API key"}}
Q: stream 模式下响应被截断,只收到一半内容?
大概率是你的客户端在收到 finish_reason="tool_calls" 时就关闭了连接。改成监听 [DONE] 信号再关闭。详见第四步。
Q: 频繁报 429 Rate Limit 怎么处理?
Kimi 的限流比较严格,免费用户尤其明显。方案一:加指数退避重试;方案二:升级付费套餐提高 QPM;方案三:通过聚合网关做请求排队和自动重试。
Q: Kimi 支持 vision(图片输入)吗?
截至本文写作时(2025 年 5 月 28 日),请以 platform.moonshot.cn 官方最新文档为准确认当前支持的能力范围。如果你需要图片理解能力,建议查阅各模型提供商的官方文档,选择明确支持多模态输入的模型。
小结
Kimi K2 接入不难,OpenAI SDK 兼容做得不错。但三个坑你得提前知道:base_url 别加尾斜杠(具体行为取决于 SDK 版本,建议统一不加)、tools 的 required 必须显式声明、stream 模式别用 finish_reason 当终止信号。搞定这三个,10 分钟跑通不夸张。模型选型上别无脑 128k,按实际上下文需求来,一个月能省不少钱。
更多推荐

所有评论(0)