Skip to main content
对话与会话

多轮对话

一次性、无状态查询用 query()(Java 为 QoderClient.query())即可,见 快速开始。要在同一会话里发多条用户消息,三个 SDK 的方式不同:
  • TypeScript:给 query() 传入一个按顺序产出用户消息的异步消息流,输入流结束后会话自动关闭。
  • Python:使用 QoderSDKClient——它维护长连接,可以根据模型回复决定下一句。
  • Java:用 client.openSession(options) 打开 QoderSession。调用 session.send(...) 发送一条新消息,再从同一个消息游标读取回复,读到 ResultMessage 表示这一轮结束。

多消息会话

import { qodercliAuth, query, type SDKUserMessage } from '@qodercn-ai/qodercn-agent-sdk';

async function* messages(): AsyncGenerator<SDKUserMessage> {
  yield {
    type: 'user',
    message: { role: 'user', content: [{ type: 'text', text: 'Analyze this codebase for security issues.' }] },
    parent_tool_use_id: null,
  };

  // Wait for any external condition before sending the next message.
  await new Promise((resolve) => setTimeout(resolve, 2000));

  yield {
    type: 'user',
    message: { role: 'user', content: [{ type: 'text', text: 'Now write a brief report.' }] },
    parent_tool_use_id: null,
  };
}

for await (const msg of query({
  prompt: messages(),
  options: {
    auth: qodercliAuth(),
    allowedTools: ['Read', 'Grep'],
  },
})) {
  if (msg.type === 'result' && msg.subtype === 'success') {
    console.log(msg.result);
  }
}
通常,发送用户消息后,模型会开始回复。在回复结束前发送的新消息,会在 priority 指定的时机处理。TypeScript 中输入消息流结束后会话自动关闭,消息字段定义见 SDKUserMessage;Python 中每次调用 client.query(...) 追加一轮输入,再用 client.receive_response() 消费到本轮回复结束。 在 Java 中,每条输入是一个 UserInput。send() 完成只表示输入已写入 CLI,不表示回复结束。同一个会话的消息流只能消费一次,应跨轮复用同一个游标;关闭游标会同时关闭会话。

预先启动 CLI(Java)

Java 还可以在拿到用户输入之前启动 CLI。client.prepare(options) 完成启动、认证和初始化,但不发送输入,用来消除首次请求的启动延迟:
Java
// Prewarm at application startup: start the CLI and finish authentication and initialization without sending input.
PreparedQuery prepared = client.prepare(options).toCompletableFuture().join();

// When user input arrives, turn it into a session; toQuery(input) turns it into a one-shot query instead.
QoderSession session = prepared.toSession();
session.send(UserInput.text("Check this codebase for security issues."));
PreparedQuery 只能转换一次。转换后由新的 QoderSession 或 QoderQuery 负责释放资源;一直没有用上时,调用 close() 丢弃它。

运行中插话

模型回复期间,仍可继续向同一会话发送新消息:
yield {
  type: 'user',
  message: { role: 'user', content: [{ type: 'text', text: 'Stop the current direction and analyze only the failing tests.' }] },
  parent_tool_use_id: null,
  priority: 'now',
};
priority 决定消息何时交给会话:
值行为
now停止当前回复,立即处理这条消息
next默认值;在下一个合适的时机处理
later等当前回复结束后处理
相同优先级的消息按发送顺序处理。priority 为 now 适合立即改变当前方向;如果只想停止当前回复、不发送新消息,应使用 中断当前回复。 Java 用 UserInput.Builder 的 priority(SendPriority.NOW / NEXT / LATER) 设置优先级。Java 的 UserInput 默认优先级是 NOW,不是 next;运行中追加消息时,按需要显式设置。

添加上下文但不触发回复

设置 shouldQuery: false(TypeScript)/ should_query=False(Python)/ shouldQuery(false)(Java)会把消息加入对话,但不会仅凭这条消息触发回复。消息的处理时机仍由 priority 决定。
yield {
  type: 'user',
  message: { role: 'user', content: [{ type: 'text', text: 'All subsequent suggestions must be compatible with Python 3.10.' }] },
  parent_tool_use_id: null,
  shouldQuery: false,
};

中断当前回复

