paint-brush
Word Embeddings: Nước sốt bí mật để đưa ra bối cảnh ChatBot của bạn để có câu trả lời tốt hơntừ tác giả@tomfernblog
2,991 lượt đọc
2,991 lượt đọc

Word Embeddings: Nước sốt bí mật để đưa ra bối cảnh ChatBot của bạn để có câu trả lời tốt hơn

từ tác giả Tomas Fernandez18m2023/07/26
Read on Terminal Reader

dài quá đọc không nổi

Tìm hiểu cách xây dựng một bot chuyên nghiệp bằng cách sử dụng tính năng nhúng từ và ChatGPT. Tận dụng sức mạnh của vectơ từ để nâng cao phản hồi của chatbot của bạn.
featured image - Word Embeddings: Nước sốt bí mật để đưa ra bối cảnh ChatBot của bạn để có câu trả lời tốt hơn
Tomas Fernandez HackerNoon profile picture
0-item
1-item

Không còn nghi ngờ gì nữa, ChatGPT của OpenAI đặc biệt thông minh — nó đã vượt qua bài kiểm tra của luật sư , nó sở hữu kiến thức tương đương với bác sĩ và một số bài kiểm tra đã cho chỉ số IQ của nó là 155 . Tuy nhiên, nó có xu hướng bịa đặt thông tin thay vì thừa nhận sự thiếu hiểu biết. Xu hướng này, cùng với thực tế là kiến thức của nó sẽ ngừng hoạt động vào năm 2021, đặt ra những thách thức trong việc xây dựng các sản phẩm chuyên biệt sử dụng API GPT.


Làm thế nào chúng ta có thể vượt qua những trở ngại này? Làm cách nào chúng tôi có thể truyền đạt kiến thức mới cho một mô hình như GPT-3? Mục tiêu của tôi là giải quyết những câu hỏi này bằng cách xây dựng bot trả lời câu hỏi sử dụng Python, API OpenAI và nhúng từ.

Những gì tôi sẽ xây dựng

Tôi dự định tạo một bot tạo ra các quy trình tích hợp liên tục từ một lời nhắc, như bạn có thể biết, được định dạng bằng YAML trong Semaphore CI/CD.


Dưới đây là một ví dụ về bot đang hoạt động:

Ảnh chụp màn hình của chương trình đang chạy. Trên màn hình, lệnh được thực thi: python query.py "Tạo đường dẫn CI xây dựng và tải hình ảnh Docker lên Docker Hub" và chương trình in ra YAML tương ứng với đường dẫn CI thực hiện hành động được yêu cầu. Ảnh chụp màn hình của chương trình đang chạy. Trên màn hình, lệnh python query.py "Create a CI pipeline that builds and uploads a Docker image to Docker Hub" được thực thi và chương trình in ra YAML tương ứng với đường dẫn CI thực hiện hành động được yêu cầu.


Theo tinh thần của các dự án như DocsGPT , My AskAILibraria , tôi dự định "dạy" mô hình GPT-3 về Semaphore và cách tạo tệp cấu hình đường ống. Tôi sẽ đạt được điều này bằng cách tận dụng các tài liệu hiện có .


Tôi sẽ không cho rằng mình đã có kiến thức trước về xây dựng bot và sẽ duy trì mã sạch để bạn có thể điều chỉnh mã theo yêu cầu của mình.

điều kiện tiên quyết

Bạn không cần có kinh nghiệm viết mã bot hoặc kiến thức về mạng thần kinh để làm theo hướng dẫn này. Tuy nhiên, bạn sẽ cần:


Nhưng ChatGPT không thể học được phải không?

ChatGPT, hay chính xác hơn là GPT-3 và GPT-4, các Mô hình ngôn ngữ lớn (LLM) cung cấp năng lượng cho chúng, đã được đào tạo trên một tập dữ liệu lớn với ngày giới hạn vào khoảng tháng 9 năm 2021.


Về bản chất, GPT-3 biết rất ít về các sự kiện sau ngày đó. Chúng tôi có thể xác minh điều này bằng một lời nhắc đơn giản:

