Stories

Detail Return Return

圖文並茂:從Ubuntu服務器上安裝Ollama,到下載大模型,到寫前端頁實現大模型問答對話功能 - Stories Detail

本文圖文並茂,記錄安裝 Ollama過程,下載大模型,啓動ollama大模型服務,然後通過nginx代理外部請求訪問,實現一個前端問答效果

準備工作

  • 一台服務器,操作系統 Ubuntu,版本大於等於 20.04,筆者的是 Ubuntu 22.04 64 bit
  • 當然了 有域名最好
  • 一個圖形可視化化鏈接服務器軟件,筆者推薦 winscp
  • 一架梯子,筆者用的是,clash小貓

效果圖

效果演示地址

請點擊一下:https://ashuai.site/reactExamples/ollama

第一步,試試ollama官方提供的命令

ollama官方:https://ollama.com/download/linux

第二步,網絡不行,ollama下載不下來

文件太大了,有一個G多

第三步,到github上找對應ollama安裝包資源,下載,再上傳到烏班圖服務器上

地址如下:https://github.com/ollama/ollama/releases/tag/v0.12.0

然後,在瀏覽器中下載

下載完畢以後,在用winscp將其移動到服務器上

ollama安裝包上傳完畢

第四步,解壓對應的這個文件

1. 解壓 .tgz 壓縮包並統一路徑管理

當前我們是已在 /var/selfDownload 目錄(通過 ls 能看到 ollama-linux-amd64.tgz),直接執行解壓命令即可。.tgz 是 .tar.gz 的簡寫,需用 tar 命令解壓:

tar -zxvf ollama-linux-amd64.tgz
  • 命令説明:

    • z:處理 gzip 壓縮格式(對應 .gz/.tgz);
    • x:執行 “解壓” 操作;
    • v:顯示解壓過程(可選,方便查看文件是否正常解壓);
    • f:指定要解壓的文件名(必須放在命令最後,這裏是 ollama-linux-amd64.tgz)。

解壓成功 得到兩個新的文件夾

在winscp中也能直觀看到

把對應的文件移動走到對應位置,統一管理,注意下圖的路徑

然後跑起來服務

2. 安裝大模型,使用curl命令進行對話

ollama服務 默認運行的端口是11434端口

然後,使用curl命令,在服務器內部的命令行中,發送給ollama發請求,調用通義千文大模型試試

curl http://localhost:11434/api/generate -d '{
  "model": "qwen2.5:0.5b",
  "prompt": "你是什麼模型",
  "stream": false
}'

  • 我們發現跑通了,至此,烏班圖服務器上,安裝Ollama就全部完成了
  • 但是,單單隻有Ollama的上安裝的模型服務還不太夠,效果不明顯
  • 接下,我們要讓服務器上的大模型服務能夠在外部調用

3. nginx代理外部請求到ollama

  • 筆者服務器上安裝nginx,以便於代理外部的請求,而後把外部請求轉發到服務器上的ollama的大模型服務
  • 以/ollama開頭的請求,轉到ollama這裏使用11434端口上
# ollama服務
location /ollama/ {
    # 允許所有來源訪問
    add_header 'Access-Control-Allow-Origin' '*' always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
    add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
    add_header 'Access-Control-Max-Age' 1728000 always;

    # 代理到 Ollama
    proxy_pass http://localhost:11434/;

    # 完全清理所有頭,只保留必要的
    proxy_pass_request_headers off;

    # 手動設置最基本的頭信息 ollama的響應頭過於複雜可能導致403報錯
    proxy_set_header Host localhost;
    proxy_set_header Content-Type application/json;
    proxy_set_header Content-Length $content_length;
    proxy_set_header Connection close;
}

第五步,在外部訪問請求服務器上的ollama大模型接口

寫一個react頁面,直接發請求即可

效果圖

交互效果

接口請求返回

react代碼

即分為對話區和輸入區,前端代碼倒是很簡單

