OpenAI가 ChatGPT를 일반 대중에게 출시했을 때 OpenAI 경영진을 포함하여 일반 대중의 채택 속도를 예상할 수 있는 사람은 거의 없었습니다. 그 이후로 ChatGPT는 TikTok을 제치고 사용자 수 1억 명을 달성한 가장 빠른 앱으로 자리매김했습니다. 각계각층의 사람들이 ChatGPT를 사용하여 효율성을 높이는 방법을 찾았으며 기업에서는 ChatGPT 사용에 대한 지침을 개발하기 위해 노력하고 있습니다. 많은 학술 기관을 포함한 일부 조직은 대부분 이 사용에 대해 회의적인 반면, 기술 회사와 같은 다른 조직은 ChatGPT API를 중심으로 애플리케이션을 만드는 등 훨씬 더 자유로운 정책을 채택했습니다. 오늘은 그러한 애플리케이션을 구축하는 과정을 살펴보겠습니다.
이 기사는 1) 애플리케이션의 기본 기술에 대한 설명, 2) 애플리케이션의 백엔드, 3) 애플리케이션의 프런트엔드의 세 부분으로 구성됩니다. 기본적인 Python 코드를 읽을 수 있다면 처음 두 섹션을 쉽게 따라갈 수 있고, React.js에 대한 기본적인 경험이 있다면 세 번째 섹션도 문제 없이 따라갈 수 있습니다.
오늘 우리가 구축하고 있는 애플리케이션은 외국어 소스를 사용하여 정기적으로 연구하는 모든 사람에게 유용할 것입니다. 좋은 예는 외국어로 출판된 정부 보고서를 자주 읽어야 하는 거시경제학자일 것입니다. 때로는 이러한 보고서를 기계 번역 서비스에 복사하여 붙여넣을 수도 있지만 검색할 수 없는 PDF 형식으로 게시되는 경우도 있습니다. 이러한 경우 연구자는 인간 번역가를 고용해야 하지만 리소스 제약으로 인해 번역할 수 있는 보고서 수가 크게 제한됩니다. 문제를 더욱 복잡하게 만드는 것은 이러한 보고서가 매우 길고 읽기 지루하기 때문에 번역 및 분석에 비용과 시간이 많이 소모된다는 점입니다.
우리의 애플리케이션은 OCR, 기계 번역, 대형 언어 모델 등 다양한 AI 및 기계 학습 도구를 결합하여 이 프로세스를 더 쉽게 만들어줍니다. OCR을 사용하여 PDF에서 원시 텍스트 콘텐츠를 추출하고 기계 번역을 사용하여 영어로 번역한 다음 대규모 언어 모델을 사용하여 번역된 추출을 분석합니다.
오늘의 적용을 위해 일본 정부가 발행한 PDF 간행물과 문부과학성이 발행한 혁신 백서를 살펴보겠습니다. PDF 자체는 검색 가능하고 번역 엔진에 복사할 수 있지만, 애플리케이션에 사용된 기술을 보여주기 위해 PDF를 검색할 수 없는 것처럼 행동할 것입니다. 원본 문서는 여기에서 찾을 수 있습니다.
지금 앱을 빌드하고 싶다면 다음 섹션을 건너뛰어도 됩니다. 그러나 이 기사에서 사용할 다양한 기술을 더 잘 이해하고 싶다면 다음 섹션에서 약간의 배경 지식을 제공할 것입니다.
우리가 사용할 첫 번째 기술은 일반 대중에게 제공되는 최초의 상용 기계 학습 애플리케이션 중 하나인 OCR(광학 문자 인식)입니다. OCR 모델 및 애플리케이션은 사진이나 이미지를 촬영한 다음 이미지에서 텍스트 정보를 식별하고 추출하는 것을 목표로 합니다. 처음에는 간단한 작업처럼 보일 수 있지만 문제는 실제로 매우 복잡합니다. 예를 들어 글자가 약간 흐릿하여 확실한 식별이 어려울 수 있습니다. 문자는 비정상적인 방향으로 배열될 수도 있습니다. 즉, 기계 학습 모델이 수직 및 거꾸로 된 텍스트를 식별해야 함을 의미합니다. 이러한 과제에도 불구하고 연구자들은 빠르고 강력한 OCR 모델을 많이 개발했으며 그 중 다수는 상대적으로 저렴한 비용으로 사용할 수 있습니다. 오늘의 애플리케이션에서는 Google Cloud API를 사용하여 액세스할 수 있는 Google의 Cloud Vision 모델을 사용하겠습니다.
우리가 사용할 다음 기술은 기계 번역입니다. 이는 OCR과 마찬가지로 매우 어려운 기계 학습 문제입니다. 인간의 언어는 특이성과 문맥상의 복잡성으로 가득 차 있어 컴퓨터가 처리하고 이해하기가 특히 어렵습니다. 중국어와 영어와 같은 서로 다른 언어 쌍 간의 번역은 디지털화 및 임베딩을 위해 매우 다른 전략이 필요한 언어의 본질적으로 다른 구조로 인해 특히 부정확하고 유머러스한 결과를 생성하는 경향이 있습니다. 그러나 이러한 어려움에도 불구하고 연구자들은 강력하고 정교한 모델을 개발하여 일반적으로 사용할 수 있게 만들었습니다. 오늘 우리는 가장 훌륭하고 널리 사용되는 기계 번역 도구 중 하나인 Google의 번역 API를 사용할 것입니다.
우리가 사용할 마지막 기계 학습 기술은 소비자 인공 지능에 혁명을 가져온 LLM(Large Language Model)입니다. LLM은 자연적인 인간 언어의 구조를 이해할 수 있으며, 상세하고 유익한 답변을 생성하기 위해 대량의 데이터를 활용할 수 있습니다. 기술에는 여전히 많은 한계가 있지만 유연성과 데이터 처리 기능은 모델을 활용하기 위한 많은 새로운 기술의 탄생에 영감을 주었습니다. 그러한 기술 중 하나를 프롬프트 엔지니어링(Prompt Engineering) 이라고 합니다. 사용자는 능숙하게 표현되고 구조화된 입력 또는 프롬프트를 모델에 작성하고 조정하여 원하는 결과를 얻습니다. 오늘의 애플리케이션에서는 번역된 보고서를 분석하는 데 도움이 되도록 ChatGPT의 API와 몇 가지 간단한 프롬프트 엔지니어링을 사용하겠습니다.
애플리케이션 코딩을 시작하기 전에 먼저 서비스에 등록해야 합니다.
ChatGPT의 API 웹사이트는 항상 변경되기 때문에 ChatGPT API에 가입하는 정확한 단계를 제공할 수 없습니다. 그러나 API 문서 웹사이트 에서 따라하기 쉬운 지침을 찾을 수 있습니다. ChatGPT API를 호출하는 데 필요한 API 키를 얻을 때까지 진행하세요.
Google Cloud는 약간 더 복잡하지만 가입도 비교적 간단합니다. Google Cloud 콘솔 로 이동하여 프로젝트 설정 안내를 따르세요. 프로젝트에 들어가면 IAM 및 관리 콘솔로 이동하여 서비스 계정을 생성할 수 있습니다. Google Cloud 콘솔은 항상 변경되고 있지만 웹페이지에서 'IAM'과 '서비스 계정'을 검색하기만 하면 인터페이스를 탐색할 수 있습니다. 서비스 계정이 생성되면 개인 키 파일 사본을 컴퓨터에 다운로드할 수 있습니다. 번역 REST API는 키 파일 대신 개인 키 문자열을 사용하므로 개인 키 문자열도 복사해야 합니다.
Google Cloud 설정을 마무리하기 전에 기본 콘솔 페이지에서 Machine Vision API를 활성화해야 합니다. 간단히 Machine Vision API를 검색하고 Google에서 제품을 클릭한 후 API를 활성화하세요. 또한 이 프로젝트에 사용할 데이터를 보관할 버킷을 생성할 수도 있습니다.
이제 적절한 서비스에 등록했으므로 Python에서 애플리케이션 코딩을 시작할 준비가 되었습니다. 먼저 Python 환경에 필수 패키지를 설치해야 합니다.
Pip install google-cloud-storage google-cloud-vision openai requests
설치가 완료되면 새 폴더를 만들고, PDF 파일을 다운로드한 후, 같은 폴더에 새 Python 파일을 만들어 보겠습니다. document_analyze.py라고 부르겠습니다. 필요한 패키지를 가져오는 것부터 시작합니다.
import requests Import openai from google.cloud import vision from google.cloud import storage Import os Import json Import time Import shutil
그런 다음 애플리케이션이 방금 가입한 클라우드 서비스를 사용할 수 있도록 몇 가지 기본 설정을 수행할 수 있습니다.
os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = [Path to your Google Cloud key file] openai.api_key = [your openAI API key]
이러한 사용자 인증 정보가 있으면 이제 Python 스크립트에서 Google Cloud 및 ChatGPT API에 액세스할 수 있습니다. 이제 앱에 원하는 기능을 제공하는 함수를 작성할 수 있습니다.
이제 애플리케이션의 구성 요소가 될 일부 기능 구축을 시작할 수 있습니다. 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')
각 기능이 어떤 역할을 하는지 자세히 살펴보겠습니다.
upload_file
함수는 Google Cloud Storage 컨테이너에서 버킷을 가져와서 여기에 파일을 업로드하는 함수입니다. Google Cloud의 탁월한 추상화 덕분에 이 함수를 매우 쉽게 작성할 수 있습니다.
async_detect_document
함수는 Google Cloud의 OCR 함수를 비동기식으로 호출합니다. Google Cloud에서 사용할 수 있는 옵션이 많기 때문에 몇 가지 구성 객체를 인스턴스화해야 하지만 실제로는 Google Cloud에 소스 파일의 위치와 출력을 작성해야 하는 위치를 알려주는 것뿐입니다. batch_size
변수가 100으로 설정되어 있으므로 Google Cloud는 한 번에 100페이지의 문서를 처리합니다. 이렇게 하면 작성되는 출력 파일 수가 줄어들어 처리가 더 쉬워집니다. 주목해야 할 또 다른 중요한 점은 호출이 비동기식이라는 것입니다. 즉, 처리가 완료될 때까지 기다리지 않고 Python 스크립트 실행이 계속됩니다. 이것이 이 특정 단계에서는 큰 차이를 만들지 않지만 나중에 Python 코드를 웹 API로 전환할 때 더 유용해질 것입니다.
check_results
함수는 처리가 완료되었는지 확인하는 간단한 클라우드 저장 함수입니다. OCR 함수를 비동기적으로 호출하기 때문에 이 함수를 주기적으로 호출하여 결과 파일이 있는지 확인해야 합니다. 결과 파일이 있으면 함수는 true를 반환하고 분석을 계속할 수 있습니다. 결과 파일이 없으면 함수는 false를 반환하고 처리가 완료될 때까지 계속 기다립니다.
write_to_text
함수는 추가 처리를 위해 결과 파일을 디스크에 다운로드합니다. 이 함수는 특정 접두사가 있는 버킷의 모든 파일을 반복하고, 출력 문자열을 검색하고, 결과를 로컬 파일 시스템에 씁니다.
delete_objects
함수는 OCR과 엄격하게 관련되지는 않지만 시스템이 Google Cloud Storage에 불필요한 아티팩트를 유지하지 않도록 업로드된 파일을 정리합니다.
이제 OCR 호출을 마쳤으니 기계 번역 코드를 살펴보겠습니다!
이제 번역 기능을 정의할 수 있습니다.
# 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']
이러한 기능은 다소 간단합니다. detect_language
함수는 언어 감지 API를 호출하여 후속 translate_text
호출에 대한 소스 언어를 결정합니다. PDF가 일본어로 작성되었다는 것을 알고 있지만 응용 프로그램이 다른 언어를 처리할 수 있도록 언어 감지를 실행하는 것이 여전히 모범 사례입니다. translate_text
함수는 단순히 Google 번역 API를 사용하여 감지된 소스 언어의 텍스트를 영어로 번역하지만, 소스 언어가 이미 영어라고 판단되면 번역을 건너뜁니다.
마지막으로 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']
Python 호출은 상대적으로 간단한 API 호출이지만 프롬프트는 특정 결과를 생성하는 방식으로 작성되었습니다.
프롬프트는 보고서의 텍스트를 컨텍스트로 제공하므로 ChatGPT는 보고서를 쉽게 분석할 수 있습니다. 텍스트는 점선으로 구분되어 보고서가 끝나는 위치와 질문이 시작되는 위치를 쉽게 인식할 수 있습니다.
질문은 단락 형식으로 기술되지 않고 열거되어 있습니다. 따라서 응답은 유사한 구조를 따를 가능성이 높습니다. 열거된 구조를 사용하면 ChatGPT가 단락 형식으로 응답하는 경우보다 결과를 코드로 구문 분석하기가 훨씬 쉽습니다.
프롬프트는 응답 형식(이 경우 JSON 형식)을 지정합니다. JSON 형식은 코드로 처리하기가 매우 쉽습니다.
프롬프트는 JSON 개체의 키를 지정하고 질문과 연결하기 매우 쉬운 키를 선택합니다.
또한 키는 ChatGPT가 인식해야 하는 일반적으로 사용되는 규칙(camelCase)을 사용합니다. JSON 키는 축약형이 아닌 전체 단어입니다. ChatGPT는 처리의 일부로 "맞춤법 수정"을 수행하는 습관이 있으므로 ChatGPT가 응답에서 실제 키를 사용할 가능성이 더 높습니다.
extraContextString은 ChatGPT가 추가 정보를 전달할 수 있는 출구를 제공합니다. 이는 대규모 언어 모델의 자유 형식 분석 기능을 활용합니다.
프롬프트는 해당 주제에 대한 기술적인 토론에서 자주 발견되는 표현을 사용합니다. API 호출의 구조에서 짐작할 수 있듯이 ChatGPT의 "진정한" 목표는 반드시 프롬프트에 대한 답변을 제공하는 것이 아니라 대화의 다음 줄을 예측하는 것입니다.
따라서 프롬프트가 표면 수준 토론의 대사처럼 표현되면 표면 수준의 답변을 얻을 가능성이 높지만, 프롬프트가 전문가 토론의 대사처럼 표현되면 전문적인 결과를 얻을 가능성이 더 높습니다. 이 효과는 수학이나 기술과 같은 과목에서 특히 두드러지지만 여기에도 관련이 있습니다.
위의 내용은 사용자가 특정 결과를 얻기 위해 프롬프트를 구성하는 "프롬프트 엔지니어링"이라는 새로운 작업 본문의 기본 기술입니다. ChatGPT와 같은 대규모 언어 모델의 경우 프롬프트를 주의 깊게 작성하면 모델의 효율성이 극적으로 향상될 수 있습니다. 코딩에는 많은 유사점이 있지만 신속한 엔지니어링에는 엔지니어 측에서 훨씬 더 많은 직관과 모호한 추론이 필요합니다. ChatGPT와 같은 도구가 작업장에 더욱 많이 내장됨에 따라 엔지니어링은 더 이상 순전히 기술적인 노력이 아니라 철학적인 노력이 될 것이며 엔지니어는 직관과 대규모 언어 모델에 대한 "마음 이론"을 개발하는 데 주의를 기울여야 합니다. 그들의 효율성.
이제 모든 것을 하나로 묶어 보겠습니다. 이전 섹션을 따랐다면 아래 코드 블록은 이해하기 매우 간단할 것입니다.
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'])
보고서를 버킷에 업로드하고 OCR을 시작한 다음 OCR이 완료될 때까지 기다립니다. 그런 다음 OCR 결과를 다운로드하여 목록에 넣습니다. 결과 목록을 번역하여 ChatGPT로 보내 분석하고, 분석 결과를 인쇄해 드립니다.
AI 도구는 결정적이지 않기 때문에 이 코드를 실행하면 유사하지만 동일하지 않은 결과를 얻을 가능성이 높습니다. 그러나 위에 링크된 PDF를 사용하여 내가 얻은 결과는 다음과 같습니다.
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.
일본어 사용자로서 분석이 상당히 좋은 것을 확인할 수 있습니다! 자신만의 PDF를 제공하고 필요에 맞게 질문을 변경하여 직접 실험할 수 있습니다. 이제 귀하는 접하는 모든 외국어 PDF를 번역하고 요약할 수 있는 강력한 AI 도구를 갖게 되었습니다. 이 기사의 대상 독자라면 방금 열린 가능성에 대해 상당한 흥분을 느끼셨기를 바랍니다!
전체 백엔드 코드는 내 GitHub 에서 찾을 수 있습니다.
우리는 이미 강력한 앱을 구축했지만 현재 형태에서는 사용자가 애플리케이션을 최대한 활용하려면 약간의 Python을 알아야 합니다. 코드를 읽거나 쓰고 싶지 않은 사용자가 있다면 어떻게 될까요? 이 경우 우리는 이 도구를 중심으로 웹 애플리케이션을 구축하여 사람들이 브라우저에서 편안하게 AI의 모든 기능에 액세스할 수 있도록 할 것입니다.
React 애플리케이션을 만드는 것부터 시작해 보겠습니다. Node가 설치되어 있는지 확인하고 애플리케이션 코드를 저장할 폴더로 이동한 후 create-react-app 스크립트를 실행하고 몇 가지 기본 패키지를 설치합니다.
npx create-react-app llm-frontend
그런 다음:
cd llm-frontend npm install bootstrap react-bootstrap axios
완전한 애플리케이션을 개발하는 경우 상태 관리 및 라우팅을 처리하기 위해 패키지도 설치하고 싶지만 이는 이 기사의 범위를 벗어납니다. App.jsx 파일을 편집하기만 하면 됩니다.
npm run start
를 실행하여 개발 서버를 시작하면 브라우저에서 http://localhost:3000 페이지가 열립니다. 해당 페이지를 유지하고 즐겨 사용하는 텍스트 편집기에서 App.jsx 파일을 엽니다. 다음과 같은 내용이 표시됩니다.
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;
계속해서 상용구 코드를 삭제하고 일부 기본 Bootstrap 구성 요소로 바꾸십시오.
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;
앱을 저장하면 브라우저에서 업데이트된 것을 볼 수 있습니다. 지금은 매우 드물게 보일 수 있지만 걱정하지 마세요. 곧 수정할 예정입니다.
이 애플리케이션이 작동하려면 파일을 업로드하기 위한 파일 선택기, 번역 및 요약을 표시하기 위한 결과 표시, 사용자가 스스로 질문할 수 있는 텍스트 입력, 표시할 결과 표시 등 네 가지 주요 구성 요소가 필요합니다. 사용자 질문에 대한 답변입니다.
지금은 더 간단한 구성 요소를 구축하고 더 복잡한 인터페이스를 위해 자리 표시자를 넣을 수 있습니다. 그 동안 인터페이스를 구동하는 데 사용할 데이터 컨테이너를 만들어 보겠습니다.
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>); }
새로운 코드가 꽤 많지만 새 코드에 대해 복잡하거나 획기적인 것은 없습니다. 코드는 React-Bootstrap 구성 요소를 기반으로 하는 기본 데이터 입력 및 표시 인터페이스일 뿐입니다.
파일을 저장하면 브라우저 업데이트가 표시되어 파일 업로드 인터페이스가 표시됩니다.
변수를 가지고 놀아보면 다음과 같은 인터페이스가 보일 것입니다. 이것이 우리 애플리케이션의 프런트엔드 인터페이스가 될 것입니다.
이제 기본 프런트엔드 인터페이스를 작성했으므로 애플리케이션을 (아직 작성되지 않은) API에 연결하는 함수를 작성해 보겠습니다. 이러한 함수는 모두 앱 개체에 정의되므로 모든 React 후크에 액세스할 수 있습니다. 이러한 기능이 어디로 가야 할지 완전히 확신하지 못하는 경우 GitHub 에 호스팅된 전체 코드를 참조할 수 있습니다.
먼저 사용자에게 메시지를 전달하기 위한 몇 가지 유틸리티 함수를 작성해 보겠습니다.
const flashMessageBuilder = (setMessage) => (message) => { setMessage(message); setTimeout(() => { setMessage(''); }, (5000)); } const flashMessage = flashMessageBuilder(setMessage); const flashUserQuestionMessage = flashMessageBuilder(setUserQuestionMessage);
보시다시피 적절한 위치에 메시지를 표시하고 5초 후에 메시지를 삭제할 시간을 생성하는 간단한 기능입니다. 이는 단순한 UI 기능이지만 앱이 훨씬 더 동적이고 사용하기 쉬운 느낌을 줍니다.
다음으로, 파일을 분석하고 결과를 확인하는 함수를 작성해 보겠습니다.
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.'); }) }
다시 한 번 매우 간단한 기능 세트입니다. analyzeFile 함수는 분석을 위해 파일을 analyze_file 엔드포인트로 보냅니다. API는 pollForResults 함수로 결과를 확인하는 데 사용하는 배치 ID를 제공합니다. pollForResults 함수는 check_if_finished 엔드포인트에 도달하여 분석이 완료되면 결과를 반환하고, 분석이 아직 처리 중이면 5초 동안 기다립니다. 그런 다음 analyzeFile "스레드"가 계속 실행되어 데이터를 적절한 위치에 배치합니다.
마지막으로 사용자가 자유 형식 질문을 할 수 있는 함수를 작성해 보겠습니다.
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'); }); }
다시 말하지만, 매우 간단한 기능입니다. API가 ChatGPT 프롬프트를 구성할 수 있도록 사용자 질문과 함께 번역된 텍스트를 제공합니다. 그런 다음 결과는 표시를 위해 적절한 데이터 컨테이너로 푸시됩니다.
React 앱은 거의 끝났지만 API 코딩으로 넘어가기 전에 외관상 변경 사항을 한 가지 더 적용해 보겠습니다. 현재 분석 결과 표시는 ChatGPT 분석을 문자열로 표시하도록 구성되어 있습니다. 그러나 ChatGPT 분석은 실제로 JSON 데이터 개체이므로 사람이 사용할 수 있도록 올바르게 표시하려면 표시 개체에 일부 형식을 추가해야 합니다. 첫 번째 Accordion 항목을 다음 코드로 바꿉니다.
<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>
이제 프론트엔드가 완료되었습니다. Python 코드로 이동하여 백엔드를 구축해 보겠습니다.
먼저 백엔드를 작성하는 데 사용할 Flask를 설치하겠습니다.
Pip install flask flask-cors
Flask는 웹 애플리케이션과 웹 API를 구축하기 위한 간단한 프레임워크입니다. 인터페이스는 믿을 수 없을 정도로 간단하며 서버를 실행하는 것은 다음과 같이 쉽습니다.
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)
이 파일을 실행하고 브라우저에서 http://localhost:5000 으로 이동하면 "Hello from Flask!"가 표시됩니다. 메시지.
이제 API 기능 구축을 시작할 수 있습니다. 필요한 함수를 가져오고 몇 가지 상수를 정의하는 것부터 시작해 보겠습니다.
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]'
이 코드는 서버 코드가 이전에 작성한 document_analyze.py 파일과 동일한 폴더에 있다고 가정하지만 서버 코드가 document_analyze.py에서 찾아 가져올 수 있는 한 원하는 디렉터리 구조를 선택할 수 있습니다. 파일 업로드 엔드포인트에 대한 핸들러를 작성해 보겠습니다.
@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 })
보시다시피 이 함수는 업로드된 파일을 가져와서 Google Cloud Storage로 전송하고 OCR 프로세스를 시작합니다. 꽤 익숙해 보일 것입니다. 그러나 여기에 지적할 가치가 있는 몇 가지 작은 변경 사항이 있습니다. 먼저 파일은 배치 이름으로도 사용되는 UUID로 식별됩니다. 이렇게 하면 API가 여러 번 호출될 때 발생할 수 있는 잠재적인 충돌 문제를 방지할 수 있으며 특정 분석 배치에 사용되는 모든 파일을 고유하게 식별하므로 진행 상황을 더 쉽게 확인하고 정리 작업을 수행할 수 있습니다.
이제 앱이 분석이 완료되었는지 확인할 수 있는 핸들러를 작성해 보겠습니다.
@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) })
이번에도 꽤 익숙해 보일 것입니다. 먼저 OCR이 완료되었는지 확인하고 OCR이 완료되지 않은 경우 배치가 아직 처리 중이라는 메시지를 반환합니다. OCR이 완료되면 분석을 계속하여 OCR 결과를 다운로드하고 번역 및 ChatGPT 파이프라인을 실행합니다. 또한 불필요한 저장 비용이 발생하지 않도록 분석이 완료되면 소스 파일을 정리합니다. 결과를 ChatGPT 분석 JSON, 번역된 텍스트, OCR에서 추출한 원시 텍스트가 포함된 최종 결과 개체로 패키징합니다.
사용자 정의 질문 백엔드는 새로운 기능이지만 매우 간단합니다. 먼저 맞춤 질문을 하는 함수를 정의하겠습니다.
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']
이제 함수를 가져오고 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 })
이제 API를 작성했으므로 프런트엔드에서 테스트해 보겠습니다. 계속해서 프런트엔드를 통해 파일을 업로드하세요. 다음과 같은 내용이 표시됩니다.
잠시 기다리면 WebApp에 결과가 표시됩니다.
주위를 둘러보면 번역된 텍스트도 볼 수 있습니다.
그리고 원시 소스 텍스트는 다음과 같습니다.
이제 사용자 정의 질문 인터페이스를 테스트해 보겠습니다.
꽤 좋은! 이를 통해 우리는 AI 도구를 중심으로 React.js 애플리케이션을 성공적으로 구축했습니다!
오늘 기사에서는 현재 시장에 나와 있는 가장 강력한 AI 도구 중 일부를 활용하는 애플리케이션을 구축했습니다. 이 특정 응용 프로그램은 외국어 PDF를 구문 분석하고 요약하는 데 적합하지만 유사한 기술을 적용하여 여러 분야에서 강력하고 혁신적인 응용 프로그램을 개발할 수 있습니다.
이 기사가 여러분이 AI 기반 애플리케이션을 직접 작성하는 데 영감을 주기를 바랍니다. AI 애플리케이션에 대한 귀하의 관점에 대해 저에게 연락하고 이야기하고 싶다면 귀하의 의견을 듣고 싶습니다. 유사한 앱을 구축하려는 경우 프런트엔드 및 백엔드 용 리포지토리를 찾을 수 있는 내 Github 페이지에 호스팅된 코드를 자유롭게 참조하세요.
이 앱을 직접 구축할 필요 없이 사용하고 싶다면 일반적인 용도로 사용할 수 있는 보다 정교한 버전의 애플리케이션을 구축하고 호스팅했습니다. 이 애플리케이션에 액세스하려면 연락해 주세요. 웹사이트에 대한 액세스 권한을 제공할 수 있습니다.
자체 LLM 인스턴스를 구축하고 실행하는 후속 기사를 살펴보시기 바랍니다. 기사가 게시되면 여기에 기사 링크를 게시하겠습니다. 계속 지켜봐 주시기 바랍니다!