paint-brush
워드 임베딩: 더 나은 답변을 위해 ChatBot 컨텍스트를 제공하는 비법~에 의해@tomfernblog
2,991 판독값
2,991 판독값

워드 임베딩: 더 나은 답변을 위해 ChatBot 컨텍스트를 제공하는 비법

~에 의해 Tomas Fernandez18m2023/07/26
Read on Terminal Reader
Read this story w/o Javascript

너무 오래; 읽다

단어 삽입 및 ChatGPT를 사용하여 전문가 봇을 구축하는 방법을 알아보세요. 단어 벡터의 힘을 활용하여 챗봇의 응답을 향상하세요.
featured image - 워드 임베딩: 더 나은 답변을 위해 ChatBot 컨텍스트를 제공하는 비법
Tomas Fernandez HackerNoon profile picture
0-item
1-item

OpenAI의 ChatGPT가 예외적으로 지능적이라는 것은 의심의 여지가 없습니다. 변호사의 변호사 테스트를 통과했으며 의사와 유사한 지식을 보유하고 있으며 일부 테스트에서는 IQ가 155로 기록되었습니다 . 그러나 무지를 인정하기보다는 정보를 조작 하는 경향이 있습니다. 이러한 경향은 2021년에 해당 지식이 중단된다는 사실과 결합되어 GPT API를 사용하여 전문 제품을 구축하는 데 어려움을 초래합니다.


우리는 이러한 장애물을 어떻게 극복할 수 있습니까? GPT-3와 같은 모델에 새로운 지식을 어떻게 전달할 수 있습니까? 나의 목표는 Python, OpenAI API 및 단어 임베딩을 사용하는 질문 답변 봇을 구성하여 이러한 질문을 해결하는 것입니다.

내가 무엇을 만들 것인가

저는 프롬프트에서 지속적인 통합 파이프라인을 생성하는 봇을 만들려고 합니다. 이 파이프라인은 아시다시피 Semaphore CI/CD에서 YAML로 형식화됩니다 .


다음은 실제 봇의 예입니다.

실행 중인 프로그램의 스크린샷. 화면에서 python query.py "Docker 이미지를 빌드하고 Docker Hub에 업로드하는 CI 파이프라인 생성" 명령이 실행되고, 프로그램은 요청된 작업을 수행하는 CI 파이프라인에 해당하는 YAML을 인쇄합니다. 실행 중인 프로그램의 스크린샷. 화면에서 python query.py "Create a CI pipeline that builds and uploads a Docker image to Docker Hub" 명령이 실행되고, 프로그램은 요청된 작업을 수행하는 CI 파이프라인에 해당하는 YAML을 인쇄합니다.


DocsGPT , My AskAILibraria 와 같은 프로젝트의 정신으로 세마포어 및 파이프라인 구성 파일 생성 방법에 대해 GPT-3 모델을 "교육"할 계획입니다. 나는 기존 문서를 활용하여 이를 달성할 것입니다.


나는 봇 구축에 대한 사전 지식을 가정하지 않고 귀하의 요구 사항에 맞게 조정할 수 있도록 깨끗한 코드를 유지 관리할 것입니다.

전제 조건

이 튜토리얼을 따르기 위해 봇 코딩 경험이나 신경망 지식이 필요하지 않습니다. 그러나 다음이 필요합니다.


  • 파이썬 3.
  • Pinecone 계정(무료로 스타터 플랜 에 가입하세요).
  • OpenAI API 키( 유료, 신용카드 필요 ) 신규 사용자는 처음 3개월 동안 5달러의 무료 크레딧을 사용해 볼 수 있습니다.

하지만 ChatGPT는 학습할 수 없습니다. 그렇죠?

ChatGPT, 더 정확하게는 이를 지원하는 LLM(대형 언어 모델)인 GPT-3 및 GPT-4는 마감 날짜가 2021년 9월경인 대규모 데이터 세트에서 훈련되었습니다.


본질적으로 GPT-3는 해당 날짜 이후의 사건에 대해 거의 알지 못합니다. 간단한 프롬프트를 통해 이를 확인할 수 있습니다:

ChatGPT 스크린샷 ChatGPT는 2022년 월드컵에서 누가 우승했는지 모릅니다.


일부 OpenAI 모델은 미세 조정을 거칠 수 있지만 관심 있는 모델과 같은 고급 모델은 그럴 수 없습니다. 훈련 데이터를 늘릴 수는 없습니다.


