Ollama + React 学习笔记

87 阅读8分钟

一、Ollama 核心认知

1.1 大模型分类与Ollama定位

(1)大模型两类核心形态

  • 闭源模型:OpenAI 5.1、Claude(擅长代码生成)、Gemini 3.0+、DeepSeek、Qwen(闭源版本)—— 需依赖官方API,数据安全性较弱
  • 开源大模型:LLama3(羊驼)、DeepSeek(开源版本)、Qwen(开源版本)—— 支持本地部署,数据可本地化存储

(2)Ollama 核心作用与实现原理

核心作用:Ollama 是一款专门用于本地部署开源大模型的工具,解决开源大模型本地化运行的适配、配置、依赖管理等复杂问题,降低本地使用大模型的门槛。

实现原理:Ollama 本质是一个“大模型运行时封装工具”,核心逻辑包括:① 预封装主流开源大模型的运行依赖(如CUDA、TensorRT等加速框架);② 提供统一的命令行接口(CLI),简化模型拉取、启动、停止流程;③ 内置API服务模块,将本地模型封装为兼容OpenAI规范的HTTP接口,让前端/后端可通过网络请求调用模型能力。

1.2 Ollama 部署相关核心要点

(1)部署硬件/环境需求与原理

  • 本地部署:核心依赖 GPU 性能(用于模型推理加速,大参数模型需显存支撑)、内存大小(加载模型权重文件,参数越大需求越高)—— 原理:模型推理需实时计算矩阵运算,GPU并行计算能力远优于CPU,内存不足会导致模型加载失败或推理卡顿
  • 云端部署:需考虑服务器配置、算力支撑—— 原理:云端部署本质是“远程化本地部署”,通过服务器的高性能GPU/CPU提供推理服务,多用户通过网络请求共享算力

(2)安全场景与解决方案

核心安全痛点:ToB 场景中,将设计稿、代码等敏感数据上传至第三方 AI 服务(如 Cursor、公共 LLM 服务)存在数据泄露风险—— 原理:第三方服务需将用户数据传输至其服务器进行处理,传输过程和存储过程均可能存在数据泄露。

解决方案

  • 使用企业自研 AI 产品/模型—— 数据全程在企业内网流转,无外部传输
  • 通过 Ollama 自行部署大模型—— 实现数据本地化处理,所有交互数据不离开本地/企业内网,从源头规避泄露风险

1.3 Ollama 基础操作命令与实现原理

  • 拉取模型ollama pull 模型名称(如 ollama pull qwen2.5:0.5b)—— 原理:从 Ollama 官方模型仓库下载预封装的模型权重文件、配置文件和运行依赖,类似 Docker pull 镜像
  • 运行模型ollama run 模型名称—— 原理:启动本地模型运行时,加载下载的模型权重至内存/GPU显存,初始化推理环境,并启动内置API服务
  • API 服务:运行后默认占用 11434 端口,提供兼容 OpenAI 的开源大模型 API 服务—— 原理:Ollama 内置 HTTP 服务器,将模型推理逻辑封装为 /chat/completions 等接口,请求参数和返回格式完全兼容 OpenAI,降低开发迁移成本

二、React + Ollama 实现 ChatBot

核心思路:通过 React 构建前端交互界面,调用基于 Ollama 部署的本地大模型 API,实现聊天功能。整体架构分为「前端界面(用户交互)」「Hook 封装(状态与逻辑复用)」「API 请求(与 Ollama 通信)」三层,遵循“分层解耦”设计原则,便于维护和扩展。

2.1 项目依赖与基础配置

(1)核心依赖

  • UI 样式:Tailwind CSS—— 快速构建响应式界面,通过原子化类减少自定义 CSS 代码
  • 请求工具:Axios—— 封装 HTTP 请求,支持拦截器、错误处理、请求取消等功能,比原生 fetch 更易用
  • 核心框架:React—— 基于组件化和 Hooks 实现状态管理与界面渲染,实现“数据驱动视图”

(2)Ollama API 基础信息

  • 基础地址:http://localhost:11434/v1—— Ollama 内置 API 服务的基础路径
  • 聊天接口:/chat/completions(兼容 OpenAI 接口规范)—— 核心交互接口,用于发送聊天消息并获取模型回复
  • 请求头:需携带 Authorization: Bearer ollama(Ollama 服务的身份验证,默认固定值)、Content-Type: application/json(指定请求体为 JSON 格式)

