Trong bối cảnh số hóa phát triển nhanh chóng ngày nay, cùng với sự trỗi dậy của các Mô hình Ngôn ngữ Lớn (LLM), các ứng dụng hội thoại đã trở nên vô cùng phổ biến. Chatbot đã thay đổi cách chúng ta tương tác với ứng dụng, trang web và thậm chí cả các kênh dịch vụ khách hàng trong đời sống hằng ngày.
Khả năng tự xây dựng chatbot của riêng bạn đã trở thành yếu tố tạo khác biệt, giúp doanh nghiệp và cá nhân tạo ra các giao diện hội thoại cá nhân hóa, hiệu quả và hấp dẫn phù hợp với nhu cầu cụ thể.
Bài viết này nhằm phân tích cách xây dựng một chatbot bằng Python tận dụng sức mạnh của LLM. Chúng ta sẽ tìm hiểu cách tạo chatbot của riêng mình bằng ChatGPT và cách tối ưu hóa thêm với khung phổ biến LangChain.
Nếu bạn mới làm quen với ChatGPT và LangChain, hãy xem phần giới thiệu về Mô hình Ngôn ngữ Lớn với GPT & LangChain kèm code dưới đây:
Bài viết này bao quát mọi khía cạnh then chốt trong quá trình phát triển một chatbot tối ưu sử dụng ChatGPT làm mô hình nền tảng. Chúng ta sẽ dùng Jupyter Notebook với cả hai thư viện openai và langchain đã được cài đặt. Bạn có thể theo dõi trong DataLab workbook này.
Ở các phần tiếp theo, chúng ta sẽ tìm hiểu cách dùng OpenAI API để thực hiện các lời gọi API cơ bản tới ChatGPT như những viên gạch nền cho cuộc hội thoại. Bên cạnh đó, chúng ta sẽ triển khai khả năng nhận biết ngữ cảnh cho chatbot bằng các cấu trúc Python cơ bản. Cuối cùng, chúng ta sẽ tối ưu cả phần triển khai chatbot lẫn lịch sử hội thoại bằng LangChain và khám phá các tính năng bộ nhớ của nó.
Các lời gọi API ChatGPT cơ bản
Chúng ta sẽ dùng ChatGPT làm mô hình đứng sau chatbot. Vì vậy, tác vụ thực hành đầu tiên là viết một lời gọi API đơn giản nhưng đầy đủ tới ChatGPT để thiết lập tương tác ban đầu.
Hàm Python dưới đây bao bọc lời gọi API mong muốn tới ChatGPT chỉ với thư viện openai.
import os
import openai
openai_api_key = os.environ["OPENAI_API_KEY"]defchatgpt_call(prompt, model="gpt-3.5-turbo"):
response = openai.ChatCompletion.create(
model=model,
messages=[{"role":"user","content": prompt}])return response.choices[0].message["content"]Được hỗ trợ bởi
Lưu ý rằng trong trường hợp này, chúng ta dùng mô hình gpt-3.5-turbo vì đây là mô hình GPT-3.5 mạnh nhất, được tối ưu cho trò chuyện và cũng là một trong những mô hình có chi phí thấp. Thông tin chi tiết về các mô hình khả dụng có trong tài liệu của OpenAI.
Hãy phân tích phần triển khai của chúng ta từng bước một!
Hàm chatgpt_call() là một lớp bao quanh lời gọi hàm gốc của OpenAI ChatCompletion.create để tạo yêu cầu hoàn tất chat.
Với prompt đã cho, hàm sẽ trả về phần hoàn tất tương ứng từ ChatGPT. Để gọi hoàn tất thành công, chỉ cần cung cấp ID của mô hình sẽ dùng (model) và prompt mong muốn (prompt).
Phần hoàn tất thực tế do ChatGPT trả về có kèm siêu dữ liệu cùng với phản hồi. Tuy nhiên, phần hoàn tất luôn nằm trong trường response.choices[0].message[“content”]. Thông tin thêm về nội dung hoàn tất đầy đủ có trong tài liệu của OpenAI.
Với phần triển khai này, chúng ta có thể nhắn ChatGPT chỉ bằng một dòng:
prompt ="My name is Andrea"
response = chatgpt_call(prompt)print(response)# Output: Nice to meet you, Andrea! How can I assist you today?Được hỗ trợ bởi
Lời gọi như một tương tác độc lập
Bạn có nhận ra điểm cần lưu ý trong phần triển khai ở trên không?
Gợi ý nhé, hãy thử một tương tác tiếp theo:
prompt ="Do you remember my name?"
response = chatgpt_call(prompt)print(response)# Output: I'm sorry, as an AI language model, I don't have the ability to remember specific information about individual users.Được hỗ trợ bởi
Đúng vậy, như ta thấy, mỗi lời gọi API là một tương tác độc lập. Điều này có nghĩa là mô hình không có ký ức về các prompt đầu vào hay phần hoàn tất trước đó. Mỗi lần gọi hàm chatgpt_call() là ta bắt đầu một cuộc hội thoại hoàn toàn mới.
Đó chắc chắn là vấn đề khi xây dựng chatbot, vì không thể đối thoại nếu mô hình không nhớ những gì đã trao đổi trước đó. Do vậy, bước tiếp theo là giúp mô hình nhận biết các tương tác trước.
Nhận biết ngữ cảnh
Nhận biết ngữ cảnh cho phép chatbot hiểu và phản hồi phù hợp dựa trên cuộc hội thoại đang diễn ra.
Bằng cách xây dựng ngữ cảnh, các tương tác sau có thể được cá nhân hóa dựa trên những trao đổi trước đó, duy trì tính liên tục và tránh lặp lại. Hơn nữa, với chatbot nhận biết ngữ cảnh, người dùng có thể chủ động đặt câu hỏi làm rõ hoặc đưa ra gợi ý để chỉnh sửa hiểu nhầm.
Tổng thể, nhận biết ngữ cảnh giúp chatbot tự nhiên hơn, hiệu quả hơn và nâng cao trải nghiệm người dùng. Từ giờ, chúng ta sẽ gọi tập hợp các tương tác trước đó là lịch sử hội thoại hoặc bộ nhớ.
API gốc của OpenAI cho ChatGPT đã cung cấp cách gửi lịch sử hội thoại kèm theo mỗi prompt mới. Ngoài ra, khung LangChain cũng cung cấp cách quản lý bộ nhớ của ChatGPT hiệu quả hơn và tối ưu chi phí.
Hiểu về thông điệp System, Assistant và User
Cấu trúc lịch sử hội thoại của ChatGPT
Lúc này, chúng ta có thể dùng ngay phương thức chatgpt_call() chỉ bằng cách truyền prompt mong muốn. Nếu xem kỹ phần triển khai, prompt đầu vào được định dạng như một phần của danh sách messages. Danh sách này thực chất dùng để lưu vết lịch sử hội thoại, vì vậy nó phải được định dạng theo một cách rất cụ thể.
Hãy xem kỹ hơn!
Đến giờ, chúng ta đã định dạng danh sách messages trong chatgpt_call() như sau:
messages =[{"role":"user","content": prompt}]Được hỗ trợ bởi
Danh sách messages lưu lịch sử hội thoại dưới dạng một tập hợp các từ điển. Mỗi từ điển trong danh sách đại diện cho một thông điệp trong cuộc trò chuyện với hai cặp khóa-giá trị.
Khóa role với giá trị user cho biết thông điệp được gửi bởi người dùng.
Khóa content với giá trị prompt chứa prompt thực tế của người dùng.
Đây là ví dụ rất đơn giản chỉ có một tương tác. Mặc dù khóa content sẽ luôn lưu prompt của người dùng hoặc phần hoàn tất của chatbot, khóa role linh hoạt hơn nhiều.
Các vai trò System, Assistant và User
Khóa role cho biết chủ thể của thông điệp trong content. Khi dùng ChatGPT, chúng ta có tối đa ba vai trò:
User. Như đã đề cập, vai trò user tương ứng với người dùng thực sự đang tương tác với chatbot.
Assistant. Vai trò assistant tương ứng với chatbot.
System. Vai trò system dùng để cung cấp hướng dẫn, chỉ dẫn và ngữ cảnh cho chatbot. Thông điệp hệ thống giúp định hướng tương tác với người dùng, thiết lập hành vi của chatbot, cung cấp thông tin ngữ cảnh và xử lý các tương tác cụ thể trong cuộc trò chuyện. Có thể xem thông điệp hệ thống là chỉ dẫn cấp cao cho chatbot, trong suốt với người dùng.
Hình tự làm. Sự tương tác giữa các vai trò khác nhau trong chuỗi hội thoại khi xây dựng chatbot với chatGPT.
Xây dựng lịch sử hội thoại
Chúng ta có thể tiến hành xây dựng lịch sử hội thoại bằng cách sử dụng ba vai trò user, assistant và system trong danh sách messages.
Dựa trên các tương tác thử nghiệm, một lịch sử hội thoại được định dạng đúng, sử dụng cả ba vai trò nói trên có thể như sau:
messages =[{'role':'system','content':'You are friendly chatbot.'},{'role':'user','content':'Hi, my name is Andrea'},{'role':'assistant','content':"Nice to meet you, Andrea! How can I assist you today?"},{'role':'user','content':'Do you remember my name?'}]Được hỗ trợ bởi
Chúng ta có thể chỉnh sửa nhẹ hàm call_chatgpt() để đưa lịch sử hội thoại vào như một tham số đầu vào:
messages =[{'role':'system','content':'You are friendly chatbot.'},{'role':'user','content':'Hi, my name is Andrea'},{'role':'assistant','content':"Nice to meet you, Andrea! How can I assist you today?"},{'role':'user','content':'Do you remember my name?'}]
response = call_chatgpt_with_memory(messages)print(response)# Output: Yes, your name is Andrea.Được hỗ trợ bởi
Tuyệt! Chúng ta đã giúp mô hình nhận biết các tương tác trước đó để có thể thực sự trò chuyện. Tuy nhiên, chúng ta cần cách tự động cập nhật danh sách messages.
Tự động hóa lịch sử hội thoại
Chúng ta có thể tự động thu thập thông điệp bằng cách mở rộng thêm một chút hàm gọi API:
defchatgpt_conversation(prompt):
context.append({'role':'user','content':f"{prompt}"})
response = call_chatgpt_with_memory(context)
context.append({'role':'assistant','content':f"{response}"})return response
Được hỗ trợ bởi
Giờ hãy thử nhé!
chatgpt_conversation("Hello, my name is Andrea")# Output: 'Nice to meet you, Andrea! How can I assist you today?'Được hỗ trợ bởi
Và một tương tác tiếp theo:
chatgpt_conversation("Do you remember my name?")# Output: 'Yes, your name is Andrea.'Được hỗ trợ bởi
Giờ đây lịch sử hội thoại được cập nhật tự động. Cuối cùng, lưu ý rằng bạn có thể xem lịch sử hội thoại — và cấu trúc của nó — bất cứ lúc nào chỉ bằng cách in danh sách messages.
print(messages)# Output:[{'role':'user','content':'Hello, my name is Andrea'},{'role':'assistant','content':'Nice to meet you, Andrea! How can I assist you today?'},{'role':'user','content':'Do you remember my name?'},{'role':'assistant','content':'Yes, your name is Andrea.'}]Được hỗ trợ bởi
Khung LangChain
Đến đây, có thể đồng ý rằng phần triển khai của chúng ta tạo ra một chatbot hữu dụng, nhưng chưa hẳn là phiên bản tối ưu nhất.
Liên tục cung cấp mọi prompt và phần hoàn tất trước đó có thể nhanh chóng dẫn đến một lượng lớn token, và ChatGPT sẽ phải xử lý tất cả trước khi trả về phần hoàn tất mới nhất.
Cho đến nay, chúng ta dùng các thao tác .append để gom các tương tác trước lại nhằm cung cấp một dạng bộ nhớ cho chatbot. Tuy nhiên, việc quản lý bộ nhớ có thể hiệu quả hơn bằng cách dùng các gói chuyên dụng như LangChain.
LangChain là một khung phát triển ứng dụng vận hành bởi các mô hình ngôn ngữ. Vì chatbot là một trong những ứng dụng phổ biến nhất hiện nay, LangChain cung cấp cách triển khai sạch sẽ hơn để xây dựng lịch sử hội thoại của chatbot, cùng các lựa chọn khác để xử lý hiệu quả hơn.
Trong phần này, chúng ta sẽ tìm hiểu cách dùng Khung LangChain để xây dựng chatbot.
Thư viện langchain có thể thay thế thư viện openai ở trên, vì mô-đun langchain.llms đã cho phép bạn tương tác với ChatGPT chỉ với hai dòng mã:
# OpenAI key from environmentfrom langchain.llms import OpenAI
llm = OpenAI()Được hỗ trợ bởi
Sau khi nạp mô hình llm, hãy nhập đối tượng bộ nhớ sẽ lưu lịch sử hội thoại. Để lưu từng tương tác trước với chatbot, LangChain cung cấp ConversationBufferMemory:
from langchain.memory import ConversationBufferMemory
memory = ConversationBufferMemory()Được hỗ trợ bởi
Giờ là lúc nhập ConversationChain, một lớp bao sẽ sử dụng llm và memory để gửi prompt của người dùng tới ChatGPT và trả về phần hoàn tất:
from langchain.chains import ConversationChain
conversation = ConversationChain(
llm=llm,
memory=memory
)Được hỗ trợ bởi
Cuối cùng, để gửi prompt tới ChatGPT, ta có thể dùng phương thức .predict như sau:
conversation.predict(input="Hello, my name is Andrea")# Output: "Hi Andrea, I'm an AI created to help you with whatever you may need. What can I do for you today?"Được hỗ trợ bởi
Hãy đặt verbose=True để quan sát thông tin mà mô hình cân nhắc khi đưa ra phần hoàn tất. Hãy thử trong một tương tác tiếp theo:
Ảnh chụp màn hình ngữ cảnh sẵn có cho ChatGPT khi tạo phần hoàn tất. Đầu ra Verbose màu xanh lá.
Cuối cùng, hãy kiểm tra rằng mô hình ghi nhớ hoàn hảo các tương tác trước:
Ảnh chụp màn hình ngữ cảnh sẵn có cho ChatGPT khi tạo phần hoàn tất. Đầu ra Verbose màu xanh lá.
Bổ sung lịch sử hội thoại
Chúng ta có thể nạp thêm ngữ cảnh cho chatbot bằng phương thức memory.save_context(). Bằng cách này, ta có thể giúp chatbot biết về bất kỳ nội dung mong muốn nào mà không cần cung cấp thông tin đó trong cuộc trò chuyện. Trong LangChain, các từ khóa dùng cho mục đích này là input và output. Ví dụ,
memory.save_context({"input":"Hi"},{"output":"What's up"})
memory.save_context({"input":"Not much, just hanging"},{"output":"Cool"})Được hỗ trợ bởi
Ngoài ra, ta cũng có thể xem nội dung bộ nhớ bất cứ lúc nào bằng cách chạy print(memory.buffer) hoặc memory.load_memory_variables({}).
Các loại bộ nhớ của LangChain
LangChain triển khai các loại bộ nhớ khác nhau cho ChatGPT. ConversationBufferMemory dùng ở trên sẽ theo dõi mọi tương tác với ChatGPT. Tuy nhiên, như đã bàn, việc gửi toàn bộ lịch sử hội thoại cho ChatGPT ở mỗi tương tác mới có thể nhanh chóng làm tăng mạnh số token cần xử lý.
Hãy nhớ rằng ngoài giới hạn token mỗi tương tác của ChatGPT, chi phí sử dụng cũng phụ thuộc vào số lượng token. Xử lý toàn bộ lịch sử trong mỗi tương tác mới có thể sẽ tốn kém theo thời gian.
Để tránh phải xử lý liên tục lịch sử hội thoại, LangChain triển khai những cách tối ưu khác để quản lý bộ nhớ của ChatGPT. Chúng ta sẽ khám phá một trong những loại tiên tiến ở phần tiếp theo.
Lịch sử hội thoại nâng cao với LangChain
Một cách nâng cao để quản lý bộ nhớ mà không cần lưu từng tương tác trước là lưu một bản tóm tắt các tương tác.
Đây là loại bộ nhớ tôi yêu thích vì nó thực sự giúp tối ưu quản lý bộ nhớ mà không đánh mất bất kỳ tương tác nào trong quá khứ.
Lưu ý rằng loại bộ nhớ này dùng llm đã định để tạo bản tóm tắt các tương tác trước đó.
Ngoài ra, bộ nhớ này có thể lưu trữ các tương tác đầy đủ cho đến một số lượng token tối đa (max_token_limit). Vượt quá giới hạn này, nó sẽ đưa thông tin của những thông điệp đó vào bản tóm tắt.
Để thử nghiệm, hãy tạo một tương tác có số lượng token lớn. Xét schedule sau:
schedule = "There is a meeting at 8am with your product team. \
You will need your powerpoint presentation prepared. \
9am-12pm have time to work on your LangChain \
project which will go quickly because Langchain is such a powerful tool. \
At Noon, lunch at the italian resturant with a customer who is driving \
from over an hour away to meet you to understand the latest in AI. \
Be sure to bring your laptop to show the latest LLM demo."
memory = ConversationSummaryBufferMemory(llm=llm, max_token_limit=100)
memory.save_context({"input":"Hello"},{"output":"What's up"})
memory.save_context({"input":"Not much, just hanging"},{"output":"Cool"})
memory.save_context({"input":"What is on the schedule today?"},{"output":f"{schedule}"})Được hỗ trợ bởi
Giờ, ta chỉ cần khai báo một chuỗi hội thoại mới với llm và memory như các ví dụ trước.
conversation = ConversationChain(
llm=llm,
memory=memory,
verbose=True)Được hỗ trợ bởi
Chúng ta có thể dùng .predict để gửi prompt cho ChatGPT:
Ảnh chụp màn hình ngữ cảnh sẵn có cho ChatGPT khi tạo phần hoàn tất. Đầu ra Verbose màu xanh lá.
Như ta thấy, chatbot giữ một bản tóm tắt lịch sử hội thoại gốc, giúp giảm số token ChatGPT phải xử lý ở mỗi tương tác tiếp theo.
Vậy là xong! Giờ chúng ta có thể trò chuyện hàng giờ với chatbot đã được tối ưu!
Đặt verbose=False để có trải nghiệm Hỏi & Đáp dễ chịu hơn trong notebook.
Kết luận
Trong bài viết này, chúng ta đã thấy cách xây dựng một chatbot nhận biết ngữ cảnh dùng mô hình ChatGPT nổi tiếng.
Chúng ta đã thấy việc xây dựng chatbot nhận biết ngữ cảnh của riêng mình có thể dẫn đến những tương tác liên tục. Cách tiếp cận lặp này nuôi dưỡng một vòng phản hồi thường xuyên, giúp bạn duy trì nhu cầu và sở thích của người dùng xuyên suốt cuộc trò chuyện.
Chúng ta đã thấy hai cách chính để quản lý lịch sử hội thoại của chatbot. Thứ nhất, dùng API gốc của ChatGPT để có một chatbot bản địa hoạt động được. Thứ hai, dùng khung chuyên dụng LangChain và triển khai loại bộ nhớ tối ưu.
Giờ là lượt của bạn! Hãy dùng những viên gạch xây dựng được trình bày trong bài để tùy chỉnh một chatbot phù hợp với trường hợp sử dụng cụ thể của bạn!
Andrea Valenzuela hiện đang làm việc cho thí nghiệm CMS tại máy gia tốc hạt (CERN) ở Geneva, Thụy Sĩ. Với sáu năm kinh nghiệm về kỹ thuật dữ liệu và phân tích, công việc của cô bao gồm phân tích dữ liệu và phát triển phần mềm. Hiện cô đang nỗ lực dân chủ hóa việc học các công nghệ liên quan đến dữ liệu thông qua ấn phẩm Medium ForCode'Sake.
Cô có bằng Cử nhân Vật lý Kỹ thuật từ Đại học Bách khoa Catalonia, cũng như bằng Thạc sĩ Hệ thống Tương tác Thông minh từ Đại học Pompeu Fabra. Kinh nghiệm nghiên cứu của cô bao gồm công việc chuyên môn với các thuật toán trước đây của OpenAI cho tạo sinh hình ảnh, chẳng hạn như Normalizing Flows.
Trong bối cảnh số hóa phát triển nhanh chóng ngày nay, cùng với sự trỗi dậy của các Mô hình Ngôn ngữ Lớn (LLM), các ứng dụng hội thoại đã trở nên vô cùng phổ biến. Chatbot đã thay đổi cách chúng ta tương tác với ứng dụng, trang web và thậm chí cả các kênh dịch vụ khách hàng trong đời sống hằng ngày.
Khả năng tự xây dựng chatbot của riêng bạn đã trở thành yếu tố tạo khác biệt, giúp doanh nghiệp và cá nhân tạo ra các giao diện hội thoại cá nhân hóa, hiệu quả và hấp dẫn phù hợp với nhu cầu cụ thể.
Bài viết này nhằm phân tích cách xây dựng một chatbot bằng Python tận dụng sức mạnh của LLM. Chúng ta sẽ tìm hiểu cách tạo chatbot của riêng mình bằng ChatGPT và cách tối ưu hóa thêm với khung phổ biến LangChain.
Nếu bạn mới làm quen với ChatGPT và LangChain, hãy xem phần giới thiệu về Mô hình Ngôn ngữ Lớn với GPT & LangChain kèm code dưới đây:
Bài viết này bao quát mọi khía cạnh then chốt trong quá trình phát triển một chatbot tối ưu sử dụng ChatGPT làm mô hình nền tảng. Chúng ta sẽ dùng Jupyter Notebook với cả hai thư viện
openaivàlangchainđã được cài đặt. Bạn có thể theo dõi trong DataLab workbook này.Ở các phần tiếp theo, chúng ta sẽ tìm hiểu cách dùng OpenAI API để thực hiện các lời gọi API cơ bản tới ChatGPT như những viên gạch nền cho cuộc hội thoại. Bên cạnh đó, chúng ta sẽ triển khai khả năng nhận biết ngữ cảnh cho chatbot bằng các cấu trúc Python cơ bản. Cuối cùng, chúng ta sẽ tối ưu cả phần triển khai chatbot lẫn lịch sử hội thoại bằng LangChain và khám phá các tính năng bộ nhớ của nó.
Các lời gọi API ChatGPT cơ bản
Chúng ta sẽ dùng ChatGPT làm mô hình đứng sau chatbot. Vì vậy, tác vụ thực hành đầu tiên là viết một lời gọi API đơn giản nhưng đầy đủ tới ChatGPT để thiết lập tương tác ban đầu.
Hàm Python dưới đây bao bọc lời gọi API mong muốn tới ChatGPT chỉ với thư viện
openai.Lưu ý rằng trong trường hợp này, chúng ta dùng mô hình
gpt-3.5-turbovì đây là mô hình GPT-3.5 mạnh nhất, được tối ưu cho trò chuyện và cũng là một trong những mô hình có chi phí thấp. Thông tin chi tiết về các mô hình khả dụng có trong tài liệu của OpenAI.Bạn cần trợ giúp để lấy khóa API OpenAI? Vậy khóa học Bắt đầu với OpenAI API và ChatGPT là dành cho bạn.
Hãy phân tích phần triển khai của chúng ta từng bước một!
Hàm
chatgpt_call()là một lớp bao quanh lời gọi hàm gốc của OpenAIChatCompletion.createđể tạo yêu cầu hoàn tất chat.Với prompt đã cho, hàm sẽ trả về phần hoàn tất tương ứng từ ChatGPT. Để gọi hoàn tất thành công, chỉ cần cung cấp ID của mô hình sẽ dùng (
model) và prompt mong muốn (prompt).Phần hoàn tất thực tế do ChatGPT trả về có kèm siêu dữ liệu cùng với phản hồi. Tuy nhiên, phần hoàn tất luôn nằm trong trường
response.choices[0].message[“content”]. Thông tin thêm về nội dung hoàn tất đầy đủ có trong tài liệu của OpenAI.Với phần triển khai này, chúng ta có thể nhắn ChatGPT chỉ bằng một dòng:
Lời gọi như một tương tác độc lập
Bạn có nhận ra điểm cần lưu ý trong phần triển khai ở trên không?
Gợi ý nhé, hãy thử một tương tác tiếp theo:
Đúng vậy, như ta thấy, mỗi lời gọi API là một tương tác độc lập. Điều này có nghĩa là mô hình không có ký ức về các prompt đầu vào hay phần hoàn tất trước đó. Mỗi lần gọi hàm
chatgpt_call()là ta bắt đầu một cuộc hội thoại hoàn toàn mới.Đó chắc chắn là vấn đề khi xây dựng chatbot, vì không thể đối thoại nếu mô hình không nhớ những gì đã trao đổi trước đó. Do vậy, bước tiếp theo là giúp mô hình nhận biết các tương tác trước.
Nhận biết ngữ cảnh
Nhận biết ngữ cảnh cho phép chatbot hiểu và phản hồi phù hợp dựa trên cuộc hội thoại đang diễn ra.
Bằng cách xây dựng ngữ cảnh, các tương tác sau có thể được cá nhân hóa dựa trên những trao đổi trước đó, duy trì tính liên tục và tránh lặp lại. Hơn nữa, với chatbot nhận biết ngữ cảnh, người dùng có thể chủ động đặt câu hỏi làm rõ hoặc đưa ra gợi ý để chỉnh sửa hiểu nhầm.
Tổng thể, nhận biết ngữ cảnh giúp chatbot tự nhiên hơn, hiệu quả hơn và nâng cao trải nghiệm người dùng. Từ giờ, chúng ta sẽ gọi tập hợp các tương tác trước đó là lịch sử hội thoại hoặc bộ nhớ.
API gốc của OpenAI cho ChatGPT đã cung cấp cách gửi lịch sử hội thoại kèm theo mỗi prompt mới. Ngoài ra, khung LangChain cũng cung cấp cách quản lý bộ nhớ của ChatGPT hiệu quả hơn và tối ưu chi phí.
Hiểu về thông điệp System, Assistant và User
Cấu trúc lịch sử hội thoại của ChatGPT
Lúc này, chúng ta có thể dùng ngay phương thức
chatgpt_call()chỉ bằng cách truyền prompt mong muốn. Nếu xem kỹ phần triển khai, prompt đầu vào được định dạng như một phần của danh sáchmessages. Danh sách này thực chất dùng để lưu vết lịch sử hội thoại, vì vậy nó phải được định dạng theo một cách rất cụ thể.Hãy xem kỹ hơn!
Đến giờ, chúng ta đã định dạng danh sách messages trong
chatgpt_call()như sau:Danh sách
messageslưu lịch sử hội thoại dưới dạng một tập hợp các từ điển. Mỗi từ điển trong danh sách đại diện cho một thông điệp trong cuộc trò chuyện với hai cặp khóa-giá trị.rolevới giá trịusercho biết thông điệp được gửi bởi người dùng.contentvới giá trịpromptchứa prompt thực tế của người dùng.Đây là ví dụ rất đơn giản chỉ có một tương tác. Mặc dù khóa
contentsẽ luôn lưu prompt của người dùng hoặc phần hoàn tất của chatbot, khóarolelinh hoạt hơn nhiều.Các vai trò System, Assistant và User
Khóa
rolecho biết chủ thể của thông điệp trongcontent. Khi dùng ChatGPT, chúng ta có tối đa ba vai trò:usertương ứng với người dùng thực sự đang tương tác với chatbot.assistanttương ứng với chatbot.systemdùng để cung cấp hướng dẫn, chỉ dẫn và ngữ cảnh cho chatbot. Thông điệp hệ thống giúp định hướng tương tác với người dùng, thiết lập hành vi của chatbot, cung cấp thông tin ngữ cảnh và xử lý các tương tác cụ thể trong cuộc trò chuyện. Có thể xem thông điệp hệ thống là chỉ dẫn cấp cao cho chatbot, trong suốt với người dùng.Hình tự làm. Sự tương tác giữa các vai trò khác nhau trong chuỗi hội thoại khi xây dựng chatbot với chatGPT.
Xây dựng lịch sử hội thoại
Chúng ta có thể tiến hành xây dựng lịch sử hội thoại bằng cách sử dụng ba vai trò
user,assistantvàsystemtrong danh sáchmessages.Dựa trên các tương tác thử nghiệm, một lịch sử hội thoại được định dạng đúng, sử dụng cả ba vai trò nói trên có thể như sau:
Chúng ta có thể chỉnh sửa nhẹ hàm
call_chatgpt()để đưa lịch sử hội thoại vào như một tham số đầu vào:Hãy thử phần triển khai mới này!
Tuyệt! Chúng ta đã giúp mô hình nhận biết các tương tác trước đó để có thể thực sự trò chuyện. Tuy nhiên, chúng ta cần cách tự động cập nhật danh sách
messages.Tự động hóa lịch sử hội thoại
Chúng ta có thể tự động thu thập thông điệp bằng cách mở rộng thêm một chút hàm gọi API:
Giờ hãy thử nhé!
Và một tương tác tiếp theo:
Giờ đây lịch sử hội thoại được cập nhật tự động. Cuối cùng, lưu ý rằng bạn có thể xem lịch sử hội thoại — và cấu trúc của nó — bất cứ lúc nào chỉ bằng cách in danh sách
messages.Khung LangChain
Đến đây, có thể đồng ý rằng phần triển khai của chúng ta tạo ra một chatbot hữu dụng, nhưng chưa hẳn là phiên bản tối ưu nhất.
Liên tục cung cấp mọi prompt và phần hoàn tất trước đó có thể nhanh chóng dẫn đến một lượng lớn token, và ChatGPT sẽ phải xử lý tất cả trước khi trả về phần hoàn tất mới nhất.
Cho đến nay, chúng ta dùng các thao tác
.appendđể gom các tương tác trước lại nhằm cung cấp một dạng bộ nhớ cho chatbot. Tuy nhiên, việc quản lý bộ nhớ có thể hiệu quả hơn bằng cách dùng các gói chuyên dụng như LangChain.LangChain là một khung phát triển ứng dụng vận hành bởi các mô hình ngôn ngữ. Vì chatbot là một trong những ứng dụng phổ biến nhất hiện nay, LangChain cung cấp cách triển khai sạch sẽ hơn để xây dựng lịch sử hội thoại của chatbot, cùng các lựa chọn khác để xử lý hiệu quả hơn.
Để biết thêm về chính khung LangChain, tôi rất khuyến nghị bài viết nhập môn Giới thiệu LangChain cho Kỹ thuật Dữ liệu & Ứng dụng Dữ liệu.
Lịch sử hội thoại đơn giản với LangChain
Trong phần này, chúng ta sẽ tìm hiểu cách dùng Khung LangChain để xây dựng chatbot.
Thư viện
langchaincó thể thay thế thư việnopenaiở trên, vì mô-đunlangchain.llmsđã cho phép bạn tương tác với ChatGPT chỉ với hai dòng mã:Sau khi nạp mô hình
llm, hãy nhập đối tượng bộ nhớ sẽ lưu lịch sử hội thoại. Để lưu từng tương tác trước với chatbot, LangChain cung cấpConversationBufferMemory:Giờ là lúc nhập
ConversationChain, một lớp bao sẽ sử dụngllmvàmemoryđể gửi prompt của người dùng tới ChatGPT và trả về phần hoàn tất:Cuối cùng, để gửi prompt tới ChatGPT, ta có thể dùng phương thức
.predictnhư sau:Hãy đặt
verbose=Trueđể quan sát thông tin mà mô hình cân nhắc khi đưa ra phần hoàn tất. Hãy thử trong một tương tác tiếp theo:Ảnh chụp màn hình ngữ cảnh sẵn có cho ChatGPT khi tạo phần hoàn tất. Đầu ra Verbose màu xanh lá.
Cuối cùng, hãy kiểm tra rằng mô hình ghi nhớ hoàn hảo các tương tác trước:
Ảnh chụp màn hình ngữ cảnh sẵn có cho ChatGPT khi tạo phần hoàn tất. Đầu ra Verbose màu xanh lá.
Bổ sung lịch sử hội thoại
Chúng ta có thể nạp thêm ngữ cảnh cho chatbot bằng phương thức
memory.save_context(). Bằng cách này, ta có thể giúp chatbot biết về bất kỳ nội dung mong muốn nào mà không cần cung cấp thông tin đó trong cuộc trò chuyện. Trong LangChain, các từ khóa dùng cho mục đích này làinputvàoutput. Ví dụ,Ngoài ra, ta cũng có thể xem nội dung bộ nhớ bất cứ lúc nào bằng cách chạy
print(memory.buffer)hoặcmemory.load_memory_variables({}).Các loại bộ nhớ của LangChain
LangChain triển khai các loại bộ nhớ khác nhau cho ChatGPT.
ConversationBufferMemorydùng ở trên sẽ theo dõi mọi tương tác với ChatGPT. Tuy nhiên, như đã bàn, việc gửi toàn bộ lịch sử hội thoại cho ChatGPT ở mỗi tương tác mới có thể nhanh chóng làm tăng mạnh số token cần xử lý.Hãy nhớ rằng ngoài giới hạn token mỗi tương tác của ChatGPT, chi phí sử dụng cũng phụ thuộc vào số lượng token. Xử lý toàn bộ lịch sử trong mỗi tương tác mới có thể sẽ tốn kém theo thời gian.
Để tránh phải xử lý liên tục lịch sử hội thoại, LangChain triển khai những cách tối ưu khác để quản lý bộ nhớ của ChatGPT. Chúng ta sẽ khám phá một trong những loại tiên tiến ở phần tiếp theo.
Lịch sử hội thoại nâng cao với LangChain
Một cách nâng cao để quản lý bộ nhớ mà không cần lưu từng tương tác trước là lưu một bản tóm tắt các tương tác.
Đây là loại bộ nhớ tôi yêu thích vì nó thực sự giúp tối ưu quản lý bộ nhớ mà không đánh mất bất kỳ tương tác nào trong quá khứ.
Hãy bắt tay vào làm!
Lưu ý rằng loại bộ nhớ này dùng
llmđã định để tạo bản tóm tắt các tương tác trước đó.Ngoài ra, bộ nhớ này có thể lưu trữ các tương tác đầy đủ cho đến một số lượng token tối đa (
max_token_limit). Vượt quá giới hạn này, nó sẽ đưa thông tin của những thông điệp đó vào bản tóm tắt.Để thử nghiệm, hãy tạo một tương tác có số lượng token lớn. Xét
schedulesau:Giờ, ta chỉ cần khai báo một chuỗi hội thoại mới với
llmvàmemorynhư các ví dụ trước.Chúng ta có thể dùng
.predictđể gửi prompt cho ChatGPT:Ảnh chụp màn hình ngữ cảnh sẵn có cho ChatGPT khi tạo phần hoàn tất. Đầu ra Verbose màu xanh lá.
Như ta thấy, chatbot giữ một bản tóm tắt lịch sử hội thoại gốc, giúp giảm số token ChatGPT phải xử lý ở mỗi tương tác tiếp theo.
Vậy là xong! Giờ chúng ta có thể trò chuyện hàng giờ với chatbot đã được tối ưu!
Đặt
verbose=Falseđể có trải nghiệm Hỏi & Đáp dễ chịu hơn trong notebook.Kết luận
Trong bài viết này, chúng ta đã thấy cách xây dựng một chatbot nhận biết ngữ cảnh dùng mô hình ChatGPT nổi tiếng.
Chúng ta đã thấy việc xây dựng chatbot nhận biết ngữ cảnh của riêng mình có thể dẫn đến những tương tác liên tục. Cách tiếp cận lặp này nuôi dưỡng một vòng phản hồi thường xuyên, giúp bạn duy trì nhu cầu và sở thích của người dùng xuyên suốt cuộc trò chuyện.
Chúng ta đã thấy hai cách chính để quản lý lịch sử hội thoại của chatbot. Thứ nhất, dùng API gốc của ChatGPT để có một chatbot bản địa hoạt động được. Thứ hai, dùng khung chuyên dụng LangChain và triển khai loại bộ nhớ tối ưu.
Giờ là lượt của bạn! Hãy dùng những viên gạch xây dựng được trình bày trong bài để tùy chỉnh một chatbot phù hợp với trường hợp sử dụng cụ thể của bạn!
Nếu bạn quan tâm đến việc xây dựng các ứng dụng khác bằng cả LangChain và ChatGPT, hội thảo Xây dựng Ứng dụng AI với LangChain và GPT là dành cho bạn. Muốn bắt kịp tốc độ với NLP? Hãy xem các mô-đun về Xử lý Ngôn ngữ Tự nhiên bằng Python!