Что? У вас еще нет своего ChatGPT? Создайте ChatGPT и используйте идеи домена DDD для взаимодействия с общедоступными учетными записями.
Что? У вас еще нет своего ChatGPT? Создайте ChatGPT и используйте идеи домена DDD для взаимодействия с общедоступными учетными записями.

Предисловие

"Что? У вас еще нет своего ChatGPT? 》Буквально вчера в ChatGPT возникла серьезная ошибка. В командном режиме можно пригласить неограниченное количество пользователей 3.5 для игры в функцию 4.0, поэтому я как можно скорее подключился к ней. Затем я разместил ее в своей группе с открытым исходным кодом и созвал группу. большие ребята бесплатно. В результате они спросили меня, как создать свой ChatGPT, и попросили опубликовать обучающую статью, и вот оно!!!

В ходе ChatGPT мы стали свидетелями удивительного развития искусственного интеллекта: от первоначальной модели диалога до сегодняшнего ChatGPT. Эта эволюция является не только технологическим прогрессом, но и блестящим проявлением человеческого мышления. Со временем ChatGPT постепенно стал неотъемлемой частью жизни людей, предоставляя неограниченные возможности для общения, обучения и инноваций.

ChatGPT может помочь нам с рядом задач, таких как рисование, ролевые игры, генератор кода, анализ кода и т. д. Хотя в официальную модель ChatGPT3.5 можно играть бесплатно, разработчикам, которые хотят иметь свою собственную, необходим API , тогда чиновнику API нужны деньги, поэтому эта статья здесь! Создайте свой собственный КЛЮЧ API для ChatGPT Baiguo 3.5, прежде всего, отдайте должное «Цинь Шихуану» Если вы меня не знаете, вы узнаете это позже. В этой статье мы создадим ChatGPT с нуля и подключим его к нашей личной общедоступной учетной записи для индивидуального ответа. Разве это не здорово? Сегодня утром я создал ее с нуля, и это заняло у меня больше двух часов. Можете себе представить, сколько времени заняла эта статья. Если она вам понравилась и она вам помогла, пожалуйста, поставьте ей палец вверх и добавьте ее в избранное~.

Первоначально для доступа планировалось использовать большую модель Hunyuan, но для API требуется корпоративное приложение. Хотя он у меня есть, а у вас его может не быть, я не буду выполнять эту операцию, но я продолжу расширять код Hunyuan для реализации стыковки. в конце. Приходите и учитесь. Давайте создадим собственное программное обеспечение для общения. В этой статье более 30 000 слов и полностью создадим свой собственный GPT.

В этой статье рассматривается создание ChatGPT, а затем создание сервисов на основе Proxy Api для подключения к общедоступным учетным записям, использование идей, основанных на домене DDD, для создания проектов SpringBoot и использование фабрик сеансов для создания запросов OpenAI на основе наиболее полезного okHttp. См. рисунок ниже. подробное введение

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

Подготовить

Сначала мы работаем

  1. Служитьустройствоодинбашня нуждатьсястроить docker、nginx、jdk
  2. один github из Аккаунт должен быть заполнен 180 Над небом
  3. Я много знаю о Java-разработке и хочу написать функцию текстового ответа для закрепления официальных аккаунтов.
  4. одинличный-подписной аккаунт (публичный аккаунт) Просто зарегистрируйтесь напрямую без из и все готово, так что easy to happy!
  5. Самый важный аккаунт изOpenAI,Зарегистрироваться напрямую без регистрации так же просто, как пить воду

Посетите Juhuasuan, чтобы приобрести серверы Tencent Cloud в конце года: https://curl.qcloud.com/Ukg0wxww

https://curl.qcloud.com/Ukg0wxww
https://curl.qcloud.com/Ukg0wxww

Конфигурация сервера

Это всего лишь сто долларов, так что если тебе нужно спешить с закрытыми глазами, ладно?

Выбор конфигурации centOs 7.6

Остальное — настроить среду для развертывания приложения. Я не буду вдаваться в подробности. Я предполагаю, что вы являетесь экспертом в основах Linux.

Создание и развертывание среды приложений

Все следующие методы установки взяты из JPOM.

Установите полную среду

Этот скрипт автоматически проверит, существуют ли jdk, mvn, node в текущей среде. Если они не существуют, установите их.

Язык кода:ruby
копировать
curl -fsSL https://jpom.top/docs/install.sh | bash -s Server jdk+mvn+node+default

Установить Докер

Можно использовать пошаговую установку, а можно комплексную установку (рекомендуется использовать пошаговую установку в один клик, я раньше этого не делал)

Язык кода:bash
копировать
# Загрузите и Установить Докер Местонуждатьсяизпакет программного обеспечения
sudo yum install -y yum-utils
# Добавить официальный адрес склада Docker
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# Установите адрес склада Alibaba Cloud зеркалоизюм
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# Обновить кэш yum
sudo yum makecache fast
# Установите новую версию Docker
sudo yum install -y docker-ce docker-ce-cli containerd.io

Установите докер одной командой

Язык кода:agda
копировать
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun

Установите JDK 1.8 онлайн

https://jpom.top/pages/feb7c1/#%E5%89%8D%E8%A8%80

Язык кода:java
копировать
по путь по умолчанию: /usr/java/xxxx

Версия: 1.8

Источник: https://mirrors.tuna.tsinghua.edu.cn/Adoptium/

curl -fsSL https://jpom.top/docs/install.sh | bash -s Server jdk+only-module+default

Установите Nginx онлайн

Язык кода:html
копировать
#gccУстановить, nginxИсходный кодкомпилироватьнуждаться
yum install -y gcc-c++

#PCRE pcre-devel Установить, nginx из http модульиспользовать pcre анализировать регулярные выражения
yum install -y pcre pcre-devel

#zlibУстановить, nginx Usezlib сжимает содержимое http-пакета из
yum install -y zlib zlib-devel

#OpenSSL Установка, мощная криптографическая библиотека Secure Sockets Layer, nginx. не только поддержка http протокол, также поддерживает https (т.е. http передается по протоколу существуетsl)
yum install -y openssl openssl-devel

Загрузите с помощью команды wget

Язык кода:bash
копировать
mkdir -p /usr/local/nginx && cd /usr/local/nginx
#Номер версии загрузки можно изменить в соответствии с последней стабильной версией текущего официального сайта.
wget -O nginx-1.20.2.tar.gz https://nginx.org/download/nginx-1.20.2.tar.gz

Скомпилировать nginx

Язык кода:bash
копировать
Команда #Root DirectoryUsls позволяет увидеть, как приезжает загрузить сжатый пакет изnginx, а затем разархивировать его.
tar -zxvf nginx-1.20.2.tar.gz

#Входим в каталог после распаковки
cd nginx-1.20.2

#использоватьпо умолчанию Конфигурация
./configure

# компилировать Установить
make
make install

# Запустить nginx
cd /usr/local/nginx
./sbin/nginx

тестовый доступ к да успешен, если появляется следующий контент, это полный успех.

Выше приведена базовая среда. Это займет около 10 минут, и ошибок не будет. Я делал это несколько раз! !

PandoraNext-ChatGPT, благодаря которому вам станет легче дышать

Краткое введение

Pandora Cloud + Pandora Server + Shared Chat + BackendAPI Proxy + Chat2API = PandoraNext

PandoraNext да zhile(Цинь Шихуан)Место Писатьдокументадрес:Документация Пандоры

  • более мощный,Но это также тот, от которого вам становится легче дышать благодаря ChatGPT. Поддержка тегов GPT,Последний пользовательский интерфейс.
  • Поддерживает несколько методов входа в систему: (эквивалент Pandora Cloud)
    • Аккаунт/пароль
    • Access Token
    • Session Token
    • Refresh Token
    • Share Token
  • Может иметь встроенные токены (можно использовать все вышеперечисленные токены),Настройки поддержкипароль。(довольно ВPandora Server)

Здесь нам нужно использовать только его вызов API-прокси, чтобы позвонить в Chat.openai.com. Прямой доступ к да внутри страны затруднен, поэтому я не буду объяснять это подробно здесь. Все это понимают~.

Итак, без лишних слов, приступим непосредственно к сборке PandoraNext. Откройте документ, и вы увидите три метода развертывания. Мы сразу выбираем Docker. Самый удобный из них создан.

Создать веб-версию PandoraNext

Если здесь есть сервер, вы можете использовать локальный, если сервера нет. Здесь я буду использовать MacOS в качестве примера для локальной сборки PandoraNext.

⚠️: Linux Можете ли вы понять, что мы с сервером работаем точно так же и продолжаем работать? Если он не локальный, пропустите здесь и прочтите ниже. ⚠️: Proxy модель Не поддерживается местныйвызов

Docker Desktop: https://docs.docker.com/desktop/install/mac-install/

⚠️: Пожалуйста, заранее установите компьютерную версию Docker Desktop. Я не буду брать ее с собой. Просто следуйте официальной документации, и все готово.

После завершения загрузки Да Так

Не забудьте настроить ускоренную загрузку внутри страны.

Язык кода:json
копировать
 "registry-mirrors": [
    "https://xv6jnj6e.mirror.aliyuncs.com"
]

Затем перезагрузка завершена, затем откройте терминал

Создайте новую структуру каталогов

Добавляем новую папку под названием pandoranext-deploy. Файлы в ней основаны на картинках.

mkdir -p data session

Просто добавьте файл config.json и файл tokens.json в данные.

