Spaces:
No application file
No application file
Delete app.py
Browse files
app.py
DELETED
|
@@ -1,109 +0,0 @@
|
|
| 1 |
-
import gradio as gr
|
| 2 |
-
import zipfile
|
| 3 |
-
import os
|
| 4 |
-
import csv
|
| 5 |
-
from sentence_transformers import SentenceTransformer, util
|
| 6 |
-
from openai import OpenAI
|
| 7 |
-
|
| 8 |
-
# OpenAIクライアントの初期化(APIキーは環境変数から取得)
|
| 9 |
-
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
|
| 10 |
-
|
| 11 |
-
# 記事フォルダがなければZIPから展開
|
| 12 |
-
if not os.path.exists("articles"):
|
| 13 |
-
with zipfile.ZipFile("articles.zip", "r") as zip_ref:
|
| 14 |
-
zip_ref.extractall("articles")
|
| 15 |
-
|
| 16 |
-
# 記事CSVの読み込みと、内容・メタ情報の格納
|
| 17 |
-
documents, file_map = [], {}
|
| 18 |
-
with open("articles/articles.csv", "r", encoding="cp932") as f:
|
| 19 |
-
reader = csv.DictReader(f)
|
| 20 |
-
for row in reader:
|
| 21 |
-
content = row["text"]
|
| 22 |
-
documents.append(content)
|
| 23 |
-
file_map[content] = {"title": row["title"], "url": row["url"]}
|
| 24 |
-
|
| 25 |
-
# 文章埋め込みモデルのロードと記事のベクトル化
|
| 26 |
-
model = SentenceTransformer("sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2")
|
| 27 |
-
document_embeddings = model.encode(documents, convert_to_tensor=True)
|
| 28 |
-
|
| 29 |
-
# 回答生成関数
|
| 30 |
-
def generate_answer(query):
|
| 31 |
-
# ユーザーの質問をベクトル化し、記事と類似検索
|
| 32 |
-
query_embedding = model.encode(query, convert_to_tensor=True)
|
| 33 |
-
hits = util.semantic_search(query_embedding, document_embeddings, top_k=2)[0]
|
| 34 |
-
|
| 35 |
-
# 上位記事の内容とURLを抽出
|
| 36 |
-
context_parts, urls = [], []
|
| 37 |
-
for hit in hits:
|
| 38 |
-
content = documents[hit["corpus_id"]]
|
| 39 |
-
meta = file_map[content]
|
| 40 |
-
urls.append(meta["url"])
|
| 41 |
-
context_parts.append(content[:300]) # 300文字まで抜粋
|
| 42 |
-
|
| 43 |
-
# プロンプト作成(指示文+質問+記事抜粋)
|
| 44 |
-
context = "\n\n".join(context_parts)
|
| 45 |
-
prompt = (
|
| 46 |
-
"あなたは転職面接の専門家です。以下の参考記事をもとに、共感、具体策、応援の順で回答してください。"
|
| 47 |
-
"回答には、共感、具体策、応援という言葉は入れず、必ず改行を入れて、読みやすいフレンドリーで自然な日本語にしてください。\n\n"
|
| 48 |
-
f"質問: {query}\n\n---\n{context}\n---"
|
| 49 |
-
)
|
| 50 |
-
|
| 51 |
-
try:
|
| 52 |
-
# OpenAI APIで回答生成
|
| 53 |
-
response = client.chat.completions.create(
|
| 54 |
-
model="gpt-3.5-turbo",
|
| 55 |
-
messages=[{"role": "user", "content": prompt}],
|
| 56 |
-
temperature=0.6,
|
| 57 |
-
)
|
| 58 |
-
answer = response.choices[0].message.content.strip()
|
| 59 |
-
|
| 60 |
-
# 参考記事リンクのHTML生成
|
| 61 |
-
link_html = "<br>".join(
|
| 62 |
-
f'<a href="{url}" target="_blank" style="color:#1d4ed8;">{url}</a>' for url in urls
|
| 63 |
-
)
|
| 64 |
-
link_html = f"<br><br><strong>もっと詳しく知りたい方はこちら:</strong><br>{link_html}"
|
| 65 |
-
|
| 66 |
-
return answer, link_html
|
| 67 |
-
except Exception as e:
|
| 68 |
-
# エラー時のメッセージ
|
| 69 |
-
return f"エラーが発生しました:\n{str(e)}", ""
|
| 70 |
-
|
| 71 |
-
# Gradio UIの構築
|
| 72 |
-
with gr.Blocks(css="") as demo:
|
| 73 |
-
gr.Markdown("## 面接で不安なことを入力して下さい")
|
| 74 |
-
|
| 75 |
-
with gr.Row():
|
| 76 |
-
with gr.Column():
|
| 77 |
-
# 質問入力欄
|
| 78 |
-
question = gr.Textbox(
|
| 79 |
-
label="質問",
|
| 80 |
-
placeholder="例:面接で緊張してしまいます。どうすればよいですか?",
|
| 81 |
-
lines=2,
|
| 82 |
-
)
|
| 83 |
-
# 回答ボタン
|
| 84 |
-
submit_btn = gr.Button("転職面接ナビに回答してもらう", elem_id="submit-btn")
|
| 85 |
-
# リセットボタン
|
| 86 |
-
clear_btn = gr.Button("リセット")
|
| 87 |
-
with gr.Column():
|
| 88 |
-
# 回答表示欄
|
| 89 |
-
answer = gr.Textbox(label="回答", lines=10)
|
| 90 |
-
# 参考リンク表示欄
|
| 91 |
-
linkbox = gr.HTML(elem_id="custom-linkbox")
|
| 92 |
-
|
| 93 |
-
# ボタンクリック時の処理設定
|
| 94 |
-
submit_btn.click(fn=generate_answer, inputs=question, outputs=[answer, linkbox])
|
| 95 |
-
clear_btn.click(fn=lambda: ("", ""), inputs=[], outputs=[question, answer, linkbox])
|
| 96 |
-
|
| 97 |
-
# ボタンの色をカスタマイズ(JS)
|
| 98 |
-
demo.load(None, None, None, js="""
|
| 99 |
-
() => {
|
| 100 |
-
const btn = document.querySelector("#submit-btn");
|
| 101 |
-
if (btn) {
|
| 102 |
-
btn.style.backgroundColor = "#f97316";
|
| 103 |
-
btn.style.color = "white";
|
| 104 |
-
}
|
| 105 |
-
}
|
| 106 |
-
""")
|
| 107 |
-
|
| 108 |
-
# アプリ起動
|
| 109 |
-
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|