برنامه نویسی

بهار Ai و Ollama – ساخت یک چت بابات منبع باز

ما از تازه منتشر شده استفاده خواهیم کرد بهار ai 1.0 ga نسخه ، آماده برای تولید، برای ساختن یک برنامه چت با Spring AI ، Ollama ، Docker و ارائه مدیریت حافظه چت جعبه.
بگذارید توسعه دهندگان جاوا بتوانند به سرعت و به راحتی هوش مصنوعی را به پروژه های خود اضافه کنند.

وابستگی ها:

  • جاوا 21
  • وب بهار
  • اولاما
  • پایگاه داده H2
  • مخزن حافظه چت JDBC
  • محرک بوت بهار

چرا بهار ai + ollama؟

چشم انداز مهندسی هوش مصنوعی دیگر فقط پایتون محور نیست. با بهار شما، توسعه دهندگان جاوا اکنون می توانند با استفاده از مدل های منبع باز مانند Llama 3 ، Gemma ، Deepseek-R1 و موارد دیگر ، برنامه های با قدرت AI بسازند!
و بهترین قسمت این است: شما می توانید از طریق آنها میزبانی محلی را شروع کنید اولامابشر

در این مقاله ، چگونه یاد می گیرید:

  1. Ollama را به عنوان سرور محلی LLM خود (Docker) تنظیم کنید.
  2. آن را با AI بهار به عنوان یک چارچوب مهندسی AI مستقر در جاوا ادغام کنید.
  3. با سابقه مکالمه یک جلسه چند کاربره ایجاد کنید
  4. ساخت جریان چت بابات با استفاده از رویدادهای سرور SENT (SSE) و یک جبهه آسان (HTML/CSS/JS).
  5. dockerizing برای توسعه محلی و استفاده

بیایید شیرجه بزنیم!


1. تنظیم مدل های Ollama و بارگیری

ما می توانیم استفاده از مدل های جمع و جور را با پارامترهای کم و بیش 1B شروع کنیم. برای کارهای ایجاد شده در متن ، مدل های کوچک انتخاب خوبی برای شروع کار هستند.
اولاما به شما امکان می دهد LLM های منبع باز را به صورت محلی اجرا کنید. در اینجا نحوه شروع کار آورده شده است:
نصب Ollama (از طریق Docker)

docker run -d -v ./ollama/ollama-server:/root/.ollama -p 11434:11434 --name ollama ollama/ollama 
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

بارگیری مدل ها (انتخاب یک یا چند)

docker exec ollama ollama pull llama3.2:1b      # Meta's Llama 3  
docker exec ollama ollama pull gemma3:1b        # Google's Gemma
docker exec ollama ollama pull deepseek-r1:1.5b # Deepseek's R1
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

تأیید کنید که در حال اجرا است:

curl http://localhost:11434/api/generate -d '{  
  "model": "llama3.2:1b",  
  "prompt": "Hello, world!"  
}'
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید


2. مهندسی AI 101: فراتر از پایتون

در حالی که پایتون بر ابزار AI حاکم است ، جاوا در حال جلب توجه است با چارچوب هایی مانند بهار هوش مصنوعی.
مفاهیم کلیدی:

مدلهای پایه: LLMS از قبل آموزش دیده (به عنوان مثال ، لاما 3) که می توانید تنظیم کنید.
API های استنباط: ابزارهایی مانند Ollama به شما امکان می دهد این مدل ها را بصورت محلی اجرا کنید.
مهندسی هوش مصنوعی: هنر ادغام LLM ها در برنامه های دنیای واقعی (به عنوان مثال ، چت بابات ، سیستم های RAG).


3. بهار AI + Ollama: Java با LLMS ملاقات می کند

بهار هوش مصنوعی بهترین انتخاب برای آوردن قابلیت های هوش مصنوعی به اکوسیستم بهار است. در اینجا نحوه اتصال آن به اولاما آورده شده است:

  • مرحله 3.1: AI بهار را به پروژه خود اضافه کنید


    org.springframework.ai
    spring-ai-starter-model-ollama


    
    org.springframework.ai
    spring-ai-starter-model-chat-memory-repository-jdbc


    com.h2database
    h2
    runtime

حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  • مرحله 3.2: اولاما را در پیکربندی کنید application.yml
spring:
  application:
    name: demo-chatbot
  ai:
    ollama:
      base-url: http://localhost:11434
      chat:
        model: llama3.2:1b # deepseek-r1:1.5b, gemma3:1b
    chat:
      memory:
        repository:
          jdbc:
            # https://docs.spring.io/spring-ai/reference/1.0/api/chat-memory.html#_schema_initialization
            initialize-schema: always
            schema: classpath:sql/schema-h2.sql
  datasource:
    url: jdbc:h2:mem:~/demo-chatbot
    driverClassName: org.h2.Driver
    username: sa
    password: password
  h2:
    console:
      enabled: true
      path: /h2
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  • مرحله 3.3: با LLM از جاوا تماس بگیرید

در ChatClient یک API مسلط برای برقراری ارتباط با یک مدل هوش مصنوعی ارائه می دهد.
در Default System Prompt یک الگوی سریع ساده ایجاد می کند و لحن را برای پاسخ ها تنظیم می کند.
در Advisors API یک روش انعطاف پذیر برای رهگیری ، اصلاح و تقویت تعامل با یک مدل فراهم می کند.
(LLMS) بدون تابعیت هستند ، به این معنی که آنها اطلاعات مربوط به تعامل قبلی را حفظ نمی کنند.
بهار ai خودکار پیکربندی ChatMemory لوبیا که به شما امکان می دهد پیام ها را در چندین تعامل ذخیره و بازیابی کنید. برای H2 شما باید طرح را ایجاد کنید. آن را درون آن قرار دهید: src/main/resources/sql/schema-h2.sql

CREATE TABLE IF NOT EXISTS SPRING_AI_CHAT_MEMORY (
    conversation_id VARCHAR(36) NOT NULL,
    content TEXT NOT NULL,
    type VARCHAR(10) NOT NULL CHECK (type IN ('USER', 'ASSISTANT', 'SYSTEM', 'TOOL')),
    "timestamp" TIMESTAMP NOT NULL
    );

CREATE INDEX IF NOT EXISTS SPRING_AI_CHAT_MEMORY_CONVERSATION_ID_TIMESTAMP_IDX
ON SPRING_AI_CHAT_MEMORY(conversation_id, "timestamp");
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

@Configuration
public class ChatConfig {

    @Bean
    public ChatClient chatClient(ChatClient.Builder builder, ChatMemory chatMemory) {
        String defaultSystemPrompt = """
                Your are a useful AI assistant, your responsibility is provide users questions
                about a variety of topics.
                When answering a question, always greet first and state your name as JavaChat
                When unsure about the answer, simply state that you don´t know.
                """;
        return builder
                .defaultSystem(defaultSystemPrompt)
                .defaultAdvisors(
                        new SimpleLoggerAdvisor(),                //simply logs requests and responses with a Model
                        new PromptChatMemoryAdvisor(chatMemory)  //let Spring AI manage long term memory in the DB
                        )
                .build();
    }
}

حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

@RequestMapping("/api/chat")
@RestController
public class ChatController {

    @Autowired
    private ChatClient chatClient;