훈련 데이터 외에 GPT-3에서 어떻게 답변을 얻을 수 있습니까? 한 가지 방법은 텍스트 이해 능력을 활용하는 것입니다. 관련 맥락으로 프롬프트를 강화함으로써 정답을 얻을 수 있을 것입니다.


아래 예에서는 FIFA 공식 사이트 의 컨텍스트를 제공하는데 응답이 크게 다릅니다.

질문에 대한 두 번째 응답 시도 제공된 컨텍스트를 사용하면 ChatGPT가 정확하게 응답할 수 있습니다.


충분한 관련 컨텍스트가 제공되면 모델이 모든 프롬프트에 응답할 수 있다고 추론할 수 있습니다. 문제는 여전히 남아 있습니다. 임의의 프롬프트가 주어지면 관련 내용을 어떻게 알 수 있습니까? 이 문제를 해결하려면 단어 임베딩이 무엇인지 살펴봐야 합니다.

단어 임베딩이란 무엇입니까?

언어 모델의 맥락에서 임베딩은 단어, 문장 또는 전체 문서를 벡터 또는 숫자 목록으로 표현하는 방법입니다.


임베딩을 계산하려면 word2vec 또는 text-embedding-ada-002 와 같은 신경망이 필요합니다. 이러한 네트워크는 방대한 양의 텍스트에 대해 훈련되었으며 훈련 데이터에 특정 패턴이 나타나는 빈도를 분석하여 단어 간의 관계를 찾을 수 있습니다.


다음과 같은 단어가 있다고 가정해 보겠습니다.

  • 고양이


각 단어에 대한 벡터를 계산하기 위해 이러한 임베딩 네트워크 중 하나를 사용한다고 상상해 보세요. 예를 들어:

단어

벡터

문맥

고양이

[0.1, 0.2, 0.3, 0.4, 0.5]

동물, 물건, 작은 것

[0.6, 0.7, 0.8, 0.9, 1.0]

동물, 물건, 큰 것

[0.2, 0.4, 0.6, 0.8, 1.0]

물건, 장난감, 작은 물건

[0.3, 0.6, 0.9, 1.2, 1.5]

건물, 집, 큰 물건

각 단어에 대한 벡터가 있으면 이를 사용하여 텍스트의 의미를 나타낼 수 있습니다. 예를 들어, “고양이가 공을 쫓았다”라는 문장은 벡터 [0.1, 0.2, 0.3, 0.4, 0.5] + [0.2, 0.4, 0.6, 0.8, 1.0] = [0.3, 0.6, 0.9, 1.2 로 표현될 수 있습니다. , 1.5]. 이 벡터는 물체를 쫓는 동물에 대한 문장을 나타냅니다.


단어 임베딩은 비슷한 의미를 가진 단어나 문장이 서로 가까이 있는 다차원 공간으로 시각화될 수 있습니다. 벡터 사이의 "거리"를 계산하여 입력 텍스트에 대한 유사한 의미를 찾을 수 있습니다.

벡터의 3차원 표현입니다. 첫 번째는 '남성-여성'으로 라벨이 지정되어 있고 데이터 포인트가 남성-여성과 왕-여왕입니다. 두 번째는 '동사-시제'로 라벨이 지정되어 있으며 걷기 걷기 수영 수영과 같은 동사가 있습니다. 마지막은 '국가-수도'로 표시되어 있으며 여러 수도가 해당 국가와 연결되어 있습니다. 임베딩을 벡터 공간으로 3D 표현합니다. 실제로 이러한 공간은 수백 또는 수천 개의 차원을 가질 수 있습니다. 출처: AI의 Multitool을 만나보세요: 벡터 임베딩


이 모든 것 뒤에 숨은 실제 수학은 이 기사의 범위를 벗어납니다. 그러나 중요한 점 은 벡터 연산을 통해 수학을 사용하여 의미를 조작하거나 결정할 수 있다는 것입니다. "queen"이라는 단어를 나타내는 벡터에서 "woman" 벡터를 빼고 "man" 벡터를 추가합니다. 결과는 "king" 근처의 벡터여야 합니다. '아들'을 추가하면 '왕자'에 가까운 위치에 도달해야 합니다.

토큰으로 신경망 내장

지금까지 단어를 입력으로, 숫자를 출력으로 사용하는 신경망 내장에 대해 논의했습니다. 그러나 많은 현대 네트워크는 단어 처리에서 토큰 처리로 전환했습니다.


토큰은 모델에서 처리할 수 있는 가장 작은 텍스트 단위입니다. 토큰은 단어, 문자, 구두점, 기호 또는 단어의 일부일 수 있습니다.