Ảnh chụp màn hình của ChatGPT ChatGPT không biết đội nào vô địch World Cup 2022.


Mặc dù một số mô hình OpenAI có thể trải qua quá trình tinh chỉnh , nhưng các mô hình cao cấp hơn, chẳng hạn như những mô hình được quan tâm, thì không thể; chúng tôi không thể tăng cường dữ liệu đào tạo của họ.


Làm cách nào chúng tôi có thể nhận được câu trả lời từ GPT-3 ngoài dữ liệu đào tạo của nó? Một phương pháp liên quan đến việc khai thác khả năng hiểu văn bản của nó; bằng cách tăng cường lời nhắc với ngữ cảnh phù hợp, chúng ta có thể có được câu trả lời chính xác.


Trong ví dụ bên dưới, tôi cung cấp ngữ cảnh từ trang web chính thức của FIFA và phản hồi có sự khác biệt đáng kể:

Nỗ lực thứ hai để trả lời câu hỏi Với ngữ cảnh được cung cấp, ChatGPT có thể trả lời chính xác.


Chúng ta có thể suy luận rằng mô hình có thể phản hồi bất kỳ lời nhắc nào nếu được cung cấp đủ ngữ cảnh phù hợp. Câu hỏi vẫn là: làm thế nào chúng ta có thể biết điều gì có liên quan khi được nhắc tùy ý? Để giải quyết vấn đề này, chúng ta cần khám phá nhúng từ là gì.

Nhúng từ là gì?

Trong ngữ cảnh của các mô hình ngôn ngữ, nhúng là một cách biểu diễn các từ, câu hoặc toàn bộ tài liệu dưới dạng vectơ hoặc danh sách số.


Để tính toán các lần nhúng, chúng ta sẽ cần một mạng thần kinh chẳng hạn như word2vec hoặc text-embedding-ada-002 . Các mạng này đã được đào tạo trên một lượng lớn văn bản và có thể tìm thấy mối quan hệ giữa các từ bằng cách phân tích tần suất xuất hiện của các mẫu cụ thể trong dữ liệu đào tạo.


Giả sử chúng ta có các từ sau:

  • Con mèo
  • Chó
  • Quả bóng
  • Căn nhà


Hãy tưởng tượng chúng ta sử dụng một trong những mạng nhúng này để tính toán các vectơ cho mỗi từ. Ví dụ:

Từ

véc tơ

Bối cảnh

Con mèo

[0,1, 0,2, 0,3, 0,4, 0,5]

Động vật, đồ vật, đồ vật nhỏ

Chó

[0,6, 0,7, 0,8, 0,9, 1,0]

Động vật, đồ vật, vật lớn

Quả bóng

[0,2, 0,4, 0,6, 0,8, 1,0]

Đồ vật, đồ chơi, đồ vật nhỏ

Căn nhà

[0,3, 0,6, 0,9, 1,2, 1,5]

Tòa nhà, nhà ở, những thứ lớn

Khi chúng ta có các vectơ cho mỗi từ, chúng ta có thể sử dụng chúng để biểu thị ý nghĩa của văn bản. Ví dụ: câu “Con mèo đuổi theo quả bóng” có thể được biểu diễn dưới dạng vectơ [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]. Vectơ này đại diện cho một câu nói về một con vật đang đuổi theo một vật thể.


Các nhúng từ có thể được hình dung dưới dạng không gian đa chiều trong đó các từ hoặc câu có nghĩa tương tự gần nhau. Chúng ta có thể tính toán "khoảng cách" giữa các vectơ để tìm ý nghĩa tương tự cho bất kỳ văn bản đầu vào nào.

Ba biểu diễn ba chiều của vectơ. Cái đầu tiên được gắn nhãn 'Nam-Nữ' và có các điểm dữ liệu nam-nữ và vua-nữ hoàng, cái thứ hai được gắn nhãn 'Động từ-Thì' và có các động từ như đi bộ-đi bộ bơi-swam. Cái cuối cùng được gắn nhãn 'Quốc gia-Thủ đô' và có một số thủ đô được kết nối với các quốc gia của họ Biểu diễn 3D của các phần nhúng dưới dạng không gian vectơ. Trong thực tế, những không gian này có thể có hàng trăm hoặc hàng nghìn chiều. Nguồn: Gặp gỡ Multitool của AI: Vector Embeddings