touch config.json tokens.json

инструкции по настройке config.json

здесьдапо умолчаниюиз Конфигурационный официальный файл, уберите его, мы только меняем его

Всего четыре параметра: License_id, site_password, setup_password, proxy_api_prefix.

Язык кода:json
копировать
{
  "bind": "0.0.0.0:8181",
  "tls": {
    "enabled": false,
    "cert_file": "",
    "key_file": ""
  },
  "timeout": 600,
  "proxy_url": "",
  "license_id": «очень важно»,
  "public_share": false,
  "site_password": «Пароль доступа к вашей веб-странице»,
  "setup_password": «Управление конфигурацией паролей»,
  "server_tokens": true,"proxy_api_prefix": "prox api просить префикс",
  "isolated_conv_title": "*",
  "disable_signup": false,
  "auto_conv_arkose": false,
  "proxy_file_service": true,
  "custom_doh_host": "",
  "captcha": {
    "provider": "",
    "site_key": "",
    "site_secret": "",
    "site_login": false,
    "setup_login": false,
    "oai_username": false,
    "oai_password": false,
    "oai_signup": false
  },
  "whitelist": null
}

Сертификат License_id

Укажите свой идентификатор лицензии для уникальных сервисов Pandora

  • существоватьздесьполучать:https://dash.pandoranext.com(opens in a new tab)
  • Авторизоватьсятыиз GitHub Номер счета присваивается вам исходя из количества лет. token Квота
  • Будьте осторожны и не используйте невидимые символы, такие как пробелы или дополнительные пробелы.
  • Поле License_id не заполняется в файле ifconfig.json, и при запуске будет сообщено об ошибке «Требуется идентификатор лицензии».
  • Нет фиксированного IP из-за ситуации,После изменения IP-адреса он автоматически повторит попытку.

Скопируйте License_id в config.json

⚠️ Разрешите нам копировать все, что находится вокруг нас, и вводить это непосредственно на сервер.

Нажмите Enter, чтобы завершить загрузку, и появится файл License.jwt.

Итак, мы завершили базовую настройку. Далее приступим непосредственно к сборке сервиса Pandora.

setup_password

когдатынастраивать Понятноэтотпароль,ты Вот и все Проходить Проходить<тыразвертыватьизстоятьточка>/setupтакиз Сделайте некоторые настройки для адреса,Такие как: существование конфигурацииconfig.json, tokens.json, горячая перезагрузка и т. д.

⚠️: Обратите внимание на требования к надежности пароля: он должен быть не менее 8 символов и содержать как цифры, так и буквы!

site_password

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

⚠️: Обратите внимание на требования к надежности пароля: он должен быть не менее 8 символов и содержать как цифры, так и буквы!

proxy_api_prefix

Даодиночень важноизпараметр,правильныйизнастраивать Только тогда можнотыразвертыватьизPandoraNextвключатьproxyмодель(ты Может Проходить Проходить启动时из В журналеModeда Не включаетproxyсудить)

⚠️: Обратите внимание на требования к надежности пароля: он должен быть не менее 8 символов и содержать как цифры, так и буквы!

Окончательная общая модификация

у меня тут все едино

Язык кода:json
копировать
  "license_id": «Фронт из лицензии»,
  "site_password": "yangbuyiya123",
  "setup_password": "yangbuyiya123",
  "proxy_api_prefix": "yangbuyiya123",

Все приведенные выше конфигурации взяты из config.json. Не заблуждайтесь.

конфигурация tokens.json

Целью этого файла является доступ к веб-версии /shared.html (общий чатgpt), и его можно использовать напрямую, когда к нему обращаются другие.

Существующая прокси-модельсредида не работает, поэтому вот краткое введение. Если вам интересно, вы можете изучить ее.

существовать tokens.json записать в файл

Язык кода:json
копировать
{
  "token1": {
    // Он поддерживает несколько токенов, а также поддерживает авторизацию учетной записи и пароля (рекомендуется использовать учетную запись и пароль). например "token": "тыизсчет&пароль"
    "token": "access token / session token / refresh token / share token / username & password",
    "password": "12345"
  }
}

Если вам интересно, вы можете ознакомиться с подробной документацией: https://docs.pandoranext.com/zh-CN/configuration/tokens

На этом наша базовая настройка завершена. Далее мы создадим сервис Pandora.

Вытащить Пандору Следующее изображение

Язык кода:json
копировать
docker pull pengzhile/pandora-next

Запустив контейнер, мы попадаем на самый внешний слой. pandoranext-deploy В каталоге Linux Служить Посудада Так Ой

Язык кода:json
копировать
docker run -d --restart always --name PandoraNext --net=bridge \
            -p 8181:8181 \
            -v ./data:/data \
            -v ./sessions:/root/.cache/PandoraNext \
            pengzhile/pandora-next

Используйте большую модель Хуньюань, чтобы объяснить этот код.

Даодиниспользовать Docker Запустите программу под названием pengzhile/pandora-next из контейнера из команды. Команда даэтот объясняется ниже:

  1. docker run: Да Docker Базовая команда для создания и запуска одного нового контейнера.
  2. -d: Эта опция означает отсоединенную модель (отдельную mode) для запуска контейнера. Это означает, что контейнер будет работать в фоновом режиме, не занимая терминал на переднем плане.
  3. --restart always: этотопция указывает, что контейнер всегда долженсуществоватьсистема Автоматический запуск при запуске,Несмотря на тосуществоватьсистема Даже после перезапускадатаким образом。
  4. --name PandoraNext: эта опция для контейнера указывает одно имя, здесь да PandoraNext
  5. --net=bridge: Эта опция указывает, что контейнер будет подключаться к месту проживания. Docker по умолчаниюизмостовая сеть。
  6. -p 8181:8181: этот вариант будет содержать экспорт 8181 Картирование прибытия контейнера в порт 8181. Это означает отправку с хоста в контейнер из 8181 Портиз трафик будет перенаправлен приезжать внутри контейнера из 8181 порт。
  7. -v ./data:/data: этот вариант будет размещаться из ./data Каталог mount привезти контейнер из /data Оглавление Это означает, что внутри контейнера. /data Любые изменения в каталоге будут отражены на хосте. ./data в каталоге.
  8. -v ./sessions:/root/.cache/PandoraNext: этот вариант будет размещаться из ./sessions Каталог mount привезти контейнер из /root/.cache/PandoraNext Оглавление Это означает, что внутри контейнера. /root/.cache/PandoraNext Любые изменения в каталоге будут отражены на хосте. ./sessions в каталоге.
  9. pengzhile/pandora-next: Да хочу бежать из Docker зеркалоизимя。

Таким образом, эта команда создает и запускает файл с именем PandoraNext контейнер, используя pengzhile/pandora-next зеркало. Контейнер существует работает в фоновом режиме и запускается автоматически при запуске системы. экспорт контейнера 8181 Порт, сопоставленный с хостом 8181 и внутри контейнера /data и /root/.cache/PandoraNext Каталоги монтируются к хосту соответственно. ./data и ./sessions Оглавление.

Запустить веб-версию

Просто нажмите Enter и дождитесь успешного завершения операции.

Далее мы посещаем PandoraNext Служить IP:8181 ты да Linux Служитьправила использования посудыиспользоватьтыиз IP:порт

⚠️ Служить не забудьте обратиться к поставщику облака Конфигурация группы безопасности для утверждения. 8181 порт

за этим Давходитьтыссебяиз ChatGPT счетипароль Авторизоваться Понятно

После успешного входа вы попадете на главную страницу Моя мама полностью воспроизводит официальную. UI стиль,Записи разговоров тоже сохраняются. Они точно такие же, как и официальные.,Единственная разницаиз Да Нетнуждаться Маленький самолет прибыл, чтобы отвезти нас туда поиграть.~

Мы можем попытаться поговорить,Тогда диалог и нас поглотит из Квота,Да используется в начале github Вход на сайт

Проверьте сумму: https://dash.pandoranext.com

Запрос API режима прокси

⚠️ Proxy модель Не поддерживается местныйвызов Переднийиз Базовый Конфигурация Всесуществовать linux Служить сторону агрегата Вот и все

Если вы пойдете, чтобы понять ChatGPT из API просить будет знать, что он дануждаться денегиз купить То есть, если мы будем заниматься свободной проституцией, мы будем продолжать заниматься свободной проституцией до конца. PandoraNext Поддержка прямой полной агентской переадресации приезда должностного лица из API Никакой разницы выбирать вызов только да. Слой агента добавляется посередине, и он очень шелковистый.

существоватьпрежде чем мысуществовать config.json который установлен proxy_api_prefix для xxxx Мы используем нуждаться для доступа к официальному сайту через этот API

Предположим, вы используете proxy_api_prefixдля yangbuyiya123,тыизbindдля 127.0.0.1:8181

нотыизBaseURLдля:http://127.0.0.1:8181/yangbuyiya123,Даниже Местоиметьинтерфейсизпрефикс

Официальный специфический интерфейс: https://platform.openai.com/docs/api-reference

Мы будем использовать этот интерфейс для работы

Вызовы API для общения

использовать IP:8181/proxy_api_prefix Плюс официальный интерфейс /v1/chat/completions

Язык кода:json
копировать
локон http://СлужитьустройствоIP:8181/yangbuyiya123/v1/chat/completions \
  -H "Тип контента: приложение/json" \
  -H "Авторизация: носитель $OPENAI_API_KEY" \
  -д '{
     "модель": "gpt-3.5-турбо",
     "messages": [{"role": "user", "content": "Скажите, что это тест!"}],
     «температура»: 0,7
   }'

