Website được thiết kế tối ưu cho thành viên chính thức. Hãy Đăng nhập hoặc Đăng ký để truy cập đầy đủ nội dung và chức năng. Nội dung bạn cần không thấy trên website, có thể do bạn chưa đăng nhập. Nếu là thành viên của website, bạn cũng có thể yêu cầu trong nhóm Zalo "HI.AI Members" các nội dung bạn quan tâm.

Phụ lục 7. Mẫu tích hợp Ollama với Drupal

1. Nguyên tắc tích hợp

Drupal nên gọi API trung gian hoặc Ollama từ phía server-side. Không nên để JavaScript trên trình duyệt gọi trực tiếp Ollama.

Mô hình khuyến nghị:

 
Drupal Form/Controller

Drupal Service

API AI trung gian hoặc Ollama localhost

Kết quả trả về Drupal
 

2. Cấu trúc module mẫu

Ví dụ module tên:

 
ai_local_chat
 

Cấu trúc:

 
ai_local_chat/
ai_local_chat.info.yml
ai_local_chat.routing.yml
ai_local_chat.services.yml
src/
Controller/
AiLocalChatController.php
Service/
OllamaClient.php
 

3. File ai_local_chat.info.yml

 
name: 'AI Local Chat'
type: module
description: 'Tích hợp chatbot AI local với Ollama hoặc API trung gian.'
core_version_requirement: ^10 || ^11
package: 'Custom'
 

4. File ai_local_chat.routing.yml

 
ai_local_chat.form:
path: '/ai-local-chat'
defaults:
_controller: '\Drupal\ai_local_chat\Controller\AiLocalChatController::chatPage'
_title: 'AI Local Chat'
requirements:
_permission: 'access content'

ai_local_chat.ask:
path: '/ai-local-chat/ask'
defaults:
_controller: '\Drupal\ai_local_chat\Controller\AiLocalChatController::ask'
requirements:
_permission: 'access content'
methods: [POST]
 

5. File ai_local_chat.services.yml

 
services:
ai_local_chat.ollama_client:
class: Drupal\ai_local_chat\Service\OllamaClient
arguments: ['@http_client']
 

6. Service gọi Ollama: OllamaClient.php

 
<?php

namespace Drupal\ai_local_chat\Service;

use GuzzleHttp\ClientInterface;

class OllamaClient {

protected ClientInterface $httpClient;

public function __construct(ClientInterface $http_client) {
$this->httpClient = $http_client;
}

public function chat(string $question): string {
$url = 'http://127.0.0.1:11434/api/chat';

$payload = [
'model' => 'gemma3:4b',
'stream' => FALSE,
'messages' => [
[
'role' => 'system',
'content' => 'Bạn là trợ lý AI nội bộ bệnh viện. Không bịa số liệu hoặc căn cứ. Không tư vấn điều trị cá nhân.',
],
[
'role' => 'user',
'content' => $question,
],
],
'options' => [
'temperature' => 0.2,
],
];

$response = $this->httpClient->request('POST', $url, [
'json' => $payload,
'timeout' => 120,
]);

$data = json_decode((string) $response->getBody(), TRUE);

return $data['message']['content'] ?? 'Không nhận được phản hồi hợp lệ từ AI.';
}

}
 

7. Controller mẫu: AiLocalChatController.php

 
<?php

namespace Drupal\ai_local_chat\Controller;

use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Drupal\ai_local_chat\Service\OllamaClient;

class AiLocalChatController extends ControllerBase {

protected OllamaClient $ollamaClient;

public function __construct(OllamaClient $ollama_client) {
$this->ollamaClient = $ollama_client;
}

public static function create(ContainerInterface $container): static {
return new static(
$container->get('ai_local_chat.ollama_client')
);
}

public function chatPage(): array {
return [
'#markup' => '
<div id="ai-local-chat">
<p><strong>Lưu ý:</strong> Không nhập dữ liệu định danh người bệnh, bệnh án, kết quả xét nghiệm hoặc thông tin nhạy cảm vào chatbot chung.</p>
<textarea id="ai-question" rows="5" style="width:100%;"></textarea>
<br>
<button id="ai-send">Gửi câu hỏi</button>
<pre id="ai-answer" style="white-space: pre-wrap; margin-top: 20px;"></pre>
</div>
<script>
document.getElementById("ai-send").addEventListener("click", async function () {
const question = document.getElementById("ai-question").value;
const answerBox = document.getElementById("ai-answer");
answerBox.textContent = "Đang xử lý...";

const response = await fetch("/ai-local-chat/ask", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({question})
});

const data = await response.json();
answerBox.textContent = data.answer || data.error || "Không có phản hồi.";
});
</script>
',
'#allowed_tags' => ['div', 'p', 'strong', 'textarea', 'br', 'button', 'pre', 'script'],
];
}

public function ask(Request $request): JsonResponse {
$content = json_decode($request->getContent(), TRUE);
$question = trim($content['question'] ?? '');

if ($question === '') {
return new JsonResponse(['error' => 'Câu hỏi không được để trống.'], 400);
}

try {
$answer = $this->ollamaClient->chat($question);
return new JsonResponse(['answer' => $answer]);
}
catch (\Throwable $e) {
\Drupal::logger('ai_local_chat')->error($e->getMessage());
return new JsonResponse(['error' => 'Có lỗi khi gọi hệ thống AI.'], 500);
}
}

}
 

8. Lưu ý triển khai thực tế

Trong production, nên bổ sung:

  • CSRF token.
  • Phân quyền riêng.
  • Rate limit.
  • API trung gian thay vì gọi trực tiếp Ollama.
  • Log user, role, thời gian, tác vụ.
  • Kiểm soát dữ liệu đầu vào.
  • Không lưu prompt nhạy cảm.
  • Hiển thị nguồn nếu dùng RAG.