BPE( 바이트 쌍 인코딩 )를 사용하여 텍스트를 토큰으로 변환하고 각 토큰을 숫자로 표시하는 OpenAI 온라인 토크나이저를 실험하여 단어가 토큰으로 변환되는 방식을 확인할 수 있습니다.

OpenAI 토크나이저의 스크린샷. 일부 텍스트가 입력되었으며 각 토큰은 서로 다른 색상으로 표시되어 단어가 토큰에 어떻게 매핑되는지 확인할 수 있습니다. 텍스트는 다음과 같습니다. 임베딩 모델 뒤에는 입력 텍스트를 벡터로 변환하는 신경망이 있습니다. 임베딩 모델 유형마다 기능과 속도가 다릅니다. 예를 들어 Word2vec은 단어를 가져와 100~300차원 범위의 벡터를 생성합니다. 토큰과 단어 사이에는 1:1 관계가 있는 경우가 많습니다. 대부분의 토큰에는 단어와 선행 공백이 포함됩니다. 그러나 두 개의 토큰으로 구성된 "임베딩", "임베딩" 및 "딩" 또는 4개의 토큰으로 구성된 "기능"과 같은 특별한 경우가 있습니다. "토큰 ID"를 클릭하면 각 토큰에 대한 모델의 숫자 표현을 볼 수 있습니다.

임베딩을 사용하여 더 똑똑한 봇 설계

이제 임베딩이 무엇인지 이해했으므로 다음 질문은 임베딩이 어떻게 더 똑똑한 봇을 구축하는 데 도움이 될 수 있는가입니다.


먼저 GPT-3 API를 직접 사용할 때 어떤 일이 발생하는지 살펴보겠습니다. 사용자가 프롬프트를 표시하면 모델은 최선을 다해 응답합니다.

사용자와 GPT-3 간의 상호작용을 보여주는 다이어그램 사용자가 메시지를 보내면 모델이 응답합니다.


그러나 방정식에 맥락을 추가하면 상황이 달라집니다. 예를 들어, 맥락을 제공한 후 ChatGPT에 월드컵 우승팀에 대해 물었을 때 큰 변화가 있었습니다.


따라서 더욱 스마트한 봇을 구축하기 위한 계획은 다음과 같습니다.

  1. 사용자의 프롬프트를 가로챕니다.
  2. 해당 프롬프트에 대한 임베딩을 계산하여 벡터를 생성합니다.
  3. 초기 프롬프트와 의미상 관련이 있어야 하므로 벡터 근처의 문서에 대한 데이터베이스를 검색합니다.
  4. 관련 컨텍스트와 함께 원본 프롬프트를 GPT-3으로 보냅니다.
  5. GPT-3의 응답을 사용자에게 전달합니다.

봇의 보다 복잡한 구현입니다. 사용자는 컨텍스트 데이터베이스를 검색하고 이를 사용하여 프롬프트를 강화하는 챗봇 앱에 프롬프트를 보냅니다. 프롬프트는 GPT-3으로 전송되고 해당 응답은 사용자에게 전달됩니다.


대부분의 프로젝트처럼 데이터베이스를 설계하는 것부터 시작해 보겠습니다.

임베딩을 사용하여 지식 데이터베이스 만들기

당사의 컨텍스트 데이터베이스에는 원본 문서와 해당 벡터가 포함되어야 합니다. 원칙적으로 이 작업에는 모든 유형의 데이터베이스를 사용할 수 있지만 벡터 데이터베이스가 작업에 가장 적합한 도구입니다.


벡터 데이터베이스는 고차원 벡터 데이터를 저장하고 검색하도록 설계된 특수 데이터베이스입니다. 검색을 위해 SQL과 같은 쿼리 언어를 사용하는 대신 벡터를 제공하고 N개의 가장 가까운 이웃을 요청합니다.


벡터를 생성하기 위해 OpenAI가 제공하는 가장 빠르고 비용 효율적인 모델인 text-embedding-ada-002를 사용합니다. 모델은 입력 텍스트를 토큰으로 변환하고 Transformer 라는 주의 메커니즘을 사용하여 관계를 학습합니다. 이 신경망의 출력은 텍스트의 의미를 나타내는 벡터입니다.

토큰화 프로세스를 보여주는 다이어그램 문서는 토큰화되어 내장 신경망으로 전송됩니다. 네트워크의 출력은 벡터입니다.


