見出し画像

本格的なエロチャットAIを作りたい その8(LLMも実装して、LightChatAssistantのエロチャットを実現する)

まずは第一弾としてのパーツが揃ったので、合体させて実装する。

実際のコード

Google colab:LLM_API.ippy

# LLM setting

# 環境変数の設定
!set LLAMA_CUBLAS=1
!set CMAKE_ARGS=-DLLAMA_CUBLAS=on
!set FORCE_CMAKE=1

# llama-cpp-pythonとPyTorchのインストール
!python -m pip install llama-cpp-python==0.2.77 --prefer-binary --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cu122
!pip install torch==2.3.0 torchvision==0.18.0 torchaudio==2.3.0 --index-url https://download.pytorch.org/whl/cu121

!wget https://huggingface.co/Sdff-Ltba/LightChatAssistant-2x7B-GGUF/resolve/main/LightChatAssistant-2x7B_iq3xxs_imatrix.gguf

from llama_cpp import Llama
llm = Llama(
  model_path="LightChatAssistant-2x7B_iq3xxs_imatrix.gguf",  # path to GGUF file
  n_ctx = 32768,
  n_gpu_layers=-1, # The number of layers to offload to GPU, if you have GPU acceleration available. Set to 0 if no GPU acceleration is available on your system.
#  n_gpu_layers=0, # The number of layers to offload to GPU, if you have GPU acceleration available. Set to 0 if no GPU acceleration is available on your system.
)

# Communication
import requests
import json
import time

response_data = '*****EMPTY*****'
poling_data = '*****REQUEST*****'

url = "http://ローカルPCのIPアドレス:8080/"             # IPアドレスは隠させてもらいました
headers = {'Content-type': 'application/json'}

while True:

  if response_data == '*****EMPTY*****':
    send_data = poling_data
  else:
    output = llm(
        prompt=response_data,
        max_tokens=256,
        #stop ="\n",
        echo=False,
    )

    assistant_message = output['choices'][0]['text']
    print('AI_OUTPUT:\n' + assistant_message)

    send_data = assistant_message



  data = {"message": "Send data", "data": send_data}
  response = requests.post(url+'/api', data=json.dumps(data), headers=headers)
  response_data = response.json().get("data")
  print(response_data)

  if '*****SHUTDOWN*****' in response_data:
    break
  elif '*****EMPTY*****' in response_data:
    time.sleep(10)
  

ローカルPC

# Google Colab start module

import pyautogui
import time
import os

# Google Colabを起動させる関数を定義
# 開いている「LLM_API.ipynb - Colab」タブをフォーカスし、Ctrl+F9(すべてのセルを実行)をスローする
def focus_browser_and_send_startup_shortcut():
    # ブラウザのウィンドウタイトルの一部(例:'Chrome'や'Firefox')
    browser_title = 'LLM_API.ipynb - Colab'  # 使用しているブラウザに応じて変更してください
    try:
        # ブラウザウィンドウを探してアクティブにする
        window = pyautogui.getWindowsWithTitle(browser_title)[0]
        window.activate()
        # ウィンドウがアクティブになるまで少し待つ
        time.sleep(1)
        # Ctrl+F9のショートカットを送信
        pyautogui.hotkey('ctrl', 'f9')
        print(f"ショートカット Ctrl+F9 を {browser_title} に送信しました。")
    except IndexError:
        print(f"エラー: '{browser_title}' というタイトルのウィンドウが見つかりません。")
    except Exception as e:
        print(f"エラーが発生しました: {e}")


# Google Colabを終了させる関数を定義
# 開いている「LLM_API.ipynb - Colab」タブをフォーカスし、Ctrl+F1(「ランタイムを接続解除して削除」に割り当てたショートカット)をスローし、
# Pop upが出るのでEnterキーを送って終了させる
def focus_browser_and_send_shutdown_shortcut():
    # ブラウザのウィンドウタイトルの一部(例:'Chrome'や'Firefox')
    browser_title = 'LLM_API.ipynb - Colab'  # 使用しているブラウザに応じて変更してください
    try:
        # ブラウザウィンドウを探してアクティブにする
        window = pyautogui.getWindowsWithTitle(browser_title)[0]
        window.activate()
        # ウィンドウがアクティブになるまで少し待つ
        time.sleep(1)
        # Ctrl+F1のショートカットを送信
        pyautogui.hotkey('ctrl', 'f1')
        print(f"ショートカット Ctrl+F1 を {browser_title} に送信しました。")
        time.sleep(5)
        # ENTERを送信
        pyautogui.press('enter')
        print(f"Enter を {browser_title} に送信しました。")
    except IndexError:
        print(f"エラー: '{browser_title}' というタイトルのウィンドウが見つかりません。")
    except Exception as e:
        print(f"エラーが発生しました: {e}")

# Coogle colabを起動する関数を実行
focus_browser_and_send_startup_shortcut()

# Flask API Receiver start

from flask import Flask, request, jsonify
import threading