Вы можете посмотреть, как приезжатьнаша бедность идет к получению Authorization token этот token Да access_token

нода PandoraNext Обработано и необходимо сгенерировать PandoraNext из share token смотреть вниз

Собственный интерфейс PandoraNext

Получить токен доступа для входа

⚠️ интерфейспотреблятьдля1:100

Язык кода:shell
копировать
curl http://СлужитьустройствоIP:8181/yangbuyiya123/api/auth/login' \
    -H 'Тип контента: приложение/x-www-form-urlencoded' \
    -d 'имя_пользователя=user%40mail.com' \
    -d 'пароль=abc123'
  • интерфейсдляPOSTпросить。
  • параметр имя пользователя для учетной записи ChatGPT.
  • параметрpasswordдлятыизChatGPTпароль。
  • интерфейсбудет возвращен в то же времяaccess tokenиsession Токен, пожалуйста, сохрани его.

Продолжим получать access_token

Возвращаемые параметры: {"access_token":"Выходной токен","token_type":"Носитель"}

получать share token Fk вызов

Язык кода:shell
копировать
curl 'http://127.0.0.1:8181/yangbuyi123/api/token/register' \
    -H 'Content-Type: application/x-www-form-urlencoded' \
    -d 'unique_name=abcdefg' \
    -d 'access_token=eyxxxx' \
    -d 'site_limit=https%3A%2F%2Fchat.oaifree.com' \
    -d 'expires_in=0' \
    -d 'show_conversations=false' \
    -d 'show_userinfo=false'
  • интерфейсдляPOSTпросить。
  • Параметрunique_name для вашего уникального идентификатора токена Share, вы можете заполнить его по своему желанию.
  • параметрaccess_tokenдлятыизaccess token。
  • Параметр site_limit предназначен непосредственно для пустого пространства.
  • параметр expires_in для вашей доли Срок действия токена, единица измерения в секунду, если вы не хотите ограничивать срок действия, вы можете указать 0 (по умолчанию), тогда срок действия будет следовать за вами из доступа token。
  • параметрshow_conversationsдляда Не показывать список бесед,если вы не хотите, чтобы этот токен общего доступа показывался другим пользователям вне сеансов,Вы можете ввести false (по умолчанию)。
  • параметрshow_userinfoдляда Отображать ли информацию о пользователе,если вы не хотите отображать информацию о пользователе,Вы можете ввести false (по умолчанию)。если Нетпоказыватьиспользоватьсемьяинформация,Некоторые функции GPT могут работать некорректно.
  • интерфейсвозвращатьсяshare токен и связанная с ним информация.

получатьприезжать Понятно Fk token Тогда вы можете запросить его напрямую!!!

Продолжите исследовательскую беседу Модель ChatGPT3.5

вызовуспех! да, нет, да so easy to happy Нравится нарезать овощи?

Итак, дальше мы Да Введите код программированияизизучать Понятно,Подготовить, вы готовы? Поехали~

Драйвер домена DDD Какая именно у меня мать?

Честно говоря, я прочитал всего одну-две статьи.,Позвольте мне сначала объяснить, что это такое, исходя из моего собственного понимания.,Босс, не критикуйте меня, просто научите меня в разделе комментариев.,Если что-то не так с из, пожалуйста, дайте мне несколько советов~

Доменно-ориентированный дизайн (англ. Domain-Driven Design) Design, аббревиатура DDD) да Проектирование на основе модели и метод, который собирает знания предметной области через поля Модель, исполь зовать поля Конструкции моделей легче поддерживать, но да DDD Трудно реализовать да. Оно не имеет единой сущностииз приложения, поэтому. MVC Имеет ряд строгих правил

MVC Датретий этажиз Архитектурас уровня управления(controller) -> Служитьслой(service) -> Уровень взаимодействия данных (Дао) Набор сборочных линий, но мы существуем, сотрудничаем с использованием сложных проектов из сцен, которые мы находим здесь из ПО, ВО, сущности объектов существуют. Service Длительное взаимодействие между слоями приведет к расширению количества атрибутивных полей и возникнут некоторые проблемы в текущей ситуации. PO по сути бесполезно из поля атрибута, потому что для из Служить это тоже цитировалось PO Возможно, будут добавлены какие-то новые поля атрибутов (просто делаю это иногда для удобства)

DDD Архитектура Решите сначалаэтотвопрос Дарод Вссебяполе(domain)в пределах досягаемостииз Логически инкапсулируйте это Да DDD Спроектируйте этоизодинточка,подробныйиз Даон надеетсясуществоватьразделяй и властвуйслой Разумная резка поверхностейвопроскосмосдляменьший масштабизнесколько штуквопрос,Чем меньше проблема, тем легче ее понять и решить.,Обеспечьте приезжать высокую сплоченность и низкую связанность. Об этом также упоминает закон да Конвея.,Решение сложных сценариев и дизайн в основном делятся на: разделяй и властвуй, абстракция и знание.

DDD из Архитектура Модель

  1. Уровень пользовательского интерфейса (Пользователь Interface Слой): эквивалент да MVC из Controller。
  2. Прикладной уровень Layer):довольно Винтерфейсопределение。
  3. Слой домена (Домен Layer):системаизосновной,Отвечает за выражение бизнес-концепций.,Информация о бизнес-статусе и бизнес-правилах. То есть он содержит все сложные абстракции и правила бизнес-знаний в этой области (проблемной области).
  4. Уровень инфраструктуры Layer):дляполе Модель Обеспечить механизм сохранения,и другие уровни для обеспечения общих возможностей технической поддержки.,новости связи,общие инструменты,Конфигурацияждатьизвыполнить。

Не буду вдаваться в подробности, рекомендую Tencent. DDD Концепции и методы Реконструировано из Tencent Video Архитектура, смотрите DDDиз Концепции и методы Также есть статья брата Сяо Фу. Я также следил за братом Сяо Фу (руководителем JD.com), чтобы учиться из этой статьи. От MVC к DDD, как начать рефакторинг?

Приложение ChatGPT и стыковка с публичными аккаунтами для общения в чате

Строительство проекта

Строительство проекта Новое название проекта yby6PandoraChatGPTJDK 17 ХОРОШО JDK1.8 Это не имеет значения

общийиз Архитектура Датаким образом

Построить архитектуру DDD

Давайте создадим архитектуру DDD следующим образом.

DDD из Архитектура Модель

  1. Уровень пользовательского интерфейса (Пользователь Interface Слой): эквивалент да MVC из Controller
  2. Прикладной уровень Layer):довольно Винтерфейсопределение
  3. Слой домена (Домен Layer):системаизосновной Отвечает за выражение бизнес-концепций.бизнес-статусинформацияи бизнесправило Это включает в себя Понятно Долженполе(вопросдомен)Местоиметьсложныйизабстракция бизнес-знанийиправилоопределение
  4. Уровень инфраструктуры Layer):дляполе Модель Обеспечить механизм сохраненияи другие уровни для обеспечения общих возможностей технической поддержки.новости связи、общие инструменты、Конфигурацияждатьизвыполнить

Если вам лень, то скачайте принципизацию DDD Архитектура. Под каждым одним полем папки я аннотировал ее, чтобы было легче понять, почему используется каждый уровень да.

yby6PandoraChatGPT.zip

Вопросы и ответы о стыковке программ

Помните, что мы делали раньше Запрос API режима прокси, продолжим использовать apifox Приходите на вызов и получитеотправьте код просить

⚠️ если token Недействительно, пожалуйста, посмотрите каталог PandoraNext Собственная регенерация интерфейса Share token аббревиатура FK

Затем продолжайте сначала отправлять запрос вручную, а затем появится фактическая точка запроса.

Существует множество кодов запросов, которые мы можем просмотреть непосредственно в Java и выполнить их непосредственно для нас.

будет кодироватькопироватьприезжать Юнит-тестсреди Вот и все

Потом я тайно добавил лог просить, а потом импортировал okhttp изполагаться

Язык кода:xml
копировать
<!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
<dependency>
  <groupId>com.squareup.okhttp3</groupId>
  <artifactId>okhttp</artifactId>
  <version>4.12.0</version>
</dependency>
<dependency>
  <groupId>com.squareup.okhttp3</groupId>
  <artifactId>logging-interceptor</artifactId>
  <version>4.12.0</version>
</dependency>
Язык кода:java
копировать
/**
 * Юнит-тест
 */
@SpringBootTest
class Yby6PandoraChatGptApplicationTests {

    @Test
    void testApi() throws IOException {
        // бревно Конфигурация        HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
        httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);

        // Создайте, пожалуйста, окhttp клиент
        OkHttpClient client = new OkHttpClient().newBuilder()
        .addInterceptor(httpLoggingInterceptor)
        .build();
        MediaType mediaType = MediaType.parse("application/json");
        String req = "{\n" +
        "    \"model\": \"gpt-3.5-turbo\",\n" +
        "    \"messages\": [\n" +
        "        {\n" +
        "            \"role\": \"user\",\n" +
        "            \"content\": «Привет, я да Ян Буи\"\n" +
        "        }\n" +
        "    ],\n" +
        "    \"temperature\": 0.7,\n" +
        "    \"stream\": false\n" +
        "}";
        RequestBody body = RequestBody.create(req, mediaType);
        Request request = new Request.Builder()
        .url("http://IP:8181/prefix/v1/chat/completions")
        .method("POST", body)
        .addHeader("Authorization", "Bearer FK token")
        .addHeader("Content-Type", "application/json")
        .addHeader("Accept", "*/*")
        .addHeader("Host", "IP:8181")
        .addHeader("Connection", "keep-alive")
        .build();
        Response response = client.newCall(request).execute();
        System.out.println(response.body().string());
    }

}

