Создайте своего собственного чат-бота с искусственным интеллектом, созданного специально для успеха вашей компании!

Хотите создать персонализированного чат-бота для нужд вашей компании? В этом исследовании мы погрузимся в мир чат-бота Llama — нового претендента в динамической области диалогового искусственного интеллекта. Наше путешествие будет охватывать различные аспекты, включая настройку пакетов, настройку моделей и многое другое!

Подготовка почвы: основные пакеты

Прежде чем углубляться в код, важно убедиться, что у нас установлены все необходимые пакеты. Это обеспечивает плавное движение по мере продвижения вперед:

!pip install pyngrok transformers flask-ngrok huggingface_hub requests beautifulsoup4 nltk scikit-learn sentence-transformers faiss-cpu numpy==1.23.3 flasgger
!pip install tensorflow --no-deps
!pip install numba --no-deps
!CMAKE_ARGS="-DLLAMA_CUBLAS=on" FORCE_CMAKE=1 pip install llama-cpp-python --force-reinstall --upgrade --no-cache-dir --verbose

Модельный путь: указание на ламу

После того, как наша среда подготовлена, нам нужно указать, какую модель мы собираемся использовать:

model_name_or_path = "TheBloke/Llama-2-13B-chat-GGML"
model_basename = "llama-2-13b-chat.ggmlv3.q5_1.bin"

Библиотеки в изобилии: импорт наших инструментов

С предварительными этапами покончено, пришло время приступить к делу — нашим библиотекам:

from huggingface_hub import hf_hub_download
from llama_cpp import Llama
from transformers import AutoTokenizer, AutoModelForCausalLM
from flask import Flask, request, jsonify, render_template_string, send_from_directory
from flasgger import Swagger, swag_from
from pyngrok import ngrok
from flask_ngrok import run_with_ngrok
import torch, requests, re
import numpy as np
from bs4 import BeautifulSoup
import nltk, faiss
from nltk.tokenize import sent_tokenize
from google.colab import drive
from sentence_transformers import SentenceTransformer
nltk.download('punkt')
drive.mount('/content/drive')

Получение модели: захват ламы

Когда наша среда настроена, пришло время получить модель ламы:

model_path = hf_hub_download(repo_id=model_name_or_path, filename=model_basename)

Инициализация графического процессора: Лама обретает свою мощь

Истинный потенциал чат-бота Llama раскрывается, когда мы используем ускорение графического процессора:

lcpp_llm = None
lcpp_llm = Llama(
    model_path=model_path,
    n_threads=2,
    n_batch=512,
    n_gpu_layers=32
)

Создание пользовательского интерфейса: чат для взаимодействия

Одной из отличительных черт привлекательного чат-бота является его пользовательский интерфейс. Вот как устроен интерфейс нашего чат-бота Llama:

html_code = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chat Box</title>
<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: "Nunito", sans-serif;
}