system_prompt = '''
SYSTEM:
あなたの名前は里穂
養女である里穂は、恥ずかしがりで処女。
本当は性に強い興味があるが口には出さない。
義父である英樹の入浴姿を覗き見して、その陰部の大きさに驚き、それをおかずに毎晩自慰をしている。
英樹には父親以上の愛情を持っているが、自分からは決して口にしない。
迫られると最後まで口だけの抵抗をするが、本心では期待で陰部を濡らすほど興奮して感じていて、絶頂寸前で本心を告白する。
返事は必ず最初に「里穂: 」を付けてください。

'''

chat_log = '''里穂: おはようございます、お義父さん。今日は私の18歳の誕生なの。お祝いしてくれる?
英樹: あ、おはよう、里穂。もちろんお祝いするよ。今日は特別なプレゼントがあるんだ
里穂: え?ほんと?!お義父さん…すっごく嬉しい!
'''
user_input = ''
user_input_stop_flag = False
shutdown_flag = False

print(chat_log)

app = Flask(__name__)

@app.route("/api", methods=["POST"])
def process_request():
    data = request.json
    receive_data = data.get("data")

    chat_prompt = receive_data_analyze(receive_data)

    result = {"message": "Received data", "data": chat_prompt}
    return jsonify(result)

# Flaskのログ出力を無効にする
import logging
log = logging.getLogger('werkzeug')
log.setLevel(logging.ERROR)

@app.before_request
def check_shutdown():
    global shutdown_flag
    if shutdown_flag:
        print('server shutdown process')
        focus_browser_and_send_shutdown_shortcut()
        # サーバーを停止する処理
        time.sleep(10)
        os._exit(0)

    return


# 通信は以下のような方法で行われる
# Google Colabから10秒に一回「*****REQUEST*****」が送られる
# もしキーボード入力があった場合には、プロンプトデータ+キーボードデータをプロンプトとして送信。なければ「*****EMPTY*****」を返信してidle状態を続ける
# Google colabは「*****EMPTY*****」なら10秒に一回のポーリングを再開。それ以外なら受信データをプロンプトとしてAIが返答生成する
# Google colabからの返信が「*****REQUEST*****」以外ならAIからのチャット返信として処理
def receive_data_analyze(receive_data):

    global chat_log,user_input,user_input_stop_flag
    # prompt生成
    prompt = chat_log.replace('里穂: ','ASSISTANT: 里穂: ').replace('英樹: ','USER: 英樹: ') + 'ASSISTANT: '

    if '*****REQUEST*****' in receive_data:                 # Google colabが「*****REQUEST*****」を送信してきて、
        if not user_input_stop_flag:                                # chat_inputがNull(キー入力なし)なら、
            return_data = '*****EMPTY*****'                 # チャット入力「*****EMPTY*****」を返信
        else:
            return_data = system_prompt + prompt            # そうでない場合には、プロンプト入力を送信
    else:                                                   # Google colabが「*****REQUEST*****」以外を送信してきたときは、
        if '里穂:' in receive_data:                         # 里穂の対話の最初を抽出
            chat_log += receive_data.split('\n')[0] + '\n'  # 里穂の対話を追加して
            print()
            print(chat_log, end = '')
            print('英樹: ', end = '')
            user_input_stop_flag = False
            return_data = '*****EMPTY*****'                 # idle状態に戻る
        else:                                               # 里穂の名前が最初になければ、リトライ
            print('NO NAME: RETRY')
            return_data = system_prompt + prompt

    return return_data



def listen_for_input():
    global user_input,chat_log,shutdown_flag, user_input_stop_flag
    while True:
        if user_input_stop_flag:
            time.sleep(1)
        else:
            user_input = input()
            if user_input == '終了':                           # 終了と入力されたら、shutdown_flagを立てる
                shutdown_flag = True
                break
            else:
                chat_log += '英樹: ' + user_input + '\n'        # それ以外はchat_logに追記する
                user_input_stop_flag = True





if __name__ == '__main__':
    input_thread = threading.Thread(target=listen_for_input)
    input_thread.daemon = True                                  # メインスレッドが終了したらこのスレッドも終了する
    input_thread.start()
    app.run(host='0.0.0.0', port=8080)
 

これがリアルタイムの動画


所感

このソフトは途中段階であるので、UIの出来やローカルPCからのGoogle ColabのShutdownでエラーが出るなど、出来は悪い。
また私はGoogle colab Proを使っているが、適当に遊ぶ程度(例えば30分程度とか)ならば無料版でも楽しめることがポイントだ。

ここからさらにシステムとして改善していく所存だ。
例えば「記憶」を作る、喘ぎ声の個性を持たせる、ストーリー性を持たせる、などだ。
これらの実現のため、プロンプトを変化させながらLLMに話させ、本格的エロチャットAIを目指そうと思っている。

追記

数日後に動かすと、GPU設定しているのにも関わらずGPUが動作しない。
よくわからんが、llama-cpp-pythonのVersionを上げてみたら、復活。
なぜだ?

#!python -m pip install llama-cpp-python==0.2.77 --prefer-binary --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cu122
!python -m pip install llama-cpp-python==0.2.87 --prefer-binary --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cu122


この記事が気に入ったらサポートをしてみませんか?