тест OkHttp вызов

Вы можете видеть, что добраться до точки нам тоже удалось без проблем, в конце концов, да рождается из ха-ха-ха.

Взгляните на возвращаемую структуру данных,сейчассуществоватьмы получаемприезжатьиздаодиннанизай наснуждатьсяруководитьсоздавать Перепискаответ получен,Удобные эксплуатационные свойства,здесьпрямой IDEA из плагина GsonFormatPlus использовать JSON Просто преобразуйте его в класс сущности, и все готово.

Язык кода:json
копировать
{
    "id": "chatcmpl-OtyWJeU0gQBFhfyA5XHHv37wRLyNx",
    "object": "chat.completion",
    "created": 1705425154,
    "model": "gpt-3.5-turbo",
    "usage": {
        "prompt_tokens": 0,
        "completion_tokens": 0,
        "total_tokens": 0
    },
    "choices": [
        {
            "message": {
                "role": "assistant",
                "content": «Привет, Ян Буи! Чем я могу тебе помочь?»
            },
            "finish_reason": "stop",
            "index": 0
        }
    ]
}
Язык кода:java
копировать
package com.yby6.yby6pandorachatgpt;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * ../
 *
 * @author Yang Shuai
 * Create By 2024/01/17
 */
@Data
public class ChatGPTResponse {

    /**
     * Удостоверение личности
     */
    @JsonProperty("id")
    private String id;
    /**
     * объект
     */
    @JsonProperty("object")
    private String object;
    /**
     * создавать
     */
    @JsonProperty("created")
    private Integer created;
    /**
     * Модель
     */
    @JsonProperty("model")
    private String model;
    /**
     * использование токена
     */
    @JsonProperty("usage")
    private UsageDTO usage;

    /**
     * Ответ на сообщение
     */
    @JsonProperty("choices")
    private List<ChoicesDTO> choices;


    @NoArgsConstructor
    @Data
    public static class UsageDTO {
        @JsonProperty("prompt_tokens")
        private Integer promptTokens;
        @JsonProperty("completion_tokens")
        private Integer completionTokens;
        @JsonProperty("total_tokens")
        private Integer totalTokens;
    }

    @NoArgsConstructor
    @Data
    public static class ChoicesDTO {
        @JsonProperty("message")
        private MessageDTO message;
        @JsonProperty("finish_reason")
        private String finishReason;
        @JsonProperty("index")
        private Integer index;

        @NoArgsConstructor
        @Data
        public static class MessageDTO {
            @JsonProperty("role")
            private String role;
            @JsonProperty("content")
            private String content;
        }
    }
}

Нажмите ok Вы можете увидеть это позже, чтобы мы могли следить за ним. JSON Класс сущности генерируется так, чтобы мы могли использовать JSON Чтобы преобразовать сущности в отображение, нам нужно JSON Инструменты для работы, которые мы представляем Java Мощная библиотека инструментов Hutool

Язык кода:java
копировать
<!--    Мощная библиотека инструментов Java. -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.25</version>
</dependency>

Таким образом, мы можем легко управлять данными атрибутов. Вы можете просмотреть ответ и сохранить данные. MessageDTO среди прочего, когда мы приезжаем, мы используем это только для возврата в общедоступный аккаунт Вот и вседругойиз Я не имею ничего общего с дождем~

На данный момент мы завершили самые основные DDD Архитектурное строительство и завершение OkHttp Юнит-тестстыковкауспех,Следующий Да Текстовый ответ на официальный аккаунт WeChat,Иди, иди, иди, позволь мне вести тебя шаг за шагом!

Создайте общедоступную учетную запись (пропустите, если необходимо)

Нажмите на меня, чтобы перейти на платформу общедоступных учетных записей и зарегистрироваться.

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

Пассивно отвечать на сообщения пользователей

Давайте сначала посмотрим на текстовый ответ из документа нуждаться ЧТО Конфигурация параметра

документ: Пассивно отвечать на сообщения пользователей

Когда пользователь отправляет сообщение в официальную учетную запись (или когда определенные действия пользователя вызывают отправку события),произведу один ПОСТпросить,Разработчики могут возвращать определенные XML-структуры в существующем пакете ответов (Get).,ответить на сообщение (теперь поддерживается текст ответа, изображение, графика, голос, видео, музыка). строго говоря,Отправка пассивного ответного сообщения на самом деле не является разновидностью пассивного ответного сообщения.,И да один раз ответил на сообщение, отправленное устройством WeChat Служить.

После того, как WeChat Служить существовать отправляет пользователю сообщение в общедоступную учетную запись разработчика Служить существовать (Центр разработчиков в Конфигурации).,Если WeChat Служить существовать не ответит в течение пяти секунд, соединение будет разорвано.,и перезапустить просить,Всего три попытки,еслисуществовать Отладка,Было обнаружено, что пользователь не смог получить ответ на сообщение «Приехать».,Вы можете проверить, истекло ли время обработки сообщения. О повторной попытке сообщения Смещаемый вес,Рекомендуемые новости о msgiduse msgid Смещаемый вес. Рекомендация по сообщению типа событияиспользоватьFromUserName + CreateTime Смещаемый вес

Согласно официальной инструкции, я составил следующую блок-схему.

основной Да Пользователь отправляет сообщениеприезжать Официальный аккаунт,Тогда WeChat последует за нами Конфигурацияиз Служитьустройствоадрес(Подробности позже)посетите насизодин POST интерфейс(сопределение)существоватьвперенимать xml параметр Просто занимайтесь своим бизнесом

Прежде чем существовать писать код, давайте сделаем все это за один раз.,В любом случае Да Этот процесс Нет Станет мертвымиз Сразу Такдействовать,Далее, позвольте мне сначала представить, что такое нуждаться в параметре.

  1. нуждатьсяполучать Идентификатор разработчика WeChat (оригинал ID)
  1. Конфигурация сервердля WeChat Служить нам вызовиз post интерфейс(сопределение)

⚠️ Потому что для нас нужна локальная отладка,Местокнуждаться Проникнуть через интранетизменятьволосыпроситьприезжатьяихместныйиз Служить,Здесь я использую скорлупу арахиса для работы

Далее приступим к настройке публичного аккаунта.

Официальная конфигурация аккаунта

Войдите в официальную панель управления аккаунтом. Нажмите строку меню. Настройка и разработкаполучатьоригинальный ID ,отвечатьизкогдануждатьсяиспользоватьприезжать Идентификатор разработчика WeChatэтот Да

копироватьприезжатьпроектсредииз Конфигурационный файл application.yml

Язык кода:yaml
копировать
# Вичат Официальная конфигурация аккаунта Информация
шх:  config:
    originalid: Ваш исходный идентификатор
    token: token

Конфигурация сервера

Войдите в официальную панель управления аккаунтом. Нажмите строку меню. Настройка и разработкаНажмите Базовый Конфигурация,смотретьpriezzatIspravrativConfiguratsiya edamagn izorzet dotka goin

Просто настройте URL, токен и EncodingAESKey.

URL дасопределениеиз: напримеряиз ip да Публичная сеть IP/wx/gzh/яиз публичной учетной записииз appid

В сети мы заменим нуждаться на наш IP Служить Вот и все

Итак, теперь WeChat использует нашу местную нуждаться. Служить вызовприезжать Здесь я использую скорлупу арахиса, чтобы проникнуть в интранет.

Мое текущее доменное имя: https://34330745e8.picp.vip/ Агент приезжать по местным из 9632 порт позжеяихизпроект Служитьпорт Даэтот,Вы можете изменить его на что-нибудь другое

Затем я начну конфигурацию ниже. Обратите внимание, что все эти конфигурации являются конфиденциальной информацией, не пропустите ее.

Затем мы Нажмите представили,Сообщим об ошибке,Дадля Что?

Дапотому чтодля Вичатнуждатьсяпроверятьты Даватьприезжатьизпроситьадресданетвызовиз Проходить,и выполнить проверку внутри,документследующеезаинтересованныйиз Можетподробныйсмотретьсмотреть документ: Сообщение о подтверждениеиииз действительно от WeChat

Затем мы непосредственно напишем код официального аккаунта WeChat.

Заключительные услуги по ремонту

Перейти к проектированию существования DDD Архитектураиз interfaceв каталоге Новый WechatControllerконтрольустройство,просить приставку для себя Конфигурацияиз.

Сообщение о подтверждении

После того как разработчик предоставит информацию,WeChat Служить отправит GET проситьприезжать, введите адрес из Служить по URL-адресу.,GET просить CARRY параметр, показанный в следующей таблице:

параметр

описывать

signature

Шифрование WeChatзнак,Подпись объединяет параметр изтокена параметрипросить, параметр из отметки времени и параметр nonce, заполненные разработчиком.

timestamp

Временная метка

nonce

случайное число

echostr

случайная строка

знак

Ранназадяихнуждатьсяруководить Проверить знак предотвращает подделку просить,Приведен официальный метод работы.

