Appearance
Kimi API 是无状态服务,每次请求不会记住上次对话内容,需要将历史消息全部传入 messages 参数。本文介绍多轮对话实现原理和 token 滑动窗口优化方案。
使用 Kimi API 进行多轮对话
Kimi API 与 Kimi 智能助手不同,API 本身不具有记忆功能,它是无状态的。每次请求 API 时,模型不知道此前的对话内容,需要手动将历史消息加入 messages 参数。
与 OpenAI API 相同:OpenAI API 同样是无状态的,多轮对话实现方式完全一致。DeepSeek API 也如此。
基础实现
python
from openai import OpenAI
client = OpenAI(
api_key="MOONSHOT_API_KEY",
base_url="https://api.moonshot.cn/v1",
)
messages = [
{"role": "system", "content": "你是 Kimi,由 Moonshot AI 提供的人工智能助手。"},
]
def chat(input: str) -> str:
messages.append({"role": "user", "content": input})
completion = client.chat.completions.create(
model="kimi-k2.6",
messages=messages
)
assistant_message = completion.choices[0].message
messages.append(assistant_message)
return assistant_message.content
print(chat("你好,我今年 27 岁。"))
print(chat("你知道我今年几岁吗?")) # 模型会记住"27 岁"关键点:
- 每次请求都要传入完整历史(user + assistant 消息)
- 必须将模型的回复也加入 messages,否则模型不知道自己说过什么
控制上下文长度(滑动窗口)
随着对话轮次增加,token 消耗线性增长。推荐只保留最近 N 条消息:
python
from openai import OpenAI
client = OpenAI(
api_key="MOONSHOT_API_KEY",
base_url="https://api.moonshot.cn/v1",
)
system_messages = [
{"role": "system", "content": "你是 Kimi,由 Moonshot AI 提供的人工智能助手。"}
]
messages = []
def make_messages(input: str, n: int = 20) -> list:
messages.append({"role": "user", "content": input})
# 保留最近 n 条消息(system 始终保留)
recent = messages[-n:] if len(messages) > n else messages
return system_messages + recent
def chat(input: str) -> str:
completion = client.chat.completions.create(
model="kimi-k2.6",
messages=make_messages(input)
)
assistant_message = completion.choices[0].message
messages.append(assistant_message)
return assistant_message.content
print(chat("你好,我今年 27 岁。"))
print(chat("你知道我今年几岁吗?"))实际使用注意事项:
- 并发场景下需要为每个用户维护独立的 messages 列表并加锁
- 生产环境建议持久化 messages(Redis / DB)
- 如果需要更精确的 token 控制,用 token 预估接口 计算
TypeScript 实现
typescript
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.MOONSHOT_API_KEY,
baseURL: "https://api.moonshot.cn/v1",
});
const systemMessages = [
{ role: "system" as const, content: "你是 Kimi,由 Moonshot AI 提供的人工智能助手。" },
];
let messages: OpenAI.ChatCompletionMessageParam[] = [];
async function chat(input: string, n = 20): Promise<string> {
messages.push({ role: "user", content: input });
const recent = messages.length > n ? messages.slice(-n) : messages;
const completion = await client.chat.completions.create({
model: "kimi-k2.6",
messages: [...systemMessages, ...recent],
});
const msg = completion.choices[0].message;
messages.push(msg);
return msg.content ?? "";
}
(async () => {
console.log(await chat("你好,我今年 27 岁。"));
console.log(await chat("你知道我今年几岁吗?"));
})();常见问题
Q: 为什么模型会"忘记"之前说的话?
A: 因为 API 是无状态的,你没有把历史消息传入 messages。确保每次请求都将完整历史(包括模型回复)传入。
Q: 上下文太长导致请求报错怎么办?
A: 触发了上下文窗口限制。kimi-k2.6 支持 256K token,通常足够长对话。如果超限,用滑动窗口只保留最近 N 条消息,或对旧消息做摘要压缩。
Q: 思考模式下 reasoning_content 要放入 messages 吗?
A: 是的,启用 thinking 模式时,每轮 assistant 消息中的 reasoning_content 必须原样保留并传入下一次请求,否则会报错。
Kimi API 是无状态的,不会自动记忆对话历史。实现多轮对话,需要在每次请求中手动带上历史 messages。本文给出完整实现方案,包括如何处理上下文长度溢出问题。
使用 Kimi API 进行多轮对话
为什么 Kimi API 没有记忆?
Kimi API 本身不具有记忆功能,它是无状态的。当你多次调用 API 时,模型不知道之前的请求内容。例如,你在上一次请求中告诉 Kimi"我今年 27 岁",下一次请求时模型并不记得这件事。
与 OpenAI API 行为相同:ChatGPT 的"记忆功能"是应用层实现的,API 本身同样无状态。DeepSeek、Kimi 等兼容 OpenAI 格式的 API 也是如此。
实现多轮对话,需要将历史消息手动加入到每次请求的 messages 参数中。
基本实现
typescript
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.MOONSHOT_API_KEY,
baseURL: "https://api.moonshot.cn/v1",
});
// 全局 messages 列表,记录完整对话历史
const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [
{
role: "system",
content: "你是 Kimi,由 Moonshot AI 提供的人工智能助手。",
},
];
async function chat(input: string): Promise<string> {
// 添加用户消息
messages.push({ role: "user", content: input });
const completion = await client.chat.completions.create({
model: "kimi-k2.6",
messages,
});
const assistantMessage = completion.choices[0].message;
// 必须把助手回复也加进去,否则下次请求模型不知道自己说过什么
messages.push(assistantMessage);
return assistantMessage.content ?? "";
}
console.log(await chat("你好,我今年 27 岁。"));
console.log(await chat("你知道我今年几岁吗?")); // 会正确回答 27 岁python
from openai import OpenAI
client = OpenAI(
api_key="MOONSHOT_API_KEY",
base_url="https://api.moonshot.cn/v1",
)
messages = [
{"role": "system", "content": "你是 Kimi,由 Moonshot AI 提供的人工智能助手。"},
]
def chat(input: str) -> str:
global messages
messages.append({"role": "user", "content": input})
completion = client.chat.completions.create(
model="kimi-k2.6",
messages=messages,
)
assistant_message = completion.choices[0].message
messages.append(assistant_message)
return assistant_message.content
print(chat("你好,我今年 27 岁。"))
print(chat("你知道我今年几岁吗?"))控制上下文长度(重要)
随着对话轮次增加,messages 越来越长,token 消耗线性增加,最终会超出模型的上下文窗口限制。
推荐方案:滑动窗口,只保留最新 N 条消息
typescript
const MAX_HISTORY = 20; // 保留最新 20 条历史消息
function makeMessages(
input: string,
history: OpenAI.Chat.ChatCompletionMessageParam[],
systemPrompt: string
): OpenAI.Chat.ChatCompletionMessageParam[] {
// 添加新消息
history.push({ role: "user", content: input });
// 截断:只保留最新的 N 条
const truncated = history.length > MAX_HISTORY
? history.slice(-MAX_HISTORY)
: history;
// System prompt 始终保留,不受截断影响
return [
{ role: "system", content: systemPrompt },
...truncated,
];
}注意:System prompt 不应该被截断,即使历史消息被截断,system 消息也要每次都带上。
生产环境的额外考量
以上示例仅覆盖最简单的场景,实际业务中还需要处理:
- 并发安全:多个请求并发时,需要为每个会话维护独立的 messages,避免竞态
- 多用户隔离:按
userId维护独立的 messages 列表 - 持久化:messages 存入数据库,服务重启后恢复会话
- 精确的 token 计数:使用 Estimate API 预算,而不是凭条数截断
- 历史摘要:被丢弃的消息先做一次总结,压缩后保留,而不是直接丢弃
常见问题
Q: 为什么我的多轮对话里模型"忘记"了之前说的话?
A: 检查是否把 assistant 回复也加入了 messages。两个方向的消息都要保存:user 消息和 assistant 消息,缺一不可。
Q: system prompt 需要每次都带吗?
A: 是的,每次请求都要带 system prompt,因为 API 无状态。最简单的做法是把 system prompt 单独存放,在构建 messages 时始终放在最前面。
Q: 截断消息后会影响对话质量吗?
A: 会。截断太激进会让模型丢失早期重要信息。可以先用 token 数控制(比保留条数更精准),或对被丢弃的消息做摘要后压缩保留。