Toán học thực tế đằng sau tất cả những điều này nằm ngoài phạm vi của bài viết này. Tuy nhiên, điều quan trọng cần rút ra là các phép toán vectơ cho phép chúng ta thao tác hoặc xác định ý nghĩa bằng toán học . Lấy vectơ đại diện cho từ “nữ hoàng”, trừ vectơ “phụ nữ” khỏi từ đó và thêm vectơ “đàn ông”. Kết quả phải là một vectơ trong vùng lân cận của “vua”. Nếu chúng ta thêm "con trai", chúng ta sẽ đến một nơi nào đó gần với "hoàng tử".

Nhúng mạng thần kinh với mã thông báo

Cho đến nay, chúng ta đã thảo luận về việc nhúng các mạng thần kinh lấy từ làm đầu vào và số làm đầu ra. Tuy nhiên, nhiều mạng hiện đại đã chuyển từ xử lý từ sang xử lý mã thông báo.


thông báo là đơn vị văn bản nhỏ nhất mà mô hình có thể xử lý. Mã thông báo có thể là từ, ký tự, dấu chấm câu, ký hiệu hoặc một phần của từ.


Chúng ta có thể xem cách các từ được chuyển đổi thành mã thông báo bằng cách thử nghiệm với trình mã thông báo trực tuyến OpenAI , sử dụng Mã hóa cặp byte (BPE) để chuyển đổi văn bản thành mã thông báo và đại diện cho mỗi mã bằng một số:

Ảnh chụp màn hình của mã thông báo OpenAI. Một số văn bản đã được nhập và mỗi mã thông báo được thể hiện bằng các màu khác nhau, cho phép chúng tôi xem cách các từ được ánh xạ tới mã thông báo. Văn bản có nội dung: Đằng sau bất kỳ mô hình nhúng nào, có một mạng lưới thần kinh chuyển đổi văn bản đầu vào thành các vectơ. Mỗi loại mô hình nhúng có khả năng và tốc độ khác nhau. Chẳng hạn, Word2vec nhận các từ và tạo các vectơ trong phạm vi từ 100 đến 300 chiều. Thường có mối quan hệ 1-1 giữa mã thông báo và từ. Hầu hết các mã thông báo bao gồm từ và khoảng trắng ở đầu. Tuy nhiên, có những trường hợp đặc biệt như "nhúng", bao gồm hai mã thông báo, "nhúng" và "ding" hoặc "khả năng", bao gồm bốn mã thông báo. Nếu bạn nhấp vào "ID mã thông báo", bạn có thể thấy biểu thị số của mô hình cho từng mã thông báo.

Thiết kế Bot thông minh hơn bằng cách sử dụng nhúng

Bây giờ chúng ta đã hiểu nhúng là gì, câu hỏi tiếp theo là: làm thế nào chúng có thể giúp chúng ta xây dựng một bot thông minh hơn?


Trước tiên, hãy xem xét điều gì xảy ra khi chúng ta sử dụng trực tiếp API GPT-3. Người dùng đưa ra lời nhắc và mô hình phản hồi với khả năng tốt nhất của nó.

Sơ đồ hiển thị tương tác giữa người dùng và GPT-3. Người dùng gửi lời nhắc, mô hình phản hồi.


Tuy nhiên, khi chúng ta thêm bối cảnh vào phương trình, mọi thứ sẽ thay đổi. Ví dụ: khi tôi hỏi ChatGPT về đội vô địch World Cup sau khi cung cấp ngữ cảnh, nó đã tạo nên sự khác biệt.


