fudii0921 commited on
Commit
fc5d3b5
·
verified ·
1 Parent(s): 06de90a

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +169 -0
app.py ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import cohere
3
+ import psycopg2
4
+ from psycopg2.extras import RealDictCursor
5
+ from datetime import datetime
6
+ from dotenv import load_dotenv
7
+ import os
8
+
9
+ load_dotenv(verbose=True)
10
+
11
+ # --- 設定 ---
12
+ COHERE_API_KEY = os.getenv("COHERE_API_KEY")
13
+ DB_CONFIG = {
14
+ "host": "www.ryhintl.com",
15
+ "dbname": "smair",
16
+ "user": "smairuser",
17
+ "password": "smairuser",
18
+ "port": 10629,
19
+ "connect_timeout": 15
20
+ }
21
+
22
+ co = cohere.Client(COHERE_API_KEY)
23
+
24
+ # --- 登録関数 ---
25
+ def register_trouble(date, process, equipment, title, phenomenon, cause, action, prevention, severity):
26
+ if not title or not phenomenon:
27
+ return "⚠️ タイトルと現象は必須入力項目です。"
28
+
29
+ conn = None
30
+ try:
31
+ # 1. 登録用テキストを結合してベクトル化 (Embedding生成)
32
+ # 検索精度を高めるため、主要な情報をすべて含める
33
+ input_text = f"【{process}】{title} 現象:{phenomenon} 原因:{cause} 処置:{action} 防止策:{prevention}"
34
+
35
+ res = co.embed(
36
+ texts=[input_text],
37
+ model='embed-multilingual-v3.0',
38
+ input_type='search_document' # 登録時は document を指定
39
+ )
40
+ embedding = res.embeddings[0]
41
+
42
+ # 2. DB接続とインサート
43
+ conn = psycopg2.connect(**DB_CONFIG, client_encoding='utf8')
44
+ cur = conn.cursor()
45
+
46
+ insert_query = """
47
+ INSERT INTO public.factory_troubles (
48
+ occurrence_date, process_name, equipment_name, title,
49
+ phenomenon, cause, action, prevention, severity, embedding
50
+ ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
51
+ """
52
+
53
+ cur.execute(insert_query, (
54
+ date, process, equipment, title,
55
+ phenomenon, cause, action, prevention, int(severity),
56
+ embedding
57
+ ))
58
+
59
+ conn.commit()
60
+ return f"✅ 登録完了: 「{title}」をナレッジベースに保存しました。"
61
+
62
+ except Exception as e:
63
+ return f"### ⚠️ 登録エラー\n詳細: `{repr(e)}`"
64
+ finally:
65
+ if conn: conn.close()
66
+
67
+ # --- 検索関数(前回までの機能) ---
68
+ def search_troubles(query):
69
+ if not query.strip():
70
+ return "検索ワードを入力してください。"
71
+
72
+ conn = None
73
+ try:
74
+ # 1. 質問文をベクトル化
75
+ res = co.embed(
76
+ texts=[query],
77
+ model='embed-multilingual-v3.0',
78
+ input_type='search_query'
79
+ )
80
+ query_embedding = res.embeddings[0]
81
+
82
+ # 2. DB接続
83
+ conn = psycopg2.connect(**DB_CONFIG, client_encoding='utf8')
84
+ cur = conn.cursor(cursor_factory=RealDictCursor)
85
+
86
+ # Citationのために全カラムを取得するように変更
87
+ cur.execute("""
88
+ SELECT
89
+ id, occurrence_date, process_name, equipment_name, title,
90
+ phenomenon, cause, action, prevention, severity,
91
+ 1 - (embedding <=> %s::vector) AS similarity
92
+ FROM factory_troubles
93
+ ORDER BY similarity DESC
94
+ LIMIT 3
95
+ """, (query_embedding,))
96
+ results = cur.fetchall()
97
+
98
+ if not results:
99
+ return "該当する事例が見つかりませんでした。"
100
+
101
+ # 3. 表示用にフォーマット
102
+ output = f"## 🔍 類似事例の検索結果(クエリ: {query})\n\n"
103
+
104
+ for i, r in enumerate(results):
105
+ # メインの回答部分
106
+ output += f"### {i+1}. {r['title']} (類似度: {r['similarity']:.3f})\n"
107
+ output += f"**✅ 推奨される処置:**\n{r['action']}\n\n"
108
+ output += f"**⚠️ 今後の防止策:**\n{r['prevention']}\n\n"
109
+
110
+ # --- Citation (出典詳細情報) ---
111
+ output += f"""
112
+ <details>
113
+ <summary><b>📄 この事例の出典詳細を表示 (ID: {r['id']})</b></summary>
114
+ <div style="background-color: #f9f9f9; padding: 15px; border-radius: 8px; border: 1px solid #ddd; color: #333; margin-top: 10px;">
115
+ <p><b>📅 発生日:</b> {r['occurrence_date']} | <b>🏢 工程:</b> {r['process_name']} | <b>⚙️ 設備:</b> {r['equipment_name']}</p>
116
+ <p><b>🚨 重要度:</b> {r['severity']} / 5</p>
117
+ <hr style="border: 0.5px solid #ccc;">
118
+ <p><b>【元の現象】</b><br>{r['phenomenon']}</p>
119
+ <p><b>【推定原因】</b><br>{r['cause']}</p>
120
+ </div>
121
+ </details>
122
+ \n"""
123
+ output += "\n---\n\n"
124
+
125
+ return output
126
+
127
+ except Exception as e:
128
+ return f"### ⚠️ 検索エラー\n詳細: `{repr(e)}`"
129
+ finally:
130
+ if conn: conn.close()
131
+
132
+ # --- Gradio UI 構成 ---
133
+ with gr.Blocks(title="工場トラブル・ナレッジ・プラットフォーム", css=".gradio-container {background-color: #f0f2f5}") as demo:
134
+ gr.Markdown("# 🛠 工場トラブル・ナレッジ・プラットフォーム")
135
+
136
+ with gr.Tab("事例を検索する"):
137
+ with gr.Column():
138
+ search_input = gr.Textbox(label="状況を入力して過去の知見を検索", placeholder="例: モーターの異音...")
139
+ search_btn = gr.Button("検索実行", variant="primary")
140
+ search_output = gr.Markdown()
141
+
142
+ search_btn.click(fn=search_troubles, inputs=search_input, outputs=search_output)
143
+
144
+ with gr.Tab("新規事例を登録する"):
145
+ gr.Markdown("### 📝 新しいトラブル報告の入力")
146
+ with gr.Row():
147
+ date_input = gr.Textbox(label="発生日", value=datetime.now().strftime('%Y-%m-%d'))
148
+ process_input = gr.Dropdown(label="工程名", choices=["露光", "エッチング", "成膜", "プレス", "溶接", "切削", "充填", "包装", "組立", "エッチング", "成膜", "調合", "イオン注入", "洗浄", "CMP", "熱処理", "酸洗浄", "スパッター", "封止", "殺菌", "包装", "調合", "搬送", "テスト", "共通"])
149
+ severity_input = gr.Slider(label="重要度 (1-5)", minimum=1, maximum=5, step=1, value=3)
150
+
151
+ equip_input = gr.Textbox(label="設備名")
152
+ title_input = gr.Textbox(label="トラブル件名 (Title)")
153
+ phenom_input = gr.TextArea(label="現象 (Phenomenon)")
154
+ cause_input = gr.TextArea(label="原因 (Cause)")
155
+ action_input = gr.TextArea(label="処置 (Action)")
156
+ prevent_input = gr.TextArea(label="防止策 (Prevention)")
157
+
158
+ reg_btn = gr.Button("🚀 データベースに登録", variant="primary")
159
+ reg_output = gr.Markdown()
160
+
161
+ reg_btn.click(
162
+ fn=register_trouble,
163
+ inputs=[date_input, process_input, equip_input, title_input, phenom_input, cause_input, action_input, prevent_input, severity_input],
164
+ outputs=reg_output,
165
+ show_progress="full"
166
+ )
167
+
168
+ if __name__ == "__main__":
169
+ demo.launch()