Разработчики проверяют просить, проверяя подпись (способ проверки указан ниже). Если подтвердится, что этот GET просить пришел с устройства WeChat Служить,Пожалуйста, верните содержимое параметра echostr как есть.,Доступ вступает в силу,Станьте успешным разработчиком,В противном случае доступ невозможен. Процесс шифрования/верификации следующий:

1)Сортировка токена, метки времени и nonce в лексикографическом порядке.

2)Объедините три строки параметров в одну строку для шифрования sha1.

3) После того, как разработчик получит зашифрованную строку, ее можно сравнить с подписью, чтобы определить, что запрос исходит от WeChat.

яздесь Давать大家Писать好Понятнопрямойиспользоватьэтот Приходитьруководить Проверить знак,Не нужно писать вручную,Знай, что есть что-то Вот и все

существовать DDD Архитектурасредииз infrastructure -> util Новый wechat Создайте класс средства проверки подписи в папке

Язык кода:java
копировать
package com.yby6.yby6pandorachatgpt.infrastructure.util.wechat;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * Wechat знакutil
 *
 * @author Yang Shuai
 * Create By 2024/01/13
 */
public class WechatSignatureUtil {

    private static final Logger logger = LoggerFactory.getLogger(WechatSignatureUtil.class);


    /**
     * Проверить знак
     */
    public static boolean check(String token, String signature, String timestamp, String nonce) {
        String[] arr = new String[]{token, timestamp, nonce};
        // Сортировка токена, метки времени и nonce в лексикографическом порядке.
        sort(arr);
        StringBuilder content = new StringBuilder();
        for (String s : arr) {
            content.append(s);
        }
        MessageDigest md;
        String tmpStr = null;
        try {
            md = MessageDigest.getInstance("SHA-1");
            // Объедините три строки параметров в одну строку для шифрования sha1.
            byte[] digest = md.digest(content.toString().getBytes());
            tmpStr = byteToStr(digest);
        } catch (NoSuchAlgorithmException e) {
            logger.error(e.getMessage());
        }
        // После шифрования sha1 строку «из» можно сравнить с подписью, чтобы указать, что запрос поступил от WeChat.
        return tmpStr != null && tmpStr.equals(signature.toUpperCase());
    }

    /**
     * Преобразовать массив байтов в шестнадцатеричную строку
     */
    private static String byteToStr(byte[] byteArray) {
        StringBuilder strDigest = new StringBuilder();
        for (byte b : byteArray) {
            strDigest.append(byteToHexStr(b));
        }
        return strDigest.toString();
    }

    /**
     * Преобразование байтов в шестнадцатеричную строку
     */
    private static String byteToHexStr(byte mByte) {
        char[] Digit = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        char[] tempArr = new char[2];
        tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
        tempArr[1] = Digit[mByte & 0X0F];
        return new String(tempArr);
    }

    /**
     * Выполнить сортировку по словарю
     */
    private static void sort(String[] str) {
        for (int i = 0; i < str.length - 1; i++) {
            for (int j = i + 1; j < str.length; j++) {
                if (str[j].compareTo(str[i]) < 0) {
                    String temp = str[i];
                    str[i] = str[j];
                    str[j] = temp;
                }
            }
        }
    }
}

Конфигурация Проверкапараметр Исправлять application.yml Конфигурационный файл

Язык кода:agda
копировать
# Вичат Официальная конфигурация аккаунта Информация
шх:config:
originalid: Исходный идентификатор 
token: Конфигурация серверавизtoken Просто настройте это

писать Сообщение о подтверждениикод

Здесь я также дам вам универсальное руководство. Оно очень простое, его можно прочитать и понять. Мне не нужно, чтобы вы его писали.

Язык кода:java
копировать
package com.yby6.yby6pandorachatgpt.interfaces;

import com.yby6.yby6pandorachatgpt.infrastructure.util.wechat.WechatSignatureUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Yang Shuai
 * Create By 2024/1/18
 */
@RestController
@RequestMapping("/chat-api/wx/portal")
public class WechatController {

    private final Logger logger = LoggerFactory.getLogger(WechatController.class);

    // Служитьустройство Token 
    @Value("${wx.config.token}")
    private String token;

    /**
     * Обработать сообщение WeChat Служить, отправленное изgetпросить, для проверки знака
     *
     * <p>
     * appid     Идентификатор приложения WeChat
     * signature Отправлено через WeChat
     * timestamp Отправлено через WeChat из Временная метка
     * nonce     Отправлено через WeChat строка
     * echostr   Строка подтверждения отправлена ​​из WeChat
     */
    @GetMapping(produces = "text/plain;charset=utf-8")
    public String validate(
            @RequestParam(value = "signature", required = false) String signature,
            @RequestParam(value = "timestamp", required = false) String timestamp,
            @RequestParam(value = "nonce", required = false) String nonce,
            @RequestParam(value = "echostr", required = false) String echostr) {
        try {
            logger.info("Информация о проверке подписи общедоступной учетной записи WeChat {} начинается [{}, {}, {}]", signature, timestamp, nonce, echostr);
            if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
                throw new IllegalArgumentException("проситьпараметр недопустим, проверьте!");
            }
            boolean check = WechatSignatureUtil.check(token, signature, timestamp, nonce);
            logger.info("Проверка подписи публичного аккаунта WeChat: {}", check);
            if (!check) {
                return null;
            }
            return echostr;
        } catch (Exception e) {
            logger.error("Информация о проверке подписи общедоступной учетной записи WeChat {} не удалась [{}, {}, {}]", signature, timestamp, nonce, echostr, e);
            return null;
        }
    }

}

тест

Включить проникновение в интрасеть、Запустить серверную часть Служить,Сразу после этогояруководить Понятнопредставлять на рассмотрениетест Прямая отправка прошла успешно. Проверьте серверную консоль, и она также распечатает подтверждение того, что предыдущий успех был успешным.

Затем включаем нуждаться в сервере, иначе мы не будем получать сообщения, отправленные с официального аккаунта.

включатьназаднуждатьсяждать Подожди пять-шесть минут.,Дайте устройству WeChat Служить передышку,Подходит ли мне? Продолжить работу смотреть вниз?

Так что приезжать сюда,В принципе мы это завершили,Прогресс напрямую увеличился на 80%,Следующий Сразу剩下 post Напишем интерфейс шаг за шагом Но как писать? смотреть вниз

Пассивно отвечать на сообщения пользователей

Давайте продолжим читать документ, и там сказано, что мы получим к нему доступ. post интерфейс(мы устанавливаем из) А нам ничего не сказал параметр? пока я не увидел XML Пакет данных Нет Данить嘛那Сразупрямойиспользоватьнить Приходитьперенимать Сразу行

Язык кода:java
копировать
/**
 * Здесь да ручки WeChat Служить для пересылки сообщений из
 */
@PostMapping(produces = "application/xml; charset=UTF-8")
public String post(@RequestBody String requestBody) {

    logger.info("Получить параметр:{}",requestBody);

    return "толькосуществоватьоткрытьволосысередина....";
}

тест

Начните проникновение в интранет, запустите бэкэнд «Служить», а затем откройте свою собственную официальную учетную запись, чтобы отправлять сообщения.

⚠️ Конфигурация сервер должен быть включен, иначе вы не сможете получать сообщения о прибытии. 10 Около минут...

Вы можете увидеть приезжать. Я отправил сообщение через официальный аккаунт, и устройство WeChat Служить вернуло один. xmk верни мне, тогда нам нужно xml Проанализируйте и преобразуйте его в нас из Java Bean

Преобразование текстовых данных обычного сообщения

Новыйполагаться xstream для картографирования xml поле, оно также имеет функцию да можно Конфигурация restful интерфейс руководитьпроситьвызов,середина Конфигурацияодин Перехватчикинъекцияодиннекоторыйпроситьпараметрждать Конфигурация

Язык кода:yaml
копировать
<!-- Подключитесь к общедоступной учетной записи WeChat, нуждайтесь в анализе xml -->
<!-- https://mvnrepository.com/artifact/com.thoughtworks.xstream/xstream -->
<dependency>
    <groupId>com.thoughtworks.xstream</groupId>
    <artifactId>xstream</artifactId>
    <version>1.4.20</version>
</dependency>

писать xml Переписка JavaBean ,существовать DDD Архитектурасредииз Domain полеслой Новый Wechat папкаписать MessageTextEntity сущность

Язык кода:java
копировать
package com.yby6.yby6pandorachatgpt.domain.wechat;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Getter;

/**
 * Текст ответного сообщения сущность
 *
 * @author Yang Shuai
 * Create By 2024/01/18
 */
@Getter
public class MessageTextEntity {

    @XStreamAlias("MsgId")
    private String msgId;
    @XStreamAlias("ToUserName")
    private String toUserName;
    @XStreamAlias("FromUserName")
    private String fromUserName;
    @XStreamAlias("CreateTime")
    private String createTime;
    @XStreamAlias("MsgType")
    private String msgType;
    @XStreamAlias("Content")
    private String content;
    @XStreamAlias("Event")
    private String event;
    @XStreamAlias("EventKey")
    private String eventKey;

    public MessageTextEntity() {
    }

    public void setMsgId(String msgId) {
        this.msgId = msgId;
    }

    public void setToUserName(String toUserName) {
        this.toUserName = toUserName;
    }

    public void setFromUserName(String fromUserName) {
        this.fromUserName = fromUserName;
    }

    public void setCreateTime(String createTime) {
        this.createTime = createTime;
    }

