OpenAI が ChatGPT を一般向けにリリースしたとき、OpenAI 自体の幹部を含め、一般向けに採用されるスピードを予想できた人はほとんどいませんでした。それ以来、ChatGPT は TikTok を抜き、1 億ユーザーを達成する最速のアプリとなりました。あらゆる階層の人々が ChatGPT を使用して効率を向上させる方法を見つけており、企業はその使用に関するガイドラインの作成に躍起になっています。多くの学術機関を含む一部の組織は、その使用についてほぼ懐疑的ですが、テクノロジー企業などの他の組織は、より自由なポリシーを採用し、ChatGPT API を中心としたアプリケーションを作成することさえあります。今日は、そのようなアプリケーションの構築について説明します。
この記事は、1) アプリケーションの基礎となるテクノロジの説明、2) アプリケーションのバックエンド、3) アプリケーションのフロントエンドの 3 つの部分に分かれています。基本的な Python コードを読むことができれば、最初の 2 つのセクションを簡単に理解できるはずです。React.js の基本的な経験がある場合は、3 番目のセクションを問題なく理解できるはずです。
私たちが今日構築しているアプリケーションは、外国語ソースを使用して定期的に調査を行う人にとって役立ちます。良い例は、外国語で発行された政府報告書に目を通さなければならないことが多いマクロ経済学者です。これらのレポートは機械翻訳サービスにコピー&ペーストできる場合もありますが、検索できない PDF の形式で公開される場合もあります。このような場合、研究者は人間の翻訳者を雇う必要がありますが、リソースの制約により、翻訳できるレポートの数は大幅に制限されます。さらに問題を複雑にするのは、これらのレポートは非常に長くて読むのが面倒な場合があり、そのため翻訳と分析にコストと時間がかかることになります。
私たちのアプリケーションは、OCR、機械翻訳、大規模言語モデルなど、自由に使えるいくつかの AI および機械学習ツールを組み合わせることで、このプロセスを容易にします。 OCR を使用して PDF から生のテキスト コンテンツを抽出し、機械翻訳を使用して英語に翻訳し、大規模な言語モデルを使用して翻訳された抽出を分析します。
今日のアプリケーションでは、日本政府の PDF 出版物、文部科学省のイノベーション白書を見ていきます。 PDF 自体は検索可能で翻訳エンジンにコピーできますが、アプリケーションで使用されているテクノロジを紹介するために、PDF が検索不可能であるかのように動作します。オリジナルのドキュメントはここにあります。
今すぐアプリを構築したい場合は、次のセクションをスキップしてください。ただし、この記事で使用するさまざまなテクノロジをより深く理解したい場合は、次のセクションで背景について説明します。
最初に使用するテクノロジーは OCR (光学式文字認識) です。これは、一般の人々が利用できるようになった最も初期の商用機械学習アプリケーションの 1 つです。 OCR モデルとアプリケーションは、写真または画像を撮影し、画像からテキスト情報を識別して抽出することを目的としています。これは最初は簡単なタスクのように思えるかもしれませんが、実際には非常に複雑な問題です。たとえば、文字がわずかにぼやけていて、確実な識別が困難になる場合があります。文字が通常とは異なる方向に配置されている場合もあります。これは、機械学習モデルが縦書きと逆さまのテキストを識別する必要があることを意味します。これらの課題にもかかわらず、研究者は多くの高速で強力な OCR モデルを開発しており、その多くは比較的低コストで入手できます。今日のアプリケーションでは、Google Cloud API を使用してアクセスできる Google の Cloud Vision モデルを使用します。
次に使用するテクノロジーは機械翻訳です。これは、OCR と同様、非常に難しい機械学習の問題です。人間の言語は特異性と文脈の複雑さに満ちており、コンピューターによる処理と理解が特に困難になっています。中国語と英語のような異なる言語ペア間での翻訳は、これらの言語の本質的に異なる構造により、デジタル化と埋め込みに大きく異なる戦略が必要となるため、特に不正確でユーモラスな結果が得られる傾向があります。しかし、これらの課題にもかかわらず、研究者たちは強力で洗練されたモデルを開発し、一般に利用できるようにしました。今日は、利用可能な機械翻訳ツールの中で最も優れており、最も広く使用されているツールの 1 つである Google の Translation API を使用します。
私たちが使用する最後の機械学習テクノロジーは、消費者向け人工知能にとって革命的な LLM (ラージ言語モデル) です。 LLM は人間の自然言語の構造を理解することができ、大量のデータを利用して詳細で有益な応答を生成できます。このテクノロジーにはまだ多くの制限がありますが、その柔軟性とデータ処理能力は、モデルを活用するための多くの新しい技術の作成にインスピレーションを与えてきました。そのような手法の 1 つはプロンプト エンジニアリングと呼ばれ、ユーザーはモデルに対して巧みに表現され構造化された入力 (プロンプト) を作成および微調整して、望ましい結果を得ることができます。今日のアプリケーションでは、ChatGPT の API といくつかの簡単なプロンプト エンジニアリングを使用して、翻訳されたレポートを分析します。
アプリケーションのコーディングを開始する前に、まずサービスにサインアップする必要があります。
ChatGPT の API Web サイトは常に変更されているため、ChatGPT API にサインアップするための正確な手順を提供することはできません。ただし、わかりやすい手順はAPI ドキュメント Web サイトで見つけることができます。 API キーを取得するまでそのまま進めます。API キーは ChatGPT 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 ページのドキュメントを処理します。これにより、書き込まれる出力ファイルの数が減り、処理が容易になります。もう 1 つ注意すべき重要な点は、呼び出しが非同期であることです。つまり、Python スクリプトの実行は、処理の終了を待たずに続行されます。これはこの特定の段階では大きな違いはありませんが、後で Python コードを Web 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 Translation 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 が認識できる一般的に使用される規則 (キャメルケース) も使用されます。 JSON キーは短縮形ではなく完全な単語です。これにより、ChatGPT は処理の一部として「スペル修正」を行う習慣があるため、ChatGPT が応答で実際のキーを使用する可能性が高くなります。
addedContextString は、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 の全機能にアクセスできるように、このツールを中心とした Web アプリケーションを構築したいと考えます。
まずは 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;
ボイラープレート コードを削除し、いくつかの基本的なブートストラップ コンポーネントに置き換えてください。
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;
アプリを保存すると、ブラウザーでアプリが更新されるのが確認できます。かなりまばらに見えますが、心配する必要はありません。すぐに修正されます。
このアプリケーションが動作するには、ファイルをアップロードするためのファイル セレクター、翻訳と概要を表示するための結果表示、ユーザーが独自に質問できるようにするテキスト入力、および表示するための結果表示の 4 つの主要コンポーネントが必要です。ユーザーの質問に対する答え。
現時点では、より単純なコンポーネントを構築し、より複雑なインターフェイス用のプレースホルダーを配置できます。その一方で、インターフェイスを強化するために使用するデータ コンテナーを作成しましょう。
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 はバッチ ID を与え、pollForResults 関数で結果をチェックするために使用します。 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 のコーディングに進む前に、表面的な変更をもう 1 つ加えてみましょう。現在、解析結果表示は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 は、Web アプリケーションと Web 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ページでホストされているコードを自由に参照してください。
このアプリを自分で構築せずに使用したい場合は、一般的な使用のために、より洗練されたバージョンのアプリケーションを構築してホストしています。このアプリケーションへのアクセスをご希望の場合は、ご連絡ください。Web サイトへのアクセスを提供いたします。
独自の LLM インスタンスを構築して実行するフォローアップ記事に注目してください。記事が公開されたら、ここに記事へのリンクを公開します。乞うご期待!