Windows Server ollama站点访问方案
2024-10-27
<?php
// 设置请求的 URL - 使用 generate 接口进行对话
$url = 'http://192.168.3.249:11434/api/generate';
// 准备对话数据
$data = [
'model' => 'deepseek-r1:14b', // 替换为你实际安装的模型
'prompt' => '你好,请介绍一下你自己',
'stream' => false
];
// 初始化 cURL
$ch = curl_init();
// 设置 cURL 选项
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
],
CURLOPT_TIMEOUT => 30
]);
// 执行请求并获取响应
$response = curl_exec($ch);
// 检查错误
if (curl_error($ch)) {
echo '错误: ' . curl_error($ch);
} else {
// 解析 JSON 响应
$result = json_decode($response, true);
// 输出 AI 的回复
if (isset($result['response'])) {
echo "AI 回复: " . $result['response'];
} else {
echo "未收到有效回复";
print_r($result); // 打印完整响应用于调试
}
}
// 关闭 cURL
curl_close($ch);
?>// 创建 XMLHttpRequest 对象
var xhr = new XMLHttpRequest();
// 配置请求
xhr.open('GET', 'http://localhost:11434/api/tags', true);
// 设置响应类型
xhr.responseType = 'json';
// 定义请求完成后的回调函数
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
// 请求成功
console.log('请求成功:', xhr.response);
} else {
// 请求失败
console.error('请求失败,状态码:', xhr.status);
}
};
// 定义错误处理
xhr.onerror = function() {
console.error('请求发生错误');
};
// 发送请求
xhr.send();<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ollama大模型测试工具</title>
<style>
:root {
--primary-color: #4a6fa5;
--secondary-color: #6b8cbc;
--success-color: #28a745;
--danger-color: #dc3545;
--warning-color: #ffc107;
--light-color: #f8f9fa;
--dark-color: #343a40;
--border-radius: 8px;
--box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: #f5f7fa;
color: #333;
line-height: 1.6;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
header {
text-align: center;
margin-bottom: 30px;
padding: 20px;
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
color: white;
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
}
h1 {
font-size: 2.5rem;
margin-bottom: 10px;
}
.subtitle {
font-size: 1.2rem;
opacity: 0.9;
}
.card {
background: white;
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
padding: 25px;
margin-bottom: 25px;
}
.card-title {
font-size: 1.5rem;
margin-bottom: 20px;
color: var(--primary-color);
border-bottom: 2px solid var(--light-color);
padding-bottom: 10px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 600;
}
input, select, textarea {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: var(--border-radius);
font-size: 1rem;
transition: border 0.3s;
}
input:focus, select:focus, textarea:focus {
border-color: var(--primary-color);
outline: none;
box-shadow: 0 0 0 2px rgba(74, 111, 165, 0.2);
}
.btn {
display: inline-block;
background-color: var(--primary-color);
color: white;
padding: 12px 24px;
border: none;
border-radius: var(--border-radius);
cursor: pointer;
font-size: 1rem;
font-weight: 600;
transition: all 0.3s;
}
.btn:hover {
background-color: var(--secondary-color);
transform: translateY(-2px);
}
.btn-success {
background-color: var(--success-color);
}
.btn-success:hover {
background-color: #218838;
}
.btn-danger {
background-color: var(--danger-color);
}
.btn-danger:hover {
background-color: #c82333;
}
.btn-warning {
background-color: var(--warning-color);
color: var(--dark-color);
}
.btn-warning:hover {
background-color: #e0a800;
}
.btn-group {
display: flex;
gap: 10px;
margin-top: 20px;
}
.flex-container {
display: flex;
gap: 25px;
flex-wrap: wrap;
}
.flex-item {
flex: 1;
min-width: 300px;
}
.test-area {
display: flex;
flex-direction: column;
height: 400px;
}
.chat-container {
flex: 1;
overflow-y: auto;
border: 1px solid #ddd;
border-radius: var(--border-radius);
padding: 15px;
margin-bottom: 15px;
background-color: #f9f9f9;
}
.message {
margin-bottom: 15px;
padding: 12px;
border-radius: var(--border-radius);
max-width: 80%;
}
.user-message {
background-color: var(--primary-color);
color: white;
margin-left: auto;
}
.ai-message {
background-color: #e9ecef;
color: var(--dark-color);
}
.input-group {
display: flex;
gap: 10px;
}
.input-group input {
flex: 1;
}
.status {
margin-top: 15px;
padding: 10px;
border-radius: var(--border-radius);
text-align: center;
font-weight: 600;
}
.status-success {
background-color: rgba(40, 167, 69, 0.1);
color: var(--success-color);
border: 1px solid var(--success-color);
}
.status-error {
background-color: rgba(220, 53, 69, 0.1);
color: var(--danger-color);
border: 1px solid var(--danger-color);
}
.status-loading {
background-color: rgba(74, 111, 165, 0.1);
color: var(--primary-color);
border: 1px solid var(--primary-color);
}
.hidden {
display: none;
}
.params-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 15px;
}
.info-box {
background-color: rgba(74, 111, 165, 0.1);
border-left: 4px solid var(--primary-color);
padding: 15px;
margin-bottom: 20px;
border-radius: 4px;
}
.info-box h3 {
margin-top: 0;
color: var(--primary-color);
}
.info-box ul {
padding-left: 20px;
}
.info-box li {
margin-bottom: 8px;
}
@media (max-width: 768px) {
.flex-container {
flex-direction: column;
}
.params-grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>Ollama大模型测试工具</h1>
<p class="subtitle">专为自部署Ollama设计的API测试页面</p>
</header>
<div class="info-box">
<h3>Ollama使用说明</h3>
<ul>
<li><strong>Base URL格式</strong>: http://localhost:11434 或 http://您的服务器IP:11434</li>
<li><strong>Ollama默认端口</strong>: 11434</li>
<li><strong>模型名称</strong>: 使用 <code>ollama list</code> 命令查看已安装的模型</li>
<li><strong>常见模型</strong>: llama2, codellama, mistral, gemma, qwen 等</li>
<li><strong>无需API Key</strong>: Ollama本地部署通常不需要API密钥</li>
</ul>
</div>
<div class="flex-container">
<div class="flex-item">
<div class="card">
<h2 class="card-title">Ollama配置</h2>
<div class="form-group">
<label for="base-url">Ollama服务地址 (Base URL)</label>
<input type="text" id="base-url" placeholder="http://localhost:11434" value="http://localhost:11434">
<small>格式: http://IP地址或域名:端口 (默认端口11434)</small>
</div>
<div class="form-group">
<label for="model-name">模型名称</label>
<input type="text" id="model-name" placeholder="例如: llama2, mistral, codellama" value="llama2">
<small>使用 <code>ollama list</code> 查看已安装的模型</small>
</div>
<div class="btn-group">
<button id="save-config" class="btn">保存配置</button>
<button id="test-connection" class="btn btn-warning">测试连接</button>
<button id="reset-config" class="btn btn-danger">重置</button>
</div>
</div>
<div class="card">
<h2 class="card-title">模型参数</h2>
<div class="params-grid">
<div class="form-group">
<label for="temperature">Temperature</label>
<input type="number" id="temperature" min="0" max="2" step="0.1" value="0.7">
<small>控制随机性 (0-2)</small>
</div>
<div class="form-group">
<label for="top-k">Top K</label>
<input type="number" id="top-k" min="1" max="100" value="40">
<small>限制候选token数量</small>
</div>
<div class="form-group">
<label for="top-p">Top P</label>
<input type="number" id="top-p" min="0" max="1" step="0.05" value="0.9">
<small>核采样 (0-1)</small>
</div>
<div class="form-group">
<label for="seed">Seed</label>
<input type="number" id="seed" min="0" value="">
<small>随机种子 (留空为随机)</small>
</div>
</div>
</div>
<div class="card">
<h2 class="card-title">可用模型列表</h2>
<div class="form-group">
<label for="model-list">已安装模型</label>
<select id="model-list">
<option value="">-- 点击刷新模型列表 --</option>
</select>
</div>
<button id="refresh-models" class="btn">刷新模型列表</button>
</div>
</div>
<div class="flex-item">
<div class="card">
<h2 class="card-title">对话测试</h2>
<div class="test-area">
<div class="chat-container" id="chat-container">
<div class="message ai-message">
您好!我是Ollama AI助手。请配置您的Ollama服务地址和模型,然后开始与我对话。
</div>
</div>
<div class="input-group">
<input type="text" id="user-input" placeholder="输入您的问题...">
<button id="send-btn" class="btn btn-success">发送</button>
</div>
</div>
<div id="status" class="status hidden"></div>
</div>
<div class="card">
<h2 class="card-title">测试结果</h2>
<div class="form-group">
<label for="response-time">响应时间</label>
<input type="text" id="response-time" readonly>
</div>
<div class="form-group">
<label for="tokens-used">Tokens使用量</label>
<input type="text" id="tokens-used" readonly>
</div>
<div class="form-group">
<label for="raw-response">原始响应</label>
<textarea id="raw-response" rows="5" readonly></textarea>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// 获取DOM元素
const baseUrlInput = document.getElementById('base-url');
const modelNameInput = document.getElementById('model-name');
const saveConfigBtn = document.getElementById('save-config');
const resetConfigBtn = document.getElementById('reset-config');
const testConnectionBtn = document.getElementById('test-connection');
const userInput = document.getElementById('user-input');
const sendBtn = document.getElementById('send-btn');
const chatContainer = document.getElementById('chat-container');
const statusDiv = document.getElementById('status');
const responseTimeInput = document.getElementById('response-time');
const tokensUsedInput = document.getElementById('tokens-used');
const rawResponseInput = document.getElementById('raw-response');
const modelListSelect = document.getElementById('model-list');
const refreshModelsBtn = document.getElementById('refresh-models');
// 从本地存储加载配置
loadConfig();
// 保存配置
saveConfigBtn.addEventListener('click', function() {
const config = {
baseUrl: baseUrlInput.value,
modelName: modelNameInput.value
};
localStorage.setItem('ollamaTestConfig', JSON.stringify(config));
showStatus('配置已保存!', 'success');
});
// 重置配置
resetConfigBtn.addEventListener('click', function() {
baseUrlInput.value = 'http://localhost:11434';
modelNameInput.value = 'llama2';
localStorage.removeItem('ollamaTestConfig');
showStatus('配置已重置!', 'success');
});
// 测试连接
testConnectionBtn.addEventListener('click', testConnection);
// 刷新模型列表
refreshModelsBtn.addEventListener('click', refreshModelList);
// 发送消息
sendBtn.addEventListener('click', sendMessage);
userInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
sendMessage();
}
});
// 选择模型
modelListSelect.addEventListener('change', function() {
if (this.value) {
modelNameInput.value = this.value;
}
});
function testConnection() {
const baseUrl = baseUrlInput.value;
if (!baseUrl) {
showStatus('请输入Ollama服务地址!', 'error');
return;
}
showStatus('正在测试连接...', 'loading');
fetch(`${baseUrl}/api/tags`)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP错误: ${response.status}`);
}
return response.json();
})
.then(data => {
showStatus('连接成功!Ollama服务运行正常。', 'success');
console.log('Ollama连接测试响应:', data);
})
.catch(error => {
console.error('连接测试失败:', error);
showStatus(`连接失败: ${error.message}`, 'error');
});
}
function refreshModelList() {
const baseUrl = baseUrlInput.value;
if (!baseUrl) {
showStatus('请输入Ollama服务地址!', 'error');
return;
}
showStatus('正在获取模型列表...', 'loading');
fetch(`${baseUrl}/api/tags`)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP错误: ${response.status}`);
}
return response.json();
})
.then(data => {
modelListSelect.innerHTML = '<option value="">-- 选择模型 --</option>';
if (data.models && data.models.length > 0) {
data.models.forEach(model => {
const option = document.createElement('option');
option.value = model.name;
option.textContent = model.name;
modelListSelect.appendChild(option);
});
showStatus(`成功加载 ${data.models.length} 个模型`, 'success');
} else {
showStatus('未找到任何模型,请先使用 ollama pull 命令下载模型', 'error');
}
})
.catch(error => {
console.error('获取模型列表失败:', error);
showStatus(`获取模型列表失败: ${error.message}`, 'error');
});
}
function sendMessage() {
const message = userInput.value.trim();
if (!message) return;
// 获取配置
const config = JSON.parse(localStorage.getItem('ollamaTestConfig') || '{}');
const baseUrl = config.baseUrl || baseUrlInput.value;
const modelName = config.modelName || modelNameInput.value;
if (!baseUrl || !modelName) {
showStatus('请先配置Ollama服务地址和模型名称!', 'error');
return;
}
// 添加用户消息到聊天界面
addMessage(message, 'user');
userInput.value = '';
// 显示加载状态
showStatus('正在请求Ollama服务...', 'loading');
// 获取参数
const temperature = parseFloat(document.getElementById('temperature').value);
const topK = parseInt(document.getElementById('top-k').value);
const topP = parseFloat(document.getElementById('top-p').value);
const seed = document.getElementById('seed').value ? parseInt(document.getElementById('seed').value) : undefined;
// 记录开始时间
const startTime = Date.now();
// 构建Ollama请求数据
const requestData = {
model: modelName,
prompt: message,
stream: false,
options: {
temperature: temperature,
top_k: topK,
top_p: topP
}
};
// 添加seed参数(如果设置了)
if (seed) {
requestData.options.seed = seed;
}
// 发送请求到Ollama
fetch(`${baseUrl}/api/generate`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestData)
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP错误: ${response.status}`);
}
return response.json();
})
.then(data => {
// 计算响应时间
const responseTime = Date.now() - startTime;
// 显示响应时间
responseTimeInput.value = `${responseTime} ms`;
// 显示Tokens使用量
if (data.eval_count !== undefined) {
tokensUsedInput.value = `上下文: ${data.prompt_eval_count || 'N/A'}, 生成: ${data.eval_count}, 总计: ${(data.prompt_eval_count || 0) + data.eval_count}`;
}
// 显示原始响应
rawResponseInput.value = JSON.stringify(data, null, 2);
// 添加AI回复到聊天界面
if (data.response) {
addMessage(data.response, 'ai');
showStatus('请求成功!', 'success');
} else {
throw new Error('响应格式不正确,未找到response字段');
}
})
.catch(error => {
console.error('请求失败:', error);
addMessage(`抱歉,请求失败: ${error.message}`, 'ai');
showStatus(`请求失败: ${error.message}`, 'error');
});
}
function addMessage(content, sender) {
const messageDiv = document.createElement('div');
messageDiv.classList.add('message');
messageDiv.classList.add(sender === 'user' ? 'user-message' : 'ai-message');
messageDiv.textContent = content;
chatContainer.appendChild(messageDiv);
chatContainer.scrollTop = chatContainer.scrollHeight;
}
function showStatus(message, type) {
statusDiv.textContent = message;
statusDiv.className = 'status';
if (type === 'success') {
statusDiv.classList.add('status-success');
} else if (type === 'error') {
statusDiv.classList.add('status-error');
} else if (type === 'loading') {
statusDiv.classList.add('status-loading');
}
statusDiv.classList.remove('hidden');
// 3秒后自动隐藏成功/错误状态
if (type !== 'loading') {
setTimeout(() => {
statusDiv.classList.add('hidden');
}, 3000);
}
}
function loadConfig() {
const config = JSON.parse(localStorage.getItem('ollamaTestConfig') || '{}');
baseUrlInput.value = config.baseUrl || 'http://localhost:11434';
modelNameInput.value = config.modelName || 'llama2';
}
});
</script>
</body>
</html>第1步:设置环境变量(让Ollama监听所有接口)
在Windows Server上以管理员身份打开PowerShell,依次执行:
# 1. 设置环境变量
[Environment]::SetEnvironmentVariable("OLLAMA_HOST", "0.0.0.0:11434", "Machine")
# 2. 立即应用到当前会话
$env:OLLAMA_HOST="0.0.0.0:11434"
# 3. 重启Ollama服务
Restart-Service ollama
第2步:配置防火墙(放行端口)
继续在同一个PowerShell窗口中执行:
# 添加防火墙规则
New-NetFirewallRule -DisplayName "Ollama" -Direction Inbound -Protocol TCP -LocalPort 11434 -Action Allow
第3步:验证配置
# 检查端口监听状态
netstat -ano | findstr :11434
应该看到:
TCP 0.0.0.0:11434 0.0.0.0:0 LISTENING
参考
- https://blog.csdn.net/weixin_46759000/article/details/142175930