    public void setMsgType(String msgType) {
        this.msgType = msgType;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public void setEvent(String event) {
        this.event = event;
    }

    public void setEventKey(String eventKey) {
        this.eventKey = eventKey;
    }
}

Далее мы будем xml Анализ для насиз MessageTextEntity Bean ,существовать DDD Архитектурасредииз Domain продолжать Новый XmlUtil Инструменты

Язык кода:java
копировать
package com.yby6.yby6pandorachatgpt.infrastructure.util.wechat;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;
import org.apache.commons.lang3.StringUtils;

import java.io.Writer;

/**
 * XmlUtil
 *
 * @author yangbuyiya
 * Create By 2024/01/18
 */
public class XmlUtil {

    /**
     * расширение xstream, автоматически добавляется bean-компонент, преобразованный в xml [CDATA[]]!
     */
    public static XStream getMyXStream() {
        return new XStream(new XppDriver() {
            @Override
            public HierarchicalStreamWriter createWriter(Writer out) {
                return new PrettyPrintWriter(out) {
                    // Добавить тег CDATA во все разделы xml.
                    final boolean cdata = true;

                    @Override
                    public void startNode(String name, Class clazz) {
                        super.startNode(name, clazz);
                    }

                    @Override
                    protected void writeText(QuickWriter writer, String text) {
                        if (cdata && !StringUtils.isNumeric(text)) {
                            writer.write("<![CDATA[");
                            writer.write(text);
                            writer.write("]]>");
                        } else {
                            writer.write(text);
                        }
                    }
                };
            }
        });
    }

    /**
     * Преобразование bean-компонента в формат сообщения WeChat изxml
     */
    public static String beanToXml(Object object) {
        XStream xStream = getMyXStream();
        xStream.alias("xml", object.getClass());
        xStream.processAnnotations(object.getClass());
        String xml = xStream.toXML(object);
        if (!StringUtils.isEmpty(xml)) {
            return xml;
        } else {
            return null;
        }
    }

    /**
     * Преобразование xml в общий метод bean-компонента
     */
    public static <T> T xmlToBean(String resultXml, Class clazz) {
        // XStreamобъектнастраиватьпо умолчанию Защита безопасности,в то же времянастраиватьпозволятьиздобрый
        Поток XStream = новый XStream (новый DomDriver());
        XStream.setupDefaultSecurity(поток);
        поток.allowTypes(новый класс[]{clazz});
        stream.processAnnotations(new Class[]{clazz});
        поток.setMode(XStream.NO_REFERENCES);
        stream.alias("xml", clazz);
        вернуть (T) поток.fromXML(resultXml);
    }

}
Язык кода:java
копировать
logger.info("Получить параметр:{}", requestBody);
MessageTextEntity messageTextEntity = XmlUtil.xmlToBean(requestBody, MessageTextEntity.class);
logger.info("Разобрать XML-данные:{}", messageTextEntity);

Затем мы продолжаем тест, чтобы проверить, успешно ли отображается да.

Успешно завершили анализ,Такнаконецодиншаг Да Пассивно отвечать на сообщения пользователей, Собираемся ниже из XML Вернуться в WeChat и все

Я написал это прямо здесь, так что больше ничего делать не нужно.

для обеспечения безопасности мы используем сами Конфигурационный файлсредииз Исходный идентификатор

Язык кода:java
копировать
@PostMapping(produces = "application/xml; charset=UTF-8")
public String post(@RequestBody String requestBody) {

    logger.info("Получить параметр:{}", requestBody);
    MessageTextEntity messageTextEntity = XmlUtil.xmlToBean(requestBody, MessageTextEntity.class);
    logger.info("Разобрать XML-данные:{}", messageTextEntity);

    // Соберите текстовые ответы
    MessageTextEntity res = new MessageTextEntity();
    // Аккаунт получателя (получатель приезжатьизOpenID) Да Возьми этоиз FromUserName этотдаресиверизопенид
    res.setToUserName(messageTextEntity.getFromUserName());
    // 	Идентификатор разработчика WeChat  Да разбирает из ToUserName этотда Идентификатор разработчика WeChat(Исходный идентификатор)
    //  Но дадля безопасно, мы используем сами Конфигурационный файлсредииз Исходный идентификатор
    res.setFromUserName(originalId);
    // Время создания сообщения (целочисленный тип)
    res.setCreateTime(String.valueOf(System.currentTimeMillis() / 1000L));
    // Тип сообщения, текст для текста
    res.setMsgType("text");
    // Ответ из содержимого сообщения (перенос строк: существующее содержимое может быть перенесено, а клиент WeChat поддерживает отображение переноса строк)
    res.setContent("Что ты делаешь!!!!");
    String result = XmlUtil.beanToXml(res);
    logger.info("Получить информацию об общедоступной учетной записи WeChat просить завершено {}, openid:{}", result, messageTextEntity.getFromUserName());

    return result;
}

тест

Запустите скорлупу арахиса, запустите бэкэнд Служить,существовать общедоступную учетную запись и отправьте сообщение, чтобы узнать, ответил ли да на текстовое сообщение.

Так что приезжать сюдаяих Сразууже完成Базовыйиз Операция,Следующий Дастыковка ChatGPT Операция вопросов и ответов

яих Можетпрямой把яих Юнит-тестиз OkHttp Просьба принести его сюда эквивалентна заполнению ответа на вопросы и ответы. Мы можем сначала это продемонстрировать.

Затем проведите тест,Проверьте эффект,включатьпроникновение в интранет、включатьназадконец Служить、тестпроверятьсмотретьконтрольбашняданетвыходвызов ChatGPT Возврат изпараметра

На самом деле, это можно сделать здесь, если ваше стремление к совершенству продолжится, смотрите вниз!

Вы можете посмотреть, как приезжают. Мы усовершенствовали интерактивную функцию вопросов и ответов, вам не кажется, что это очень просто? настоящийиз Да Это похоже на нарезку овощей. Хотя так выглядит очень удобно, это требует много производительности. Прежде чем отправить новое сообщение, нам приходится повторно отправлять его. Например:

Язык кода:bash
копировать
 OkHttpClient client = new OkHttpClient().newBuilder().build();

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

Итак, мы решаем проблему потребности в этом потенциальном существовании.,использоватьфабрика сеансовмодель,Здесь я ссылка mybatis извыполнить способ,Далее я просто расскажу о раше-раше!

Реализация фабрики сеансов

Путь к пакету представления исходного пакета Mybatis: org.apache.ibatis.session

если вы уже изучали исходный код mybatis, вы это поймете,mybatisизосновной ДаSqlSessionFactory

Вы можете увидеть это только через идею SqlSessionFactory извыполнитьобъектда DefaultSqlSessionFactory и SqlSessionManager

Такяих Сразу Нажмите DefaultSqlSessionFactory Введите исходный код, чтобы увидеть, что он сделал

Осуществленный SqlSessionFactory интерфейс openSession метод Начать сеанс оченьиз Перегрузка

существоватьopenSession методсреди Можетсмотретьприезжатьвызов openSessionFromDataSource Созданный DefaultSqlSession по умолчаниюизсессия

Итак, давайте продолжим нажимать DefaultSqlSession, чтобы посмотреть, что было сделано?

существовать DefaultSqlSession в Осуществленный SqlSession интерфейсизметод

DefaultSqlSession Это эквивалентно последней функции калибровки из класса, Мой анализ очень прост. Если вам интересно, вы можете посмотреть исходный код.

Следующийя Сразу根据mybatisизфабрика сеансовмодель Приходитьвыполнитьодин

ссылка структура mybatis, первое определение выходит из существования, чтобы увидеть специфику отношений

Создать OpenAiSessionFactory.

  1. первыйяихнуждатьсяодинфабрика сеансовинтерфейс OpenAiSessionFactory один метод внутри openSession Вернуться к параметру интерфейсаOpenAiSession (см. первое и упоминание о посещении ниже)

существоватьснаружислой Новый session Папки представляют функции сеанса

Язык кода:java
копировать
/**
 * Фабрика сессий OpenAI
 *
 * @author yangs
 * @date 2024/01/18
 */
public interface OpenAiSessionFactory {

    /**
     * Начать сеанс
     *
     * @return {@link OpenAiSession}
     */
    OpenAiSession openSession();

}

проситьпараметр Строитьссылкачиновникизпросить

яужедля Все молодцы Понятно Не нужно делать это самому

Язык кода:java
копировать
package com.yby6.yby6pandorachatgpt.domain.chatgpt;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.*;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

/**
 * Модель строителя Строитьпроситьинформация * проситьинформацияв соответствии с;OpenAIОфициальный сайтAPIСтроитьпараметр;<a href="https://platform.openai.com/playground">...</a>
 *
 * @author yangs
 * @date 2024/01/18
 */
@Data
@Builder
@Slf4j
@JsonInclude(JsonInclude.Include.NON_NULL)
@NoArgsConstructor
@AllArgsConstructor
public class ChatGPTRequest implements Serializable {

