前言
随着AI大模型的快速发展,在本地部署自己的AI助手,既保证数据安全,又避免高昂的API费用。本文将详细介绍如何使用Ollama在本地部署开源大语言模型,并开发一个美观实用的AI助手应用,全程使用TypeScript、React和Node.js技术栈。
一、Ollama简介与优势
Ollama是一个轻量级框架,可让你在本地电脑上轻松运行、自定义和共享大型语言模型。
主要优势:
- 隐私保护:所有数据和交互都在本地完成,无需担心敏感信息泄露
- 零成本使用:无需支付API调用费用
- 低延迟体验:本地部署避免了网络延迟
- 丰富模型选择:支持Llama 2、Mistral、Gemma等众多开源模型
- 简单易用:安装配置极其简便,无需复杂设置
二、本地环境配置
1. 安装Ollama
macOS:
brew install ollama
如果未安装Homebrew,请先执行:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Windows:从Ollama官网下载安装包安装。
Linux:
curl -fsSL https://ollama.ai/install.sh | shcurl -fsSL https://ollama.ai/install.sh | sh
2. 启动Ollama服务
ollama serve
注意:首次启动时,Ollama会自动创建必要的文件和配置
3. 拉取你的第一个模型
ollama pull llama4:latest
可选的其他优秀模型:
- mistral:性能出众的7B模型
- gemma:2b、gemma:7b:Google开源的Gemma模型
- phi:Microsoft的小而强大的Phi-2模型
- llama2-uncensored:内容限制较少的Llama 2版本
4. 验证模型可用性
ollama run llama4:latest
输入一些测试问题,验证模型是否正常工作。 到这里本地ollama配置就完成了,ollama官网地址
接下来是自己开发一个AI助手,连接到本地的ollama服务
三、后端服务开发
现在,让我们开始构建AI助手应用的核心部分。
1. 项目初始化
mkdir CodeGeniusHub
cd CodeGeniusHub
mkdir -p frontend backend
2. 配置后端项目
cd backend
pnpm init
pnpm add express cors axios typescript @types/node @types/express @types/cors
pnpm add -D nodemon ts-node
3. 实现Ollama API代理服务
后端核心代码(src/index.ts):
import express from 'express';
import cors from 'cors';
import axios from 'axios';
const app = express();
const port = 3001;
const OLLAMA_API = 'http://localhost:11434/api';
// 中间件
app.use(cors());
app.use(express.json());
// 获取可用模型列表
app.get('/api/models', async (req, res) => {
try {
const response = await axios.get(`${OLLAMA_API}/tags`);
res.json(response.data);
} catch (error) {
res.status(500).json({ error: '获取模型失败' });
}
});
// 聊天接口
app.post('/api/chat', async (req, res) => {
try {
const { model, messages } = req.body;
const response = await axios.post(`${OLLAMA_API}/chat`, {
model,
messages
});
res.json(response.data);
} catch (error) {
res.status(500).json({ error: '聊天请求失败' });
}
});
// 启动服务器
app.listen(port, () => {
console.log(`服务器运行在 http://localhost:${port}`);
});
四、前端界面实现
1. 初始化前端项目
cd ../frontend
pnpm create vite . --template react-ts
pnpm install
pnpm add antd @ant-design/icons zustand axios sass react-markdown
2. 创建API服务层
API服务核心代码(src/services/api.ts):
import axios from 'axios';
// 创建API客户端
const apiClient = axios.create({
baseURL: 'http://localhost:3001/api',
headers: {
'Content-Type': 'application/json',
},
});
// 定义消息类型
export interface Message {
role: 'user' | 'assistant' | 'system';
content: string;
}
// API服务
export const apiService = {
// 获取可用模型
getModels: async () => {
const response = await apiClient.get('/models');
return response.data;
},
// 聊天
chat: async (model: string, messages: Message[]) => {
const response = await apiClient.post('/chat', {
model,
messages,
});
return response.data;
}
};
3. 实现状态管理
Zustand状态管理核心代码(src/stores/chatStore.ts):
import { create } from 'zustand';
import { Message, apiService } from '../services/api';
interface ChatState {
models: string[];
selectedModel: string;
messages: Message[];
loading: boolean;
// 方法
setModels: (models: string[]) => void;
setSelectedModel: (model: string) => void;
addMessage: (message: Message) => void;
sendMessage: (content: string) => Promise<void>;
clearMessages: () => void;
}
export const useChatStore = create<ChatState>((set, get) => ({
models: [],
selectedModel: 'llama2',
messages: [],
loading: false,
setModels: (models) => set({ models }),
setSelectedModel: (model) => set({ selectedModel: model }),
addMessage: (message) => set((state) => ({
messages: [...state.messages, message]
})),
sendMessage: async (content) => {
const userMessage: Message = { role: 'user', content };
// 添加用户消息
set((state) => ({
messages: [...state.messages, userMessage],
loading: true
}));
try {
// 发送聊天请求
const response = await apiService.chat(
get().selectedModel,
[...get().messages, userMessage]
);
// 添加助手回复
const assistantMessage: Message = {
role: 'assistant',
content: response.message.content
};
set((state) => ({
messages: [...state.messages, assistantMessage],
loading: false
}));
} catch (error) {
console.error('发送消息失败:', error);
set({ loading: false });
}
},
clearMessages: () => set({ messages: [] })
}));
4. 开发聊天界面组件
Chat组件核心代码(src/components/Chat.tsx):
import React, { useState, useEffect, useRef } from 'react';
import { Input, Button, Select, List, Typography, Divider } from 'antd';
import { SendOutlined, DeleteOutlined } from '@ant-design/icons';
import ReactMarkdown from 'react-markdown';
import { useChatStore } from '../stores/chatStore';
import { apiService } from '../services/api';
import '../styles/Chat.scss';
const { Option } = Select;
const { Title, Paragraph } = Typography;
const Chat: React.FC = () => {
// 获取全局状态
const {
models,
selectedModel,
messages,
loading,
setModels,
setSelectedModel,
addMessage,
clearMessages
} = useChatStore();
// 本地状态
const [input, setInput] = useState('');
const messagesEndRef = useRef<HTMLDivElement>(null);
// 加载模型列表
useEffect(() => {
const fetchModels = async () => {
try {
const data = await apiService.getModels();
if (data.models) {
const modelNames = data.models.map((model: any) => model.name);
setModels(modelNames);
}
} catch (error) {
console.error('获取模型失败:', error);
}
};
fetchModels();
}, []);
// 滚动到底部
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
// 发送消息
const handleSendMessage = async () => {
if (!input.trim()) return;
// 添加用户消息
addMessage({
role: 'user',
content: input
});
setInput('');
try {
// 添加助手消息占位
addMessage({
role: 'assistant',
content: '思考中...'
});
// 发送聊天请求
const response = await apiService.chat(
selectedModel,
messages.concat({ role: 'user', content: input })
);
// 更新助手消息
const assistantMessage = {
role: 'assistant' as const,
content: response.message.content
};
// 替换占位消息
const updatedMessages = [...messages, { role: 'user', content: input }, assistantMessage];
useChatStore.setState({ messages: updatedMessages });
} catch (error) {
console.error('发送消息失败:', error);
}
};
return (
<div className="chat-container">
<div className="chat-header">
<Title level={3}>Ollama AI助手</Title>
<div className="model-selector">
<span>模型:</span>
<Select
value={selectedModel}
onChange={setSelectedModel}
style={{ width: 180 }}
>
{models.map(model => (
<Option key={model} value={model}>{model}</Option>
))}
</Select>
<Button
icon={<DeleteOutlined />}
onClick={clearMessages}
danger
>
清空对话
</Button>
</div>
</div>
<Divider />
<div className="messages-container">
{messages.length === 0 ? (
<div className="empty-messages">
<Paragraph>开始与AI助手对话吧!</Paragraph>
</div>
) : (
<List
itemLayout="horizontal"
dataSource={messages}
renderItem={(message) => (
<List.Item className={`message ${message.role}`}>
<div className="message-content">
<div className="message-header">
{message.role === 'user' ? '用户' : 'AI助手'}
</div>
<div className="message-body">
<ReactMarkdown>{message.content}</ReactMarkdown>
</div>
</div>
</List.Item>
)}
/>
)}
<div ref={messagesEndRef} />
</div>
<div className="input-container">
<Input.TextArea
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="输入你的问题..."
autoSize={{ minRows: 2, maxRows: 6 }}
onPressEnter={(e) => {
if (!e.shiftKey) {
e.preventDefault();
handleSendMessage();
}
}}
/>
<Button
type="primary"
icon={<SendOutlined />}
onClick={handleSendMessage}
loading={loading}
>
发送
</Button>
</div>
</div>
);
};
export default Chat;
五、运行项目
1. 启动后端服务
cd backend
pnpm dev
2. 启动前端应用
cd frontend
pnpm dev
六、项目优化与拓展
1. 流式响应实现
核心代码:
// 流式响应处理
const handleStreamResponse = (model, messages, onUpdate, onDone) => {
const source = new EventSource(`/api/chat?stream=true&model=${model}`);
source.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
if (data === '[DONE]') {
source.close();
onDone();
return;
}
onUpdate(data.message?.content || '');
} catch (e) {
console.error('解析流数据失败:', e);
}
};
return () => source.close();
};
3. 保存聊天历史
使用zustand持久化:
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
export const useChatStore = create(
persist(
(set, get) => ({
// 状态和方法...
}),
{
name: 'chat-storage',
getStorage: () => localStorage,
}
)
);
七、总结与思考
通过本教程,我们实现了一个基于Ollama的本地AI助手应用,它具有以下特点:
- 完全本地化部署,保护数据隐私
- 零API费用,适合长期使用
- 现代化的UI设计和用户体验
- 可扩展的架构,支持进一步功能增强
未来扩展方向
- 多模型管理:支持同时加载多个模型,根据任务自动切换
- 知识库集成:添加文档向量化和RAG(检索增强生成)功能
- 语音交互:集成语音识别和语音合成能力
- 桌面应用打包:使用Electron将应用打包为桌面应用
- 模型微调:实现简单的LoRA微调界面,让模型更符合个人使用习惯
- 如果想更方便可以做成插件,放在vscode或者cursor进行使用
性能优化建议
- 对于性能较弱的电脑,建议使用较小的模型如phi或gemma:2b
- 增加模型参数调整功能,可以通过降低温度等参数提高响应速度
- 考虑启用模型量化(ollama pull llama2:8b-q4_0)以降低内存占用
如果你觉得这篇文章有用,欢迎点赞👍、收藏⭐和评论💬!也欢迎在评论区分享你基于Ollama开发AI助手的经验和遇到的问题。