2.2 核心代码实现(含方法解析与原理)

(1)API 封装(ollamaApi.js)—— 通信层

功能:封装调用 Ollama 聊天接口的请求函数,统一处理请求参数、错误捕获、响应格式转换,避免重复代码。

import axios from 'axios';

// 创建 Axios 实例,统一配置基础地址和请求头
const ollamaApi = axios.create({
  baseURL: 'http://localhost:11434/v1',
  headers: {
    'Authorization': 'Bearer ollama',
    'Content-Type': 'application/json',
  }
});

/**
 * 调用 Ollama 聊天接口
 * @param {Array} messages - 聊天消息列表,格式:[{role: 'user/assistant', content: '消息内容'}]
 * @returns {Promise} - 模型回复内容(字符串)
 * @description 方法解析:统一封装 POST 请求,传入模型名称、消息列表等参数,返回格式化后的回复内容
 * @实现原理:通过 Axios 发送 POST 请求到 Ollama 的 /chat/completions 接口,携带模型配置和消息上下文,
 *           接收响应后提取 choices[0].message.content(模型回复的核心内容)并返回
 */
export const chatCompletions = async (messages) => {
  try {
    const response = await ollamaApi.post('/chat/completions', {
      model: 'qwen2.5:0.5b', // 指定使用的开源模型(轻量版 0.5B 参数,适合本地部署)
      messages, // 聊天上下文,用于模型理解对话历史
      stream: false, // 是否流式响应(false 为一次性获取完整回复,true 为分段获取实现打字效果)
      temperature: 0.7, // 随机性参数(0-1 区间,值越高回复越灵活,值越低越严谨)
    });
    // 提取模型回复内容并返回
    return response.data.choices[0].message.content;
  } catch(err) {
    console.error('Ollama 请求失败:', err);
    throw err; // 抛出错误,供上层组件(Hook)处理
  }
}

// 备选:原生 fetch 实现(无需 Axios 依赖)
const OLLAMA_URL = 'http://localhost:11434/v1/chat/completions';
const headers = {
  'Authorization': 'Bearer ollama',
  'Content-Type': 'application/json',
};

/**
 * 原生 fetch 实现聊天请求
 * @param {Array} messages - 聊天消息列表
 * @returns {Promise} - 模型回复内容
 * @实现原理:与 Axios 版本一致,通过原生 fetch 发送 POST 请求,手动处理 JSON 序列化与解析
 */
export const fetchChatCompletions = async (messages) => {
  try {
    const response = await fetch(OLLAMA_URL, {
      method: 'POST',
      headers,
      body: JSON.stringify({
        model: 'qwen2.5:0.5b',
        messages
      })
    });
    const data = await response.json();
    return data.choices[0].message.content;
  } catch (err) {
    console.log(`请求错误: ${err}`);
    throw err;
  }
}

(2)Hook 封装(useLLM.js)—— 逻辑层

功能:封装聊天相关的状态(消息列表、加载状态、错误信息)和操作(发送消息、重置聊天),通过自定义 Hook 实现逻辑复用,让组件只关注界面渲染。

import { useState } from 'react';
import { chatCompletions } from '../api/ollamaApi';

/**
 * 自定义 Hook:封装聊天相关的状态与业务逻辑
 * @returns {Object} - 包含状态(messages/loading/error)和方法(sendMessage/resetChat)
 * @实现原理:利用 React 的 useState 管理本地状态,通过闭包封装业务逻辑,
 *           将状态和方法暴露给组件使用,实现“逻辑与界面分离”
 */
