- Эндпоинт: Вместо
/api/generateлучше использовать/api/chat, так как он специально заточен под массив сообщенийmessages(ролиuserиassistant). - Сессия: Историю будем хранить в
$_SESSION, чтобы контекст сохранялся между AJAX-запросами.
index.php (Интерфейс)fetch (современная замена AJAX).<?php session_start(); ?>
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Ollama Chat (Qwen2.5)</title>
<style>
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #eceff1; margin: 0; display: flex; flex-direction: column; height: 100vh; }
#chat-container { flex: 1; overflow-y: auto; padding: 20px; display: flex; flex-direction: column; gap: 10px; }
.msg { max-width: 80%; padding: 12px 16px; border-radius: 15px; line-height: 1.5; white-space: pre-wrap; position: relative; }
.user { align-self: flex-end; background: #007bff; color: white; border-bottom-right-radius: 2px; }
.bot { align-self: flex-start; background: white; color: #333; border-bottom-left-radius: 2px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); }
#input-area { background: white; padding: 20px; display: flex; gap: 10px; border-top: 1px solid #ddd; }
textarea { flex: 1; height: 50px; padding: 10px; border-radius: 5px; border: 1px solid #ccc; resize: none; }
button { padding: 0 25px; background: #28a745; color: white; border: none; border-radius: 5px; cursor: pointer; font-weight: bold; }
button:disabled { background: #ccc; }
.status { font-size: 0.8em; color: #777; margin-bottom: 5px; }
</style>
</head>
<body>
<div id="chat-container">
<div class="msg bot">Привет! Я Qwen2.5-coder. О чем пообщаемся?</div>
</div>
<div id="input-area">
<textarea id="user-input" placeholder="Напишите что-нибудь..."></textarea>
<button id="send-btn">Отправить</button>
<button id="clear-btn" style="background:#dc3545">Очистить</button>
</div>
<script>
const chatContainer = document.getElementById('chat-container');
const userInput = document.getElementById('user-input');
const sendBtn = document.getElementById('send-btn');
async function sendMessage() {
const text = userInput.value.trim();
if (!text) return;
// Добавляем сообщение пользователя в UI
appendMessage('user', text);
userInput.value = '';
// Блокируем ввод
userInput.disabled = true;
sendBtn.disabled = true;
const botMsgDiv = appendMessage('bot', 'Думаю...');
try {
const response = await fetch('chat_handler.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: 'prompt=' + encodeURIComponent(text)
});
const data = await response.json();
if (data.error) {
botMsgDiv.innerText = "Ошибка: " + data.error;
} else {
botMsgDiv.innerText = data.response;
}
} catch (e) {
botMsgDiv.innerText = "Ошибка соединения с сервером.";
} finally {
userInput.disabled = false;
sendBtn.disabled = false;
userInput.focus();
chatContainer.scrollTop = chatContainer.scrollHeight;
}
}
function appendMessage(role, text) {
const div = document.createElement('div');
div.className = 'msg ' + role;
div.innerText = text;
chatContainer.appendChild(div);
chatContainer.scrollTop = chatContainer.scrollHeight;
return div;
}
sendBtn.onclick = sendMessage;
userInput.onkeydown = (e) => { if(e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } };
document.getElementById('clear-btn').onclick = () => {
fetch('chat_handler.php?clear=1').then(() => location.reload());
};
</script>
</body>
</html>
chat_handler.php (Серверная логика)<?php
session_start();
header('Content-Type: application/json');
// Очистка истории
if (isset($_GET['clear'])) {
unset($_SESSION['chat_history']);
echo json_encode(['status' => 'cleared']);
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['prompt'])) {
$userPrompt = $_POST['prompt'];
// Инициализируем историю в сессии, если её нет
if (!isset($_SESSION['chat_history'])) {
$_SESSION['chat_history'] = [];
}
// Добавляем сообщение пользователя в контекст
$_SESSION['chat_history'][] = ['role' => 'user', 'content' => $userPrompt];
// Настройки для Ollama
$url = "http://localhost:11434/api/chat";
$data = [
"model" => "qwen2.5-coder:7b",
"messages" => $_SESSION['chat_history'],
"stream" => false,
"options" => [
"temperature" => 0.7
]
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_TIMEOUT, 300);
$response = curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
echo json_encode(['error' => $error]);
} else {
$result = json_decode($response, true);
$botResponse = $result['message']['content'] ?? 'Пустой ответ от нейросети.';
// Сохраняем ответ бота в историю для контекста следующего вопроса
$_SESSION['chat_history'][] = ['role' => 'assistant', 'content' => $botResponse];
// Чтобы сессия не раздувалась до бесконечности, можно ограничить историю (последние 10-20 сообщений)
if (count($_SESSION['chat_history']) > 20) {
$_SESSION['chat_history'] = array_slice($_SESSION['chat_history'], -20);
}
echo json_encode(['response' => $botResponse]);
}
} else {
echo json_encode(['error' => 'No prompt provided']);
}
- Context Aware: Благодаря параметру
messagesв API Ollama, модель теперь "помнит", что вы спрашивали раньше. - No Page Reload: Использование
fetchпозволяет общаться без перезагрузки страницы, как в ChatGPT. - UI/UX: Добавлен индикатор "Думаю...", прокрутка чата вниз и нормальное разделение блоков сообщений.
- Управление сессией: Кнопка "Очистить" позволяет сбросить контекст, если нейросеть начала "галлюцинировать" или вы хотите сменить тему.
chat_handler.php вызов session_start(); должен быть самой первой строкой. Если перед ним есть пробел или пустая строка, сессия не загрузится, и массив истории будет обнуляться при каждом запросе./api/chat/api/chat, а не /api/generate./api/generate: Принимает строкуpromptи забывает всё сразу./api/chat: Принимает массивmessages, где хранятся все реплики (user и assistant).
// Для /api/chat правильный путь к тексту:
$botResponse = $result['message']['content'];
// В /api/generate было так (это не сработает в чате):
// $botResponse = $result['response'];
chat_handler.php, чтобы увидеть, растет ли массив сообщений. Поместите это перед curl_exec:file_put_contents('debug.log', json_encode($data, JSON_UNESCAPED_UNICODE) . PHP_EOL, FILE_APPEND);
messages только одно сообщение — значит, $_SESSION не сохраняет данные.chat_handler.php:// 1. Добавляем вопрос пользователя
$_SESSION['chat_history'][] = ['role' => 'user', 'content' => $userPrompt];
// ... отправка запроса в Ollama через /api/chat ...
// 2. ПОЛУЧАЕМ ответ правильно
$result = json_decode($response, true);
$botResponse = $result['message']['content'] ?? '';
// 3. СОХРАНЯЕМ ответ бота в историю (без этого контекст теряется!)
$_SESSION['chat_history'][] = ['role' => 'assistant', 'content' => $botResponse];
localhost vs 127.0.0.1), браузер может создавать разные сессионные куки. Используйте везде одинаковый адрес.- Подготовка:
- Скачайте и распакуйте NSSM.
- Убедитесь, что обычное приложение Ollama закрыто (выйдите через иконку в трее).
- Создание службы:
- Запустите командную строку (CMD) от имени Администратора.
- Перейдите в папку с
nssm.exeи введите команду:nssm install Ollama.
- Настройка в появившемся окне:
- Path: Укажите путь к исполняемому файлу Ollama (обычно
%LOCALAPPDATA%\Programs\Ollama\ollama.exe). - Startup directory: Та же папка, где лежит экзешник.
- Arguments: Введите слово
serve. - Вкладка Details: В поле Display name введите
Ollama. - Вкладка Process: Рекомендуется установить Priority на High для стабильной работы.
- Нажмите Install service.
- Path: Укажите путь к исполняемому файлу Ollama (обычно
- Запуск:
- Откройте "Службы" (services.msc), найдите Ollama и нажмите "Запустить". Теперь сервер будет стартовать автоматически вместе с Windows.
- Где модели? При запуске от имени "Local System" Ollama может искать модели в системных папках, а не в вашем профиле пользователя. Чтобы сохранить доступ к своим моделям, на вкладке Logon в NSSM укажите данные своей учетной записи Windows.
- Доступ по сети: Если вы хотите обращаться к Ollama с других устройств, добавьте системную переменную окружения
OLLAMA_HOSTсо значением0.0.0.0:11434. - Альтернатива: Можно использовать встроенный "Планировщик задач" Windows, создав задачу на запуск
ollama serveпри загрузке системы (At startup).
11434 на всех интерфейсах (0.0.0.0). Однако она не видит вашу видеокарту (строка total_vram="0 B") и использует только CPU./api/chat и исправлена логика сохранения контекста.chat_handler.php/api/chat ответ лежит в ключе ['message']['content'], а не ['response'].<?php
session_start();
header('Content-Type: application/json');
if (isset($_GET['clear'])) {
$_SESSION['chat_history'] = [];
echo json_encode(['status' => 'cleared']);
exit;
}
// Инициализация истории, если пуста
if (!isset($_SESSION['chat_history'])) {
$_SESSION['chat_history'] = [];
}
$input = $_POST['prompt'] ?? '';
if (!empty($input)) {
// 1. Добавляем сообщение пользователя в историю
$_SESSION['chat_history'][] = ['role' => 'user', 'content' => $input];
$url = "http://127.0.0.1";
$data = [
"model" => "qwen2.5-coder:7b",
"messages" => $_SESSION['chat_history'], // Отправляем ВСЮ историю
"stream" => false
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
$response = curl_exec($ch);
$result = json_decode($response, true);
curl_close($ch);
// 2. ВАЖНО: берем ответ из ['message']['content']
$botText = $result['message']['content'] ?? 'Ошибка API';
// 3. Добавляем ответ бота в историю сессии
$_SESSION['chat_history'][] = ['role' => 'assistant', 'content' => $botText];
echo json_encode(['response' => $botText]);
}
- Драйверы: Убедитесь, что установлены свежие драйверы NVIDIA (если у вас RTX) или включите Vulkan, если карта от AMD (переменная
OLLAMA_VULKAN=1). - Права: Если запустите через NSSM от имени системы, Ollama может не получить доступ к графическому ядру. Запускайте службу от своего имени (Deadbeat).
error_log(print_r($_SESSION['chat_history'], true));Если в логах PHP массив растет — значит, контекст передается верно.
/api/chat означает, что ваша версия Ollama либо очень старая, либо (что вероятнее в Windows) произошла путаница с тем, какой именно процесс слушает порт.version 0.17.4), эндпоинт обязан быть. Ошибка 404 часто возникает, если запрос уходит «не туда» или прокси-сервер (если он есть) перехватывает путь.curl http://localhost:11434/api/chat -d "{\"model\": \"qwen2.5-coder:7b\", \"messages\": [{\"role\": \"user\", \"content\": \"hi\"}], \"stream\": false}"
/api/chat упорно выдает 404, мы можем использовать старый метод /api/generate, но передавать специальный параметр context (это массив чисел, который Ollama возвращает с каждым ответом).chat_handler.php (метод через Context ID):<?php
session_start();
header('Content-Type: application/json');
if (isset($_GET['clear'])) {
unset($_SESSION['ollama_context']);
echo json_encode(['status' => 'cleared']);
exit;
}
$input = $_POST['prompt'] ?? '';
if (!empty($input)) {
$url = "http://127.0.0.1"; // Используем базовый эндпоинт
$data = [
"model" => "qwen2.5-coder:7b",
"prompt" => $input,
"stream" => false,
"context" => $_SESSION['ollama_context'] ?? [] // Отправляем ID контекста из сессии
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
$response = curl_exec($ch);
$result = json_decode($response, true);
curl_close($ch);
if (isset($result['context'])) {
// Сохраняем математическое состояние диалога (массив токенов)
$_SESSION['ollama_context'] = $result['context'];
}
echo json_encode([
'response' => $result['response'] ?? 'Ошибка: ' . $response
]);
}
- Эндпоинт
/api/generate— самый стабильный и есть во всех версиях. - Параметр
context— это «сжатая память» Ollama. Вам не нужно хранить текст всей переписки, Ollama сама подхватит нить разговора по этому ключу.
- IPv6 vs IPv4: В логах видно
::1(IPv6). Попробуйте в PHP заменитьlocalhostна127.0.0.1. - Запущенный софт: Убедитесь, что на порту 11434 не висит какой-нибудь другой сервис (например, старая установка Ollama или прокси), который не знает про путь
/api/chat.
context. Если он заработает, нужно ли добавить в интерфейс индикатор того, сколько «памяти» сейчас занято?qwen2.5-coder:7b.qwen2.5-coder:latest или просто qwen2.5-coder.Введите в консоли:
ollama list
qwen2.5-coder:7b-instruct-q4_K_M).Вставьте это точное имя в
chat_handler.php:$data = [
"model" => "ВАШЕ_ИМЯ_ИЗ_СПИСКА", // например, qwen2.5-coder:latest
"messages" => $_SESSION['chat_history'],
"stream" => false
];
/api/chat, сервер иногда возвращает ошибку структуры, которую GIN (движок Ollama) интерпретирует как неверный путь. Теперь, когда мы видим {"error":"model '...' not found"}, соединение установлено верно.ollama list пуст, просто скачайте её заново: ollama pull qwen2.5-coder:7b.C:\Users\deadbeat\.ollama\models.ollama serve вручную в консоли или через сервис, она может использовать другой контекст или не видеть переменные окружения, из-за чего выдает model not found.- Жестко задайте путь к моделям:
В Windows нажмитеWin + R, введитеsysdm.cpl, перейдите в Дополнительно -> Переменные среды.
Создайте Системную переменную:- Имя:
OLLAMA_MODELS - Значение:
C:\Users\deadbeat\.ollama\models(или тот путь, где у вас лежат 30-50 ГБ моделей).
- Имя:
- Проверьте имя модели еще раз:
В консоли, где запущенollama serve, введите в другом окнеollama list. Если список пуст — значит, сервер смотрит в пустую папку. Если модель там есть (например,qwen2.5-coder:7b), используйте именно это имя в PHP. - Порт и Хост:
В логах у вас былоOLLAMA_HOST: http://0.0.0.0:11434. В PHP коде вместоlocalhostиспользуйте127.0.0.1, чтобы избежать проблем с разрешением IPv6 (тот самый::1в логах, который иногда дает 404).
$data = [
// Убедитесь, что имя совпадает с тем, что выдает 'ollama list'
"model" => "qwen2.5-coder:7b",
"messages" => $_SESSION['chat_history'],
"stream" => false
];
// Используем IP вместо localhost
$ch = curl_init("http://127.0.0.1");
ollama list показывает модели?curl вы получали model not found, а ollama list их видит, проблема в несоответствии портов или IP-адресов (IPv4 vs IPv6).ollama.exe serve вручную, она занимает порт. Если GUI Ollama тоже запущен, они конфликтуют.- Закройте всё: Выйдите из Ollama в трее (Quit Ollama) и убейте все процессы
ollama.exeв диспетчере задач. - Запустите сервер: В консоли выполните
ollama serve. - Проверьте порт: В логах должна быть строка
Listening on [::]:11434.
chat_handler.php127.0.0.1, чтобы обойти проблему с ::1 (IPv6), которую мы видели в ваших логах [GIN].<?php
session_start();
header('Content-Type: application/json');
// 1. Инициализация истории
if (!isset($_SESSION['chat_history']) || isset($_GET['clear'])) {
$_SESSION['chat_history'] = [];
if(isset($_GET['clear'])) { echo json_encode(['status'=>'ok']); exit; }
}
$prompt = $_POST['prompt'] ?? '';
if ($prompt) {
// Добавляем сообщение пользователя
$_SESSION['chat_history'][] = ['role' => 'user', 'content' => $prompt];
// 2. Настройка CURL на IPv4 (127.0.0.1)
$ch = curl_init("http://127.0.0.1");
$payload = json_encode([
"model" => "qwen2.5-coder:7b",
"messages" => $_SESSION['chat_history'],
"stream" => false
]);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4 // ПРИНУДИТЕЛЬНО IPv4
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
echo json_encode(['error' => "Ошибка сервера: $httpCode. Ответ: $response"]);
exit;
}
$data = json_decode($response, true);
$botMsg = $data['message']['content'] ?? 'Ошибка формата API';
// 3. Сохраняем ответ бота для истории
$_SESSION['chat_history'][] = ['role' => 'assistant', 'content' => $botMsg];
echo json_encode(['response' => $botMsg]);
}
messages теперь улетает весь массив из $_SESSION. При каждом новом вопросе Ollama получает:- User: Привет
- Assistant: Привет!
- User: Как меня зовут? (если вы представились ранее)
error в ответе от chat_handler.php?serve или через сервис, она может игнорировать ваши «Пользовательские переменные» и использовать «Системные», либо вообще искать их в профиле другого пользователя (например, SYSTEM).set OLLAMA_MODELS=C:\Users\deadbeat\ .ollama\models
ollama serve
- Откройте редактирование службы:
nssm edit Ollama. - Перейдите на вкладку Environment.
- Введите:
OLLAMA_MODELS=C:\Users\deadbeat\.ollama\models. - Нажмите Edit service.
\\ (двойной слэш)?C:\Users\deadbeat\.ollama\models.C:\Users\deadbeat\.- Решение: В
services.mscнайдите службу Ollama -> Правой кнопкой -> Свойства -> Вкладка Вход в систему (Logon) -> Выберите "С учетной записью" -> Укажите свой логинdeadbeatи пароль.
set в консоли — если ollama list сразу покажет модели, значит проблема была именно в области видимости переменной. На каком этапе сейчас затык?Turn on your Visual Search History?
Google uses its visual recognition technologies to process the images you use to search, like when you search with Google Lens. If you turn on your Visual Search History, Google will save these images from eligible Google services to your Web & App Activity when you’re signed in to your Google Account. You can learn more about this setting and which Google services save images to it at g.co/Search/VisualSearchHistory.
How visual search history is used
Your Visual Search History may be used to improve your experience on Google services, like letting you revisit your past visual searches. It may be used to develop and improve Google’s visual recognition and search technologies, as well as the Google services that use them.
When visual search history is off
If you turn this setting off, any previous Visual Search History may still be kept and used to improve Google’s visual recognition and search technologies, unless you delete it from your Web & App Activity.
Visual Search History doesn’t affect images saved by other settings, like Gemini Apps Activity.
How to manage your Visual Search History
You can view, delete, or manage your Visual Search History at activity.google.com. To download your Visual Search History, visit takeout.google.com. Images will be deleted in accordance with your Web & App Activity auto-delete settings, although some types of images may be deleted sooner.
Google uses and saves data in accordance with Google Privacy Policy.