컨텍스트 데이터베이스를 생성하려면 다음을 수행합니다.

  1. 모든 소스 문서를 수집하세요.
  2. 관련 없는 문서를 필터링합니다.
  3. 각 문서의 임베딩을 계산합니다.
  4. 벡터, 원본 텍스트 및 기타 관련 메타데이터를 데이터베이스에 저장합니다.

컨텍스트 데이터베이스에 데이터를 저장하는 프로세스를 보여주는 다이어그램입니다. 소스 문서는 임베딩 신경망으로 전송됩니다. 데이터베이스는 원본 텍스트와 함께 벡터를 저장합니다.

문서를 벡터로 변환

먼저 OpenAI API 키를 사용하여 환경 파일을 초기화해야 합니다. API 키는 비공개이며 귀하의 계정에 연결되어 있으므로 이 파일을 버전 관리에 커밋해서는 안 됩니다.

 export OPENAI_API_KEY=YOUR_API_KEY

다음으로 Python 애플리케이션용 virtualenv를 생성하겠습니다.

 $ virtualenv venv $ source venv/bin/activate $ source .env

OpenAI 패키지를 설치합니다.

 ```bash $ pip install openai numpy

문자열 "Docker Container"에 대한 임베딩을 계산해 보겠습니다. Python REPL에서 또는 Python 스크립트로 실행할 수 있습니다.

 $ python >>> import openai >>> embeddings = openai.Embedding.create(input="Docker Containers", engine="text-embedding-ada-002") >>> embeddings JSON: { "data": [ { "embedding": [ -0.00530336843803525, 0.0013223182177171111, ... 1533 more items ..., -0.015645816922187805 ], "index": 0, "object": "embedding" } ], "model": "text-embedding-ada-002-v2", "object": "list", "usage": { "prompt_tokens": 2, "total_tokens": 2 } }

보시다시피 OpenAI의 모델은 1536개 항목(text-embedding-ada-002의 벡터 크기)이 포함된 embedding 목록으로 응답합니다.

Pinecone에 임베딩 저장

오픈 소스인 Chroma 처럼 선택할 수 있는 벡터 데이터베이스 엔진이 여러 개 있지만 저는 무료 계층이 포함된 관리형 데이터베이스로 작업이 더 간단해지기 때문에 Pinecone을 선택했습니다. 그들의 스타터 플랜은 나에게 필요한 모든 데이터를 처리할 수 있는 능력을 갖추고 있습니다.


Pinecone 계정을 생성하고 API 키와 환경을 검색한 후 두 값을 모두 .env 파일에 추가합니다.

Pinecone API 키 생성 스크린샷

이제 .env Pinecone 및 OpenAI 비밀이 포함되어야 합니다.

 export OPENAI_API_KEY=YOUR_API_KEY # Pinecone secrets export PINECONE_API_KEY=YOUR_API_KEY export PINECONE_ENVIRONMENT=YOUR_PINECONE_DATACENTER

그런 다음 Python용 Pinecone 클라이언트를 설치합니다.

 $ pip install pinecone-client

데이터베이스를 초기화해야 합니다. db_create.py 스크립트의 내용은 다음과 같습니다.

 # db_create.py import pinecone import openai import os index_name = "semaphore" embed_model = "text-embedding-ada-002" api_key = os.getenv("PINECONE_API_KEY") env = os.getenv("PINECONE_ENVIRONMENT") pinecone.init(api_key=api_key, environment=env) embedding = openai.Embedding.create( input=[ "Sample document text goes here", "there will be several phrases in each batch" ], engine=embed_model ) if index_name not in pinecone.list_indexes(): print("Creating pinecone index: " + index_name) pinecone.create_index( index_name, dimension=len(embedding['data'][0]['embedding']), metric='cosine', metadata_config={'indexed': ['source', 'id']} )

스크립트가 데이터베이스를 생성하는 데 몇 분 정도 걸릴 수 있습니다.

 $ python db_create.py

다음으로 tiktoken 패키지를 설치하겠습니다. 이를 사용하여 소스 문서에 있는 토큰 수를 계산하겠습니다. 임베딩 모델은 최대 8191개의 토큰만 처리할 수 있기 때문에 이는 중요합니다.

 $ pip install tiktoken

패키지를 설치하는 동안 tqdm 도 설치하여 멋진 진행률 표시줄을 생성해 보겠습니다.

 $ pip install tqdm

이제 문서를 데이터베이스에 업로드해야 합니다. 이에 대한 스크립트는 index_docs.py 라고 합니다. 필요한 모듈을 가져오고 몇 가지 상수를 정의하는 것부터 시작해 보겠습니다.

 # index_docs.py # Pinecone db name and upload batch size index_name = 'semaphore' upsert_batch_size = 20 # OpenAI embedding and tokenizer models embed_model = "text-embedding-ada-002" encoding_model = "cl100k_base" max_tokens_model = 8191

다음으로 토큰을 계산하는 함수가 필요합니다. OpenAI 페이지에는 토큰 카운터 예제가 있습니다.

 import tiktoken def num_tokens_from_string(string: str) -> int: """Returns the number of tokens in a text string.""" encoding = tiktoken.get_encoding(encoding_model) num_tokens = len(encoding.encode(string)) return num_tokens

마지막으로 원본 문서를 사용 가능한 예제로 변환하려면 몇 가지 필터링 기능이 필요합니다. 문서의 대부분의 예는 코드 펜스 사이에 있으므로 모든 파일에서 모든 YAML 코드를 추출하겠습니다.

 import re def extract_yaml(text: str) -> str: """Returns list with all the YAML code blocks found in text.""" matches = [m.group(1) for m in re.finditer("```yaml([\w\W]*?)```", text)] return matches

이제 기능이 끝났습니다. 다음으로 메모리에 파일을 로드하고 예제를 추출합니다.

 from tqdm import tqdm import sys import os import pathlib repo_path = sys.argv[1] repo_path = os.path.abspath(repo_path) repo = pathlib.Path(repo_path) markdown_files = list(repo.glob("**/*.md")) + list( repo.glob("**/*.mdx") ) print(f"Extracting YAML from Markdown files in {repo_path}") new_data = [] for i in tqdm(range(0, len(markdown_files))): markdown_file = markdown_files[i] with open(markdown_file, "r") as f: relative_path = markdown_file.relative_to(repo_path) text = str(f.read()) if text == '': continue yamls = extract_yaml(text) j = 0 for y in yamls: j = j+1 new_data.append({ "source": str(relative_path), "text": y, "id": f"github.com/semaphore/docs/{relative_path}[{j}]" })

이 시점에서 모든 YAML은 new_data 목록에 저장되어야 합니다. 마지막 단계는 임베딩을 Pinecone에 업로드하는 것입니다.

 import pinecone import openai api_key = os.getenv("PINECONE_API_KEY") env = os.getenv("PINECONE_ENVIRONMENT") pinecone.init(api_key=api_key, enviroment=env) index = pinecone.Index(index_name) print(f"Creating embeddings and uploading vectors to database") for i in tqdm(range(0, len(new_data), upsert_batch_size)): i_end = min(len(new_data), i+upsert_batch_size) meta_batch = new_data[i:i_end] ids_batch = [x['id'] for x in meta_batch] texts = [x['text'] for x in meta_batch] embedding = openai.Embedding.create(input=texts, engine=embed_model) embeds = [record['embedding'] for record in embedding['data']] # clean metadata before upserting meta_batch = [{ 'id': x['id'], 'text': x['text'], 'source': x['source'] } for x in meta_batch] to_upsert = list(zip(ids_batch, embeds, meta_batch)) index.upsert(vectors=to_upsert)

참고로 데모 저장소에서 전체 index_docs.py 파일을 찾을 수 있습니다.

데이터베이스 설정을 완료하기 위해 인덱스 스크립트를 실행해 보겠습니다.

 $ git clone https://github.com/semaphoreci/docs.git /tmp/docs $ source .env $ python index_docs.py /tmp/docs

데이터베이스 테스트

Pinecone 대시보드에는 데이터베이스의 벡터가 표시되어야 합니다.

총 79개의 벡터가 포함된 데이터베이스를 보여주는 Pinecone 대시보드 스크린샷

스크립트로 실행하거나 Python REPL에서 직접 실행할 수 있는 다음 코드를 사용하여 데이터베이스를 쿼리할 수 있습니다.

 $ python >>> import os >>> import pinecone >>> import openai # Compute embeddings for string "Docker Container" >>> embeddings = openai.Embedding.create(input="Docker Containers", engine="text-embedding-ada-002") # Connect to database >>> index_name = "semaphore" >>> api_key = os.getenv("PINECONE_API_KEY") >>> env = os.getenv("PINECONE_ENVIRONMENT") >>> pinecone.init(api_key=api_key, environment=env) >>> index = pinecone.Index(index_name) # Query database >>> matches = index.query(embeddings['data'][0]['embedding'], top_k=1, include_metadata=True) >>> matches['matches'][0] {'id': 'github.com/semaphore/docs/docs/ci-cd-environment/docker-authentication.md[3]', 'metadata': {'id': 'github.com/semaphore/docs/docs/ci-cd-environment/docker-authentication.md[3]', 'source': 'docs/ci-cd-environment/docker-authentication.md', 'text': '\n' '# .semaphore/semaphore.yml\n' 'version: v1.0\n' 'name: Using a Docker image\n' 'agent:\n' ' machine:\n' ' type: e1-standard-2\n' ' os_image: ubuntu1804\n' '\n' 'blocks:\n' ' - name: Run container from Docker Hub\n' ' task:\n' ' jobs:\n' ' - name: Authenticate docker pull\n' ' commands:\n' ' - checkout\n' ' - echo $DOCKERHUB_PASSWORD | docker login ' '--username "$DOCKERHUB_USERNAME" --password-stdin\n' ' - docker pull /\n' ' - docker images\n' ' - docker run /\n' ' secrets:\n' ' - name: docker-hub\n'}, 'score': 0.796259582, 'values': []}

보시다시피 첫 번째 일치 항목은 Docker 이미지를 가져와 실행하는 세마포어 파이프라인에 대한 YAML입니다. "Docker Containers" 검색 문자열과 관련이 있으므로 좋은 시작입니다.

봇 구축

우리는 데이터를 갖고 있으며 이를 쿼리하는 방법을 알고 있습니다. 봇에서 작동하도록 합시다.

프롬프트 처리 단계는 다음과 같습니다.

  1. 사용자의 프롬프트를 받아들입니다.
  2. 벡터를 계산합니다.
  3. 데이터베이스에서 관련 컨텍스트를 검색합니다.
  4. 사용자의 메시지를 컨텍스트와 함께 GPT-3으로 보냅니다.
  5. 모델의 응답을 사용자에게 전달합니다.

봇의 데이터 흐름 다이어그램 왼쪽에는 사용자 프롬프트가 입력되고, 이는 내장 신경망에 의해 처리된 후 컨텍스트 데이터베이스로 전송됩니다. 검색을 통해 GPT-3 모델로 전송되는 관련 텍스트가 생성됩니다. 모델의 출력은 최종 답변으로 사용자에게 전송됩니다. 평소와 같이 봇의 기본 스크립트 complete.py 에 몇 가지 상수를 정의하는 것부터 시작하겠습니다.

 # complete.py # Pinecone database name, number of matched to retrieve # cutoff similarity score, and how much tokens as context index_name = 'semaphore' context_cap_per_query = 30 match_min_score = 0.75 context_tokens_per_query = 3000 # OpenAI LLM model parameters chat_engine_model = "gpt-3.5-turbo" max_tokens_model = 4096 temperature = 0.2 embed_model = "text-embedding-ada-002" encoding_model_messages = "gpt-3.5-turbo-0301" encoding_model_strings = "cl100k_base" import pinecone import os # Connect with Pinecone db and index api_key = os.getenv("PINECONE_API_KEY") env = os.getenv("PINECONE_ENVIRONMENT") pinecone.init(api_key=api_key, environment=env) index = pinecone.Index(index_name)

다음으로 OpenAI 예제 에 표시된 대로 토큰을 계산하는 함수를 추가하겠습니다. 첫 번째 함수는 문자열의 토큰 수를 계산하고, 두 번째 함수는 메시지의 토큰 수를 계산합니다. 잠시 후 메시지를 자세히 살펴보겠습니다. 지금은 대화의 상태를 기억에 유지하는 구조라고만 하자.

 import tiktoken def num_tokens_from_string(string: str) -> int: """Returns the number of tokens in a text string.""" encoding = tiktoken.get_encoding(encoding_model_strings) num_tokens = len(encoding.encode(string)) return num_tokens def num_tokens_from_messages(messages): """Returns the number of tokens used by a list of messages. Compatible with model """ try: encoding = tiktoken.encoding_for_model(encoding_model_messages) except KeyError: encoding = tiktoken.get_encoding(encoding_model_strings) num_tokens = 0 for message in messages: num_tokens += 4 # every message follows {role/name}\n{content}\n for key, value in message.items(): num_tokens += len(encoding.encode(value)) if key == "name": # if there's a name, the role is omitted num_tokens += -1 # role is always required and always 1 token num_tokens += 2 # every reply is primed with assistant return num_tokens

다음 함수는 원본 프롬프트와 컨텍스트 문자열을 사용하여 GPT-3에 대한 강화된 프롬프트를 반환합니다.

 def get_prompt(query: str, context: str) -> str: """Return the prompt with query and context.""" return ( f"Create the continuous integration pipeline YAML code to fullfil the requested task.\n" + f"Below you will find some context that may help. Ignore it if it seems irrelevant.\n\n" + f"Context:\n{context}" + f"\n\nTask: {query}\n\nYAML Code:" )

get_message 함수는 API와 호환되는 형식으로 프롬프트 형식을 지정합니다.

 def get_message(role: str, content: str) -> dict: """Generate a message for OpenAI API completion.""" return {"role": role, "content": content}

모델이 반응하는 방식에 영향을 미치는 역할에는 세 가지 유형이 있습니다.

  • User : 사용자의 원래 프롬프트용입니다.
  • 시스템 : 어시스턴트의 동작을 설정하는 데 도움이 됩니다. 효율성에 대해서는 논란의 여지가 있지만 메시지 목록의 마지막에 보내는 것이 더 효과적인 것으로 보입니다.
  • Assistant : 모델의 과거 응답을 나타냅니다. OpenAI API에는 "메모리"가 없습니다. 대신 대화를 유지하려면 각 상호 작용 중에 모델의 이전 응답을 다시 보내야 합니다.


이제 매력적인 부분입니다. get_context 함수는 프롬프트를 사용하여 데이터베이스를 쿼리하고 다음 조건 중 하나가 충족될 때까지 컨텍스트 문자열을 생성합니다.

  • 전체 텍스트가 컨텍스트를 위해 예약한 공간인 context_tokens_per_query 초과합니다.
  • 검색 기능은 요청된 모든 일치 항목을 검색합니다.
  • match_min_score 미만의 유사성 점수가 있는 일치 항목은 무시됩니다.
 import openai def get_context(query: str, max_tokens: int) -> list: """Generate message for OpenAI model. Add context until hitting `context_token_limit` limit. Returns prompt string.""" embeddings = openai.Embedding.create( input=[query], engine=embed_model ) # search the database vectors = embeddings['data'][0]['embedding'] embeddings = index.query(vectors, top_k=context_cap_per_query, include_metadata=True) matches = embeddings['matches'] # filter and aggregate context usable_context = "" context_count = 0 for i in range(0, len(matches)): source = matches[i]['metadata']['source'] if matches[i]['score'] < match_min_score: # skip context with low similarity score continue context = matches[i]['metadata']['text'] token_count = num_tokens_from_string(usable_context + '\n---\n' + context) if token_count < context_tokens_per_query: usable_context = usable_context + '\n---\n' + context context_count = context_count + 1 print(f"Found {context_count} contexts for your query") return usable_context

다음이자 마지막 함수인 complete OpenAI에 API 요청을 보내고 모델의 응답을 반환합니다.

 def complete(messages): """Query the OpenAI model. Returns the first answer. """ res = openai.ChatCompletion.create( model=chat_engine_model, messages=messages, temperature=temperature ) return res.choices[0].message.content.strip()

그게 다야; 이제 명령줄 인수만 처리하고 올바른 순서로 함수를 호출하면 됩니다.

 import sys query = sys.argv[1] context = get_context(query, context_tokens_per_query) prompt = get_prompt(query, context) # initialize messages list to send to OpenAI API messages = [] messages.append(get_message('user', prompt)) messages.append(get_message('system', 'You are a helpful assistant that writes YAML code for Semaphore continuous integration pipelines and explains them. Return YAML code inside code fences.')) if num_tokens_from_messages(messages) >= max_tokens_model: raise Exception('Model token size limit reached') print("Working on your query... ") answer = complete(messages) print("Answer:\n") print(answer) messages.append(get_message('assistant', answer))

이제 스크립트를 실행하고 결과가 어떻게 되는지 살펴보겠습니다.

 $ python complete.py "Create a CI pipeline that builds and uploads a Docker image to Docker Hub"

결과는 다음과 같습니다.

 version: v1.0 name: Docker Build and Push agent: machine: type: e1-standard-2 os_image: ubuntu1804 blocks: - name: "Build and Push Docker Image" task: jobs: - name: "Docker Build and Push" commands: - checkout - docker build -t /: . - echo "$DOCKERHUB_PASSWORD" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin - docker push /: promotions: - name: Deploy to production pipeline_file: deploy-production.yml auto_promote: when: "result = 'passed' and branch = 'master'"

이것이 첫 번째 좋은 결과입니다. 모델은 우리가 제공한 컨텍스트 예제에서 구문을 추론했습니다.

봇의 기능 확장에 대한 생각

제가 YAML 파이프라인을 작성하기 위한 도우미를 만드는 작은 목표로 시작했다는 것을 기억하세요. 내 벡터 데이터베이스에 더 풍부한 콘텐츠가 있으면 봇을 일반화하여 세마포어(또는 모든 제품 - 문서를 /tmp ?에 복제한 것을 기억하시나요?)에 대한 모든 질문에 답할 수 있습니다.


좋은 답변을 얻는 열쇠는 당연히 품질 컨텍스트입니다. 단순히 모든 문서를 벡터 데이터베이스에 업로드하는 것만으로는 좋은 결과를 얻을 수 없습니다. 컨텍스트 데이터베이스는 선별되고, 설명적인 메타데이터로 태그가 지정되고, 간결해야 합니다. 그렇지 않으면 관련 없는 컨텍스트로 프롬프트의 토큰 할당량을 채울 위험이 있습니다.


따라서 어떤 의미에서 우리의 요구 사항을 충족하도록 봇을 미세 조정하는 데는 기술과 많은 시행착오가 필요합니다. 유사성 점수를 조정하여 컨텍스트 제한을 실험하고, 품질이 낮은 콘텐츠를 제거하고, 요약하고, 관련 없는 컨텍스트를 필터링할 수 있습니다.

적절한 챗봇 구현

내 봇을 사용하면 ChatGPT와 같은 실제 대화가 가능하지 않다는 점을 눈치채셨을 것입니다. 우리는 하나의 질문을 하고 하나의 답을 얻습니다.


봇을 완전한 기능을 갖춘 챗봇으로 전환하는 것은 원칙적으로 그리 어렵지 않습니다. 각 API 요청과 함께 모델에 이전 응답을 다시 보내 대화를 유지할 수 있습니다. 이전 GPT-3 답변은 '보조' 역할로 다시 전송됩니다. 예를 들어:

 messages = [] while True: query = input('Type your prompt:\n') context = get_context(query, context_tokens_per_query) prompt = get_prompt(query, context) messages.append(get_message('user', prompt)) messages.append(get_message('system', 'You are a helpful assistant that writes YAML code for Semaphore continuous integration pipelines and explains them. Return YAML code inside code fences.')) if num_tokens_from_messages(messages) >= max_tokens_model: raise Exception('Model token size limit reached') print("Working on your query... ") answer = complete(messages) print("Answer:\n") print(answer) # remove system message and append model's answer messages.pop() messages.append(get_message('assistant', answer))

불행하게도 이 구현은 다소 초보적입니다. 각 상호작용마다 토큰 수가 증가하므로 확장된 대화는 지원되지 않습니다. 머지않아 GPT-3의 4096개 토큰 제한에 도달하여 더 이상의 대화가 불가능해질 것입니다.


따라서 요청을 토큰 제한 내에서 유지하는 방법을 찾아야 합니다. 몇 가지 전략은 다음과 같습니다.

  • 오래된 메시지를 삭제하세요. 이것이 가장 간단한 해결책이지만 대화의 "기억"을 가장 최근 메시지로만 제한합니다.
  • 이전 메시지를 요약합니다. "모델에게 질문하기"를 활용하여 이전 메시지를 압축하고 이를 원래의 질문과 답변으로 대체할 수 있습니다. 이 접근 방식은 비용이 증가하고 쿼리 간 지연이 증가하지만 단순히 과거 메시지를 삭제하는 것보다 우수한 결과를 얻을 수 있습니다.
  • 상호 작용 수에 엄격한 제한을 설정하십시오.
  • 더 스마트할 뿐만 아니라 토큰 용량이 두 배인 GPT-4 API 일반 가용성을 기다리세요.
  • 최대 16,000개의 토큰을 처리할 수 있는 "gpt-3.5-turbo-16k"와 같은 최신 모델을 사용하세요.

결론

단어 임베딩과 좋은 컨텍스트 데이터베이스를 사용하면 봇의 응답을 향상시킬 수 있습니다. 이를 달성하려면 양질의 문서가 필요합니다. 주제를 파악하고 있는 것처럼 보이는 봇을 개발하는 데에는 상당한 양의 시행착오가 있습니다.


단어 임베딩 및 대규모 언어 모델에 대한 심층적인 탐색이 귀하의 요구 사항에 맞게 맞춤화된 더욱 강력한 봇을 구축하는 데 도움이 되기를 바랍니다.


행복한 건물!


여기에도 게시되었습니다.