export const useLLM = () => {
  // 初始化消息列表(包含默认问候语,提升用户体验)
  const [messages, setMessages] = useState([
    {
      role: 'user',
      content: '你好',
    },
    {
      role: 'assistant',
      content: '你好!我是 qwen2.5:0.5b 模型,有什么我可以帮助你的吗?',
    }
  ]);
  const [loading, setLoading] = useState(false); // 加载状态:控制按钮禁用、显示加载提示
  const [error, setError] = useState(null); // 错误信息:存储请求失败的提示内容

  /**
   * 发送消息函数
   * @param {string} content - 用户输入的消息内容
   * @description 方法解析:处理用户消息发送的完整流程,包括添加用户消息、调用 API、添加模型回复、处理加载/错误状态
   * @实现原理:
   *  1. 先校验输入内容,空内容直接返回;
   *  2. 立即添加用户消息到列表并更新界面(优化交互体验,无需等待 API 响应);
   *  3. 启动加载状态,禁用发送按钮;
   *  4. 调用 chatCompletions API,传入历史消息上下文(确保模型理解对话逻辑);
   *  5. 接收模型回复后,添加到消息列表;
   *  6. 无论成功失败,最终关闭加载状态;
   *  7. 错误时捕获异常并更新错误状态,供组件显示。
   */
  const sendMessage = async (content) => {
    if (!content.trim()) return; // 空内容不发送
    // 1. 添加用户消息到列表,立即更新界面
    const newUserMessage = { role: 'user', content };
    setMessages(prev => [...prev, newUserMessage]);
    setLoading(true);
    setError(null); // 清空之前的错误

    try {
      // 2. 调用 Ollama API,传入历史消息(包含刚添加的用户消息)
      const assistantContent = await chatCompletions([...messages, newUserMessage]);
      // 3. 添加助手回复到消息列表
      setMessages(prev => [...prev, { role: 'assistant', content: assistantContent }]);
    } catch (err) {
      setError('消息发送失败,请重试!');
      console.error('发送消息错误:', err);
    } finally {
      setLoading(false); // 无论成功失败,都结束加载状态
    }
  };

  /**
   * 重置聊天函数:恢复初始消息列表
   * @description 方法解析:清空当前聊天记录,恢复到初始的问候语状态
   * @实现原理:直接重置 messages 状态为初始值,同时清空错误状态
   */
  const resetChat = () => {
    setMessages([
      { role: 'user', content: '你好' },
      { role: 'assistant', content: '你好!我是 qwen2.5:0.5b 模型,有什么我可以帮助你的吗?' }
    ]);
    setError(null);
  };

  // 暴露状态和方法给组件使用
  return {
    messages, // 消息列表(供组件渲染)
    loading, // 加载状态
    error, // 错误信息
    sendMessage, // 发送消息方法
    resetChat, // 重置聊天方法
  }
}

(3)React 组件(App.js)—— 界面层

功能:实现聊天界面渲染(消息列表、输入框、发送按钮、重置按钮、错误提示),绑定状态和事件,完成用户交互。

import 'tailwindcss';
import { useEffect, useState } from 'react';
import { chatCompletions } from './api/ollamaApi';
import { useLLM } from './hooks/useLLM';

