项目实训(2) -AI接入!DeepSeek 接口初体验与消息循环

89 阅读3分钟

项目实训(2) -AI接入!DeepSeek 接口初体验与消息循环

昨天搭了个聊天框的壳子,今天总算要来点实际的了——让 AI 真正说上话。

后端同学把 DeepSeek 的内部接口文档甩过来了,一个 URL,加上要传的 model 名称,还有一些请求体格式的约定。核心就是传一个 messages 数组,包含用户和 AI 的历史对话。

对接 API 的尝试:

我一般用 axios,但想想 fetch 也挺香的,原生,少个依赖。就用 fetch 吧。

// 在 AIChatWindow.vue 的 <script setup> 里

// ... (之前的代码) ...
const DEEPSEEK_API_URL = 'https://backend-provided.internal.api/deepseek-chat'; // 后端给的URL
const MODEL_NAME = 'deepseek-coder'; // 或者他们指定的模型

const sendMessage = async () => { // 改成 async
  if (!userInput.value.trim() || isLoading.value) return;

  const userMessageText = userInput.value;
  const newUserMessage: Message = {
    id: Date.now().toString(),
    text: userMessageText,
    sender: 'user',
    timestamp: new Date(),
  };
  messages.value.push(newUserMessage);
  userInput.value = '';
  isLoading.value = true;

  // 构造发送给API的对话历史
  // API期望的格式可能是 [{ role: 'user', content: '你好'}, { role: 'assistant', content: '你好,有什么可以帮您?'}]
  // 我们自己的 Message 接口需要转换一下
  const apiMessages = messages.value.map(msg => ({
    role: msg.sender === 'user' ? 'user' : 'assistant',
    content: msg.text,
  }));
  // 通常,最后一条是当前用户发的消息
  // 有些模型可能只需要最近几轮对话,或者有token限制,这个要注意

  try {
    const response = await fetch(DEEPSEEK_API_URL, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        // 'Authorization': 'Bearer YOUR_API_KEY' // 如果需要API Key的话,但内部接口可能不需要
      },
      body: JSON.stringify({
        model: MODEL_NAME,
        messages: apiMessages,
        // stream: false, // 先不用流式,简单点
      }),
    });

    if (!response.ok) {
      // 这里要处理错误,比如 response.statusText
      const errorData = await response.json().catch(() => ({ detail: 'AI服务返回非OK状态,且无法解析错误详情' }));
      console.error('AI API Error:', response.status, errorData);
      throw new Error(errorData.detail || `AI 服务错误,状态码: ${response.status}`);
    }

    const data = await response.json();
    // 解析返回的数据,DeepSeek返回的格式可能类似 OpenAI
    // { choices: [{ message: { role: 'assistant', content: 'AI的回复内容' } }] }
    const aiReplyText = data.choices?.[0]?.message?.content;

    if (aiReplyText) {
      const aiResponseMessage: Message = {
        id: Date.now().toString() + '-ai',
        text: aiReplyText.trim(),
        sender: 'ai',
        timestamp: new Date(),
      };
      messages.value.push(aiResponseMessage);
    } else {
      throw new Error('AI回复内容为空或格式不正确');
    }

  } catch (error: any) {
    console.error('调用AI接口失败:', error);
    const errorMessage: Message = { // 把错误信息也显示在聊天框里,方便调试
      id: Date.now().toString() + '-error',
      text: `抱歉,AI开小差了... (${error.message || '未知错误'})`,
      sender: 'ai', // 也可以用一个 'system' sender
      timestamp: new Date(),
    };
    messages.value.push(errorMessage);
  } finally {
    isLoading.value = false;
    // 滚动到底部
    scrollToBottom();
  }
};

const messageListWrapperRef = ref<HTMLElement | null>(null); // 给List的父组件加个ref

const scrollToBottom = () => {
  // nextTick 确保DOM更新后再滚动
  nextTick(() => {
    if (messageListWrapperRef.value) {
      messageListWrapperRef.value.scrollTop = messageListWrapperRef.value.scrollHeight;
    }
  });
};

// 在 <div class="message-list-wrapper"> 上加上 ref="messageListWrapperRef"
// 并且在 messages.value.push 之后都调用 scrollToBottom()
// 比如在 finally 里调用一次就行

遇到的问题和思考:

  1. API 格式对齐:后端给的 DeepSeek 接口文档虽然说了基于 OpenAI 兼容,但实际请求体和响应体总有些细微差别。role 是 user/assistant 还是别的?content 字段名叫啥?这些都得仔细核对。我这里先按常见的 user/assistant 和 content 来写。
  2. 错误处理:网络请求总可能失败。AI 服务本身也可能返回错误(比如限流、内容审查、输入无效等)。response.ok 要判断,错误信息也要尽量展示出来,不然用户只会看到一直在转圈圈。
  3. Loading 状态:AI 响应不是瞬时的,必须有个 loading 状态,防止用户重复点击发送。
  4. 自动滚动:新消息来了,聊天窗口应该自动滚到底部。这个用 nextTick 和 scrollHeightscrollTop 可以实现。
  5. 上下文传递:目前是把 messages.value 里所有的历史记录都传给 API了。如果对话太长,会不会超出 API 的 token 限制?或者影响响应速度?这是个隐患。后端同学说这个 DeepSeek 接口对上下文长度有优化,但具体限制没细说。先跑起来再说,后面可能要考虑截断或者做摘要。
  6. CORS:果然像后端同学说的,直接调他们给的内部 URL,没有遇到浏览器报跨域错误。看来他们在网关或者服务本身解决了。前端省心了!

成果:  经过一番折腾(主要是字段名对来对去),AI 终于能在我的聊天框里回复我的消息了!虽然回复还很“通用”,没有针对“面试”这个场景做任何优化,但起码证明了通路是OK的。 看到自己发的消息和AI的回复在列表里交替出现,还是有点小激动的。

下一步,就要开始琢磨怎么引导 AI 进行有意义的“面试”对话了。这就要涉及到“提示工程”(Prompt Engineering)了。感觉又是一个新世界的大门。

#AI模拟面试 #DeepSeek #API对接 #Vue3 #FetchAPI #前端开发 #小激动