挨拶
こんにちは!現在、社内SEをしている20代社会人です。
今回が初投稿となりますので、色々と内容が疎かだったり、見づらかったするかもしれませんが、ご了承ください。
今回私が作成したのは、タイトルにもある通り、「応用情報技術者試験」の午前の4択問題で出題されるワードを自分の言葉で説明できるようにするためのWEBアプリです。
このWebアプリを作ろうとした背景ですが、去年の12月に「基本情報技術者」に合格しまして、その勢いで応用情報技術者もついでに取得を目指すことにしました。
午前問題の対策として、よく出題されるワードを自分の言葉で説明できればスムーズに回答できて得点アップにも繋がり、その後の午後問題や、更にその上のネスペなど資格取得に役立つかなと思い作ってみた次第です。
本当は10月中旬に初更新をしたかったのですが、初めての一人暮らし(引っ越し、家具の選定、役所の手続き等…)や、基本情報技術者の勉強、同時期に自作パソコンの故障(hdd,ssd,マザーボード,電源の4つが同時に壊れるという異常事態が発生)など諸々の事情が重なりこの時期になりました…
具体的な構造
1. IT用語辞典のアクセスランキングの単語を全て取得し、sqliteに保存。
2. 単語をランダムで一つ呼び出し、その単語に対する説明をユーザーが入力して、openAIに噛ませる。
3. openAIによる結果を、下記3つの観点から判定して返す
- 正確性
- 不足している点や改善点について具体的の説明
- 総合的な評価コメント
具体的なコード
※ やっている内容を大まかに記載していますので、参考にしてください
IT用語辞典から単語一覧を取り出す
- bs4でhtmlを解析し、その中のaタグに囲まれた単語を取得
- ap_question_dbというDBの中に、it_wordテーブルが存在しており、その中に番号と単語を追加
import httpx
from bs4 import BeautifulSoup
import sqlite3
class GetItDictionary:
def __init__(self):
self.url = 'https://e-words.jp/p/s-ranking.html'
def get_it_word(self) -> dict:
try:
response = httpx.get(self.url, follow_redirects=True)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'html.parser')
title_dict = {}
for index, title in enumerate(soup.find_all('a', title=True), start=1):
title_dict[index] = title['title']
print(title_dict)
except httpx.RequestError as e:
print(f"リクエストエラー: {e}")
except httpx.HTTPStatusError as e:
print(f"HTTPエラー: {e}")
return title_dict
def save_to_db(self, it_word):
conn = sqlite3.connect(r'/your/path/to/ap_question_db.db')
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS it_word (
id INTEGER PRIMARY KEY,
word TEXT UNIQUE
)
''')
cursor.executemany('''
INSERT OR REPLACE INTO it_word (id, word) VALUES (?, ?)
''', [(key, value) for key, value in it_word.items()])
# コミットして接続を閉じる
conn.commit()
conn.close()
print("データベースへの挿入が完了しました!")
return
def main():
get_it_dictionary = GetItDictionary()
it_word = get_it_dictionary.get_it_word()
get_it_dictionary.save_to_db(it_word)
return
if __name__ == "__main__":
main()
DBからランダムで一つ単語を受け取り、ユーザーが単語を回答。その単語と内容をopenAIに渡して結果を受け取る
- dbから全ての単語を受け取り、その中から一つの単語をランダムに一つ取り出す
- 単語をユーザーに渡して単語の回答を受け取る
- 単語と回答をopenAIに渡して、その結果を返す
※ inputの記述がありますが、reactとFlaskを使用し、UIで直接データのやりとりをしているため、実際には使用していません…
import openai
import sqlite3
import os
import random
from dotenv import load_dotenv
load_dotenv()
class QuestionGenerator:
def __init__(self):
self.ai_api_key = os.getenv('openai_api_key')
pass
def get_random_word_from_db(self):
try:
conn = sqlite3.connect(r'/your/path/to/ap_question_db.db')
cursor = conn.cursor()
cursor.execute('SELECT id from it_word')
all_ids = [row[0] for row in cursor.fetchall()]
if not all_ids:
return None
random_id = random.choice(all_ids)
cursor.execute("SELECT word FROM it_word WHERE id = ?", (random_id,))
result = cursor.fetchall()
print(result[0][0])
return result[0][0] if result else None
except sqlite3.Error as e:
print(f"データベースエラー: {e}")
return None
finally:
conn.close()
def check_word_definition(self, question_word):
text = input(f'次の単語について説明しなさい : {question_word} => ')
print(text)
return question_word, text
def check_explanation_accuracy(self, question_word, user_answer):
openai.api_key = self.ai_api_key
prompt = f'''
あなたは応用情報技術者試験の資格を持った講師です。
以下の単語に対する生徒の回答を評価してください。
問題: {question_word}
生徒の回答: {user_answer}
評価基準:
1. 正確性(0〜100%でスコアリングしてください)。
2. 不足している点や改善点について具体的に説明してください。
3. 最終的に、総合的な評価コメントを与えてください。
'''
response = openai.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": "あなたは試験官です。"},
{"role": "user", "content": prompt}
]
)
print(response.choices[0].message.content)
return response.choices[0].message.content
def main(self):
while True:
random_word = self.get_random_word_from_db()
if not random_word:
print("データベースから単語を取得できませんでした。")
continue
question_word, user_answer = self.check_word_definition(random_word)
if user_answer is exit:
print("終了します。")
break
self.check_explanation_accuracy(question_word, user_answer)
return
if __name__ == "__main__":
question_generator = QuestionGenerator()
question_generator.main()
Flaskで単語のデータやユーザーが入力した回答、openAIの評価などを受け渡しする。
- 「QuestionGenerator」Classを呼び出し、get_random_word_from_dbを使ってランダムな単語を取得する
- 「QuestionGenerator」Classを呼び出し、check_explanation_accuracyを使って単語の意味をopenAIに解説させる
from flask import Flask, jsonify, request
from flask_cors import CORS
import sqlite3
import random
from create_ap_puestion import QuestionGenerator
app = Flask(__name__)
CORS(app)
@app.route("/api/get-word", methods=["GET"])
def get_random_word():
question_generator = QuestionGenerator()
word = question_generator.get_random_word_from_db()
if word:
return jsonify({"word": word})
else:
return jsonify({"word": "データなし"}), 404
@app.route("/api/save-word", methods=["POST"])
def save_word():
data = request.get_json()
word = data.get("word")
description = data.get("description")
if not word:
return jsonify({"message": "単語が提供されていません"}), 400
print(f"受信した単語: {word}")
print(f"ユーザーの説明: {description}")
question_generator = QuestionGenerator()
result = question_generator.check_explanation_accuracy(word, description)
response_data = {
"processed_word": word,
"processed_description": result
}
return jsonify(response_data), 200
if __name__ == "__main__":
app.run(debug=True, port=5000)
Reactを用いて実際のUi画面を表示させる
import { useState } from "react";
function App() {
const [word, setWord] = useState<string>("");
const [processedWord, setProcessedWord] = useState<string>(""); // Flaskからの処理結果
const [processedDescription, setProcessedDescription] = useState<string>("");
const fetchWord = async () => {
try {
const response = await fetch("http://localhost:5000/api/get-word");
if (!response.ok) {
throw new Error(`HTTPエラー: ${response.status}`);
}
const data = await response.json();
console.log("取得した単語:", data);
setWord(data.word);
} catch (error) {
console.error("単語の取得に失敗しました", error);
setWord("エラーが発生しました");
}
};
const sendWord = async (userInput: string) => {
if (!word) {
alert("単語を取得してください");
return;
}
if (!userInput) {
alert("入力内容がありません");
return;
}
try {
const response = await fetch("http://127.0.0.1:5000/api/save-word", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({ word, description: userInput })
});
const data = await response.json();
console.log("Flaskからのレスポンス:", data);
// データが存在する場合のみ state を更新
setProcessedWord(data.processed_word || "データなし");
setProcessedDescription(data.processed_description || "データなし");
} catch (error) {
console.error("単語の送信に失敗しました", error);
}
};
return (
<div style={{ textAlign: "center", marginTop: "50px" }}>
<h1>ITワード説明学習ツール</h1>
<button onClick={fetchWord} style={{ padding: "10px 20px", fontSize: "16px", cursor: "pointer" }}>
単語を取得
</button>
<p>あなたが説明する単語 : {word || "まだありません"}</p>
{/* FreeTextInput に word と sendWord を渡す */}
<FreeTextInput word={word} onSendWord={sendWord} />
{processedWord && (
<div style={{ marginTop: "20px", padding: "10px", border: "1px solid gray" }}>
<h2>Open AIからの処理結果</h2>
<p>処理された単語: {processedWord}</p>
<p>処理された説明: {processedDescription}</p>
</div>
)}
</div>
);
}
// FreeTextInputのPropsに word と onSendWord を追加
type FreeTextInputProps = {
word: string;
onSendWord: (inputText: string) => void;
};
const FreeTextInput: React.FC<FreeTextInputProps> = ({ word, onSendWord }) => {
const [text, setText] = useState("");
return (
<div style={{ marginTop: "20px" }}>
<label>回答記入欄</label>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="自由に入力してください"
style={{ marginLeft: "10px", padding: "5px" }}
/>
<p>入力内容: {text}</p>
{/* 送信ボタンで受信単語とユーザー入力を一緒に送信 */}
<button onClick={() => onSendWord(text)} style={{ padding: "10px 20px", fontSize: "16px", cursor: "pointer" }}>
単語を送信
</button>
</div>
);
};
export default App;
改善点
- 回答を送信してから、応答されるまでの時間が長い
- ボタンを押した際、実際に動いているか視覚的にわからない
- 画面に画面リセットボタンがない
- IT辞典ではなく、応用状技術者ドットコムに、主要な単語がまとめてあったのを、上司から教えてもらったので、そっちを使って単語収集をする
総括
作ってみた感じ、思いの外いい感じに動作している。多分gpt3.5でも良さげな感じがしている(そっちのほうが利用料金安いしね!)
あとは、改善点を修正すればだいぶいい感じに動作すると思うから、もう少し頑張ってみる(´ε` )