import { useState } from 'react'
import axios from 'axios'
import { Input } from 'antd'
import styles from './Ollama.module.css'

export default function Ollama() {
    const [messages, setMessages] = useState([]) // 存儲對話數據
    const [inputValue, setInputValue] = useState('') // 輸入框內容
    const [isLoading, setIsLoading] = useState(false) // 加載狀態

    // 發請求
    const sendApiRequest = () => {
        if (!inputValue.trim()) return // 如果輸入為空,不發送請求

        const userMessage = inputValue.trim()
        setIsLoading(true)

        // 添加用户消息到對話數據
        setMessages(prev => [...prev, { type: 'user', content: userMessage }])
        setInputValue('') // 清空輸入框

        axios.post('/ollama/api/generate', {
            model: "qwen2.5:0.5b",
            prompt: userMessage,
            stream: false
        }).then((response) => {
            const { response: aiResponse } = response.data
            // 添加AI回覆到對話數據
            setMessages(prev => [...prev, { type: 'ai', content: aiResponse }])
            setIsLoading(false)
        }).catch((error) => {
            setMessages(prev => [...prev, { type: 'error', content: JSON.stringify(error) }])
            setIsLoading(false)
        })
    }

    // 處理輸入框按鍵事件
    const handleKeyPress = (e) => {
        if (e.key === 'Enter' && !isLoading) {
            sendApiRequest()
        }
    }

    return (
        <div>
            <h3>Ollama調用大模型</h3>

            {/* 對話區 */}
            <div className={styles.chatContainer}>
                <div className={styles.messages}>
                    {messages.length === 0 ? (
                        <div className={styles.emptyMessage}>
                            暫無數據,請開始與AI對話吧!🤔🤔🤔
                        </div>
                    ) : (
                        messages.map((message, index) => (
                            <div
                                key={index}
                                className={`${styles.message} ${styles[message.type]}`}
                            >
                                <div className={styles.messageContent}>
                                    {message.content}
                                </div>
                            </div>
                        ))
                    )}
                </div>
            </div>

            {/* 輸入區 */}
            <Input
                value={inputValue}
                onChange={(e) => setInputValue(e.target.value)}
                onPressEnter={handleKeyPress}
                placeholder="輸入問題,回車發送"
                disabled={isLoading}
            />
        </div>
    )
}

樣式css代碼

.chatContainer {
    border: 1px solid #dee2e6;
    border-radius: 8px;
    height: 400px;
    margin-bottom: 20px;
    overflow-y: auto;
    background-color: #fafafa;
}

.messages {
    padding: 15px;
}

.message {
    margin-bottom: 15px;
    display: flex;
}

/* 用户輸入的在右側,ai回覆的在左側,報錯居中 */
.user { justify-content: flex-end; }
.ai { justify-content: flex-start; }
.error { justify-content: center; }

.messageContent {
    max-width: 70%;
    padding: 10px 15px;
    word-wrap: break-word;
}

.user .messageContent {
    background-color: #1890ff;
    color: white;
    border-radius: 18px 18px 4px 18px;
}

.ai .messageContent {
    background-color: #f0f0f0;
    color: #333;
    border-radius: 18px 18px 18px 4px;
}

.error .messageContent {
    background-color: #fff2f0;
    color: #ff4d4f;
    border: 1px solid #ffccc7;
    border-radius: 8px;
}

.emptyMessage {
    text-align: center;
    color: #999;
    font-style: italic;
    padding: 40px;
}

完整github代碼地址

請點擊 https://github.com/shuirongshuifu/react-examples

創造不易,感謝關注、點贊、評論支持 🙏🙏🙏

A good memory is better than a bad pen. Record it down...
user avatar kingluo Avatar linx Avatar hppyvyv6 Avatar hsr2022 Avatar euphoria Avatar tinygeeker Avatar seact Avatar ranck Avatar kanjianliao Avatar jinjidedacong Avatar juanerma Avatar wupeng_5a4de5c290b9d Avatar
Favorites 13 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.