Skip to content

Feature request: 支持 OpenAI Responses API previous_response_id + 分支会话(rewind/fork) #139

@RadonX

Description

@RadonX

背景 / Background

回退到第 N 轮,从那里开始一个新的思路,相当于在对话时间线上「开分支」。

在 OpenAI 的新 Responses API 中,引入了 previous_response_id 机制,可以让服务端记住整条对话链(包括输入和输出),后续请求只需要发送「新一轮的用户输入 + previous_response_id」即可继续会话,甚至可以从同一个 previous_response_id 开出多条不同分支。

需求 / What I’d like Kode to support

希望 Kode 在对接 OpenAI(尤其是 GPT-5.x / Codex 系列 / Responses API-only 模型)时:
1. 优先使用 Responses API + previous_response_id 来维持上下文,而不是每次都在本地手动拼接完整历史;
2. 在 CLI 的交互层面上:
• 保持现在「可以回退到任意历史轮次」的 UX;
• 当用户从某一轮回退并继续对话时,内部使用这一轮对应的 response.id 作为新的 previous_response_id,在服务端开出一个新的会话分支;
3. 在用户显式编辑历史内容 / 压缩上下文 / 超出保留期时,能够自动退回到「本地构造 input[]、不传 previous_response_id」的兼容模式。

简单说:

本地是「对话树」,服务端是「线性链条」,希望 Kode 自动帮我把这两者对齐好。

预期行为 / Expected behavior

以一个典型例子说明预期效果:
1. 用户进入 Kode,连续对话 5 轮,形成节点:T1 → T2 → T3 → T4 → T5;
2. 每一轮对应一次 responses.create 调用,并返回 response.id,内部存为:
• Tn.responseId = ;
3. 用户使用 Kode 的 UI/快捷键「回退到 T3」;
4. 用户输入新的问题 Q’;
5. Kode 对 OpenAI 的调用为:

const res = await client.responses.create({
  model: "gpt-5.1-codex",        // 示例
  input: [{ role: "user", content: QPrime }],
  previous_response_id: T3.responseId,
});
  1. 这次返回的新 response.id 记为 T3b.responseId,在本地对话树上形成分支:

    • 旧分支:T1 → T2 → T3 → T4 → T5
    • 新分支:T1 → T2 → T3 → T3b → …

用户在 UI 中可以自由切换不同分支继续聊,OpenAI 后端通过不同的 previous_response_id 分别维持上下文,无需每次由 Kode 手动重发全部历史。

实现建议 / Proposed implementation

下面是一个尽量不侵入整体架构的实现思路,仅供参考:

  1. 抽象本地对话结构为一棵树

interface TurnNode {
id: string; // 本地节点 id
parent: string | null;
responseId: string | null; // OpenAI responses.create 返回的 id
userMessage: string;
assistantMessage: string;
createdAt: number;
historyEdited: boolean; // 任意祖先是否被编辑/裁剪过
}

interface ConversationTree {
root: string;
nodes: Map<string, TurnNode>;
current: string; // 当前光标所在节点
}

  1. 发送请求时的决策逻辑
    • 如果当前节点 current 及其祖先都没有被用户编辑/裁剪,且 responseId 仍然有效:
    • 调用 responses.create 时:

const res = await client.responses.create({
model,
input: [{ role: "user", content: userInput }],
previous_response_id: nodes[current].responseId,
});

•	否则(例如用户改了历史消息、手动清理了部分上下文、或者超过一定时间阈值):
•	回退为「兼容模式」:根据当前分支的所有 messages 手动构造 input[],不传 previous_response_id;
•	把这次返回的 response.id 当作新链的起点。
  1. 回退(rewind)和分支(fork)
    • 回退:只移动 tree.current = somePastTurnId,不与后端交互;
    • 在回退位置发送新请求时:
    • 如果满足上面的 previous_response_id 条件,就从该节点继续往下链,形成分支;
    • 在本地树里挂一个新的子节点即可。

  2. 可选:配置项
    新增一个高级配置项,例如:

{
"openai": {
"useResponsesPreviousId": true,
"maxPrevIdAgeMinutes": 60
}
}

方便用户在有特殊需求(比如严格手动控制 prompt)的情况下关闭这一优化。

为什么值得做 / Why this is useful
• 更好的模型表现
对于 GPT-5 / Codex 这类针对长链式“agent 工作流”优化的模型,让服务端来管理上下文,而不是每次本地字符串拼接,会更接近它们在官方产品中的使用方式,也有利于利用服务器端的优化(缓存、压缩等)。
• 更低的 token 开销
重复历史 prompt 会浪费大量 token;previous_response_id 允许后续请求只发送「新输入」,其余上下文由服务端复用,理论上可以显著降低消耗。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions