تشغيل Discourse AI مع Ollama محليًا

هل كان لدى أحد تجربة في جعل هذا يعمل مع ollama محليًا؟

أحاول أيضًا جعل هذا يعمل مع ollama، يبدو أن واجهة برمجة التطبيقات (API) التي يستخدمها المكوّن الإضافي Discourse AI غير متوافقة مع Ollama وأي إعدادات أقوم بتغييرها لا تغير طلب واجهة برمجة التطبيقات (API).

هذا ما يعمل مع ollama:

curl http://192.168.1.2:11434/api/generate -d '{\
                     \"model\": \"llama3.2\",\
                     \"prompt\": \"Why is the sky blue?\"\
                   }'

ويبدو أن ما ورد أعلاه هو النوع الوحيد من الطلبات التي يحاولها المكوّن الإضافي. على سبيل المثال:

curl http://192.168.1.2:11434/ \
                           -X POST \
                           -H 'Content-Type: application/json' \
                           -d '{\"inputs\":\"\u003cs\u003e[INST] What is your favourite condiment? [/INST] Well, Im quite partial to a good squeeze of fresh lemon juice. It adds just the right amount of zesty flavour to whatever Im cooking up in the kitchen!\u003c/s\u003e [INST] Do you have mayonnaise recipes? [/INST]\",\"parameters\":{\"max_new_tokens\":500, \"temperature\":0.5,\"top_p\": 0.9}}'

من المفترض أن يدعم روبوت الدردشة بالذكاء الاصطناعي ollama ولكنني غير قادر على الحصول على رد منه أيضًا.

لذا، أي شخص نجح في جعل هذا يعمل مع ollama، يرجى نشر الإعدادات التي استخدمتها!

يبدو أنني تحدثت مبكرًا جدًا، إذا لم يكن لدي أي إعدادات في إضافة Discourse AI مضبوطة وقمت باتباع تعليمات RAG في هذا المنشور أحصل على ردود في دردشة الذكاء الاصطناعي من Ollama!

يعمل بشكل جيد تمامًا مع ollama، لدينا عدة أشخاص في الفريق يستخدمونه.

رائع. هل يوجد دليل يمكننا اتباعه حول كيفية تطبيق ذلك؟

أنا مهتم أكثر بالتلخيص والوظائف الأخرى بدلاً من روبوت الدردشة.

تقدير الوقت: هذه التهيئة لا تعمل:

(لقد قمت بإخفاء عنوان IP الخاص بمضيفي هنا).

ما يحدث هو أنني أحصل على خطأ داخلي في الخادم عند النقر فوق “تشغيل الاختبار”.

في /logs، أرى:

NameError (متغير محلي غير معرف أو طريقة `tokenizer' لنسخة من `DiscourseAi::Completions::Dialects::ChatGpt)
app/controllers/application_controller.rb:427:in `block in with_resolved_locale'
app/controllers/application_controller.rb:427:in `with_resolved_locale'
lib/middleware/omniauth_bypass_middleware.rb:35:in `call'
lib/content_security_policy/middleware.rb:12:in `call'
lib/middleware/anonymous_cache.rb:409:in `call'
lib/middleware/csp_script_nonce_injector.rb:12:in `call'
config/initializers/008-rack-cors.rb:14:in `call'
config/initializers/100-quiet_logger.rb:20:in `call'
config/initializers/100-silence_logger.rb:29:in `call'
lib/middleware/enforce_hostname.rb:24:in `call'
lib/middleware/processing_request.rb:12:in `call'
lib/middleware/request_tracker.rb:385:in `call'

يحدث هذا بغض النظر عن المُرمّز الذي أختاره. أنا أختبر هذا على 3.5.0beta1-dev (c1ee4e120e).

عيّن عنوان URL إلى http://localhost:11434/v1/chat/completions

لا يزال الاختبار يفشل مع خطأ في الخادم الداخلي، ونفس رسالة السجل أيضًا - خطأ NameError يقول أن ‘tokenizer’ هو متغير محلي غير معرف.

جرب حفظ النموذج كما هو، ثم قم بتحديث الصفحة، ثم عد مرة أخرى عبر زر “تعديل LLM” وقم بتشغيل اختبار جديد.

نفس النتيجة - لقد مررت بها عدة مرات، لكنني جربتها مرة أخرى للتو.

لقد قمت بالتثبيت بالطريقة المعتادة - أضفتها إلى ملف app.yml الخاص بي وقمت بتشغيل ./launcher rebuild app

السطر الذي أضفته كان:

hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - sudo -E -u discourse git clone https://github.com/discourse/discourse-ai.git

(لقد أزلت المكونات الإضافية الأخرى من القائمة ولكن تركت مكونات yml حتى تتمكن من رؤية مكان هذا في ملف yml.)

سأعيد إعداد أولاما المحلية الخاصة بي غدًا وسأبلغ هنا.

لقد فعلت:

ollama serve
ollama run phi4

# اختبار

curl http://localhost:11434/v1/chat/completions \
              -H "Content-Type: application/json" \
              -d '{
              "model": "phi4",
              "messages": [
                  {
                      "role": "system",
                      "content": "You are a helpful assistant."
                  },
                  {
                      "role": "user",
                      "content": "How much is 1+1?"
                  }
              ]
          }' -s | jq .choices[].message.content

"The sum of 1 + 1 is 2. \n\nIn basic arithmetic, when you add one unit to another single unit, the total number becomes two units. This operation is part of fundamental addition principles used in mathematics. Is there anything else I can help with regarding math or any other topic?"

تم اختباره على المساعد

شكرًا لك على اختبار هذا.

لاحظت أن لدي خيارًا إضافيًا (تعطيل إكمال التدفق) مقارنة بإعداداتك. هل من الممكن أن يكون اختيار النموذج هو السبب في المشكلة لدي؟ (لقد قمت بتثبيت deepseek-r1:32b).

curl http://localhost:11434/v1/chat/completions \
              -H "Content-Type: application/json" \
              -d '{
              "model": "deepseek-r1:32b",
              "messages": [
                  {
                      "role": "system",
                      "content": "أنت مساعد مفيد."
                  },
                  {
                      "role": "user",
                      "content": "كم يساوي 1+1؟"
                  }
              ]
          }' -s | jq .choices[].message.content

"<think>\nحسنًا، أحاول معرفة كم يساوي 1 زائد 1. حسنًا، دعنا نحلل هذا خطوة بخطوة على الرغم من أنه يبدو بسيطًا.\n\nأولاً، أعرف أن الجمع هو دمج رقمين للحصول على مجموع كلي. لذا، عندما نقول 1 + 1، فإننا في الأساس نضع كائنًا واحدًا مع كائن آخر لنرى كم لدينا إجمالاً.\n\nدعني أتخيل ذلك: تخيل أن لدي تفاحة واحدة في يدي اليسرى وتفاحة أخرى في يدي اليمنى. إذا جمعت بينهما، فكم عدد التفاحات التي لدي؟ يجب أن يكون تفاحتين، أليس كذلك؟\n\nطريقة أخرى للتفكير في ذلك هي استخدام خط الأرقام. بدءًا من الرقم 1، إذا تحركت خطوة واحدة إلى الأمام، أين سأهبط؟ هذا صحيح، على الرقم 2.\n\nيمكنني أيضًا استخدام أصابعي للعد. ارفع إصبعًا واحدًا في يدي اليسرى وآخر في يدي اليمنى. الآن، عدّها جميعًا: واحد، اثنان. إذن هذا يعطيني مجموع إصبعين، مما يعني أن 1 + 1 = 2.\n\nربما يجب أن أتحقق مما إذا كان هناك منظور أو طريقة أخرى. على سبيل المثال، في النظام الثنائي، وهو نظام الأرقام المستخدم بواسطة الحواسيب، يساوي 1 + 1 10 لأنه يحمل الرقم بعد الوصول إلى 2 (الذي يُعبر عنه بـ 10). لكن هذا سياق مختلف. في الحساب القياسي الأساسي 10 الذي نستخدمه يوميًا، يظل 1 + 1 يساوي 2.\n\nدعني أفكر أيضًا في مفهوم المجموعات. إذا كان لدي مجموعة تحتوي على عنصر واحد ومجموعة أخرى تحتوي على عنصر واحد، فإن دمجهما سيؤدي إلى مجموعة تحتوي على عنصرين. هذا يعزز أن 1 + 1 يساوي 2.\n\nقد أتساءل عما إذا كان هناك أي سيناريو لا يساوي فيه 1 + 1 2. ولكن في نطاق الحساب الأساسي والرياضيات كما نتعلمها، الإجابة هي دائمًا اثنان.\n\nللتأكد من أنني لا أغفل أي شيء، ربما يجب أن أبحث في بعض البراهين أو المبادئ الرياضية التي تؤكد ذلك. على سبيل المثال، في بديهيات بيانو، التي تشكل أساس الحساب، فكرة إضافة 1 إلى 1 ستتبع البديهية التي تنص على أن كل عدد طبيعي له خلف، و2 يُعرّف على أنه خلف 1.\n\nلذلك، عند تجميع كل هذه الأفكار معًا - تصور الأشياء، واستخدام خط الأرقام، وعد الأصابع، والنظر في المجموعات، وحتى التطرق إلى البديهيات الرياضية - فإن كل ذلك يشير إلى أن 1 + 1 يساوي 2.\n</think>\n\nنتيجة جمع 1 و1 معًا هي 2. يمكن رؤية ذلك في الحساب البسيط وطرق مختلفة لتصور المشكلة.\n\n**الإجابة:**  \n$1 + 1 = \\boxed{2}$"

نتيجة مماثلة، رغم أنني أحصل على وسوم <think> مع هذا النموذج تحديدًا - لذا ربما لن يكون هذا النموذج مناسبًا لهذا الاستخدام.

لقد قمت بإعادة بناء تثبيتتي للتو (أي ./launcher rebuild app) وتأكدت من أن كل شيء محدث - أرى “كل شيء محدث” في صفحة التحديث الخاصة بي.

عند اختيار إما deepseek-r1:32b أو phi4 (والذي قمت بتثبيته واختباره أيضًا)، ما زلت أحصل على استجابة خطأ داخلي من الخادم في المتصفح.

لكن في سجلاتي، أرى شيئًا جديدًا.

FinalDestination::SSRFDetector::DisallowedIpError (FinalDestination: all resolved IPs were disallowed) lib/final_destination/ssrf_detector.rb:105:in 'lookup_and_filter_ips' lib/final_destination/http'

lib/final_destination/ssrf_detector.rb:105:in `lookup_and_filter_ips`
lib/final_destination/http.rb:15:in `connect`
net-http (0.6.0) lib/net/http.rb:1642:in `do_start`
net-http (0.6.0) lib/net/http.rb:1631:in `start`
net-http (0.6.0) lib/net/http.rb:1070:in `start`
plugins/discourse-ai/lib/completions/endpoints/base.rb:105:in `perform_completion!`
plugins/discourse-ai/lib/completions/endpoints/open_ai.rb:44:in `perform_completion!`
plugins/discourse-ai/lib/completions/llm.rb:281:in `generate`
plugins/discourse-ai/lib/configuration/llm_validator.rb:36:in `run_test`
plugins/discourse-ai/app/controllers/discourse_ai/admin/ai_llms_controller.rb:128:in `test`
actionpack (7.2.2.1) lib/action_controller/metal/basic_implicit_render.rb:8:in `send_action`
actionpack (7.2.2.1) lib/abstract_controller/base.rb:226:in `process_action`
actionpack (7.2.2.1) lib/action_controller/metal/rendering.rb:193:in `process_action`
actionpack (7.2.2.1) lib/abstract_controller/callbacks.rb:261:in `block in process_action`
activesupport (7.2.2.1) lib/active_support/callbacks.rb:121:in `block in run_callbacks`
app/controllers/application_controller.rb:427:in `block in with_resolved_locale`
i18n (1.14.7) lib/i18n.rb:353:in `with_locale`
app/controllers/application_controller.rb:427:in `with_resolved_locale`
activesupport (7.2.2.1) lib/active_support/callbacks.rb:130:in `block in run_callbacks`
activesupport (7.2.2.1) lib/active_support/callbacks.rb:141:in `run_callbacks`
actionpack (7.2.2.1) lib/abstract_controller/callbacks.rb:260:in `process_action`
actionpack (7.2.2.1) lib/action_controller/metal/rescue.rb:27:in `process_action`
actionpack (7.2.2.1) lib/action_controller/metal/instrumentation.rb:77:in `block in process_action`
activesupport (7.2.2.1) lib/active_support/notifications.rb:210:in `block in instrument`
activesupport (7.2.2.1) lib/active_support/notifications/instrumenter.rb:58:in `instrument`
activesupport (7.2.2.1) lib/active_support/notifications.rb:210:in `instrument`
actionpack (7.2.2.1) lib/action_controller/metal/instrumentation.rb:76:in `process_action`
actionpack (7.2.2.1) lib/action_controller/metal/params_wrapper.rb:259:in `process_action`
activerecord (7.2.2.1) lib/active_record/railties/controller_runtime.rb:39:in `process_action`
actionpack (7.2.2.1) lib/abstract_controller/base.rb:163:in `process`
actionview (7.2.2.1) lib/action_view/rendering.rb:40:in `process`
rack-mini-profiler (3.3.1) lib/mini_profiler/profiling_methods.rb:115:in `block in profile_method` 
actionpack (7.2.2.1) lib/action_controller/metal.rb:252:in `dispatch`
actionpack (7.2.2.1) lib/action_controller/metal.rb:335:in `dispatch`
actionpack (7.2.2.1) lib/action_dispatch/routing/route_set.rb:67:in `dispatch`
actionpack (7.2.2.1) lib/action_dispatch/routing/route_set.rb:50:in `serve`
actionpack (7.2.2.1) lib/action_dispatch/routing/mapper.rb:32:in `block in <class:Constraints>`
actionpack (7.2.2.1) lib/action_dispatch/routing/mapper.rb:62:in `serve`
actionpack (7.2.2.1) lib/action_dispatch/journey/router.rb:53:in `block in serve`
actionpack (7.2.2.1) lib/action_dispatch/journey/router.rb:133:in `block in find_routes`
actionpack (7.2.2.1) lib/action_dispatch/journey/router.rb:126:in `each`
actionpack (7.2.2.1) lib/action_dispatch/journey/router.rb:126:in `find_routes`
actionpack (7.2.2.1) lib/action_dispatch/journey/router.rb:34:in `serve`
actionpack (7.2.2.1) lib/action_dispatch/routing/route_set.rb:896:in `call`
lib/middleware/omniauth_bypass_middleware.rb:35:in `call`
rack (2.2.11) lib/rack/tempfile_reaper.rb:15:in `call`
rack (2.2.11) lib/rack/conditional_get.rb:27:in `call`
rack (2.2.11) lib/rack/head.rb:12:in `call`
actionpack (7.2.2.1) lib/action_dispatch/http/permissions_policy.rb:38:in `call`
lib/content_security_policy/middleware.rb:12:in `call`
lib/middleware/anonymous_cache.rb:409:in `call`
lib/middleware/csp_script_nonce_injector.rb:12:in `call`
config/initializers/008-rack-cors.rb:14:in `call`
rack (2.2.11) lib/rack/session/abstract/id.rb:266:in `context`
rack (2.2.11) lib/rack/session/abstract/id.rb:260:in `call`
actionpack (7.2.2.1) lib/action_dispatch/middleware/cookies.rb:704:in `call`
actionpack (7.2.2.1) lib/action_dispatch/middleware/callbacks.rb:31:in `block in call`
activesupport (7.2.2.1) lib/active_support/callbacks.rb:101:in `run_callbacks`
actionpack (7.2.2.1) lib/action_dispatch/middleware/callbacks.rb:30:in `call`
actionpack (7.2.2.1) lib/action_dispatch/middleware/debug_exceptions.rb:31:in `call`
actionpack (7.2.2.1) lib/action_dispatch/middleware/show_exceptions.rb:32:in `call`
logster (2.20.1) lib/logster/middleware/reporter.rb:40:in `call`
railties (7.2.2.1) lib/rails/rack/logger.rb:41:in `call_app`
railties (7.2.2.1) lib/rails/rack/logger.rb:29:in `call`
config/initializers/100-quiet_logger.rb:20:in `call`
config/initializers/100-silence_logger.rb:29:in `call`
actionpack (7.2.2.1) lib/action_dispatch/middleware/request_id.rb:33:in `call`
lib/middleware/enforce_hostname.rb:24:in `call`
rack (2.2.11) lib/rack/method_override.rb:24:in `call`
actionpack (7.2.2.1) lib/action_dispatch/middleware/executor.rb:16:in `call`
rack (2.2.11) lib/rack/sendfile.rb:110:in `call`
rack-mini-profiler (3.3.1) lib/mini_profiler.rb:334:in `call`
lib/middleware/processing_request.rb:12:in `call`
message_bus (4.3.9) lib/message_bus/rack/middleware.rb:60:in `call`
lib/middleware/request_tracker.rb:385:in `call`
actionpack (7.2.2.1) lib/action_dispatch/middleware/remote_ip.rb:96:in `call`
railties (7.2.2.1) lib/rails/engine.rb:535:in `call`
railties (7.2.2.1) lib/rails/railtie.rb:226:in `public_send`
railties (7.2.2.1) lib/rails/railtie.rb:226:in `method_missing`
rack (2.2.11) lib/rack/urlmap.rb:74:in `block in call`
rack (2.2.11) lib/rack/urlmap.rb:58:in `each`
rack (2.2.11) lib/rack/urlmap.rb:58:in `call`
unicorn (6.1.0) lib/unicorn/http_server.rb:634:in `process_client`
unicorn (6.1.0) lib/unicorn/http_server.rb:739:in `worker_loop`
unicorn (6.1.0) lib/unicorn/http_server.rb:547:in `spawn_missing_workers`
unicorn (6.1.0) lib/unicorn/http_server.rb:143:in `start`
unicorn (6.1.0) bin/unicorn:128:in `<top (required)>`
vendor/bundle/ruby/3.3.0/bin/unicorn:25:in `load`
vendor/bundle/ruby/3.3.0/bin/unicorn:25:in `<main>`

