6. 对话记忆管理

LLM 没有内置记忆,多轮对话需要你自己维护消息历史。

6.1 基础多轮对话

import { generateText } from 'ai'
import { createDeepSeek } from '@ai-sdk/deepseek'
import type { ModelMessage } from 'ai'
import * as readline from 'readline'
import 'dotenv/config'

const deepseek = createDeepSeek({
    apiKey: process.env.DEEPSEEK_API_KEY,
});

async function chat() {
    // 维护对话历史
    const messages: ModelMessage[] = []

    const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout,
    })

    console.log('开始对话(输入 exit 退出)\n')

    const askQuestion = () => {
        rl.question('你: ', async (input) => {
            if (input.toLowerCase() === 'exit') {
                rl.close()
                return
            }

            // 把用户消息加入历史
            messages.push({ role: 'user', content: input })

            const { text } = await generateText({
                model: deepseek('deepseek-v4-flash'),
                system: '你是一个友好的助手,记住对话上下文,用中文回复。',
                messages, // 带上完整历史
            })

            // 把 AI 回复也加入历史
            messages.push({ role: 'assistant', content: text })

            console.log(`\nAI: ${text}\n`)
            askQuestion() // 继续下一轮
        })
    }

    askQuestion()
}
chat();

6.2 长对话的 Token 管理

对话历史越来越长,Token 消耗也越来越多,需要做裁剪:

import { generateText } from 'ai'
import { createDeepSeek } from '@ai-sdk/deepseek'
import type { ModelMessage } from 'ai'
import * as readline from 'readline'
import 'dotenv/config'

const deepseek = createDeepSeek({
    apiKey: process.env.DEEPSEEK_API_KEY,
});

async function chat() {
    // 维护对话历史
    let messages: ModelMessage[] = []

    const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout,
    })

    console.log('开始对话(输入 exit 退出)\n')

    const askQuestion = () => {
        rl.question('你: ', async (input) => {
            if (input.toLowerCase() === 'exit') {
                rl.close()
                return
            }

            // 把用户消息加入历史
            messages.push({ role: 'user', content: input })

            // 裁剪消息后再发送
            messages = trimMessages(messages, 2);
            console.log(messages);

            const { text } = await generateText({
                model: deepseek('deepseek-v4-flash'),
                system: '你是一个友好的助手,记住对话上下文,用中文回复。',
                messages, // 带上完整历史
            })

            // 把 AI 回复也加入历史
            messages.push({ role: 'assistant', content: text })

            console.log(`\nAI: ${text}\n`)
            askQuestion() // 继续下一轮
        })
    }
    askQuestion()
}

chat();

// 简单的滑动窗口策略:只保留最近 N 条消息
function trimMessages(messages: ModelMessage[], maxMessages = 20): ModelMessage[] {
    if (messages.length <= maxMessages) return messages
    // 保留最后 maxMessages 条,但始终保留第一条(通常是重要的系统上下文)
    return messages.slice(-maxMessages)
}