Vì vậy, kế hoạch xây dựng một bot thông minh hơn như sau:

  1. Chặn lời nhắc của người dùng.
  2. Tính toán các phần nhúng cho dấu nhắc đó, tạo ra một vectơ.
  3. Tìm kiếm cơ sở dữ liệu cho các tài liệu gần vectơ, vì chúng phải phù hợp về mặt ngữ nghĩa với dấu nhắc ban đầu.
  4. Gửi lời nhắc ban đầu tới GPT-3, cùng với mọi ngữ cảnh có liên quan.
  5. Chuyển tiếp phản hồi của GPT-3 cho người dùng.

Một triển khai bot phức tạp hơn. Người dùng gửi lời nhắc đến ứng dụng chatbot, ứng dụng này sẽ tìm kiếm cơ sở dữ liệu ngữ cảnh và sử dụng cơ sở dữ liệu đó để làm phong phú thêm lời nhắc. Lời nhắc được gửi tới GPT-3 và phản hồi của nó được chuyển tiếp tới người dùng.


Hãy bắt đầu giống như hầu hết các dự án, bằng cách thiết kế cơ sở dữ liệu.

Tạo cơ sở dữ liệu tri thức với nhúng

Cơ sở dữ liệu ngữ cảnh của chúng tôi phải bao gồm tài liệu gốc và các vectơ tương ứng của chúng. Về nguyên tắc, chúng ta có thể sử dụng bất kỳ loại cơ sở dữ liệu nào cho nhiệm vụ này, nhưng cơ sở dữ liệu vectơ là công cụ tối ưu cho công việc này.


Cơ sở dữ liệu vectơ là cơ sở dữ liệu chuyên biệt được thiết kế để lưu trữ và truy xuất dữ liệu vectơ chiều cao. Thay vì sử dụng một ngôn ngữ truy vấn như SQL để tìm kiếm, chúng tôi cung cấp một vectơ và yêu cầu N hàng xóm gần nhất.


Để tạo các vectơ, chúng tôi sẽ sử dụng text-embedding-ada-002 từ OpenAI, vì đây là mô hình nhanh nhất và tiết kiệm chi phí nhất mà họ cung cấp. Mô hình chuyển đổi văn bản đầu vào thành mã thông báo và sử dụng cơ chế chú ý được gọi là Transformer để tìm hiểu mối quan hệ của chúng. Đầu ra của mạng thần kinh này là các vectơ biểu thị ý nghĩa của văn bản.

Sơ đồ minh họa quá trình token hóa. Một tài liệu được mã hóa và sau đó được gửi đến một mạng thần kinh nhúng. Đầu ra của mạng là một vectơ.


Để tạo một cơ sở dữ liệu ngữ cảnh, tôi sẽ:

  1. Thu thập tất cả các tài liệu nguồn.
  2. Lọc bỏ những tài liệu không liên quan.
  3. Tính toán các nhúng cho mỗi tài liệu.
  4. Lưu trữ các vectơ, văn bản gốc và bất kỳ siêu dữ liệu có liên quan nào khác trong cơ sở dữ liệu.

Sơ đồ minh họa quá trình lưu trữ dữ liệu trong cơ sở dữ liệu bối cảnh. Tài liệu nguồn được gửi đến mạng thần kinh nhúng. Cơ sở dữ liệu lưu trữ vectơ cùng với văn bản gốc.

Chuyển đổi tài liệu thành vectơ

Trước tiên, tôi phải khởi tạo tệp môi trường bằng khóa API OpenAI. Tệp này không bao giờ được cam kết kiểm soát phiên bản vì khóa API là riêng tư và được liên kết với tài khoản của bạn.

 export OPENAI_API_KEY=YOUR_API_KEY

Tiếp theo, tôi sẽ tạo một virtualenv cho ứng dụng Python của mình:

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

