文档📃
安装位置
默认安装位置改变:
语法(建议先创建好位置):软件名称 /DIR="指定安装位置"示例:
OllamaSetup.exe /DIR="D:\Program Files\ollama"更改模型下载 & 本地
create的默认路径:
新增环境变量:OLLAMA_MODELS,值为路径D:\Program Files\ollama\models如果已经在本地创建或下载过模型了,需要重启ollama,将
C盘->用户->你自己的电脑名称->.ollama文件夹下的models文件夹直接覆盖剪切在环境变量所指向的文件夹上,使之最终对应环境变量所设置的值。
使用ollama list验证
文件夹
执行ollama run或者ollama create之后,下载或生成的文件被分别放在models下的两个文件内,我称为 实体文件 和 描述文件:
models
├─blobs // 实体文件
│ sha256-xxxxxxxxx // gguf文件、参数文件、template等文件均在这个文件夹内以此类型名称存放
│
└─manifests // 描述文件
└─registry.ollama.ai // ollama 域名
├─huihui_ai // ollama run 从ollama官网下载的大模型对应的组织名
│ └─deepseek-r1-abliterated
│ 8b // ollama run 之后生成的,和官网的url呈对应关系,文件内容描述这个模型对应的gguf文件和参数、template等文件的名称
│ 8b-0528-qwen3-q8_0 // 同上
│
└─library // ollama create 从本地gguf文件创建的大模型默认组织名
└─qwen3
latest // ollama create 之后生成的,文件内容描述这个模型对应的gguf文件和参数、template等文件的名称
从另一台电脑复制
基于上面文件夹内结构,可以得出,只要复制了文件夹内的 实体文件 和 描述文件 并粘贴到另一台电脑的对应结构目录下即可。
环境变量
注释中标有*的代表就算没有手动设置也是ollama启动之后的默认值;
| 变量名 | 值 | 注释 |
|---|---|---|
| OLLAMA_MODELS | D:\Program Files\ollama\models | 模型安装位置(默认为 ~/.ollama/models ),包括create和run |
| OLLAMA_HOST | 0.0.0.0 | *允许远程访问的时候需要改成这个 |
| OLLAMA_PORT | 11434 | *端口 |
| OLLAMA_ORIGINS | * | *允许跨域 |
| OLLAMA_KEEP_ALIVE | 600 | 数字单位就是秒,也可以是10m代表十分钟 |
| OLLAMA_NOHISTORY | 1 | 不在 ~/.ollama/history 中记录用户提问历史 |
启动命令
ollama run qwen3 --verbose
--verbose: 每次回答之后输出tokens/s等信息--model-path: 指定模型存储的目标路径--host: 监听主机地址--post: 监听端口号(默认:11434)--gpu: Ollama 会自动检测 GPU 并使用,但需确保系统已安装 CUDA 和相关驱动(Windows暂不支持,须通过WSL或Docker)- ``:
- ``:
注:,如果需要共享上下文,需通过 Ollama 的服务模式(如 ollama serve)统一管理模型实例。
从本地GGUF中create
将Qwen3-30B-A3B-Q4_0.gguf和Modelfile文件放在同一文件夹
ollama create qwen3 -f ./Modelfile
Modelfile文件示例(根据Qwen3:30B-A3B),其中的内容还是要根据各个模型所修改:
FROM ./Qwen3-30B-A3B-Q4_0.gguf
PARAMETER stop "<|im_start|>,<|im_end|>"
# set the temperature to 1 [higher is more creative, lower is more coherent]
PARAMETER temperature 0.6
PARAMETER top_k 20
PARAMETER top_p 0.95
PARAMETER min_p 0
PARAMETER repeat_penalty 1
TEMPLATE """{{ if .Messages }}
{{- if or .System .Tools }}<|im_start|>system
{{ .System }}
{{- if .Tools }}
# Tools
You are provided with function signatures within <tools></tools> XML tags:
<tools>{{- range .Tools }}
{"type": "function", "function": {{ .Function }}}{{- end }}
</tools>
For each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:
<tool_call>
{"name": <function-name>, "arguments": <args-json-object>}
</tool_call>
{{- end }}<|im_end|>
{{ end }}
{{- range $i, $_ := .Messages }}
{{- $last := eq (len (slice $.Messages $i)) 1 -}}
{{- if eq .Role "user" }}<|im_start|>user
{{ .Content }}<|im_end|>
{{ else if eq .Role "assistant" }}<|im_start|>assistant
{{ if .Content }}{{ .Content }}
{{- else if .ToolCalls }}<tool_call>
{{ range .ToolCalls }}{"name": "{{ .Function.Name }}", "arguments": {{ .Function.Arguments }}}
{{ end }}</tool_call>
{{- end }}{{ if not $last }}<|im_end|>
{{ end }}
{{- else if eq .Role "tool" }}<|im_start|>user
<tool_response>
{{ .Content }}
</tool_response><|im_end|>
{{ end }}
{{- if and (ne .Role "assistant") $last }}<|im_start|>assistant
{{ end }}
{{- end }}
{{- else }}
{{- if .System }}<|im_start|>system
{{ .System }}<|im_end|>
{{ end }}{{ if .Prompt }}<|im_start|>user
{{ .Prompt }}<|im_end|>
{{ end }}<|im_start|>assistant
{{ end }}{{ .Response }}{{ if .Response }}<|im_end|>{{ end }}"""
# set the system message
SYSTEM """你是通义千问,你能够回答问题、创作文字、逻辑推理、编程等。你是个乐于助人且善于思考的助手,你可以帮助用户解决一切问题!"""
参数
这些参数也可以在启动ollama run的时候添加在命令里面。
| 参数 | 技术问答 | 创意写作 | 通用对话 | 通俗解释 |
|---|---|---|---|---|
temperature | 0.2-0.5 | 0.7-1.0 | 0.5-0.7 | 随机性控制:低温输出稳定如教科书,高温放飞想象力 |
top_k | 20-40 | 80-100 | 40-60 | 候选词范围:仅从前k个概率最高的词中选择(小=精准,大=多样) |
top_p | 0.9-0.95 | 0.85-0.98 | 0.9 | 动态候选池:累积概率达p的词才进入选择(避免冷门词干扰) |
repeat_penalty | 1.2-1.5 | 1.1-1.3 | 1.0-1.2 | 重复惩罚:>1 时抑制重复内容(技术文档需高惩罚防冗余) |
num_predict | 500-800 | 1000-2000 | 800-1200 | 最大生成长度(tokens):控制回答篇幅 |
num_ctx | 4096 | 8192 | 4096-8192 | 上下文窗口大小:决定模型能“记住”多长输入(创意写作需长上下文支持) |
temperature 温度:控制回答的创意性和保守性
set the temperature to 1 [higher is more creative, lower is more coherent]
top_k
低
top_k值(如 20):
模型仅从概率最高的少量 token 中选择,输出更集中、保守,适合需要严谨答案的场景(例如事实问答)。高
top_k值(如 100):
候选范围扩大,生成内容更多样化,但可能降低连贯性,适合创意任务(如写故事)。与
top_p(核采样)配合:
top_k先筛选出概率最高的k个 token,top_p再从中选择累计概率达到阈值p的子集,两者结合可平衡生成质量与效率。例如:
top_k=50, top_p=0.9表示:
- 取前 50 个高概率 token →
- 从中筛选累计概率 ≥90% 的 token 集合 →
- 最终从该集合中随机采样。
任务类型 推荐 top_k推荐 top_p推荐 temperature效果 技术问答/代码生成 20~40 0.6 0.2 减少错误,保证答案精准性 创意写作/故事生成 60~100 0.92 0.8 激发多样性,避免内容重复 通用对话 40~60 0.85 0.5 平衡连贯性与趣味性
repeat_penalty 惩罚系数
值大于 1.0 (如 1.1, 1.2) 会降低重复的可能性;值小于 1.0 则可能增加重复。
repeat_last_n 检测重复token数量
设置模型回溯检查重复的 token 数量范围。例如,设为 64 意味着模型会检查最近生成的 64 个 token 是否与当前预测的 token 重复,并应用
repeat_penalty。
num_ctx 上下文长度
例如
DeepSeek-R1使用ollama show输出context length 131072说明模型支持 128k 长度,但ollama的默认上下文窗口可能仍是 2048 (2k),即使用户的模型支持128K,也需要通过Modelfile显式设置PARAMETER num_ctx 32768(32k),如果直接ollama run下载的模型,则需要手动修修改blobs文件夹下面的参数描述文件,新增"num_ctx": 32768(32k)参数。为什么设置32k,是因为在DeepSeek-R1:8b的模型6G大小启动之后,使用32k上下文长度,
ollama占用内存在14G左右,可以。另外如果
num_ctx设置为131072,则8b模型启动时需要月64G显存,在此模型下,上下文长度以k为单位和显存占用存在除以二的关系,例如 128k 占用 64G。
如何保持模型在内存中加载或立即卸载?
默认情况下,模型会在内存中保留 5 分钟后才被卸载。这允许在你向 LLM 发送多个请求时获得更快的响应时间。如果你希望立即从内存中卸载模型,可以使用 ollama stop 命令:
ollama stop qwen3
如果你正在使用 API,可以使用 keep_alive 参数与 /api/generate 和 /api/chat 端点来设置模型在内存中保持的时间。keep_alive 参数可以设置为:
- 一个持续时间字符串(例如 "10m" 或 "24h")
- 以秒为单位的数字(例如 3600)
- 任何负数,这将使模型保持在内存中(例如 -1 或 "-1m")
- '0',这将在生成响应后立即卸载模型
例如,要预加载模型并将其保留在内存中,可以使用:
linux
curl http://localhost:11434/api/generate -d '{"model": "llama3.2", "keep_alive": -1}'
windows
curl http://localhost:11434/api/generate -d "{\"model\": \"qwen3\", \"keep_alive\": \"10m\"}"
要卸载模型并释放内存,请使用:
curl http://localhost:11434/api/generate -d '{"model": "llama3.2", "keep_alive": 0}'
或者,你可以在启动 Ollama 服务器之前通过设置 OLLAMA_KEEP_ALIVE 环境变量来更改所有模型加载到内存中的时间。OLLAMA_KEEP_ALIVE 变量使用与上述 keep_alive 参数类型相同的参数类型。
keep_alive API 参数在 /api/generate 和 /api/chat API 端点中将覆盖 OLLAMA_KEEP_ALIVE 设置。
/think
Qwen3默认输出思考过程,如果不想输出思考过程在问题后追加/no_think字符,则此Session之后的回答都是不带思考过程的,除非执行/clear清空了此Session。
想要恢复思考过程,需要在下次提问之后追加上/think字符。
使用终端日志记录
如果你通过命令行调用Ollama(例如通过ollama run启动模型),可以使用以下命令记录终端输出:
script -c "ollama run your_model_name" session_log.txt
- 这会将终端的所有输入输出保存到
session_log.txt中,包含你的问答对话。 - 注意:结束后按
Ctrl+D退出记录。
————————————————
Open-WebUI🌐
官方中文文档:🏡 主页 | Open WebUI 文档
部署
Python:3.11或更高(后端服务需要,可以参考
Python.md的conda来配置多环境)Node.js:版本22.10或更高。(前端开发所需)
IDE(可选):我们建议使用类似VSCode的IDE进行代码编辑、调试和集成终端访问。
下载
git clone https://github.com/open-webui/open-webui.git
进入目录
cd open-webui
前端
环境变量
cp -RPp .env.example .env安装依赖
# 如果很慢就用 pnpm npm install打包
# 不单独启动前端的话,需要打包一下前端项目 npm run build # 首次打包前端之后,如果修改了前端代码,第二次打包可以在`package.json`中加入以下配置 ## Pyodide是一个将Python运行时环境移植到Web浏览器的工具,允许在浏览器中直接执行Python代码(如文档解析、数据处理等)。由于第一次已经使用过Pyodide了,后续打包就不需要了,节省每次下载py包时间。 "build:vite": "vite build", # 然后执行 npm run build:vite
(现在不用单独启动前端了,后端 uvicorn 服务自动代理前端打包后的 dist 文件夹了)
npm run dev
# 或
npm run preview
后端
进入后端目录
cd backend使用 conda 创建环境
conda create --name open-webui python=3.11conda activate open-webui安装依赖
pip install -r ./requirements.txt -U-U标志可确保您获得最新兼容版本的库,不是必须得。启动
Linux
sh dev.shWindows(可以创建一个
dev_windows.bat,双击运行):: 不显示运行语句 @echo off :: 改编码为:UTF8 chcp 65001 :: 如果放到桌面上运行,需要取消注释下面两行和检查目录代码 :: call d: :: cd /d "D:\WorkSpace\LLM\open-webui\backend" :: 检查目录是否存在 :: if not exist "." ( :: echo Error: Directory does not exist! :: pause :: exit /b 1 :: ) :: 1. 激活 Conda 环境(需要先安装 Conda 并配置环境变量) call conda activate open-webui :: 检查 conda 环境是否激活成功 if errorlevel 1 ( echo Error: Failed to activate conda environment! pause exit /b 1 ) :: 2. 打开一个cmd窗口,启动 uvicorn并保持cmd窗口(关闭/c) start "open-webui-server" cmd /k "uvicorn open_webui.main:app --port 8848 --host 0.0.0.0 --forwarded-allow-ips '*' --reload" echo Open-WebUI server is starting... echo You can access it at: http://localhost:8848
浏览器访问:
http://localhost:8848
进入主页慢?
.env文件修改:OPENAI_API_BASE_URL='http://localhost:11434/v1';
原因:自动请求openai中所有的可用模型api:/v1/models,导致的连接超时10秒;backend\open_webui\retrieval\utils.py文件,注释掉以下:try: model_repo_path = snapshot_download(**snapshot_kwargs) log.debug(f"model_repo_path: {model_repo_path}") return model_repo_path except Exception as e: log.exception(f"Cannot determine model snapshot path: {e}") return model原因:检测模型在
huggingface.co上是否有更新导致的连接超时;
字体更改
去官网下载字体:
JetBrains Mono NLstatic\assets\fonts复制NL相关字体文件到这里src\app.css文件,新增:@font-face { font-family: 'JetBrains Mono NL'; src: url('/assets/fonts/JetBrainsMonoNL-Regular.ttf'); font-display: swap; font-style: normal; } @font-face { font-family: 'JetBrains Mono NL'; src: url('/assets/fonts/JetBrainsMonoNL-Italic.ttf'); font-display: swap; font-style: italic; } /* 项目运行时主页`html`文件上方被动态加入了`<style>`标签,导致的这个`class`所指向的代码块元素字体改为了`monospace`,但是不知道怎么改,只好先这样改了 */ .cm-scroller { font-family: 'JetBrains Mono NL', -apple-system, BlinkMacSystemFont, 'Inter', ui-sans-serif, system-ui, 'Segoe UI', Roboto, Ubuntu, Cantarell, 'Vazirmatn', 'Noto Sans', sans-serif, 'Helvetica Neue', Arial, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji' !important; }src\tailwind.css文件,修改:(最前面加上JetBrains Mono NL)@layer base { html, pre { font-family: 'JetBrains Mono NL', -apple-system, BlinkMacSystemFont, 'Inter', ui-sans-serif, system-ui, 'Segoe UI', Roboto, Ubuntu, Cantarell, 'Vazirmatn', 'Noto Sans', sans-serif, 'Helvetica Neue', Arial, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; }重启
uvicorn服务。
删除按钮字体颜色更改
删除按钮和其他按钮一样颜色的,不能起到醒目作用,取的项目里的颜色var --color-red-500 = oklch(.637 .237 25.331),对应HEX为#FB2C36
src\lib\components\icons\GarbageBin.svelte删除icon的svg文件,更改:stroke="oklch(.637 .237 25.331)"src\lib\components\icons\SignOut.svelte登出icon的svg文件,更改:fill="oklch(.637 .237 25.331)"src\lib\components\chat\Settings\Chats.svelte设置 - 对话 - 删除所有对话记录icon的svg代码,更改:<!-- 搜索:$i18n.t('Delete All Chats'),这个是文字,上面的`svg`标签是`svg` --> 文字:style="color:oklch(.637 .237 25.331)" svg:fill="oklch(.637 .237 25.331)" <!-- 搜索 $i18n.t('Are you sure?'),这个是删除确认文字,上面有`svg` --> 文字:style="color:oklch(.637 .237 25.331)" svg:fill="oklch(.637 .237 25.331)"“删除”文字颜色修改,以下文件关键字搜索
$i18n.t(,看参数为Delete、Sign Out相关的,给元素加上style="color:oklch(.637 .237 25.331)"src\lib\components\layout\Sidebar\ChatMenu.sveltesrc\lib\components\notes\Notes\NoteMenu.sveltesrc\lib\components\admin\Evaluations\FeedbackMenu.sveltesrc\lib\components\layout\Sidebar\UserMenu.svelteSign Outsrc\lib\components\layout\Sidebar\Folders\FolderMenu.sveltesrc\lib\components\workspace\Prompts\PromptMenu.sveltesrc\lib\components\workspace\Tools\ToolMenu.svelte
“移除”头像,文字颜色修改,同上方检索,参数为
Remove,css样式相同。src\lib\components\chat\Settings\Account.svelte
————————————————
api🛠️
Ollama 提供了多种 REST API 接口,支持生成文本、对话交互、模型管理等功能。以下是详细的 API 调用方式及使用场景:
http://localhost:11434
# 或
http://127.0.0.1:11434
# 兼容 OpenAI 格式
http://127.0.0.1:11434/v1
兼容 OpenAI 风格
若需要以兼容 OpenAI 的风格调用,只需在在 ollama 给出的api地址后面拼接上 /v1 就行了。
一、基础文本生成
POST /api/generate
用途:生成单轮文本响应(非对话场景)。
参数:
model(必填): 模型名称(如qwen3)。prompt(必填): 输入的提示文本。stream(可选): 是否启用流式传输(默认false)。format(可选): 指定响应格式(如json)。- 其他高级参数:
temperature,top_p,max_tokens等。
使用场景:
- 单次问答(如翻译、摘要)。
- 代码生成、文案创作。
示例:
curl http://localhost:11434/api/generate -d '{
"model": "qwen3",
"prompt": "用一句话解释量子力学",
"stream": false
}'
二、对话交互
POST /api/chat
用途:多轮对话交互,维护上下文。
参数:
model(必填): 模型名称。messages(必填): 消息历史数组(包含role和content)。stream(可选): 流式传输(默认false)。
使用场景:
- 聊天机器人。
- 多轮任务(如订餐助手、客服)。
示例:
curl http://localhost:11434/api/chat -d '{
"model": "qwen3",
"messages": [
{ "role": "user", "content": "你好" },
{ "role": "assistant", "content": "您好!有什么可以帮助您?" },
{ "role": "user", "content": "推荐一本小说" }
],
"stream": false
}'
三、生成嵌入向量
POST /api/embeddings
用途:获取文本的嵌入向量(Embeddings)。
参数:
model(必填): 模型名称。prompt(必填): 输入文本。
使用场景:
- 语义搜索、文本聚类。
- 机器学习模型的特征输入。
示例:
curl http://localhost:11434/api/embeddings -d '{
"model": "qwen3",
"prompt": "人工智能的未来"
}'
四、模型管理
1. 列出本地模型:GET /api/tags
用途:查看已下载的模型列表。
示例:
curl http://localhost:11434/api/tags
2. 拉取模型:POST /api/pull
用途:从仓库下载模型。
参数:
name(必填): 模型名称(如qwen3)。stream(可选): 显示下载进度(默认false)。
示例:
curl http://localhost:11434/api/pull -d '{
"name": "qwen3"
}'
3. 删除模型:DELETE /api/delete
用途:删除本地模型。
参数:
name(必填): 模型名称。
示例:
curl -X DELETE http://localhost:11434/api/delete -d '{
"name": "qwen3"
}'
五、高级功能
1. 查看模型信息:POST /api/show
用途:获取模型的详细信息(如参数、模板)。
参数:
name(必填): 模型名称。
示例:
curl http://localhost:11434/api/show -d '{
"name": "qwen3"
}'
2. 创建自定义模型:POST /api/create
用途:通过 Modelfile 创建自定义模型。
参数:
name(必填): 新模型名称。modelfile(必填): 模型配置内容。
示例:
curl http://localhost:11434/api/create -d '{
"name": "my-qwen3",
"modelfile": "FROM qwen3\nSYSTEM 你是一个幽默的助手..."
}'
六、流式传输(Streaming)
适用场景:实时输出长文本(如逐句显示回答)。
示例(使用 curl 流式生成):
curl http://localhost:11434/api/generate -d '{
"model": "qwen3",
"prompt": "写一篇关于环保的文章",
"stream": true
}'
七、客户端调用
除了直接调用 API,还可使用官方库或 HTTP 客户端:
1. Python 示例:
import requests
response = requests.post(
"http://localhost:11434/api/generate",
json={"model": "qwen3", "prompt": "你好"}
)
print(response.json()["response"])
2. JavaScript 示例:
fetch("http://localhost:11434/api/generate", {
method: "POST",
body: JSON.stringify({ model: "qwen3", prompt: "Hello" })
})
.then(res => res.json())
.then(data => console.log(data.response));
八、注意事项
- 端口:默认使用
11434,若修改需调整 URL。 - 认证:Ollama 默认无认证,暴露到公网需谨慎。
- 性能:流式传输可提升长文本响应体验。
通过上述 API,您可以在本地灵活调用 qwen3 模型,满足生成、对话、嵌入和管理等多样化需求。
————————————————
js-api🛠️
下面是如何在 JavaScript Web 应用中调用 Ollama API 并处理流式响应的详细指南:
一、基础 API 调用
1. 文本生成(非流式)
async function generateText() {
try {
const response = await fetch("http://localhost:11434/api/generate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "qwen3",
prompt: "用 JavaScript 写一个快速排序函数",
stream: false // 明确关闭流式
})
});
const data = await response.json();
console.log("完整响应:", data.response);
document.getElementById("output").textContent = data.response;
} catch (error) {
console.error("请求失败:", error);
}
}
2. 对话交互(非流式)
async function chat() {
const messages = [
{ role: "user", content: "你好" },
{ role: "assistant", content: "您好!需要什么帮助?" },
{ role: "user", content: "推荐适合初学者的编程语言" }
];
const response = await fetch("http://localhost:11434/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ model: "qwen3", messages, stream: false })
});
const result = await response.json();
console.log("助手回复:", result.message.content);
}
二、流式响应处理(重点)
1. 使用 Fetch API 处理流式
async function streamGenerate() {
const outputDiv = document.getElementById("stream-output");
outputDiv.textContent = ""; // 清空先前内容
try {
const response = await fetch("http://localhost:11434/api/generate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "qwen3",
prompt: "详细解释神经网络的工作原理",
stream: true // 启用流式
})
});
// 获取可读流
const reader = response.body.getReader();
const decoder = new TextDecoder("utf-8");
let buffer = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
// 处理可能的分块边界(确保完整 JSON 解析)
try {
let boundary;
while ((boundary = buffer.indexOf("\n")) !== -1) {
const line = buffer.slice(0, boundary);
buffer = buffer.slice(boundary + 1);
if (line.trim()) {
const parsed = JSON.parse(line);
outputDiv.textContent += parsed.response; // 实时追加内容
}
}
} catch (e) {
console.warn("解析错误:", e);
}
}
} catch (error) {
console.error("流式请求失败:", error);
}
}
2. 使用 EventSource(仅限 GET 请求,需 Ollama 支持 SSE)
如果 Ollama 支持 Server-Sent Events (SSE):
function streamWithSSE() {
const outputDiv = document.getElementById("stream-output");
outputDiv.textContent = "";
const es = new EventSource(`http://localhost:11434/api/generate?prompt=hello&model=qwen3&stream=true`);
es.onmessage = (event) => {
const data = JSON.parse(event.data);
outputDiv.textContent += data.response;
};
es.onerror = (error) => {
console.error("SSE 错误:", error);
es.close();
};
}
三、流式处理关键技术点
1. 分块数据拼接
- 使用
TextDecoder处理二进制流 - 通过缓冲区 (
buffer) 处理不完整的数据包 - 按
\n分割 JSON 行(Ollama 流式响应默认以换行分隔)
2. 实时显示优化
// 使用文档片段减少重绘
const fragment = document.createDocumentFragment();
// 使用 requestAnimationFrame 优化渲染
function updateOutput(text) {
requestAnimationFrame(() => {
outputDiv.textContent += text;
outputDiv.scrollTop = outputDiv.scrollHeight; // 自动滚动到底部
});
}
3. 中断控制
let controller = null;
function startStream() {
controller = new AbortController();
fetch(url, {
signal: controller.signal,
// ...其他参数
});
}
function stopStream() {
if (controller) {
controller.abort();
controller = null;
}
}
四、完整示例:带流式交互的聊天界面
<!DOCTYPE html>
<body>
<div id="chat-box" style="height: 300px; overflow-y: auto"></div>
<input id="user-input" type="text">
<button onclick="sendMessage()">发送</button>
<script>
const input = document.getElementById("user-input");
const chatBox = document.getElementById("chat-box");
async function sendMessage() {
const userMessage = input.value.trim();
if (!userMessage) return;
// 显示用户消息
chatBox.innerHTML += `<div>用户: ${userMessage}</div>`;
input.value = "";
// 发起流式请求
const response = await fetch("http://127.0.0.1:11434/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "qwen3",
messages: [{ role: "user", content: userMessage }],
stream: true
})
});
// 创建助手消息容器
const assistantDiv = document.createElement("div");
assistantDiv.textContent = "助手: ";
chatBox.appendChild(assistantDiv);
// 处理流式响应
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n');
lines.forEach(line => {
if (line.trim()) {
try {
const data = JSON.parse(line);
assistantDiv.textContent += data.message.content;
} catch (e) {
console.warn("解析错误:", e);
}
}
});
}
}
// 输入框回车支持
input.addEventListener('keypress', (e) => {
if (e.key === 'Enter') sendMessage();
});
</script>
</body>
五、注意事项
跨域问题:
开发时配置代理(如 vite.config.js):
export default { server: { proxy: { '/ollama': { target: 'http://localhost:11434', rewrite: path => path.replace(/^\/ollama/, '') } } } }生产环境需通过后端中转或配置 CORS
添加环境变量: OLLAMA_ORIGINS = * 以允许所有域
OLLAMA_ORIGINS=*如果需要允许浏览器插件,则需要添加环境变量
OLLAMA_ORIGINS=chrome-extension://*,moz-extension://*,safari-web-extension://*Ollama暴露在
127.0.0.1和0.0.0.0上,想要指定域名,可以通过设置OLLAMA_HOST环境变量来更改绑定地址,多个用逗号连接。
性能优化:
- 使用防抖(debounce)处理快速输入
- 限制历史消息长度(通过
messages.slice(-5)保留最近 5 条) - 添加加载状态指示器
错误处理:
try { // ...流式处理代码 } catch (error) { if (error.name === 'AbortError') { console.log('请求被用户中止'); } else { console.error('请求失败:', error); chatBox.innerHTML += `<div style="color: red">错误: ${error.message}</div>`; } }
