Khi OpenAI phát hành ChatGPT ra công chúng, rất ít người, kể cả các giám đốc điều hành tại chính OpenAI, có thể dự đoán tốc độ chấp nhận của công chúng. Kể từ đó, ChatGPT đã vượt qua TikTok để trở thành ứng dụng nhanh nhất đạt được 100 triệu người dùng. Mọi người từ mọi tầng lớp xã hội đã tìm ra cách sử dụng ChatGPT để cải thiện hiệu quả của họ và các công ty đã tranh nhau phát triển các hướng dẫn về việc sử dụng nó. Một số tổ chức, bao gồm nhiều tổ chức học thuật, hầu như nghi ngờ về việc sử dụng nó, trong khi các tổ chức khác như công ty công nghệ đã áp dụng chính sách tự do hơn nhiều, thậm chí tạo ra các ứng dụng xung quanh API ChatGPT. Hôm nay, chúng ta sẽ hướng dẫn xây dựng một ứng dụng như vậy.
Bài viết này được chia thành ba phần: 1) giải thích về các công nghệ bên dưới ứng dụng, 2) phần cuối của ứng dụng và 3) phần đầu của ứng dụng. Nếu bạn có thể đọc một số mã Python cơ bản, bạn có thể dễ dàng theo dõi hai phần đầu tiên và nếu bạn có một số kinh nghiệm cơ bản với React.js, bạn có thể làm theo phần thứ ba mà không gặp vấn đề gì.
Ứng dụng chúng tôi đang xây dựng hôm nay sẽ hữu ích cho bất kỳ ai thường xuyên nghiên cứu bằng các nguồn tiếng nước ngoài. Một ví dụ điển hình là các nhà kinh tế vĩ mô thường phải đọc các báo cáo của chính phủ được xuất bản bằng tiếng nước ngoài. Đôi khi những báo cáo này có thể được sao chép và dán vào các dịch vụ dịch máy, nhưng đôi khi chúng được xuất bản ở dạng PDF không thể tìm kiếm được. Trong những trường hợp đó, nhà nghiên cứu sẽ cần thuê người dịch, nhưng những hạn chế về nguồn lực đã hạn chế đáng kể số lượng báo cáo có thể được dịch. Vấn đề càng trở nên phức tạp hơn khi các báo cáo này có thể rất dài và khó đọc, khiến cho việc dịch thuật và phân tích trở nên tốn kém và mất thời gian.
Ứng dụng của chúng tôi sẽ giúp quá trình này trở nên dễ dàng hơn bằng cách kết hợp một số công cụ AI và máy học theo ý của chúng tôi - OCR, Dịch máy và Mô hình ngôn ngữ lớn. Chúng tôi sẽ trích xuất nội dung văn bản thô từ PDF bằng OCR, dịch nó sang tiếng Anh bằng dịch máy và phân tích phần trích xuất đã dịch bằng mô hình ngôn ngữ lớn.
Đối với ứng dụng hôm nay, chúng ta sẽ xem xét một ấn phẩm PDF của chính phủ Nhật Bản, Sách trắng Đổi mới của Bộ Giáo dục, Văn hóa, Thể thao, Khoa học và Công nghệ. Mặc dù bản thân tệp PDF có thể tìm kiếm được và có thể được sao chép vào một công cụ dịch thuật, nhưng chúng tôi sẽ hành động như thể tệp PDF không thể tìm kiếm được để giới thiệu các công nghệ được sử dụng trong ứng dụng. Tài liệu gốc có thể được tìm thấy ở đây .
Nếu bạn chỉ muốn xây dựng ứng dụng ngay bây giờ, vui lòng bỏ qua phần tiếp theo. Tuy nhiên, nếu bạn muốn hiểu rõ hơn về các công nghệ khác nhau mà chúng tôi sẽ sử dụng trong bài viết này, thì phần tiếp theo sẽ cung cấp cho bạn một chút thông tin cơ bản.
Công nghệ đầu tiên chúng tôi sẽ sử dụng là OCR, hay Nhận dạng ký tự quang học, đây là một trong những ứng dụng máy học thương mại sớm nhất được cung cấp cho công chúng. Các mô hình và ứng dụng OCR nhằm mục đích chụp ảnh hoặc hình ảnh sau đó xác định và trích xuất thông tin văn bản từ hình ảnh. Điều này thoạt nghe có vẻ như là một nhiệm vụ đơn giản, nhưng vấn đề thực sự khá phức tạp. Ví dụ, các chữ cái có thể hơi mờ, gây khó khăn cho việc xác định chính xác. Chữ cái cũng có thể được sắp xếp theo một hướng khác thường, nghĩa là mô hình máy học phải xác định văn bản theo chiều dọc và lộn ngược. Bất chấp những thách thức này, các nhà nghiên cứu đã phát triển nhiều mô hình OCR nhanh và mạnh, và nhiều mô hình trong số đó có sẵn với chi phí tương đối thấp. Đối với ứng dụng ngày nay, chúng tôi sẽ sử dụng mô hình Tầm nhìn đám mây của Google mà chúng tôi có thể truy cập bằng cách sử dụng Google Cloud API.
Công nghệ tiếp theo chúng tôi sẽ sử dụng là dịch máy. Điều này, giống như OCR, là một vấn đề học máy cực kỳ khó khăn. Ngôn ngữ của con người chứa đầy những đặc điểm riêng và sự phức tạp theo ngữ cảnh khiến máy tính đặc biệt khó xử lý và hiểu. Bản dịch giữa các cặp ngôn ngữ không giống nhau như tiếng Trung và tiếng Anh có xu hướng mang lại kết quả đặc biệt không chính xác và hài hước do cấu trúc vốn không giống nhau của các ngôn ngữ này đòi hỏi các chiến lược số hóa và nhúng rất khác nhau. Tuy nhiên, bất chấp những thách thức này, các nhà nghiên cứu đã phát triển các mô hình mạnh mẽ và phức tạp và đã cung cấp chúng rộng rãi. Hôm nay, chúng ta sẽ sử dụng API dịch của Google, một trong những công cụ dịch máy tốt nhất và được sử dụng rộng rãi nhất hiện có.
Công nghệ máy học cuối cùng mà chúng tôi sẽ sử dụng là LLM, hay Mô hình ngôn ngữ lớn, đã mang tính cách mạng đối với trí tuệ nhân tạo của người tiêu dùng. LLM có thể hiểu cấu trúc ngôn ngữ tự nhiên của con người và có thể dựa trên một lượng lớn dữ liệu để tạo ra phản hồi chi tiết và nhiều thông tin. Vẫn còn nhiều hạn chế đối với công nghệ, nhưng tính linh hoạt và khả năng xử lý dữ liệu của nó đã truyền cảm hứng cho việc tạo ra nhiều kỹ thuật mới để tương tác với mô hình. Một kỹ thuật như vậy được gọi là Kỹ thuật nhắc nhở , trong đó người dùng tạo và điều chỉnh các đầu vào hoặc lời nhắc có cấu trúc và từ ngữ khéo léo cho mô hình để có được kết quả mong muốn. Trong ứng dụng ngày nay, chúng tôi sẽ sử dụng API của ChatGPT và một số kỹ thuật nhắc đơn giản để giúp chúng tôi phân tích báo cáo đã dịch.
Trước khi chúng tôi bắt đầu mã hóa ứng dụng, trước tiên chúng tôi cần đăng ký dịch vụ.
Vì trang web API của ChatGPT luôn thay đổi nên chúng tôi sẽ không thể cung cấp các bước chính xác để đăng ký API ChatGPT. Tuy nhiên, bạn nên tìm các hướng dẫn dễ thực hiện trên trang web tài liệu API . Chỉ cần tiến hành cho đến khi bạn nhận được khóa API, khóa này chúng tôi sẽ cần gọi API ChatGPT.
Google Cloud phức tạp hơn một chút nhưng đăng ký cũng tương đối đơn giản. Chỉ cần truy cập bảng điều khiển Google Cloud và làm theo hướng dẫn để thiết lập dự án. Sau khi tham gia dự án, bạn sẽ muốn điều hướng đến IAM & Bảng điều khiển dành cho quản trị viên và tạo một tài khoản dịch vụ. Mặc dù bảng điều khiển Google Cloud luôn thay đổi, nhưng bạn sẽ có thể điều hướng giao diện bằng cách tìm kiếm “IAM” và “Tài khoản dịch vụ” trên trang web. Khi tài khoản dịch vụ được tạo, bạn sẽ muốn tải bản sao của tệp khóa cá nhân xuống máy tính của mình. Bạn cũng sẽ muốn sao chép chuỗi khóa riêng, vì API REST dịch sử dụng chuỗi khóa riêng thay vì tệp khóa.
Trước khi chúng tôi kết thúc quá trình thiết lập Google Cloud, bạn sẽ muốn bật API Machine Vision mà bạn có thể thực hiện từ trang bảng điều khiển chính. Chỉ cần tìm kiếm Machine Vision API, nhấp vào sản phẩm từ Google và kích hoạt API. Bạn cũng sẽ muốn tạo một thùng chứa dữ liệu mà chúng ta sẽ sử dụng cho dự án này.
Bây giờ chúng tôi đã đăng ký các dịch vụ phù hợp, chúng tôi đã sẵn sàng bắt đầu mã hóa ứng dụng của mình bằng Python. Trước tiên, chúng tôi sẽ muốn cài đặt các gói cần thiết vào môi trường Python của chúng tôi.
Pip install google-cloud-storage google-cloud-vision openai requests
Khi quá trình cài đặt hoàn tất, hãy tạo một thư mục mới, tải xuống tệp PDF và tạo một tệp Python mới trong cùng một thư mục. Chúng tôi sẽ gọi nó là document_analyze.py. Chúng tôi bắt đầu bằng cách nhập các gói cần thiết:
import requests Import openai from google.cloud import vision from google.cloud import storage Import os Import json Import time Import shutil
Sau đó, chúng tôi có thể thực hiện một số thiết lập cơ bản để ứng dụng của chúng tôi có thể sử dụng các dịch vụ đám mây mà chúng tôi vừa đăng ký:
os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = [Path to your Google Cloud key file] openai.api_key = [your openAI API key]
Với các thông tin đăng nhập này, giờ đây bạn có thể truy cập API Google Cloud và ChatGPT từ tập lệnh Python của mình. Bây giờ chúng ta có thể viết các chức năng sẽ cung cấp chức năng mong muốn cho ứng dụng của mình.
Bây giờ chúng ta có thể bắt đầu xây dựng một số chức năng sẽ trở thành các khối xây dựng của ứng dụng. Hãy bắt đầu với các chức năng OCR:
# upload the file to google cloud storage bucket for further processing def upload_file(file_name, bucket_name, bucket_path): client = storage.Client() bucket = client.get_bucket(bucket_name) blob = bucket.blob(bucket_path) blob.upload_from_filename(file_name) # kick off the OCR process on the document. This is asynchronous because OCR can take a while def async_detect_document(gcs_source_uri, gcs_destination_uri): client = vision.ImageAnnotatorClient() input_config = vision.InputConfig(gcs_source=vision.GcsSource(uri=gcs_source_uri), mime_type= 'application/pdf') output_config = vision.OutputConfig( gcs_destination=vision.GcsDestination(uri=gcs_destination_uri), batch_size=100 ) async_request = vision.AsyncAnnotateFileRequest( features=[vision.Feature(type_=vision.Feature.Type.DOCUMENT_TEXT_DETECTION)], input_config=input_config, output_config=output_config ) operation = client.async_batch_annotate_files(requests=[async_request]) def check_results(bucket_path, prefix): storage_client = storage.Client() bucket = storage_client.get_bucket(bucket_path) blob_list = list(bucket.list_blobs(prefix=prefix)) blb = [b for b in blob_list if 'output-' in b.name and '.json' in b.name] return len(blb) != 0 # download the OCR result file def write_to_text(bucket_name, prefix): bucket = storage.Client().get_bucket(bucket_name) blob_list = list(bucket.list_blobs(prefix=prefix)) if not os.path.exists('ocr_results'): os.mkdir('ocr_results') for blob in blob_list: if blob.name.endswith('.json'): with open(os.path.join('ocr_results', blob.name), 'w') as fp_data: fp_data.write(blob.download_as_string().decode('utf-8')) def delete_objects(bucket, prefix): bucket = storage.Client().get_bucket(bucket) blob_list = list(bucket.list_blobs(prefix=prefix)) for blob in blob_list: blob.delete() print('Blob', blob.name, 'Deleted')
Hãy xem xét chi tiết từng chức năng làm gì.
Hàm upload_file
là một hàm lấy một bộ chứa từ bộ chứa Google Cloud Storage và tải tệp của bạn lên đó. Tính năng trừu tượng tuyệt vời của Google Cloud giúp viết chức năng này rất dễ dàng.
Hàm async_detect_document
gọi hàm OCR của Google Cloud một cách không đồng bộ. Do số lượng tùy chọn có sẵn trong Google Cloud, chúng tôi phải khởi tạo một vài đối tượng cấu hình, nhưng nó thực sự chỉ cho phép đám mây của Google biết vị trí của tệp nguồn và nơi ghi đầu ra. Biến batch_size
được đặt thành 100, vì vậy google cloud sẽ xử lý tài liệu 100 trang cùng một lúc. Điều này làm giảm số lượng tệp đầu ra được ghi, giúp xử lý dễ dàng hơn. Một điều quan trọng khác cần lưu ý là lời gọi không đồng bộ, nghĩa là việc thực thi tập lệnh Python sẽ tiếp tục thay vì đợi quá trình xử lý kết thúc. Mặc dù điều này không tạo ra sự khác biệt lớn đối với giai đoạn cụ thể này, nhưng nó sẽ trở nên hữu ích hơn sau này khi chúng ta chuyển mã Python thành API web.
Hàm check_results
là một hàm lưu trữ đám mây đơn giản để kiểm tra xem quá trình xử lý đã hoàn tất chưa. Bởi vì chúng ta đang gọi hàm OCR một cách không đồng bộ, nên chúng ta cần gọi hàm này định kỳ để xem liệu tệp kết quả có xuất hiện hay không. Nếu có tệp kết quả, hàm sẽ trả về true và chúng ta có thể tiếp tục phân tích. Nếu không có file kết quả, hàm sẽ trả về giá trị false và chúng ta tiếp tục đợi cho đến khi quá trình xử lý kết thúc.
Hàm write_to_text
tải (các) tệp kết quả xuống đĩa để xử lý thêm. Hàm sẽ lặp lại trên tất cả các tệp trong nhóm của bạn với một tiền tố cụ thể, truy xuất chuỗi đầu ra và ghi kết quả vào hệ thống tệp cục bộ.
Hàm delete_objects
, mặc dù không hoàn toàn liên quan đến OCR, nhưng sẽ dọn sạch các tệp đã tải lên để hệ thống không giữ các thành phần lạ không cần thiết trong Google Cloud Storage.
Bây giờ chúng ta đã hoàn thành các yêu cầu OCR, hãy xem mã dịch máy!
Bây giờ chúng ta có thể định nghĩa các hàm dịch:
# detect the language we're translating from def detect_language(text): url = 'https://translation.googleapis.com/language/translate/v2/detect' data = { "q": text, "key": [your google cloud API key] } res = requests.post(url, data=data) return res.json()['data']['detections'][0][0]['language'] # translate the text def translate_text(text): url = 'https://translation.googleapis.com/language/translate/v2' language = detect_language(text) if language == 'en': return text data = { "q": text, "source": language, "target": "en", "format": "text", "key": [your google cloud API key] } res = requests.post(url, data=data) return res.json()['data']['translations'][0]['translatedText']
Các chức năng này khá đơn giản. Hàm detect_language
gọi API phát hiện ngôn ngữ để xác định ngôn ngữ nguồn cho lệnh gọi translate_text
tiếp theo. Mặc dù chúng tôi biết rằng PDF được viết bằng tiếng Nhật, nhưng cách tốt nhất vẫn là chạy tính năng phát hiện ngôn ngữ để ứng dụng có thể xử lý các ngôn ngữ khác. Hàm translate_text
chỉ đơn giản sử dụng API dịch của Google để dịch văn bản từ ngôn ngữ nguồn được phát hiện sang tiếng Anh, mặc dù vậy, nếu nó xác định rằng ngôn ngữ nguồn đã là tiếng Anh, thì nó sẽ bỏ qua bản dịch.
Cuối cùng, chúng tôi có các lệnh gọi ChatGPT:
def run_chatgpt_api(report_text): completion = openai.ChatCompletion.create( model="gpt-3.5-turbo", messages=[ {"role": "user", "content": ''' Consider the following report: --- %s --- 1. Summarize the purpose of the report. 2. Summarize the primary conclusion of the report. 3. Summarize the secondary conclusion of the report 4. Who is the intended audience for this report? 5. What other additional context would a reader be interested in knowing? Please reply in json format with the keys purpose, primaryConclusion, secondaryConclusion, intendedAudience, and additionalContextString. ''' % report_text}, ] ) return completion.choices[0]['message']['content']
Lưu ý rằng lệnh gọi Python là lệnh gọi API tương đối đơn giản, nhưng lời nhắc được viết theo cách để tạo ra kết quả cụ thể:
Lời nhắc cung cấp văn bản của báo cáo dưới dạng ngữ cảnh, vì vậy ChatGPT có thể phân tích báo cáo một cách dễ dàng. Văn bản được phân định ranh giới bằng các đường gạch ngang, giúp bạn dễ dàng nhận ra nơi báo cáo kết thúc và nơi bắt đầu câu hỏi.
Các câu hỏi được liệt kê hơn là nêu trong một định dạng đoạn văn. Do đó, câu trả lời có khả năng tuân theo một cấu trúc tương tự. Cấu trúc liệt kê làm cho kết quả được phân tích cú pháp bằng mã dễ dàng hơn nhiều so với việc ChatGPT trả lời ở định dạng đoạn văn.
Lời nhắc chỉ định định dạng của câu trả lời, trong trường hợp này là định dạng JSON. Định dạng JSON rất dễ xử lý bằng mã.
Lời nhắc chỉ định các khóa của đối tượng JSON và chọn các khóa rất dễ liên kết với các câu hỏi.
Các khóa cũng sử dụng một quy ước thường được sử dụng (camelCase) mà ChatGPT sẽ nhận ra. Các khóa JSON là các từ đầy đủ thay vì rút gọn. Điều này khiến nhiều khả năng ChatGPT sẽ sử dụng khóa thực tế trong phản hồi, vì ChatGPT có thói quen thực hiện "sửa lỗi chính tả" như một phần của quá trình xử lý.
ContextString bổ sung cung cấp một lối thoát cho ChatGPT để chuyển thông tin bổ sung. Điều này tận dụng khả năng phân tích dạng tự do của các mô hình ngôn ngữ lớn.
Lời nhắc sử dụng cách diễn đạt thường thấy trong các cuộc thảo luận kỹ thuật về chủ đề hiện tại. Như bạn có thể đã phỏng đoán từ cấu trúc của lệnh gọi API, mục tiêu “đúng” của ChatGPT không nhất thiết là cung cấp câu trả lời cho lời nhắc, mà là dự đoán dòng tiếp theo trong cuộc đối thoại.
Do đó, nếu lời nhắc của bạn được diễn đạt giống như một dòng từ một cuộc thảo luận cấp độ bề mặt, thì bạn có thể sẽ nhận được câu trả lời ở cấp độ bề mặt, trong khi nếu lời nhắc của bạn được diễn đạt giống như một dòng từ một cuộc thảo luận chuyên gia, thì nhiều khả năng bạn sẽ nhận được kết quả của chuyên gia. Hiệu ứng này đặc biệt rõ ràng đối với các môn học như toán học hoặc công nghệ, nhưng cũng có liên quan ở đây.
Trên đây là các kỹ thuật cơ bản trong một nhóm công việc mới có tên là “kỹ thuật nhắc nhở”, trong đó người dùng cấu trúc các lời nhắc của họ để nhận được một kết quả cụ thể. Với các mô hình ngôn ngữ lớn như ChatGPT, việc tạo cẩn thận lời nhắc có thể giúp tăng đáng kể hiệu quả của mô hình. Có nhiều điểm tương đồng với mã hóa, nhưng kỹ thuật nhanh chóng đòi hỏi nhiều hơn về trực giác và lý luận mơ hồ của kỹ sư. Khi các công cụ như ChatGPT ngày càng được nhúng vào nơi làm việc, kỹ thuật sẽ không còn là một nỗ lực thuần túy kỹ thuật mà còn là một nỗ lực triết học và các kỹ sư nên quan tâm đến việc phát triển trực giác của họ và một “lý thuyết về tâm trí” cho các mô hình ngôn ngữ lớn để thúc đẩy hiệu quả của chúng.
Bây giờ chúng ta hãy đặt tất cả lại với nhau. Nếu bạn đã theo dõi phần trước, khối mã bên dưới sẽ khá dễ hiểu.
bucket = [Your bucket name] upload_file_name = [the name of the PDF in the Google Cloud bucket] upload_prefix = [the prefix to use for the analysis results] pdf_to_analyze = [path to the PDF to analyze] upload_file(pdf_to_analyze, bucket, upload_file_name) async_detect_document(f'gs://{bucket}/{upload_file_name}', f'gs://{bucket}/{upload_prefix}') while not check_results(bucket, upload_prefix): print('Not done yet... checking again') time.sleep(5) If __name__ == '__main__': write_to_text(bucket, upload_prefix) all_responses = [] for result_json in os.listdir('ocr_results'): with open(os.path.join('ocr_results', result_json)) as fp_res: response = json.load(fp_res) all_responses.extend(response['responses']) texts = [a['fullTextAnnotation']['text'] for a in all_responses] translated_text = [translate_text(t) for t in texts] print('Running cleanup...') delete_objects(bucket, upload_file_name) delete_objects(bucket, upload_prefix) shutil.rmtree('ocr_results') print('Running Analysis...') analysis = run_chatgpt_api('\n'.join(translated_text)) print('=== purpose ====') print(analysis_res['purpose']) print() print('==== primary conclusion =====') print(analysis_res['primaryConclusion']) print() print('==== secondary conclusion =====') print(analysis_res['secondaryConclusion']) print() print('==== intended audience ====') print(analysis_res['intendedAudience']) print() print('===== additional context =====') print(analysis_res['additionalContextString'])
Chúng tôi tải báo cáo lên bộ chứa, khởi động OCR và đợi OCR kết thúc. Sau đó, chúng tôi tải xuống các kết quả OCR và đưa nó vào một danh sách. Chúng tôi dịch danh sách kết quả và gửi nó tới ChatGPT để phân tích và in ra kết quả phân tích.
Vì các công cụ AI không mang tính quyết định nên nếu bạn chạy mã này, bạn có thể sẽ nhận được kết quả tương tự nhưng không giống hệt nhau. Tuy nhiên, đây là những gì tôi nhận được dưới dạng đầu ra, sử dụng tệp PDF được liên kết ở trên:
Not done yet... checking again Not done yet... checking again Running cleanup... Blob [pdf name] Deleted Blob [OCR Output Json] Deleted Running Analysis... === purpose ==== The purpose of the report is to analyze the current status and issues of Japan's research capabilities, discuss the government's growth strategy and investment in science and technology, and introduce the initiatives towards realizing a science and technology nation. ==== primary conclusion ===== The primary conclusion of the report is that Japan's research capabilities, as measured by publication index, have been declining internationally, raising concerns about a decline in research capabilities. ==== secondary conclusion ===== The secondary conclusion of the report is that the Kishida Cabinet's growth strategy emphasizes becoming a science and technology nation and strengthening investment in people to achieve growth and distribution. ==== intended audience ==== The intended audience for this report is government officials, policymakers, researchers, and anyone interested in Japan's research capabilities and science and technology policies. ===== additional context ===== The report focuses on the importance of science, technology, and innovation for Japan's future development and highlights the government's efforts to promote a 'new capitalism' based on a virtuous cycle of growth and distribution. It also mentions the revision to the Basic Law on Science, Technology, and Innovation, the 6th Science, Technology, and Innovation Basic Plan, and the concept of Society 5.0 as the future society Japan aims for. The report suggests that comprehensive knowledge is necessary to promote science, technology, and innovation and emphasizes the importance of transcending disciplinary boundaries and utilizing diverse knowledge.
Là một người nói tiếng Nhật, tôi có thể xác minh rằng phân tích khá tốt! Bạn có thể tự thử nghiệm, cung cấp bản PDF của riêng mình và thay đổi câu hỏi cho phù hợp với nhu cầu của mình. Giờ đây, bạn đã có một công cụ AI mạnh mẽ để dịch và tóm tắt bất kỳ tệp PDF tiếng nước ngoài nào mà bạn gặp phải. Nếu bạn là đối tượng mục tiêu của bài viết này, tôi hy vọng bạn đang cảm thấy khá hứng thú với những khả năng vừa được mở ra!
Mã phụ trợ đầy đủ có thể được tìm thấy trên GitHub của tôi
Chúng tôi đã xây dựng một ứng dụng mạnh mẽ, nhưng ở dạng hiện tại, người dùng sẽ phải biết một chút về Python để sử dụng ứng dụng ở mức tối đa. Điều gì sẽ xảy ra nếu bạn có một người dùng không muốn đọc hoặc viết bất kỳ mã nào? Trong trường hợp đó, chúng tôi sẽ muốn xây dựng một ứng dụng web xung quanh công cụ này để mọi người có thể truy cập toàn bộ sức mạnh của AI một cách thoải mái trên trình duyệt.
Hãy bắt đầu bằng cách tạo một ứng dụng React. Đảm bảo rằng Node đã được cài đặt, điều hướng đến thư mục mà bạn muốn mã ứng dụng tồn tại và chạy tập lệnh tạo ứng dụng phản ứng và cài đặt một số gói cơ bản:
npx create-react-app llm-frontend
Và sau đó:
cd llm-frontend npm install bootstrap react-bootstrap axios
Nếu chúng tôi đang phát triển một ứng dụng chính thức, chúng tôi cũng muốn cài đặt các gói để xử lý việc quản lý trạng thái và định tuyến, nhưng điều đó nằm ngoài phạm vi của bài viết này. Chúng tôi sẽ chỉ thực hiện các chỉnh sửa đối với tệp App.jsx.
Thực thi npm run start
để khởi động máy chủ phát triển và trình duyệt của bạn sẽ mở một trang tới http://localhost:3000 . Giữ trang đó và mở tệp App.jsx trong trình soạn thảo văn bản yêu thích của bạn. Bạn sẽ thấy một cái gì đó như thế này:
import logo from './logo.svg'; import './App.css'; function App() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.js</code> and save to reload. </p> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> </div> ); } export default App;
Hãy tiếp tục và xóa mã soạn sẵn và thay thế nó bằng một số thành phần Bootstrap cơ bản.
import React, {useState, useEffect} from 'react'; Import axios from 'axios'; import Container from 'react-bootstrap/Container'; import Row from 'react-bootstrap/Row'; import Col from 'react-bootstrap/Col'; import 'bootstrap/dist/css/bootstrap.min.css'; function App() { return ( <Container> <Row> <Col md={{ span: 10, offset: 1 }}> Main Application Here </Col> </Row> </Container> ); } export default App;
Lưu ứng dụng và bạn sẽ thấy nó cập nhật trong trình duyệt. Bây giờ nó sẽ trông khá thưa thớt, nhưng đừng lo lắng, chúng tôi sẽ sớm khắc phục điều đó.
Để ứng dụng này hoạt động, chúng tôi sẽ cần bốn thành phần chính: bộ chọn tệp để tải tệp lên, màn hình hiển thị kết quả để hiển thị bản dịch và tóm tắt, kiểu nhập văn bản để người dùng có thể đặt câu hỏi của riêng họ và màn hình hiển thị kết quả để hiển thị câu trả lời cho các câu hỏi của người dùng.
Hiện tại, chúng ta có thể xây dựng các thành phần đơn giản hơn và đặt các trình giữ chỗ cho các giao diện phức tạp hơn. Trong khi chúng tôi đang ở đó, hãy tạo các thùng chứa dữ liệu mà chúng tôi sẽ sử dụng để cung cấp năng lượng cho giao diện:
import React, {useState, useEffect} from 'react'; import axios from 'axios'; import Container from 'react-bootstrap/Container'; import Row from 'react-bootstrap/Row'; import Col from 'react-bootstrap/Col'; import Button from 'react-bootstrap/Button'; import Accordion from 'react-bootstrap/Accordion'; import Form from 'react-bootstrap/Form'; import ListGroup from 'react-bootstrap/ListGroup'; import 'bootstrap/dist/css/bootstrap.min.css'; const ResultDisplay = ({ initialAnalysis, userQuestion, setUserQuestion, userQuestionResult, userQuestionMessage, userQuestionAsked }) => { return <Row> <Col> <Row style={{marginTop: '10px'}}> <Col md={{ span: 10, offset: 1 }}> <Accordion defaultActiveKey="0"> <Accordion.Item eventKey="0"> <Accordion.Header>Analysis Result</Accordion.Header> <Accordion.Body> {initialAnalysis.analysis} </Accordion.Body> </Accordion.Item> <Accordion.Item eventKey="1"> <Accordion.Header>Raw Translated Text</Accordion.Header> <Accordion.Body> {initialAnalysis.translatedText} </Accordion.Body> </Accordion.Item> <Accordion.Item eventKey="2"> <Accordion.Header>Raw Source Text</Accordion.Header> <Accordion.Body> {initialAnalysis.rawText} </Accordion.Body> </Accordion.Item> </Accordion> </Col> </Row> <Row style={{marginTop: '10px'}}> <Col md={{ span: 8, offset: 1 }}> <Form.Control type="text" placeholder="Additional Questions" value={userQuestion} onChange={e => setUserQuestion(e.target.value)} /> </Col> <Col md={{ span: 2 }}> <Button variant="primary">Ask</Button> </Col> </Row> <Row><Col>{userQuestionMessage}</Col></Row> <Row style={{marginTop: '10px'}}> <Col md={{span: 10, offset: 1}}> {userQuestionResult && userQuestionAsked ? <ListGroup> <ListGroup.Item> <div><b>Q:</b> {userQuestionAsked}</div> <div><b>A:</b> {userQuestionResult}</div></ListGroup.Item> </ListGroup>: ''} </Col> </Row> </Col> </Row> } function App() { const [file, setFile] = useState(null); const [haveFileAnalysisResults, setHaveFileAnalysisResults] = useState(false); const [message, setMessage] = useState(''); const [userQuestionMessage, setUserMessage] = useState(''); const [initialAnalysis, setInitialAnalysis] = useState({analysis: '', translatedText: '', rawText: ''}); const [userQuestion, setUserQuestion] = useState(''); const [userQuestionResult, setUserQuestionResult] = useState(''); const [userQuestionAsked, setUserQuestionAsked] = useState(''); return ( <Container> <Row> <Col md={{ span: 8, offset: 1 }}> <Form.Group controlId="formFile"> <Form.Label>Select a File to Analyze</Form.Label> <Form.Control type="file" onChange={e => setFile(e.target.files[0])} /> </Form.Group> </Col> <Col md={{span: 2}}> <div style={{marginTop: '30px'}}><Button variant="primary" >Analyze</Button></div> </Col> <Col md={12}>{message}</Col> </Row> {haveFileAnalysisResults? <ResultDisplay initialAnalysis={initialAnalysis} userQuestion={userQuestion} setUserQuestion={setUserQuestion} userQuestionResult={userQuestionResult} userQuestionMessage={userQuestionMessage} userQuestionAsked={userQuestionAsked} />: ''} </Container>); }
Có khá nhiều mã mới, nhưng không có gì phức tạp hoặc đột phá về mã mới. Mã này chỉ là một giao diện hiển thị và nhập dữ liệu cơ bản dựa trên các thành phần React-Bootstrap.
Lưu tệp và bạn sẽ thấy bản cập nhật trình duyệt của mình để hiển thị giao diện tải tệp lên.
Chơi xung quanh với các biến và bạn sẽ thấy một giao diện như thế này. Đây sẽ là giao diện frontend của ứng dụng của chúng ta.
Bây giờ chúng ta đã viết giao diện lối vào cơ bản, hãy viết các chức năng sẽ kết nối ứng dụng với API (chưa được viết) của chúng ta. Tất cả các chức năng này sẽ được xác định trong đối tượng ứng dụng để nó có quyền truy cập vào tất cả các hook React. Nếu bạn không hoàn toàn chắc chắn những chức năng này sẽ đi đâu, bạn có thể tham khảo mã đầy đủ được lưu trữ trên GitHub .
Đầu tiên, hãy viết một vài hàm tiện ích để gửi tin nhắn cho người dùng.
const flashMessageBuilder = (setMessage) => (message) => { setMessage(message); setTimeout(() => { setMessage(''); }, (5000)); } const flashMessage = flashMessageBuilder(setMessage); const flashUserQuestionMessage = flashMessageBuilder(setUserQuestionMessage);
Như bạn có thể thấy, đây là những chức năng đơn giản hiển thị thông báo ở vị trí thích hợp và tạo thời gian để xóa thông báo sau 5 giây. Đây là một tính năng giao diện người dùng đơn giản nhưng làm cho ứng dụng trở nên năng động và dễ sử dụng hơn nhiều.
Tiếp theo, hãy viết các hàm để phân tích tệp và kiểm tra kết quả.
const pollForResults = (batchId) => { flashMessage('Checking for results...'); return new Promise((resolve, reject) => { setTimeout(() => { axios.post('http://localhost:5000/check_if_finished', {batchId}) .then(r => r.data) .then(d => { // the result should have a "status" key and a "result" key. if (d.status === 'complete') { resolve(d); // we're done! } else { resolve(pollForResults(batchId)); // wait 5 seconds and try again. } }).catch(e => reject(e)); }, 5000); }) } const analyzeFile = () => { if (file === null) { flashMessage('No file selected!'); return; } flashMessage('Uploading file...'); const formData = new FormData(); formData.append("file", file); axios.post("http://localhost:5000/analyze_file", formData, { headers: { 'Content-Type': 'multipart/form-data' } }).then(r => r.data) .then(d => { // the result should contain a batchId that we use to poll for results. flashMessage('File upload success, waiting for analysis results...'); return pollForResults(d.batchId); }) .then(({analysis, translatedText, rawText}) => { // the result should contain the initial analysis results with the proper format. setInitialAnalysis({analysis, translatedText, rawText}); setHaveFileAnalysisResults(true); // show the results display now that we have results }) .catch(e => { console.log(e); flashMessage('There was an error with the upload. Please check the console for details.'); }) }
Một lần nữa một bộ chức năng khá đơn giản. Hàm AnalyzeFile gửi tệp đến điểm cuối analy_file để phân tích. API sẽ cung cấp cho nó một ID lô mà nó sử dụng để kiểm tra kết quả với chức năng pollForResults. Hàm pollForResults sẽ chạm vào điểm cuối check_if_finished và trả về kết quả nếu quá trình phân tích kết thúc và đợi 5 giây nếu quá trình phân tích vẫn đang được xử lý. Sau đó, “luồng” phân tíchFile sẽ tiếp tục thực thi, đưa dữ liệu vào những vị trí thích hợp.
Cuối cùng, hãy viết hàm cho phép người dùng đặt câu hỏi dạng tự do:
const askUserQuestion = () => { flashUserQuestionMessage('Asking user question...') axios.post('http://localhost:5000/ask_user_question', { text: initialAnalysis.translatedText, userQuestion }).then(r => r.data) .then(d => { setUserQuestionResult(d.result); setUserQuestionAsked(userQuestion); }).catch(e => { console.log(e); flashUserQuestionMessage('There was an issue asking the question. Please check the console for details'); }); }
Một lần nữa, một chức năng khá đơn giản. Chúng tôi cung cấp văn bản đã dịch cùng với câu hỏi của người dùng để API của chúng tôi có thể tạo lời nhắc ChatGPT. Kết quả sau đó được đẩy đến các vùng chứa dữ liệu thích hợp để hiển thị.
Chúng ta đã hoàn thành khá nhiều việc với ứng dụng React, nhưng trước khi chuyển sang mã hóa API, hãy thực hiện thêm một thay đổi về mặt thẩm mỹ. Ngay bây giờ, màn hình kết quả phân tích được định cấu hình để hiển thị phân tích ChatGPT dưới dạng chuỗi. Tuy nhiên, phân tích ChatGPT thực sự là một đối tượng dữ liệu JSON, vì vậy để hiển thị chính xác nó cho con người sử dụng, chúng tôi sẽ muốn thêm một số định dạng vào đối tượng hiển thị. Thay thế mục Accordion đầu tiên bằng mã sau:
<Accordion.Item eventKey="0"> <Accordion.Header>Analysis Result</Accordion.Header> <Accordion.Body> <h6>Purpose</h6> <p>{analysis.purpose}</p> <h6>Primary Conclusion</h6> <p>{analysis.primaryConclusion}</p> <h6>Secondary Conclusion</h6> <p>{analysis.secondaryConclusion}</p> <h6>Intended Audience</h6> <p>{analysis.intendedAudience}</p> <h6>Additional Context</h6> <p>{analysis.additionalContextString}</p> </Accordion.Body> </Accordion.Item>
Bây giờ giao diện người dùng đã hoàn tất, hãy chuyển sang mã Python của chúng tôi và xây dựng phần phụ trợ.
Đầu tiên, hãy cài đặt Flask, mà chúng ta sẽ sử dụng để viết chương trình phụ trợ của mình.
Pip install flask flask-cors
Flask là một khung đơn giản để xây dựng các ứng dụng web và API web. Giao diện cực kỳ đơn giản và việc chạy máy chủ dễ dàng như:
from flask import Flask, request, jsonify from flask_cors import CORS app = Flask(__name__) CORS(app) @app.route('/') def hello_world(): return "Hello from flask!" if __name__ == '__main__': app.run(debug=True)
Chạy tệp này và điều hướng đến http://localhost:5000 trong trình duyệt của bạn và bạn sẽ thấy thông báo “Xin chào từ bình!” tin nhắn.
Bây giờ chúng ta có thể bắt đầu xây dựng chức năng API. Hãy bắt đầu bằng cách nhập các hàm cần thiết và xác định một số hằng số:
from flask import Flask, request, jsonify from flask_cors import CORS import uuid import os import json from document_analyze import upload_file, async_detect_document, check_results, \ write_to_text, translate_text, delete_objects, run_chatgpt_api,\ ask_chatgpt_question app = Flask(__name__) CORS(app) BUCKET = '[YOUR BUCKET NAME]'
Mã này giả định rằng mã máy chủ của bạn nằm trong cùng thư mục với tệp document_analyze.py mà chúng tôi đã viết trước đó, nhưng bạn có thể chọn bất kỳ cấu trúc thư mục nào mình muốn, miễn là mã máy chủ có thể tìm và nhập từ document_analyze.py. Hãy viết trình xử lý cho điểm cuối tải tệp lên:
@app.route('/analyze_file', methods=['POST']) def analyze_file(): file_to_analyze = request.files['file'] batch_name = str(uuid.uuid4()) local_file_path = 'uploads/%s.pdf' % batch_name cloud_file_path = '%s.pdf' % batch_name file_to_analyze.save(local_file_path) upload_file(local_file_path, BUCKET, cloud_file_path) async_detect_document( f'gs://{BUCKET}/{cloud_file_path}', f'gs://{BUCKET}/{batch_name}') return jsonify({ 'batchId': batch_name })
Như bạn có thể thấy, chức năng này lấy tệp đã tải lên, gửi tệp đó tới Google Cloud Storage và bắt đầu quá trình OCR. Nó sẽ trông khá quen thuộc, nhưng đây là một số thay đổi nhỏ đáng được chỉ ra. Đầu tiên, tệp được xác định bởi UUID cũng đóng vai trò là tên lô. Điều này giúp tránh các sự cố xung đột tiềm ẩn có thể phát sinh do API được gọi nhiều lần và nó cũng xác định duy nhất tất cả các tệp được sử dụng trong một đợt phân tích cụ thể, giúp dễ dàng kiểm tra tiến trình và thực hiện dọn dẹp trong dây chuyền.
Bây giờ chúng ta hãy viết trình xử lý cho phép ứng dụng kiểm tra xem quá trình phân tích đã kết thúc chưa.
@app.route('/check_if_finished', methods=['POST']) def check_if_finished(): batch_name = request.json['batchId'] if not check_results(BUCKET, batch_name): return jsonify({ 'status': 'processing' }) write_to_text(BUCKET, batch_name) all_responses = [] for result_json in os.listdir('ocr_results'): if result_json.endswith('json') and result_json.startswith(batch_name): result_file = os.path.join('ocr_results', result_json) with open(os.path.join('ocr_results', result_json)) as fp_res: response = json.load(fp_res) all_responses.extend(response['responses']) os.remove(result_file) txts = [a['fullTextAnnotation']['text'] for a in all_responses] translated_text = [translate_text(t) for t in txts] print('Running cleanup...') delete_objects(BUCKET, batch_name) os.remove('uploads/%s.pdf' % batch_name) analysis = run_chatgpt_api('\n'.join(translated_text)) analysis_res = json.loads(analysis) return jsonify({ 'status': 'complete', 'analysis': analysis, 'translatedText': translated_text, 'rawText': '\n'.join(txts) })
Một lần nữa điều này sẽ trông khá quen thuộc. Trước tiên, chúng tôi kiểm tra xem OCR đã được thực hiện chưa và nếu OCR chưa được thực hiện, chúng tôi chỉ cần trả về một thông báo cho biết lô vẫn đang được xử lý. Nếu OCR hoàn tất, chúng tôi sẽ tiếp tục phân tích, tải xuống kết quả OCR và chạy quy trình dịch và ChatGPT. Chúng tôi cũng đảm bảo dọn sạch các tệp nguồn sau khi phân tích xong để tránh phát sinh chi phí lưu trữ không cần thiết. Chúng tôi đóng gói kết quả vào đối tượng kết quả cuối cùng, chứa JSON phân tích ChatGPT, văn bản đã dịch và văn bản thô do OCR trích xuất.
Mặc dù phụ trợ câu hỏi tùy chỉnh là một tính năng mới, nhưng nó khá đơn giản. Trước tiên, chúng tôi sẽ muốn xác định hàm để đặt câu hỏi tùy chỉnh:
def ask_chatgpt_question(report_text, question_text): completion = openai.ChatCompletion.create( model="gpt-3.5-turbo", messages=[ {"role": "user", "content": ''' Consider the following report: --- %s --- Answer the following question: %s ''' % (report_text, question_text)}, ] ) return completion.choices[0]['message']['content']
Bây giờ chúng ta có thể nhập hàm và xác định điểm cuối API:
@app.route('/ask_user_question') def ask_user_question(): report_text = request.json['text'] user_question = request.json['userQuestion'] response = ask_chatgpt_question(report_text, user_question) return jsonify({ 'result': response })
Bây giờ chúng ta đã viết API của mình, hãy thử nghiệm nó từ giao diện người dùng. Hãy tiếp tục và tải tệp lên thông qua giao diện người dùng. Bạn sẽ thấy một cái gì đó như thế này:
Đợi một lúc và bạn sẽ thấy kết quả xuất hiện trên WebApp.
Nhìn xung quanh và bạn cũng sẽ thấy văn bản đã dịch:
Và văn bản nguồn thô:
Bây giờ, hãy kiểm tra giao diện câu hỏi tùy chỉnh:
Khá đẹp! Cùng với đó, chúng tôi đã xây dựng thành công ứng dụng React.js xung quanh các công cụ AI của mình!
Trong bài viết hôm nay, chúng tôi đã xây dựng một ứng dụng tận dụng một số công cụ AI mạnh mẽ nhất hiện có trên thị trường. Mặc dù ứng dụng cụ thể này hướng đến việc phân tích cú pháp và tóm tắt các tệp PDF tiếng nước ngoài, các kỹ thuật tương tự có thể được điều chỉnh để phát triển các ứng dụng mang tính cách mạng mạnh mẽ trong nhiều lĩnh vực.
Tôi hy vọng bài viết này đã truyền cảm hứng cho bạn để viết các ứng dụng dựa trên AI của riêng bạn. Nếu bạn muốn liên hệ và nói chuyện với tôi về quan điểm của bạn đối với các ứng dụng AI, tôi rất muốn nghe ý kiến của bạn. Nếu bạn đang tìm cách xây dựng một ứng dụng tương tự, vui lòng tham khảo mã được lưu trữ trên trang Github của tôi, nơi bạn có thể tìm thấy các kho lưu trữ cho Frontend và Backend
Nếu bạn muốn sử dụng ứng dụng này mà không cần phải tự xây dựng ứng dụng, thì tôi đã xây dựng và lưu trữ một phiên bản phức tạp hơn của ứng dụng để sử dụng chung. Nếu bạn muốn truy cập vào ứng dụng này, vui lòng liên hệ và chúng tôi có thể cấp quyền truy cập vào trang web.
Vui lòng theo dõi bài viết tiếp theo, nơi chúng tôi xây dựng và chạy phiên bản LLM của riêng mình. Khi bài viết được xuất bản, chúng tôi sẽ xuất bản một liên kết đến bài viết ở đây. Giữ nguyên!