Skip to content

Agent 02:加上多轮对话

上一章最后发现,现在只能聊一次。模型回答完,程序就结束了。

那多轮对话怎么搞?

我的理解其实很简单,就是搞一个数组,然后把我的输入和模型的回答都保存起来。

typescript
const messages: ChatCompletionMessageParam[] = [];

我输入一句,就往里面塞一条:

typescript
messages.push({ role: 'user', content: prompt });

模型回答以后,也往里面塞一条:

typescript
messages.push({ role: 'assistant', content });

整个顺序就是这样:

text
第一次输入
    ↓ push
messages: [user1]
    ↓ 发送给模型
第一次输出
    ↓ push
messages: [user1, assistant1]
    ↓ 下一次输入,再 push
messages: [user1, assistant1, user2]
    ↓ 发送给模型
第二次输出
    ↓ push
messages: [user1, assistant1, user2, assistant2]

比如我先告诉它“我叫小明”,然后再问它“我叫什么”,这个数组差不多就是这样:

typescript
[
  { role: 'user', content: '我叫小明' },
  { role: 'assistant', content: '你好,小明' },
  { role: 'user', content: '我叫什么?' },
]

下一次请求的时候,再把整个数组发给模型:

typescript
const completion = await client.chat.completions.create({
  model: 'deepseek-v4-flash',
  messages,
});

这样模型就能看到前面聊过什么了。

但是数组只解决了模型记不住的问题,程序还是会执行完就结束。

按照前面单轮对话的写法,程序从上往下执行。我输入一句,模型输出一句,代码走到最后,程序也就结束了。

那怎么能让程序不退出呢?

这里就要用到 while

typescript
while (true) {
  const prompt = (await readline.question('You: ')).trim();

  // 请求模型并输出回答
}

回答输出以后,这一轮就走完了。因为外面还有一个 while,代码会重新回到开头,再执行一次 question(),等我继续输入。

text
等待输入
→ 模型回答
→ 回到 while 开头
→ 再次等待输入

while (true) 会一直循环,所以还要留一个退出方式:

typescript
if (prompt === '/exit') {
  break;
}

输入 /exit,通过 break 跳出这个循环,程序才会结束。

完整代码:

typescript
import { stdin, stdout } from 'node:process';
import { createInterface } from 'node:readline/promises';
import OpenAI from 'openai';
import type { ChatCompletionMessageParam } from 'openai/resources/chat/completions.js';

const apiKey = process.env.DEEPSEEK_API_KEY;
if (!apiKey) {
  throw new Error('Missing DEEPSEEK_API_KEY');
}

const client = new OpenAI({
  apiKey,
  baseURL: 'https://api.deepseek.com',
});
const readline = createInterface({ input: stdin, output: stdout });
const messages: ChatCompletionMessageParam[] = [];

try {
  while (true) {
    const prompt = (await readline.question('You: ')).trim();

    if (prompt === '/exit') {
      break;
    }
    if (!prompt) {
      continue;
    }

    messages.push({ role: 'user', content: prompt });

    try {
      const completion = await client.chat.completions.create({
        model: 'deepseek-v4-flash',
        messages,
      });
      const content = completion.choices[0]?.message.content;
      if (!content) {
        throw new Error('DeepSeek returned an empty response');
      }

      messages.push({ role: 'assistant', content });
      console.log(`DeepSeek: ${content}`);
    } catch (error) {
      messages.pop();
      console.error(error instanceof Error ? error.message : String(error));
    }
  }
} finally {
  readline.close();
}

核心其实就是这个 messages。把双方说过的话都保存起来,每次再一起发给模型,多轮对话就有了。

然后我启动起来试了一下,发现它现在确实有记忆了。前面说过的话,后面再问,它能接得上。

但是又有一点体验不好。我输入完以后,模型没有马上返回,这中间终端里什么反馈都没有,我也不知道它到底有没有在处理。

所以下一章准备给这个对话加一个 loading。