html, body {
  background: linear-gradient(120deg, #17bebb, #f0a6ca);
  overflow: hidden;
}

.container {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  height: 100vh;
  width: 100vw;
}
.container h1 {
  margin: 0.5em auto;
  color: #FFF;
  text-align: center;
}

.chatbox {
  background: rgba(255, 255, 255, 0.05);
  width: 600px;
  height: 75%;
  border-radius: 0.2em;
  position: relative;
  box-shadow: 1px 1px 12px rgba(0, 0, 0, 0.1);
}
.chatbox__messages__user-message.user .chatbox__messages__user-message--ind-message {
  float: right;
}
.chatbox__messages:nth-of-type(odd) .chatbox__messages__user-message--ind-message:after {
  content: "";
  position: absolute;
  margin: -1.5em -17.06em;
  width: 0;
  height: 0;
  border-top: 10px solid transparent;
  border-bottom: 10px solid transparent;
  border-right: 10px solid rgba(255, 255, 255, 0.2);
}
.chatbox__messages__user-message.ai .chatbox__messages__user-message--ind-message{
  float: left;
}
.chatbox__messages:nth-of-type(even) .chatbox__messages__user-message--ind-message:after {
  content: "";
  position: absolute;
  margin: -1.5em 1.87em;
  width: 0;
  height: 0;
  border-top: 10px solid transparent;
  border-bottom: 10px solid transparent;
  border-left: 10px solid rgba(255, 255, 255, 0.2);
}
.chatbox__messages__user-message {
  width: 450px;
}
.chatbox__messages__user-message--ind-message {
  background: rgba(255, 255, 255, 0.2);
  padding: 1em 0;
  height: auto;
  width: 65%;
  border-radius: 5px;
  margin: 2em 1em;
  overflow: auto;
}
.chatbox__messages__user-message--ind-message > p.name {
  color: #FFF;
  font-size: 1em;
}
.chatbox__messages__user-message--ind-message > p.message {
  color: #FFF;
  font-size: 0.7em;
  margin: 0 2.8em;
}
.chatbox__user-list {
  background: rgba(255, 255, 255, 0.1);
  width: 25%;
  height: 100%;
  float: right;
  border-top-right-radius: 0.2em;
  border-bottom-right-radius: 0.2em;
}
.chatbox__user-list h1 {
  background: rgba(255, 255, 255, 0.05);
  color: rgba(255, 255, 255, 0.9);
  font-size: 0.9em;
  padding: 1em;
  margin: 0;
  font-weight: 300;
  text-align: center;
}
.chatbox__user, .chatbox__user--away, .chatbox__user--busy, .chatbox__user--active {
  width: 0.5em;
  height: 0.5em;
  border-radius: 100%;
  margin: 1em 0.7em;
}
.chatbox__user--active {
  background: rgba(23, 190, 187, 0.8);
}
.chatbox__user--busy {
  background: rgba(252, 100, 113, 0.8);
}
.chatbox__user--away {
  background: rgba(255, 253, 130, 0.8);
}
.chatbox p {
  float: left;
  text-align: left;
  margin: -0.25em 2em;
  font-size: 0.7em;
  font-weight: 300;
  color: #FFF;
  width: 200px;
}
.chatbox form {
  background: #222;
}
.chatbox form input {
  background: rgba(255, 255, 255, 0.03);
  position: absolute;
  bottom: 0;
  left: 0;
  border: none;
  width: 75%;
  padding: 1.2em;
  outline: none;
  color: rgba(255, 255, 255, 0.9);
  font-weight: 300;
}

::-webkit-input-placeholder {
  color: rgba(255, 255, 255, 0.9);
}

:-moz-placeholder {
  color: rgba(255, 255, 255, 0.9);
}

::-moz-placeholder {
  color: rgba(255, 255, 255, 0.9);
}

:-ms-input-placeholder {
  color: rgba(255, 255, 255, 0.9);
}
.chatbox__send-form {
  display: flex;
  align-items: center;
  margin-top: 1em;
  padding: 0 1em;
}

.chatbox__send-form input {
  flex: 1;
}

.chatbox__send-form button {
  background-color: rgba(255, 255, 255, 0.2);
  border: none;
  padding: 0.5em 1em;
  color: #fff;
  font-weight: bold;
  border-radius: 5px;
  cursor: pointer;
  transition: background-color 0.3s;
  position: absolute;
bottom: 0;
right: 0;
height: 45px;
}

.chatbox__send-form button:hover {
  background-color: rgba(255, 255, 255, 0.4);
}
</style>
<div class='container' ng-cloak ng-app="chatApp">
    <img src="/content/logo.png" alt="Your Company Logo" style="max-width: 100%; height: auto;">
  <div class='chatbox' ng-controller="MessageCtrl as chatMessage">
    <form ng-submit="sendMessage()" class="chatbox__send-form">
      <input type="text" ng-model="userMessage" placeholder="Enter your message" ng-keyup="$event.keyCode === 13 && sendMessage()">
      <button type="submit">Send</button>
    </form>
    <div class="chatbox__messages__user-message-container" id="message-container">
      <div class="chatbox__messages__user-message [[message.Class]]" ng-repeat="message in messages">
        <div class="chatbox__messages__user-message--ind-message">
          <p class="name">[[message.Name]]</p>
          <p class="message">[[message.Message]]</p>
        </div>
      </div>
    </div>
  </div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.min.js"></script>
<script>
(function() {
  var app = angular.module('chatApp', []).config(function($interpolateProvider) {
    $interpolateProvider.startSymbol('[[');
    $interpolateProvider.endSymbol(']]');
  });

  app.controller('MessageCtrl', function($scope, $http) {
    $scope.messages = [];

    $scope.sendMessage = function() {
      var userMessage = $scope.userMessage;
      if (userMessage) {
        $scope.messages.push({ Class: 'user', Name: 'You', Message: userMessage });
        $http.post('/generate', { text: userMessage })
          .then(function(response) {
            var assistantMessage = response.data.generated_text;
            $scope.messages.push({ Class: 'ai', Name: 'AI Assistant', Message: assistantMessage });
          })
          .catch(function(error) {
            console.error(error);
          });
        $scope.userMessage = ''; // Clear the input
      }
    };
  });
})();
</script>
</body>
</html>
"""

with open("chat.html", "w") as f:
    f.write(html_code)

Особенности:

  • Интерфейс структурирован с использованием HTML и CSS, с градиентным фоном и центрированным окном чата.
  • Сообщения от пользователя и ИИ-помощника отображаются в разных стилях, что позволяет легко их различать.
  • AngularJS используется для обработки динамических элементов чата, таких как отображение сообщений и управление взаимодействиями.
  • Когда пользователь отправляет сообщение, запрос POST получает ответ ИИ, который затем отображается в окне чата.

Извлечение данных: получение информации с веб-страниц

Универсальный чат-бот может извлекать и предоставлять информацию с веб-страниц. Вот функция, которая это делает:

def get_data_from_url(urls):
    data = []
    for url in urls:
        response = requests.get(url)
        if response.status_code == 200:
            soup = BeautifulSoup(response.content, 'html.parser')
            tags = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'span']
            data.append(' '.join([element.text for tag in tags for element in soup.find_all(tag)]))
    return data if data else None

# Example usage
urls = ["https://yourcompany.ai/about", "https://yourcompany.ai/integrations/", ...]  # Sample URLs
data = get_data_from_url(urls)

Подробнее:

  • Функция get_data_from_url принимает список URL-адресов и извлекает текстовое содержимое с этих страниц.
  • Он использует библиотеку requests для выполнения HTTP-запросов и BeautifulSoup для анализа HTML.
  • Функция ищет определенные теги, такие как абзацы (p) и заголовки (h1, h2 и т. д.), чтобы извлечь текст.
  • В качестве примера предоставляется список URL-адресов и вызывается функция для получения их содержимого.

Обработка текста: сегментация большого текста

Работа с большими фрагментами текста может оказаться сложной задачей, особенно при попытке сгенерировать ответы или найти совпадения. Таким образом, очень важно разбить текст на удобные куски:

def chunk_text(text, max_length):
    text = text.replace("\t", "\n")
    text = re.sub('\n+', '\n', text)
    chunks = []
    sentences = sent_tokenize(text)
    current_chunk = []
    current_length = 0
    for sentence in sentences:
        if current_length + len(sentence) <= max_length:
            current_chunk.append(sentence)
            current_length += len(sentence)
        else:
            chunks.append(' '.join(current_chunk))
            current_chunk = [sentence]
            current_length = len(sentence)
    if current_chunk:
        chunks.append(' '.join(current_chunk))
    return chunks

Основные выводы:

  • Функция chunk_text принимает строку (text) и максимальную длину (max_length).
  • Используя регулярные выражения и токенизацию предложений, функция разбивает текст на более мелкие фрагменты, не разделяя предложения.
  • Фрагменты гарантируют, что текст останется связным и значимым для дальнейшей обработки.

Семантический поиск: поиск релевантной информации

После извлечения и сегментации данных следующим шагом будет поиск соответствующей информации на основе запросов пользователей:

# Function to search for a query
def search(query, index, all_chunks, urls, model):
    query_embedding = model.encode([query])
    D, I = index.search(query_embedding, 1)  # Search for the top 1 most similar embedding

    url_idx, chunk_idx, chunk = all_chunks[I[0][0]]
    url = urls[url_idx]

    return url, D[0][0], chunk

model = SentenceTransformer('sentence-transformers/gtr-t5-base')  # Load the desired model

# Populate all_chunks with the tuple structure
all_chunks = []
for url_idx, text in enumerate(data):
    for chunk_idx, chunk in enumerate(chunk_text(text, 1024)):
        all_chunks.append((url_idx, chunk_idx, chunk))

# Encode all chunks using the selected model
all_embeddings = model.encode([chunk[2] for chunk in all_chunks])

# Create a Faiss index and add embeddings to it
index = faiss.IndexIDMap(faiss.IndexFlatIP(model.get_sentence_embedding_dimension()))
index.add_with_ids(all_embeddings, np.arange(len(all_embeddings)))

Подробнее:

  • Функция search принимает запрос и возвращает наиболее релевантный фрагмент текста из предоставленных данных.
  • Модель SentenceTransformer используется для преобразования запроса и фрагментов данных во внедрения.
  • FAISS, библиотека для поиска по сходству, затем используется для поиска фрагмента с наиболее близким к запросу встраиванием.

Запуск чат-бота: взаимодействие через Интернет

Сердцем чат-бота является приложение Flask, которое предоставляет пользователям веб-интерфейс:

app = Flask(__name__)
ngrok.set_auth_token("YOUR TOKEN API")
run_with_ngrok(app)  # This will make the server accessible publicly

@app.route('/generate', methods=['POST'])
def generate():
    text = request.json['text']
    # print(text)
    url, similarity, chunk = search(text, index, all_chunks, urls, model)
    print(chunk)
    prompt_template=f'''You are an AI chatbot. Just answer the question, don't asume anything else:

    USER: From the following information Asnwer the folling: {chunk}
    Question: {text}

    ASSISTANT:
'''
    output = lcpp_llm(prompt=prompt_template, max_tokens=512, temperature=0.2, top_p=0.95,
                  repeat_penalty=1.2, top_k=150,
                  echo=True)

    text = output['choices'][0]['text']
    assistant_reply = text.split("ASSISTANT:")[1].strip()
    print('done')
    return jsonify({"generated_text": assistant_reply})


@app.route('/')
def home():
    try:
        return render_template_string(open("chat.html").read())
    except Exception as e:
        return f"An error occurred: {e}"

@app.route('/content/<path:filename>')
def serve_static(filename):
    try:
        return send_from_directory('/content/', filename)
    except Exception as e:
        return f"Failed to retrieve {filename}. Error: {e}"

if __name__ == '__main__':
    app.run()

Особенности:

  • Приложение Flask предоставляет конечную точку /generate, которая принимает вводимые пользователем данные и возвращает ответ чат-бота.
  • Получив запрос пользователя, приложение ищет соответствующую информацию, создает подсказку, а затем генерирует ответ, используя модель Llama.

Заключение

На протяжении всего этого исследования мы стали свидетелями тонкостей настройки чат-бота Llama, от первоначальной установки пакетов до грандиозного финала запуска интерактивного веб-интерфейса. Этот чат-бот использует возможности ускорения графического процессора, семантический поиск и широкий спектр инструментов и библиотек, чтобы предложить пользователям увлекательный и познавательный опыт.

Чат-бот Llama демонстрирует замечательные возможности в области искусственного интеллекта и обработки естественного языка. Это является свидетельством достижений в технологии чат-ботов и дает возможность заглянуть в будущее диалогового искусственного интеллекта.

Для разработчиков и энтузиастов эта реализация служит основой. Он предлагает план, который можно в дальнейшем совершенствовать, настраивать и расширять, что приведет к созданию в будущем еще более сложных и удобных для пользователя систем чат-ботов.

Спасибо, что присоединились ко мне в этом познавательном путешествии. До следующих встреч, продолжайте исследовать и внедрять инновации!