调用 interrupt() 可以停止当前回复,但不会关闭会话,之后仍可继续对话。TypeScript 中在 query() 返回的对象上调用;Python 中只有 QoderSDKClient 提供运行时中断能力,一次性 query() 迭代器不提供。Java 在 QoderQuery 或 QoderSession 上调用。
import { qodercliAuth, query } from '@qodercn-ai/qodercn-agent-sdk';

const q = query({
  prompt: 'Inspect all files in the repository.',
  options: { auth: qodercliAuth() },
});

const interruptTimer = setTimeout(() => {
  void q.interrupt().catch(console.error);
}, 5_000);

try {
  for await (const msg of q) {
    console.dir(msg, { depth: null });
  }
} finally {
  clearTimeout(interruptTimer);
}
Java 的 interrupt() 返回 InterruptResult,成功只表示 CLI 接受了中断请求,本轮何时结束仍以 ResultMessage 为准。 interrupt() 不会清空排队消息,之后仍可发送下一轮输入。如果某条排队消息不能继续执行,请使用取消排队消息。结束整个会话的方式见 管理会话生命周期。

取消排队消息

给需要跟踪的消息设置会话内唯一的 UUID(TypeScript 为消息上的 uuid 字段,Python 为 message_uuid 参数),再调用取消方法,取消尚未开始执行的消息。Java 不需要自己生成 ID:用 session.submit(input) 代替 send(input) 发送,返回的 MessageSubmission 立即带有 SDK 分配的 MessageId:
const cancelled = await q.cancelAsyncMessage(uuid);
取消成功返回 true;消息不存在或已无法取消时返回 false。未设置 UUID 的消息无法通过该方法取消。不要在同一会话内复用 UUID。 Java 中,还没写入 CLI 的消息由 SDK 在本地取消,这条消息的 written() 以 CancellationException 结束,会话不受影响。已经开始写入的消息,只有 CLI 支持对应能力时才会尝试远端取消,否则返回 false。只需要写入确认时用 send(),需要在写入前拿到 ID 时用 submit()。

管理会话生命周期

  • TypeScript:单条字符串输入处理完成或输入消息流结束后,SDK 会自动关闭会话;提前结束可以用 AbortController,也可以直接调用 q.close()。
  • Python:QoderSDKClient 的连接生命周期由调用方掌握,推荐用 async with 自动管理,也可以手动 connect() / disconnect()。
  • Java:一次性查询读到消息流结束后,关闭游标或 QoderQuery。QoderSession 用完后关闭;需要确保已接受的输入都写入 CLI 时,先等待 completeInput(),它会拒绝后续输入,但不等待回复。关闭 QoderClient 会级联关闭它创建的全部查询和会话。

绑定代码块的自动收尾

适合会话生命周期跟某个函数 / 代码块绑定的场景。TypeScript 把 close() 放在 finally 中确保相关资源关闭完成;Python 在 async with 块退出时自动断开连接:
import { qodercliAuth, query } from '@qodercn-ai/qodercn-agent-sdk';

const q = query({
  prompt: 'Inspect all files in the repository.',
  options: { auth: qodercliAuth() },
});

try {
  for await (const msg of q) {
    console.dir(msg, { depth: null });
  }
} finally {
  await q.close();
}

由外部条件触发结束

适合需要从外部条件(超时、用户取消、应用退出等)触发关闭的场景。TypeScript 创建 AbortController 并通过 options.abortController 传入,条件满足时调用 abort() 关闭整个会话并结束消息迭代;Python 手动持有 client,在 finally 中调用 disconnect():
import { qodercliAuth, query } from '@qodercn-ai/qodercn-agent-sdk';

const abortController = new AbortController();
const q = query({
  prompt: 'Inspect all files in the repository.',
  options: { auth: qodercliAuth(), abortController },
});

const taskTimeout = setTimeout(() => abortController.abort(), 5_000);
try {
  for await (const msg of q) {
    console.dir(msg, { depth: null });
  }
} finally {
  clearTimeout(taskTimeout);
}
会话关闭后不能继续发送消息。 Java 中从其他线程关闭会话后,正在读取的游标以 SessionClosedException 结束。不要在 SDK 回调或推送处理器里调用阻塞式 close(),在这些位置使用 closeAsync()。