export default function App() {
  const [inputValue, setInputValue] = useState(''); // 存储输入框内容
  // 从自定义 Hook 中获取状态和方法(复用聊天逻辑)
  const { messages, loading, error, sendMessage, resetChat } = useLLM();

  /**
   * 表单提交处理函数(发送消息)
   * @param {Event} e - 表单提交事件
   * @description 方法解析:处理表单提交逻辑,触发消息发送
   * @实现原理:阻止表单默认刷新行为,调用 sendMessage 发送输入框内容,然后清空输入框
   */
  const handleSubmit = (e) => {
    e.preventDefault(); // 阻止表单默认刷新行为(关键:避免页面跳转)
    sendMessage(inputValue); // 调用 Hook 中的发送消息方法
    setInputValue(''); // 清空输入框
  };

  /**
   * 页面挂载时测试 Ollama API 连接
   * @description 方法解析:可选功能,用于验证 Ollama 服务是否正常启动
   * @实现原理:利用 React 的 useEffect Hook,在组件挂载(空依赖数组)后执行一次异步函数,
   *           调用 chatCompletions 发送测试消息,打印成功/失败日志,帮助开发者排查问题
   */
  useEffect(()=>{
    const testOllamaConnection = async () => {
      try {
        const res = await chatCompletions([
          { 
            role: 'user', 
            content: '你好' 
          },
        ]);
        console.log('Ollama API 测试成功,模型回复:', res);
      } catch (err) {
        console.log('Ollama API 测试失败,请检查服务是否启动:', err);
      }
    };
    testOllamaConnection();
  },[])
  
  return (
    
      {/* 聊天容器:包含消息列表、重置按钮、错误提示 */}
              {/* 重置按钮 */}
        <button 
          onClick={
          className='px-4 py-2 bg-gray-100 hover:bg-gray-200 transition text-sm'
        >
          重置聊天
        
        {/* 错误提示:当 error 不为 null 时显示 */}
        {error && (
         
            {error}

        )}
        {/* 消息列表:循环渲染 messages 状态,区分用户和助手消息的样式与位置 */}
         {messages.map((msg, index) => (
            <div 
              key={index} 
              className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}
            >
              <div 
                className={' 
                    ? 'bg-blue-600 text-white'  // 用户消息:蓝色背景、白色文字、右对齐
                    : 'bg-gray-200 text-gray-800' // 助手消息:灰色背景、黑色文字、左对齐
                }`}
              >
                {msg.content}

          ))}
          {/* 加载中提示:当 loading 为 true 时显示 */}
          {loading && (
            加载中...
          )}
         {/* 输入表单:用户输入消息并提交 */}
      <form className='w-full max-w-[800px] p-4 border-t mt-2' onSubmit={<input 
            type='text'
            value={ setInputValue(e.target.value)}  // 实时更新输入框内容到状态
            placeholder='请输入...按回车发送'
            disabled={loading}  // 加载中禁用输入框
            className='flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500'
          />
          <button 
            type='submit'
            className='bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition disabled:bg-gray-400'
            disabled={内容时禁用按钮
          >
            发送
          
  );
}

2.3 关键方法与实现原理汇总

方法/功能所属模块核心逻辑实现原理
chatCompletionsollamaApi.js调用 Ollama 聊天接口通过 Axios/fetch 发送 POST 请求,携带模型配置和消息上下文,解析响应获取模型回复
useLLM(自定义 Hook)useLLM.js封装聊天状态与逻辑利用 useState 管理消息/加载/错误状态,通过闭包封装 sendMessage/resetChat 方法,实现逻辑复用
sendMessageuseLLM.js发送用户消息并获取回复先更新用户消息到界面,再调用 API 传入上下文,接收回复后更新界面,全程管理加载/错误状态
handleSubmitApp.js处理表单提交阻止表单默认刷新,调用 sendMessage 发送消息,清空输入框
useEffect(API 测试)App.js验证 Ollama 服务可用性组件挂载后执行异步函数,调用 chatCompletions 测试连接,打印日志辅助排查问题

2.4 关键注意事项

  • Ollama 服务启动:前端运行前,必须先通过 ollama run 模型名称 启动本地服务,否则 API 请求会失败(11434 端口未占用)。
  • 跨域问题:若前端项目端口与 11434 不一致,会出现跨域错误—— 解决方案:通过 Node.js 后端代理(前端请求后端,后端转发到 Ollama),或配置 Ollama 允许跨域(需修改 Ollama 配置文件)。
  • 模型选择:示例使用 qwen2.5:0.5b(0.5B 参数轻量模型),适合入门测试;若硬件性能较好,可更换 qwen2.5:7b 等更大参数模型,提升回复质量。
  • 流式响应:默认 stream: false 为一次性获取回复;若需要类似 ChatGPT 的实时打字效果,可将 stream 设为true,通过监听响应流分段处理回复内容。

三、核心总结

  1. Ollama 核心价值:简化开源大模型本地部署流程,通过统一 CLI 和兼容 OpenAI 的 API,降低本地大模型的使用和开发门槛;同时实现数据本地化,解决 ToB 场景的安全痛点。
  2. React + Ollama 架构逻辑:采用“界面层-逻辑层-通信层”分层设计,通过自定义 Hook 实现逻辑复用,Axios 封装 API 通信,React 实现数据驱动的界面渲染,结构清晰、易于维护。
  3. 关键技术核心:Ollama 的 API 封装与状态管理是核心,其中 sendMessage 方法的“先更新界面再等待响应”设计提升了用户体验,useEffect 的合理使用实现了服务可用性验证。
  4. 实践重点:开发前确保 Ollama 服务正常启动,处理好跨域问题,根据硬件性能选择合适的模型,必要时实现流式响应提升交互体验。