Skip to content

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 数控制(比保留条数更精准),或对被丢弃的消息做摘要后压缩保留。