تثبيت Discourse هذا جديد نسبيًا، ولم قمت بإعداد أي حظر لعناوين IP. أنا أيضًا المستخدم الوحيد عليه. أقوم بتشغيل ollama في حاوية Docker، لكن ذلك لا ينبغي أن يكون مشكلة؛ أستخدم open-webui (أيضًا من حاوية Docker أخرى) مع هذه النسخة ويعمل بشكل جيد. أنا أستخدم عنوان IP المحلي بدلاً من “localhost”، لكن “localhost” لا يتصرف بشكل مختلف.

أقدر الوقت الذي تخصصه للمساعدة في معرفة ما يحدث هنا.

ETA: قمت بتثبيت jq في حاوية Discourse Docker، وقمت بتشغيل أمر curl (مع استبدال عنوان IP المحلي بـ localhost)، وقد تم تشغيله من هناك - لذا هناك اتصال بين الحاويات، والرابط صحيح).

هذه هي حماية SSRF الافتراضية الخاصة بنا التي يتم تفعيلها. يمكنك إضافة استثناءات إليها عبر متغير البيئة DISCOURSE_ALLOWED_INTERNAL_HOSTS.

يتم تشغيلها عندما يحاول Discourse الوصول إلى عنوان IP يعتبر داخليًا.

رائع، لقد تم الأمر - لقد قمت بذلك عبر الإعدادات بدلاً من متغير البيئة، لكن الاختبار نجح الآن. شكراً مرة أخرى على كل المساعدة!