    /**
     * По умолчанию Модель
     */
    private String model = Model.GPT_3_5_TURBO.getCode();
    /**
     * вопросописывать     */
    private List<ChatGPTResponse.ChoicesDTO.MessageDTO> messages;
    /**
     * Контролировать температуру [случайность] между 0приезжать2. Более высокие значения (например, 0,8) сделают вывод более случайным, а более низкие значения (например, 0,2) сделают вывод более целенаправленным и детерминированным.
     */
    private double temperature = 0.2;
    /**
     * Контроль разнообразия; использовать выборку температуры из альтернативного метода, называемого для отбора проб керна, где Модель рассматривается с массой вероятности top_p из токенов из результатов. Следовательно, 0,1 означает только рассмотрение перед включением 10% Вероятностная масса из токена
     */
    @JsonProperty("top_p")
    private Double topP = 1d;
    /**
     * для Число завершений, созданных для каждого запроса из
     */
    private Integer n = 1;
    /**
     * данетдля Потоковый вывод;Даодинпрыгатьодинпрыгатьиз,вне Приходитьрезультат
     */
    private boolean stream = false;
    /**
     * Флаг остановки вывода
     */
    private List<String> stop;
    /**
     * Ограничение выходной строки 0; ~ 4096
     */
    @JsonProperty("max_tokens")
    private Integer maxTokens = 2048;
    /**
     * Штраф за частоту уменьшает вероятность повторения одной и той же строки Моделью;
     */
    @JsonProperty("frequency_penalty")
    private double frequencyPenalty = 0;
    /**
     * сохранить наказание; улучшить модель возможность говорить на новые темы из
     */
    @JsonProperty("presence_penalty")
    private double presencePenalty = 0;
    /**
     * Генерируйте несколько результатов вызовов и показывайте только лучшие из них. Это поглотит вас больше api token
     */
    @JsonProperty("logit_bias")
    private Map logitBias;
    /**
     * логотип вызова, чтобы избежать дублирования вызова
     */
    private String user;

    @Getter
    @AllArgsConstructor
    public enum Model {
        /**
         * gpt-3.5-turbo
         */
        GPT_3_5_TURBO("gpt-3.5-turbo"),
        /**
         * GPT4.0
         */
        GPT_4("gpt-4"),
        /**
         * GPT4.0 Очень длинный контекст
         */
        GPT_4_32K("gpt-4-32k"),
        ;
        private final String code;
    }

}

Создать OpenAiSession

  1. определениеOpenAiSessionинтерфейсвопределениеметодcompletionsэтотметод ДаяихвызовChatGPTизметод проситьпараметрдля ChatGPTRequest Вернитесь к параметрам для нас перед определением ChatGPTResponse Используется для получения возвращаемых данных
Язык кода:java
копировать
package com.yby6.yby6pandorachatgpt.session;


import com.yby6.yby6pandorachatgpt.domain.chatgpt.ChatGPTRequest;
import com.yby6.yby6pandorachatgpt.domain.chatgpt.ChatGPTResponse;

/**
 * OpenAi сессияинтерфейс *
 * @author yangs
 * @date 2024/01/11
 */
public interface OpenAiSession {

    /**
     * по умолчанию GPT-3.5 Модель вопросов и ответов
     *
     * @param chatGPTRequest проситьинформация     * @return Возврат результатов
     */
    ChatGPTResponse completions(ChatGPTRequest chatGPTRequest);

}

CreateDefaultOpenAiSessionFactory

  1. затемяихвыполнить OpenAiSessionFactory фабрикаинтерфейс создавать DefaultOpenAiSessionFactory Класс фабрики сеансов по умолчанию руководитьвыполнить Функция

Давайте посмотрим mybatis структурная ручкапо умолчаниюиз Управляйте в одиночку Приходить Мы тоже Новыйодин defaults папка

Новый DefaultOpenAiSessionFactory Класс реализации Реализация openSession метод Функция здесьиз Да Приходитьуправлятьсессиявключать

Реализация openSession метод Функция, Функция метода используется в Строить. okhttp просить создавать API Служить Создать сеанс по По умолчанию возвращается единообразно, так что мы открываем только одну сессию Вот и всеизбегать повторенийсоздавать okhttp просить, Идея заключается в следующем

Реализация openSession

яихпервыйвыполнить Строить OkHttpClient и его нужно перехватить okhttp изпросить Даватьпросить Новый Аутентификацияждатьодиннекоторыйпроситьпараметр

Новый OpenAiInterceptor OkHttp просить Перехватчик

существоватьinfrastructure(инфраструктура)среди Новый interceptor папка Новый OpenAiInterceptor Перехватчик

нуждаться open Session Передать APIKey использовать ВСтроитьпросить Аутентификацияпараметр

Язык кода:java
копировать
package com.yby6.yby6pandorachatgpt.infrastructure.interceptor;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.Header;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;

/**
 * сопределение OpenAI Перехватчик
 *
 * @author yangs
 * @date 2024/01/18
 */
public class OpenAiInterceptor implements Interceptor {

    /**
     * OpenAi apiKey нуждатьсясуществовать Официальный сайт Применять     */
    private final String apiKey;

    public OpenAiInterceptor(String apiKey) {
        this.apiKey = apiKey;
    }

    /**
     * Перехват окhttpпросить
     * @param chain цепь
     * @return да Нет, чтобы продолжить выполнение
     */
    @NotNull
    @Override
    public Response intercept(Chain chain) throws IOException {
        return chain.proceed(this.auth(apiKey, chain.request()));
    }

    /**
     * Строить Сертификацияпроситьобъект
     *
     * @param apiKey   ключ API
     * @param original сначала
     * @return {@link Request}
     */
    private Request auth(String apiKey,Запросить оригинал) {
        HttpUrl.Builder builder = original.url().newBuilder();
        // Сборка новыйспросить адрес
        HttpUrl URL = builder.build();
        // создатьпросить
        вернуть оригинал.newBuilder()
        .url(url.url())
        .header(Header.AUTHORIZATION.getValue(), "Bearer " + apiKey)
        .header(Header.CONTENT_TYPE.getValue(), ContentType.JSON.getValue())
        .method(original.method(), original.body())
        .build();
    }

}

Строить OkHttpClient И добавьте Перехватчик Конфигурацияпроситьпараметр

В принципе изконфигурацию мы написали закончено,Следующий Даписать создавать API просить Служить, яихнуждатьсяиспользовать Retrofit 2 сотрудничать OKHTTP Тогда я представлю это подробно дальше Retrofit 2 Что такое да и его основное использование?

Встречайте «Ретрофит 2»

Retrofit2 даодин используется для Android и Java из Тип безопасностииз HTTP Клиент, он может легко восстановить RESTful API извызов

github адрес: https://github.com/square/retrofit

чиновникдокументиспользоватьметод адрес: https://square.github.io/retrofit/

Введение зависимости Retrofit2

Язык кода:yaml
копировать
<!--   http api изменять Java интерфейс     -->
<dependency>
    <groupId>com.squareup.retrofit2</groupId>
    <artifactId>retrofit</artifactId>
    <version>2.9.0</version>
</dependency>
<!--   Для сериализации     -->
<dependency>
  <groupId>com.squareup.retrofit2</groupId>
  <artifactId>converter-jackson</artifactId>
  <version>2.9.0</version>
</dependency>
<dependency>
  <groupId>com.squareup.retrofit2</groupId>
  <artifactId>adapter-rxjava2</artifactId>
  <version>2.9.0</version>
</dependency>

Согласно вышеизложенномуизкартинасредиопределение Понятно GithubService интерфейс Мы тоже определение OpenAiService интерфейс ,существовать DDDАрхитектурасредиизотвечатьиспользоватьслой Новыйинтерфейс

Язык кода:java
копировать
/**
 * ChatGPT проситьинтерфейс API
 * Официальный сайт:<a href="https://platform.openai.com/playground">...</a>
 *
 * @author Yang Shuai
 * Create By 2024/1/19
 */
public interface OpenAiService {

    /**
     * по умолчанию GPT-3.5 Модель вопросов и ответов
     *
     * @param chatGPTRequest проситьинформация     * @return Возврат результатов
     */
    @POST("v1/chat/completions")
    Call<ChatGPTResponse> completions(@Body ChatGPTRequest chatGPTRequest);


}

Создать запрос на обслуживание API

Язык кода:java
копировать
/**
 * Начать сеанс
 *
 * @return {@link OpenAiSession}
 */
@Override
public OpenAiSession openSession() {
    // 1. бревно Конфигурация    HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
    httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.NONE);
    logger.info("OpenAi API Client Start");
    // 2. включать Http клиент
    OkHttpClient okHttpClient = new OkHttpClient
    .Builder()
    .addInterceptor(httpLoggingInterceptor)
    .addInterceptor(new OpenAiInterceptor("youизapi key"))
    .connectTimeout(450, TimeUnit.SECONDS)
    .writeTimeout(450, TimeUnit.SECONDS)
    .readTimeout(450, TimeUnit.SECONDS)
    .build();

    // 3. создавать API Служить
    final Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("IP") // Вы из Pandora получаете доступ по IP
    .client(okHttpClient)
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    .addConverterFactory(JacksonConverterFactory.create())
    .build();
    // создаватьпереписыватьсяпросить API
    final OpenAiService openAiService = retrofit.create(OpenAiService.class);

    // передача Даватьпо умолчаниювыполнитьруководитьтрогатьволосыпроситьвызов
    return new DefaultOpenAiSession(openAiService);
}

ok инициализирован openSession писатьполный Следующий Давыполнитьпо умолчаниюизсессияподдерживать,Долженметодсреди Даруководитьбизнесслойвызовлогика обработки вещей(довольно В service)

Создать сеанс по умолчанию DefaultOpenAiSession.

создаватьодинпо умолчаниюизсессия Приходитьвыполнить OpenAiSession интерфейсметодпоставлятьпроситьвызов

