File size: 6,886 Bytes
fc5d3b5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
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"""
<details>
<summary><b>📄 この事例の出典詳細を表示 (ID: {r['id']})</b></summary>
<div style="background-color: #f9f9f9; padding: 15px; border-radius: 8px; border: 1px solid #ddd; color: #333; margin-top: 10px;">
    <p><b>📅 発生日:</b> {r['occurrence_date']} | <b>🏢 工程:</b> {r['process_name']} | <b>⚙️ 設備:</b> {r['equipment_name']}</p>
    <p><b>🚨 重要度:</b> {r['severity']} / 5</p>
    <hr style="border: 0.5px solid #ccc;">
    <p><b>【元の現象】</b><br>{r['phenomenon']}</p>
    <p><b>【推定原因】</b><br>{r['cause']}</p>
</div>
</details>
\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()