import gradio as gr import cohere import psycopg2 from psycopg2.extras import RealDictCursor from datetime import datetime from dotenv import load_dotenv import os load_dotenv(verbose=True) # --- 設定 --- COHERE_API_KEY = os.getenv("COHERE_API_KEY") DB_CONFIG = { "host": "www.ryhintl.com", "dbname": "smair", "user": "smairuser", "password": "smairuser", "port": 10629, "connect_timeout": 15 } co = cohere.Client(COHERE_API_KEY) # --- 登録関数 --- def register_trouble(date, process, equipment, title, phenomenon, cause, action, prevention, severity): if not title or not phenomenon: return "⚠️ タイトルと現象は必須入力項目です。" conn = None try: # 1. 登録用テキストを結合してベクトル化 (Embedding生成) # 検索精度を高めるため、主要な情報をすべて含める input_text = f"【{process}】{title} 現象:{phenomenon} 原因:{cause} 処置:{action} 防止策:{prevention}" res = co.embed( texts=[input_text], model='embed-multilingual-v3.0', input_type='search_document' # 登録時は document を指定 ) embedding = res.embeddings[0] # 2. DB接続とインサート conn = psycopg2.connect(**DB_CONFIG, client_encoding='utf8') cur = conn.cursor() insert_query = """ INSERT INTO public.factory_troubles ( occurrence_date, process_name, equipment_name, title, phenomenon, cause, action, prevention, severity, embedding ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s) """ cur.execute(insert_query, ( date, process, equipment, title, phenomenon, cause, action, prevention, int(severity), embedding )) conn.commit() return f"✅ 登録完了: 「{title}」をナレッジベースに保存しました。" except Exception as e: return f"### ⚠️ 登録エラー\n詳細: `{repr(e)}`" finally: if conn: conn.close() # --- 検索関数(前回までの機能) --- def search_troubles(query): if not query.strip(): return "検索ワードを入力してください。" conn = None try: # 1. 質問文をベクトル化 res = co.embed( texts=[query], model='embed-multilingual-v3.0', input_type='search_query' ) query_embedding = res.embeddings[0] # 2. DB接続 conn = psycopg2.connect(**DB_CONFIG, client_encoding='utf8') cur = conn.cursor(cursor_factory=RealDictCursor) # Citationのために全カラムを取得するように変更 cur.execute(""" SELECT id, occurrence_date, process_name, equipment_name, title, phenomenon, cause, action, prevention, severity, 1 - (embedding <=> %s::vector) AS similarity FROM factory_troubles ORDER BY similarity DESC LIMIT 3 """, (query_embedding,)) results = cur.fetchall() if not results: return "該当する事例が見つかりませんでした。" # 3. 表示用にフォーマット output = f"## 🔍 類似事例の検索結果(クエリ: {query})\n\n" for i, r in enumerate(results): # メインの回答部分 output += f"### {i+1}. {r['title']} (類似度: {r['similarity']:.3f})\n" output += f"**✅ 推奨される処置:**\n{r['action']}\n\n" output += f"**⚠️ 今後の防止策:**\n{r['prevention']}\n\n" # --- Citation (出典詳細情報) --- output += f"""
📄 この事例の出典詳細を表示 (ID: {r['id']})

📅 発生日: {r['occurrence_date']} | 🏢 工程: {r['process_name']} | ⚙️ 設備: {r['equipment_name']}

🚨 重要度: {r['severity']} / 5


【元の現象】
{r['phenomenon']}

【推定原因】
{r['cause']}

\n""" output += "\n---\n\n" return output except Exception as e: return f"### ⚠️ 検索エラー\n詳細: `{repr(e)}`" finally: if conn: conn.close() # --- Gradio UI 構成 --- with gr.Blocks(title="工場トラブル・ナレッジ・プラットフォーム", css=".gradio-container {background-color: #f0f2f5}") as demo: gr.Markdown("# 🛠 工場トラブル・ナレッジ・プラットフォーム") with gr.Tab("事例を検索する"): with gr.Column(): search_input = gr.Textbox(label="状況を入力して過去の知見を検索", placeholder="例: モーターの異音...") search_btn = gr.Button("検索実行", variant="primary") search_output = gr.Markdown() search_btn.click(fn=search_troubles, inputs=search_input, outputs=search_output) with gr.Tab("新規事例を登録する"): gr.Markdown("### 📝 新しいトラブル報告の入力") with gr.Row(): date_input = gr.Textbox(label="発生日", value=datetime.now().strftime('%Y-%m-%d')) process_input = gr.Dropdown(label="工程名", choices=["露光", "エッチング", "成膜", "プレス", "溶接", "切削", "充填", "包装", "組立", "エッチング", "成膜", "調合", "イオン注入", "洗浄", "CMP", "熱処理", "酸洗浄", "スパッター", "封止", "殺菌", "包装", "調合", "搬送", "テスト", "共通"]) severity_input = gr.Slider(label="重要度 (1-5)", minimum=1, maximum=5, step=1, value=3) equip_input = gr.Textbox(label="設備名") title_input = gr.Textbox(label="トラブル件名 (Title)") phenom_input = gr.TextArea(label="現象 (Phenomenon)") cause_input = gr.TextArea(label="原因 (Cause)") action_input = gr.TextArea(label="処置 (Action)") prevent_input = gr.TextArea(label="防止策 (Prevention)") reg_btn = gr.Button("🚀 データベースに登録", variant="primary") reg_output = gr.Markdown() reg_btn.click( fn=register_trouble, inputs=[date_input, process_input, equip_input, title_input, phenom_input, cause_input, action_input, prevent_input, severity_input], outputs=reg_output, show_progress="full" ) if __name__ == "__main__": demo.launch()