и положить session папкаall mobileприезжать domain , session Должен принадлежать domian Уровень домена корректирует бизнес

выполнитьпроситьволосырост

Язык кода:java
копировать
/**
 * по умолчаниюиз OpenAI сессиявыполнитьOpenAiSession *
 * @author Yang Shuai
 * Create By 2024/1/19
 */
@RequiredArgsConstructor
public class DefaultOpenAiSession implements OpenAiSession {

    private final OpenAiService openAiService;

    /**
     * по умолчанию GPT-3.5 Модель вопросов и ответов
     *
     * @param chatGPTRequest проситьинформация     * @return Возврат результатов
     */
    @Override
    public ChatGPTResponse completions(ChatGPTRequest chatGPTRequest) throws IOException {
        return openAiService.completions(chatGPTRequest).execute().body();
    }
}

Инициализируйте фабрику сеансов OpenAI, чтобы открыть сеанс.

Язык кода:java
копировать
/**
 * @author Yang Shuai
 * {@code @create} 2024/1/16:17:48
 * {@code @desc} |
 **/
@Configuration
@RequiredArgsConstructor
public class initializeOpenAISession {
    private final Logger logger = LoggerFactory.getLogger(initializeOpenAISession.class);

    /**
     * создание фабрики разговоров
     */
    @Bean("openAiSession")
    public OpenAiSession openAiSession() {
        logger.info("инициализацияпросить Конфигурационный файл");
        // 2. фабрика сеансов
        OpenAiSessionFactory factory = new DefaultOpenAiSessionFactory();
        // 3. Начать сеанс
        logger.info("Начать сеанс openAiSession");
        return factory.openSession();
    }

}

окончательная реализация

  1. Строитьинформация

существовать messageDto среди Новый Понятно Модель строителя

Язык кода:java
копировать
@NoArgsConstructor
@Data
public static class MessageDTO {
    @JsonProperty("role")
    private String role;
    @JsonProperty("content")
    private String content;

    /**
     * Конструктор Строить персонажей, контент
     */
    private MessageDTO(MessageDTO.Builder builder) {
        this.role = builder.role;
        this.content = builder.content;
    }

    public static MessageDTO.Builder builder() {
        return new MessageDTO.Builder();
    }

    /**
     * Модель строителя
     */
    public static final class Builder {

        private String role;
        private String content;

        public Builder() {
        }

        public MessageDTO.Builder role(Constants.Role role) {
            this.role = role.getCode();
            return this;
        }

        public MessageDTO.Builder content(String content) {
            this.content = content;
            return this;
        }


        public MessageDTO build() {
            return new MessageDTO(this);
        }
    }


}
  1. Строитьпроситьпараметр

Новыйпостоянныйдобрый Constants определениепросить Роль

Язык кода:java
копировать
/**
 * мужскойиспользовать Базаиспользоватьобъект
 */
public class Constants {

    /**
     * Роль
     * Официальный сайтподдерживатьизпросить Рольдобрыйформа;system、user、assistant
     * <a href="https://platform.openai.com/docs/guides/chat/introduction">...</a>
     */
    @Getter
    public enum Role {

        /**
         * система
         */
        SYSTEM("system"),/**
         * использовать ВОЗ         */
        USER("user"),
        /**
         * помощник
         */
        ASSISTANT("assistant"),
        ;

        /**
         * пароль
         */
        private final String code;

        /**
         * Роль
         *
         * @param code пароль
         */
        Role(String code) {
            this.code = code;
        }

    }

}
  1. получать gpt возвращатьсяизрезультат并且руководитьизменятьдля XML Вернитесь в WeChat Служить и отправьте пользователю

Вы можете удалить код sendChatGPT.

тест

включатьпроникновение в интранет、включатьназадконец Служить,тест Retrofit данетвызовуспех

Раннее утро 2 точка 30 Я так устала. Смотрите приезжать сюда. Пожалуйста, поставьте мне лайк и поддержите! Спасибо

ok, Теперь существовать и все готово! so easy to happy Это действительно похоже на нарезку овощей

Раннее утро 2 точка 30 Я так устала. Смотрите приезжать сюда. Пожалуйста, поставьте мне лайк и поддержите! Спасибо

наконец

Не думай, что это конец, Хотя да в настоящее время может быть нормальным, да если спрашивает обо всем GPT отвечатьиз很慢超Проходить Понятно Вичатотвечатьиз На этапе повторной попытки будет сообщено об ошибке.,Тогда вот корректировка нуждатьсясуществовать,Но это также очень просто,существоватьместный Новыйодинкэш,кэшсредируководить Сохранить задачуFuture<String>,эти задачи управляются пулом потоков,После выполнения задачи возвращается строковый результат. Однако во время выполнения дасуществовать может возникнуть тайм-аут WeChat, поэтому мы нуждаемся в определении того, превысило ли да. WeChat повторяет попытку в общей сложности три раза по пять секунд каждый раз. Если оно превысит три раза, он вернется к пользователю и попросит его ввести любой текст и продолжить предыдущую операцию.,Это как войти снова,Когда это существование определяет, что проблемная задача существуетэтот уже сохранена в одном слое, новая задача не будет ждать, пока предыдущая задача не будет завершена и возвращена пользователю.

🌊 сосредоточиться Я не растерялся. Если эта статья вам полезна, или если у вас есть какие-либо вопросы, оставьте сообщение в области комментариев. Я обычно отвечаю после прочтения. Всем пожалуйста поставьте лайк и поддержите~ 💗

картина

яУчаствуют в специальном тренировочном лагере Tencent Technology Creation 2024 Пятый выпуск Объявлен конкурс сочинений, разделите приз со мной!

boy illustration
Углубленный анализ переполнения памяти CUDA: OutOfMemoryError: CUDA не хватает памяти. Попыталась выделить 3,21 Ги Б (GPU 0; всего 8,00 Ги Б).
boy illustration
[Решено] ошибка установки conda. Среда решения: не удалось выполнить первоначальное зависание. Повторная попытка с помощью файла (графическое руководство).
boy illustration
Прочитайте нейросетевую модель Трансформера в одной статье
boy illustration
.ART Теплые зимние предложения уже открыты
boy illustration
Сравнительная таблица описания кодов ошибок Amap
boy illustration
Уведомление о последних правилах Points Mall в декабре 2022 года.
boy illustration
Даже новички могут быстро приступить к работе с легким сервером приложений.
boy illustration
Взгляд на RSAC 2024|Защита конфиденциальности в эпоху больших моделей
boy illustration
Вы используете ИИ каждый день и до сих пор не знаете, как ИИ дает обратную связь? Одна статья для понимания реализации в коде Python общих функций потерь генеративных моделей + анализ принципов расчета.
boy illustration
Используйте (внутренний) почтовый ящик для образовательных учреждений, чтобы использовать Microsoft Family Bucket (1T дискового пространства на одном диске и версию Office 365 для образовательных учреждений)
boy illustration
Руководство по началу работы с оперативным проектом (7) Практическое сочетание оперативного письма — оперативного письма на основе интеллектуальной системы вопросов и ответов службы поддержки клиентов
boy illustration
[docker] Версия сервера «Чтение 3» — создайте свою собственную программу чтения веб-текста
boy illustration
Обзор Cloud-init и этапы создания в рамках PVE
boy illustration
Корпоративные пользователи используют пакет регистрационных ресурсов для регистрации ICP для веб-сайта и активации оплаты WeChat H5 (с кодом платежного узла версии API V3)
boy illustration
Подробное объяснение таких показателей производительности с высоким уровнем параллелизма, как QPS, TPS, RT и пропускная способность.
boy illustration
Удачи в конкурсе Python Essay Challenge, станьте первым, кто испытает новую функцию сообщества [Запускать блоки кода онлайн] и выиграйте множество изысканных подарков!
boy illustration
[Техническая посадка травы] Кровавая рвота и отделка позволяют вам необычным образом ощипывать гусиные перья! Не распространяйте информацию! ! !
boy illustration
[Официальное ограниченное по времени мероприятие] Сейчас ноябрь, напишите и получите приз
boy illustration
Прочтите это в одной статье: Учебник для няни по созданию сервера Huanshou Parlu на базе CVM-сервера.
boy illustration
Cloud Native | Что такое CRD (настраиваемые определения ресурсов) в K8s?
boy illustration
Как использовать Cloudflare CDN для настройки узла (CF самостоятельно выбирает IP) Гонконг, Китай/Азия узел/сводка и рекомендации внутреннего высокоскоростного IP-сегмента
boy illustration
Дополнительные правила вознаграждения амбассадоров акции в марте 2023 г.
boy illustration
Можно ли открыть частный сервер Phantom Beast Palu одним щелчком мыши? Супер простой урок для начинающих! (Прилагается метод обновления сервера)
boy illustration
[Играйте с Phantom Beast Palu] Обновите игровой сервер Phantom Beast Pallu одним щелчком мыши
boy illustration
Maotouhu делится: последний доступный внутри страны адрес склада исходного образа Docker 2024 года (обновлено 1 декабря)
boy illustration
Кодирование Base64 в MultipartFile
boy illustration
5 точек расширения SpringBoot, супер практично!
boy illustration
Глубокое понимание сопоставления индексов Elasticsearch.
boy illustration
15 рекомендуемых платформ разработки с нулевым кодом корпоративного уровня. Всегда найдется та, которая вам понравится.
boy illustration
Аннотация EasyExcel позволяет экспортировать с сохранением двух десятичных знаков.