Và cài đặt gói OpenAI:

 ```bash $ pip install openai numpy

Hãy thử tính toán nhúng cho chuỗi "Docker Container". Bạn có thể chạy cái này trên Python REPL hoặc dưới dạng tập lệnh 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 } }

Như bạn có thể thấy, mô hình của OpenAI phản hồi bằng một danh sách embedding chứa 1536 mục — kích thước vectơ cho text-embedding-ada-002.

Lưu trữ các nhúng trong Pinecone

Mặc dù có nhiều công cụ cơ sở dữ liệu vectơ để lựa chọn, chẳng hạn như Chroma là mã nguồn mở, nhưng tôi đã chọn Pinecone vì đây là cơ sở dữ liệu được quản lý với một bậc miễn phí, giúp mọi thứ trở nên đơn giản hơn. Gói Starter của họ thừa khả năng xử lý tất cả dữ liệu tôi cần.


Sau khi tạo tài khoản Pinecone và truy xuất khóa API cũng như môi trường của mình, tôi thêm cả hai giá trị vào tệp .env của mình.

Ảnh chụp màn hình tạo khóa API Pinecone

Bây giờ .env sẽ chứa các bí mật Pinecone và OpenAI của tôi.

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

Sau đó, tôi cài đặt ứng dụng khách Pinecone cho Python:

 $ pip install pinecone-client

Tôi cần khởi tạo cơ sở dữ liệu; đây là nội dung của tập lệnh 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']} )

Tập lệnh có thể mất vài phút để tạo cơ sở dữ liệu.

 $ python db_create.py

Tiếp theo, tôi sẽ cài đặt gói tiktoken . Tôi sẽ sử dụng nó để tính xem tài liệu nguồn có bao nhiêu mã thông báo. Điều này rất quan trọng vì mô hình nhúng chỉ có thể xử lý tối đa 8191 mã thông báo.

 $ pip install tiktoken

Trong khi cài đặt các gói, chúng ta cũng hãy cài đặt tqdm để tạo ra một thanh tiến trình đẹp mắt.

 $ pip install tqdm

Bây giờ tôi cần tải tài liệu lên cơ sở dữ liệu. Tập lệnh cho điều này sẽ được gọi là index_docs.py . Hãy bắt đầu bằng cách nhập các mô-đun cần thiết và xác định một số hằng số:

 # 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

Tiếp theo, chúng ta sẽ cần một chức năng để đếm mã thông báo. Có một ví dụ về bộ đếm mã thông báo trên trang 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

Cuối cùng, tôi sẽ cần một số chức năng lọc để chuyển đổi tài liệu gốc thành các ví dụ có thể sử dụng được. Hầu hết các ví dụ trong tài liệu nằm giữa các hàng rào mã, vì vậy tôi sẽ chỉ trích xuất tất cả mã YAML từ mọi tệp:

 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

Tôi đã hoàn thành với các chức năng. Tiếp theo, thao tác này sẽ tải các tệp trong bộ nhớ và trích xuất các ví dụ:

 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}]" })

Tại thời điểm này, tất cả các YAML sẽ được lưu trữ trong danh sách new_data . Bước cuối cùng là tải các phần nhúng vào 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)

Để tham khảo, bạn có thể tìm thấy tệp index_docs.py đầy đủ trong kho lưu trữ demo

Hãy chạy tập lệnh chỉ mục để kết thúc quá trình thiết lập cơ sở dữ liệu:

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

Kiểm tra cơ sở dữ liệu

Bảng điều khiển Pinecone sẽ hiển thị các vectơ trong cơ sở dữ liệu.

Ảnh chụp màn hình bảng điều khiển Pinecone hiển thị cơ sở dữ liệu với tổng số 79 vectơ

Chúng ta có thể truy vấn cơ sở dữ liệu bằng mã sau, bạn có thể chạy mã này dưới dạng tập lệnh hoặc trực tiếp trong REPL của Python:

 $ 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': []}

Như bạn có thể thấy, đối sánh đầu tiên là YAML cho một đường dẫn Semaphore lấy một hình ảnh Docker và chạy nó. Đó là một khởi đầu tốt vì nó có liên quan đến chuỗi tìm kiếm "Docker Container" của chúng tôi.

Xây dựng bot

Chúng tôi có dữ liệu và chúng tôi biết cách truy vấn nó. Hãy để nó hoạt động trong bot.

Các bước để xử lý lời nhắc là:

  1. Lấy lời nhắc của người dùng.
  2. Tính vectơ của nó.
  3. Lấy bối cảnh có liên quan từ cơ sở dữ liệu.
  4. Gửi lời nhắc của người dùng cùng với ngữ cảnh tới GPT-3.
  5. Chuyển tiếp phản hồi của mô hình cho người dùng.

Sơ đồ luồng dữ liệu cho bot. Ở bên trái, dấu nhắc người dùng nhập, được xử lý bởi mạng thần kinh nhúng, sau đó được gửi đến cơ sở dữ liệu ngữ cảnh. Tìm kiếm mang lại văn bản có liên quan được gửi đến kiểu máy GPT-3. Đầu ra của mô hình được gửi đến người dùng dưới dạng câu trả lời cuối cùng. Như thường lệ, tôi sẽ bắt đầu bằng cách xác định một số hằng số trong complete.py , tập lệnh chính của bot:

 # 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)

Tiếp theo, tôi sẽ thêm các chức năng để đếm mã thông báo như trong các ví dụ về OpenAI . Hàm đầu tiên đếm mã thông báo trong một chuỗi, trong khi hàm thứ hai đếm mã thông báo trong tin nhắn. Chúng ta sẽ xem chi tiết các tin nhắn trong giây lát. Hiện tại, hãy coi đó là một cấu trúc lưu giữ trạng thái của cuộc hội thoại trong bộ nhớ.

 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

Hàm sau đây nhận lời nhắc ban đầu và chuỗi ngữ cảnh để trả về lời nhắc phong phú cho 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:" )

Hàm get_message định dạng lời nhắc ở định dạng tương thích với API:

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

Có ba loại vai trò ảnh hưởng đến cách mô hình phản ứng:

  • Người dùng : cho lời nhắc ban đầu của người dùng.
  • Hệ thống : giúp thiết lập hành vi của trợ lý. Mặc dù có một số tranh cãi về tính hiệu quả của nó, nhưng nó dường như hiệu quả hơn khi được gửi ở cuối danh sách tin nhắn.
  • Trợ lý : đại diện cho các phản hồi trong quá khứ của mô hình. API OpenAI không có "bộ nhớ"; thay vào đó, chúng tôi phải gửi lại các phản hồi trước đó của mô hình trong mỗi lần tương tác để duy trì cuộc trò chuyện.


Bây giờ cho phần hấp dẫn. Hàm get_context nhận lời nhắc, truy vấn cơ sở dữ liệu và tạo chuỗi ngữ cảnh cho đến khi đáp ứng một trong các điều kiện sau:

  • Văn bản hoàn chỉnh vượt quá context_tokens_per_query , không gian tôi dành riêng cho ngữ cảnh.
  • Chức năng tìm kiếm truy xuất tất cả các kết quả phù hợp được yêu cầu.
  • Các trận đấu có điểm tương đồng dưới match_min_score sẽ bị bỏ qua.
 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

Chức năng tiếp theo và chức năng cuối cùng, complete , đưa ra yêu cầu API tới OpenAI và trả về phản hồi của mô hình.

 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()

Đó là tất cả; bây giờ tôi chỉ phải xử lý các đối số dòng lệnh và gọi các hàm theo đúng thứ tự:

 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))

Đã đến lúc chạy tập lệnh và xem nó hoạt động như thế nào:

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

Kết quả là:

 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'"

Đây là kết quả tốt đầu tiên. Mô hình đã suy ra cú pháp từ các ví dụ ngữ cảnh mà chúng tôi đã cung cấp.

Suy nghĩ về việc mở rộng khả năng của Bot

Hãy nhớ rằng tôi đã bắt đầu với một mục tiêu khiêm tốn: tạo một trợ lý để viết các đường dẫn YAML. Với nội dung phong phú hơn trong cơ sở dữ liệu vectơ của tôi, tôi có thể khái quát hóa bot để trả lời bất kỳ câu hỏi nào về Semaphore (hoặc bất kỳ sản phẩm nào — hãy nhớ sao chép tài liệu vào /tmp ?).


Chìa khóa để có được câu trả lời hay là — không ngạc nhiên — ngữ cảnh chất lượng. Chỉ tải mọi tài liệu lên cơ sở dữ liệu vectơ không chắc sẽ mang lại kết quả tốt. Cơ sở dữ liệu ngữ cảnh phải được sắp xếp, gắn thẻ với siêu dữ liệu mô tả và ngắn gọn. Nếu không, chúng tôi có nguy cơ lấp đầy hạn ngạch mã thông báo trong lời nhắc với ngữ cảnh không liên quan.


Vì vậy, theo một nghĩa nào đó, có một nghệ thuật — và rất nhiều lần thử và sai — liên quan đến việc tinh chỉnh bot để đáp ứng nhu cầu của chúng ta. Chúng tôi có thể thử nghiệm giới hạn ngữ cảnh, xóa nội dung chất lượng thấp, tóm tắt và lọc ra ngữ cảnh không liên quan bằng cách điều chỉnh điểm tương đồng.

Triển khai một Chatbot phù hợp

Bạn có thể nhận thấy rằng bot của tôi không cho phép chúng tôi trò chuyện thực tế như ChatGPT. Chúng tôi hỏi một câu hỏi và nhận được một câu trả lời.


Về nguyên tắc, việc chuyển đổi bot thành một chatbot chính thức không quá khó. Chúng tôi có thể duy trì cuộc trò chuyện bằng cách gửi lại các phản hồi trước đó cho mô hình với mỗi yêu cầu API. Các câu trả lời trước về GPT-3 sẽ được gửi lại dưới vai trò "trợ lý". Ví dụ:

 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))

Thật không may, việc triển khai này khá thô sơ. Nó sẽ không hỗ trợ các cuộc hội thoại mở rộng khi số lượng mã thông báo tăng lên sau mỗi lần tương tác. Chẳng bao lâu nữa, chúng tôi sẽ đạt đến giới hạn 4096 mã thông báo cho GPT-3, điều này sẽ ngăn chặn các cuộc đối thoại tiếp theo.


Vì vậy, chúng tôi phải tìm một số cách để giữ yêu cầu trong giới hạn mã thông báo. Một vài chiến lược sau:

  • Xóa tin nhắn cũ hơn. Mặc dù đây là giải pháp đơn giản nhất nhưng nó giới hạn "bộ nhớ" của cuộc trò chuyện chỉ với những tin nhắn gần đây nhất.
  • Tổng hợp các tin nhắn trước đó. Chúng ta có thể sử dụng "Hỏi người mẫu" để cô đọng các thông điệp trước đó và thay thế chúng cho các câu hỏi và câu trả lời ban đầu. Mặc dù cách tiếp cận này làm tăng chi phí và độ trễ giữa các truy vấn, nhưng nó có thể tạo ra kết quả tốt hơn so với việc chỉ xóa các tin nhắn trước đây.
  • Đặt giới hạn nghiêm ngặt về số lượng tương tác.
  • Đợi tính khả dụng chung của API GPT-4, không chỉ thông minh hơn mà còn có dung lượng mã thông báo gấp đôi.
  • Sử dụng mô hình mới hơn như "gpt-3.5-turbo-16k" có thể xử lý tới 16 nghìn mã thông báo .

Phần kết luận

Có thể nâng cao phản hồi của bot bằng cách nhúng từ và cơ sở dữ liệu ngữ cảnh tốt. Để đạt được điều này, chúng tôi cần tài liệu chất lượng tốt. Có một lượng lớn thử nghiệm và sai sót liên quan đến việc phát triển một bot dường như nắm bắt được chủ đề.


Tôi hy vọng việc khám phá chuyên sâu về nhúng từ và các mô hình ngôn ngữ lớn này sẽ hỗ trợ bạn xây dựng một bot mạnh mẽ hơn, được tùy chỉnh theo yêu cầu của bạn.


Chúc bạn xây dựng vui vẻ!


Cũng được xuất bản ở đây .