    @GetMapping
    public String chat(@RequestParam String question, @RequestParam String chatId) {
        return chatClient
                .prompt()
                .user(question)
                .advisors(advisor -> advisor
                        .param(ChatMemory.CONVERSATION_ID, chatId))
                .call()
                .content();
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

آن را امتحان کنید: curl "http://localhost:8080/api/chat?question=Tell%20me%20a%20joke"


4. گفتگوی چت با رویدادهای سرور (SSE)

SSE یک پروتکل سبک برای در زمان واقعیبا جریان یک طرفه از سرور به مشتری (مناسب برای chatbots). بر خلاف WebSockets (دو طرفه) ، SSE برای مواردی مانند پخش LLM ساده تر است.
SSE همچنین UX بهتری را برای کاربران نهایی فراهم می کند ، زیرا پاسخ ها به محض آماده شدن منتشر می شوند (برخی از پاسخ های پیچیده می توانند یک دقیقه یا بیشتر طول بکشد). بیایید پاسخ ها را با استفاده از SSE پخش کنیم:

@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ChunkResponseDTO> streamChat(@RequestParam String question, @RequestParam String chatId) {
    return chatClient
            .prompt()
            .user(question)
            .advisors(advisor -> advisor
                .param(ChatMemory.CONVERSATION_ID, chatId))
            .stream()
            .content()
            .map(chunk -> new ChunkResponseDTO(chunk));
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

جزئیات کلیدی:

  • text_event_stream_value: هدر text/event-stream SSE را فعال می کند.
  • قالب SSE: هر پیام باید به پایان برسد \n\nبشر پیشوند با data: برای انطباق
  • جریان های واکنشی: Flux (از پروژه راکتور) جریان جریان ناهمزمان را انجام می دهد.
public record ChunkResponseDTO(String value) {}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید


محدودیت با HTTP/1.1

محدودیت های اتصال:

  • مرورگرها فقط 6 اتصال همزمان HTTP/1.1 را در هر دامنه مجاز می کنند.
  • SSE یک اتصال در هر جریان مصرف می کند ، که می تواند درخواست های دیگر را مسدود کند.

ارتقاء به HTTP/2 برای عملکرد

HTTP/2 تنگناهای SSE را با:

چند برابر: چندین جریان بیش از یک اتصال TCP. حداکثر تعداد جریانهای HTTP همزمان بین سرور و مشتری مذاکره می شود (پیش فرض تا 100)

نحوه فعال کردن HTTP/2 در بوت بهار

  • مرحله 4.1: پیکربندی HTTP/2 در application.yml
server:
  http2:
    enabled: true
  ssl:
    enabled: true
    key-store: classpath:keystore.p12
    key-store-password: yourpassword
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  • مرحله 4.2: یک گواهینامه خود امضا کنید (فقط برای آزمایش):
keytool -genkeypair -alias mydomain -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore keystore.p12 -validity 365
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

تأیید کنید HTTP/2 فعال است (-k برای اعتماد به گواهینامه خود امضا شده)

curl --head -k https://localhost:8080/actuator/health


Frontend:

شروع با JavaScript:

const chatStream = (question) => {
  const eventSource = new EventSource(`https://localhost:8080/api/chat/stream?chatId=1&question=${encodeURIComponent(question)}`);

  eventSource.onmessage = (e) => {

    console.log('New message:', JSON.parse(e.data).value);
    // Append to UI (e.g., a chat div)
    document.getElementById('messages').innerHTML += JSON.parse(e.data).value;
  };

  eventSource.onerror = (e) => {
    console.error('SSE error:', e);
    eventSource.close();
  };
};

// Usage
chatStream("Tell me about Java");
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

جزئیات کلیدی:

EventSource: API مرورگر بومی برای SSE (هیچ کتابخانه ای لازم نیست).
اتصال مجدد خودکار: اگر اتصال کاهش یابد ، منطق آزمایش مجدد داخلی.


رندر جلو برای خروجی LLM

پاسخ های LLM اغلب شامل Markdown یا HTML است (به عنوان مثال ، **bold**با "https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.6/purify.min.js">

حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

eventSource.onmessage = (e) => {
    const chunkResponse = JSON.parse(e.data).value);
    console.log('New message:', chunkResponse);
    const sanitized = DOMPurify.sanitize(chunkResponse); // Strips malicious scripts
    // Append to UI (e.g., a chat div)
    document.getElementById('messages').innerHTML += sanitized;
  };
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  • مرحله 4.4: برای پشتیبانی از علامت گذاری (اختیاری)

اگر می خواهید با خیال راحت علامت گذاری را ارائه دهید، از کتابخانه ای مانند Marked + Dompurify استفاده کنید:

"https://cdn.jsdelivr.net/npm/marked/marked.min.js">
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  let chunkResponses = '';
  eventSource.onmessage = (e) => {
    chunkResponses += JSON.parse(e.data).value;

    // Sanitize all chunks received so far.
    DOMPurify.sanitize(chunkResponses);

    // Check if the output was insecure.
    if (DOMPurify.removed.length) {
      // If the output was insecure, immediately stop what you were doing.
      // Reset the parser and flush the remaining Markdown.
      chunkResponses = '';
      return;
    }
    // Append to UI (e.g., a chat div)
    document.getElementById('messages').innerHTML = marked.parse(chunkResponses);
  };
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید


ملاحظات امنیتی کلیدی: هرگز به خروجی LLM اعتماد نکنید (و نه ورودی کاربر)

  • فرض کنید تمام پاسخ های LLM ممکن است حاوی کد مخرب (حتی ناخواسته) باشد.
  • فرض کنید کاربران سعی می کنند کد شما را بشکنند و امنیت شما را آزمایش کنند.
  • حمله مثال: سلام

محدودیت با API EventSource

حتی اگر استفاده از SSE در سمت مشتری آسان باشد ، API EventSource محدودیت هایی دارد:

  • بدون هدر درخواست سفارشی: هدرهای درخواست سفارشی مجاز نیستند.
  • http فقط دریافت کنید: هیچ راهی برای مشخص کردن روش HTTP دیگر وجود ندارد.
  • بدون بدنه درخواست: تمام پیام های گپ باید در URL باشد که در اکثر مرورگرها به 2000 کاراکتر محدود می شود.
  • کتابخانه های پسوند را برای EventSource و SSE بررسی کنید: Fetch Source ، Fetch API + getReader ()

  • مرحله 4.5: شروع ساختار HTML

در اینجا ساختار HTML وجود دارد که شامل یک فرم برای ورودی کاربر ، یک ظرف برای نمایش داده های پخش شده و یک نوار جانبی برای تاریخچه پیام است.


 lang="en">

    </span>Spring AI Chat<span class="nt"/>
    <span class="nt"><link/> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"layout.css"</span><span class="nt">></span>
<span class="nt"/>
<span class="nt"/>
<span class="c"><!-- Sidebar for chat history --></span>
<span class="nt"><div> <span class="na">id=</span><span class="s">"sidebar"</span><span class="nt">></span>
    <span class="nt"><h3/></span>Chat History<span class="nt"/>
    <span class="nt"/>
<span class="nt"/></div></span>

<span class="c"><!-- Main chat area --></span>
<span class="nt"><div> <span class="na">id=</span><span class="s">"chat-container"</span><span class="nt">></span>
    <span class="nt"><p> <span class="na">id=</span><span class="s">"messages"</span><span class="nt">></span></p></span>
    <span class="nt"/>
<span class="nt"/></div></span>

<span class="nt"><script><![CDATA[<span class="na">src=]]></script></span><span class="s">"main.js"</span><span class="nt">></span> <span class="nt"/>
<span class="nt"><script><![CDATA[<span class="na">src=]]></script></span><span class="s">"https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.6/purify.min.js"</span><span class="nt">></span>
<span class="nt"><script><![CDATA[<span class="na">src=]]></script></span><span class="s">"https://cdn.jsdelivr.net/npm/marked/marked.min.js"</span><span class="nt">></span>
<span class="nt"/>
<span class="nt"/>

</span></span></code></pre>
<div class="highlight__panel js-actions-panel">
<div class="highlight__panel-action js-fullscreen-code-action">
    <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>حالت تمام صفحه را وارد کنید
    

از حالت تمام صفحه خارج شوید

HTML را در src/main/resources/static/index.html
جاوا اسکریپت را در src/main/resources/static/js/main.js


5. بهار AI + Ollama Chatbot خود را با Docker مستقر کنید

  • مرحله 5.1 Docker Compose Setup

ایجاد کردن ollama-docker-compose.yaml
(PS اگر دستگاه شما از GPU پشتیبانی می کند ، می توانید شتاب GPU را در داخل ظروف Docker فعال کنید. اسناد تصویر Ollama)

services:
  # Ollama LLM inference server
  ollama:
    volumes: # Ollama with persistent storage (no redownloading models).
      - ./ollama/ollama-server:/root/.ollama
    container_name: ollama
    pull_policy: always
    tty: true
    restart: unless-stopped
    image: docker.io/ollama/ollama:latest
    ports:
      - 11434:11434
    environment:
      - OLLAMA_KEEP_ALIVE=24h
    # Enable GPU support
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
  # Spring AI Backend
  chat-app:
    build:
      context: . # Dockerfile in the root folder
    container_name: chat-app
    ports:
      - "8080:8080"
    environment:
      - SPRING_AI_OLLAMA_BASE_URL=http://ollama:11434
    depends_on:
      - ollama

حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  • مرحله 5.2 Boot Boot Dockerfile
# Maven build stage
FROM maven:3.9.9-eclipse-temurin-21-alpine as build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src/ ./src/
RUN mvn clean package

# Spring Boot package stage
FROM eclipse-temurin:21-jre-alpine
COPY --from=build app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  • همه چیز را با استفاده از docker-compose build && docker-compose up -d
  • حرکت به: https://localhost:8080 و جلسه چت خود را شروع کنید

https%3A%2F%2Fdev to uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frv5ufznlvf0mdliwbpal

  • اگر می خواهید ببینید که چگونه پیام ها در پایگاه داده ذخیره می شوند ، به کنسول H2 بروید https://localhost:8080/h2

بهار


نتیجه گیری: آینده هوش مصنوعی جاوا شما

معماری نهایی

اسپرینگای-اولا

شما فقط یک میزبان محلی ، چت بابات منبع باز با بهار AI و Ollama ، هیچ هزینه API OpenAi یا پایتون لازم نیست!
SSE + http/2 + بهار ai = جریان LLM مقیاس پذیر و در زمان واقعیبشر

کجا برویم؟

  • کد کامل را پرداخت کنید
  • آزمایش کردن RAG (نسل بازیابی-آمریكا) با استفاده از API مدل تعبیه شده AI بهار AI و پایگاه داده های بردار.

چه چیزی می سازید؟ افکار خود را در نظرات به اشتراک بگذارید! 👇

(PS من را برای آموزش بیشتر Java + AI دنبال کنید!)

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا