Spaces:
Sleeping
Sleeping
Delete app.py
Browse files
app.py
DELETED
|
@@ -1,135 +0,0 @@
|
|
| 1 |
-
import gradio as gr
|
| 2 |
-
import pandas as pd
|
| 3 |
-
import numpy as np
|
| 4 |
-
import os
|
| 5 |
-
from openai import OpenAI
|
| 6 |
-
|
| 7 |
-
# --- 設定 ---
|
| 8 |
-
api_key = os.getenv("OPENAI_API_KEY")
|
| 9 |
-
|
| 10 |
-
# 固定ファイル名
|
| 11 |
-
MASTER_FILE = "master.csv"
|
| 12 |
-
PLM_FILE = "plm.csv"
|
| 13 |
-
|
| 14 |
-
def try_read_csv(path):
|
| 15 |
-
"""文字コードを自動判別して読み込む"""
|
| 16 |
-
if not os.path.exists(path):
|
| 17 |
-
return None, f"ファイル '{path}' が見つかりません。"
|
| 18 |
-
for enc in ['cp932', 'utf-8', 'shift_jis']:
|
| 19 |
-
try:
|
| 20 |
-
df = pd.read_csv(path, encoding=enc)
|
| 21 |
-
# ヘッダー補正ロジック
|
| 22 |
-
if '1' in df.columns and '品番' in df.iloc[0].values:
|
| 23 |
-
df.columns = df.iloc[0]
|
| 24 |
-
df = df[1:].reset_index(drop=True)
|
| 25 |
-
return df, None
|
| 26 |
-
except UnicodeDecodeError:
|
| 27 |
-
continue
|
| 28 |
-
except Exception as e:
|
| 29 |
-
return None, str(e)
|
| 30 |
-
return None, "文字コードの判定に失敗しました。"
|
| 31 |
-
|
| 32 |
-
# データの読み込み(グローバル変数)
|
| 33 |
-
M_DF, M_ERR = try_read_csv(MASTER_FILE)
|
| 34 |
-
P_DF, P_ERR = try_read_csv(PLM_FILE)
|
| 35 |
-
|
| 36 |
-
def analyze_cd_logic(target_brand):
|
| 37 |
-
# 起動時のエラーチェック
|
| 38 |
-
if M_DF is None: return f"❌ マスタ読み込みエラー: {M_ERR}"
|
| 39 |
-
if P_DF is None: return f"❌ PLMデータ読み込みエラー: {P_ERR}"
|
| 40 |
-
if not api_key: return "❌ APIキーが設定されていません。Settings > Secretsを確認してください。"
|
| 41 |
-
if not target_brand: return "⚠️ 銘柄名または親品番を入力してください。"
|
| 42 |
-
|
| 43 |
-
client = OpenAI(api_key=api_key)
|
| 44 |
-
|
| 45 |
-
try:
|
| 46 |
-
# 1. 銘柄検索
|
| 47 |
-
# 親品番、銘柄名、一般名称銘柄(3列目)を検索
|
| 48 |
-
mask = (M_DF['親品番'].astype(str) == target_brand) | \
|
| 49 |
-
(M_DF['銘柄名'].astype(str) == target_brand) | \
|
| 50 |
-
(M_DF.iloc[:, 2].astype(str) == target_brand)
|
| 51 |
-
|
| 52 |
-
brand_data = M_DF[mask]
|
| 53 |
-
if brand_data.empty:
|
| 54 |
-
return f"❌ 銘柄「{target_brand}」はマスタに見つかりません。入力内容を確認してください。"
|
| 55 |
-
|
| 56 |
-
spec = brand_data.iloc[0]
|
| 57 |
-
findings = []
|
| 58 |
-
|
| 59 |
-
# --- 論理分析(資材1Gロジック) ---
|
| 60 |
-
|
| 61 |
-
# A. 共用銘柄化(メーカー集約)
|
| 62 |
-
same_parent = M_DF[M_DF['親品番'] == spec['親品番']].copy()
|
| 63 |
-
same_parent['price'] = pd.to_numeric(same_parent['仕入単価(新)'], errors='coerce')
|
| 64 |
-
if same_parent['製紙会社'].nunique() > 1:
|
| 65 |
-
diff = same_parent['price'].max() - same_parent['price'].min()
|
| 66 |
-
if diff > 0:
|
| 67 |
-
findings.append(f"【共用銘柄化】同一品番で複数メーカー混在。単価差 {diff}円/kg。安値側への集約。")
|
| 68 |
-
|
| 69 |
-
# B. 環境対応(古紙→FSC)
|
| 70 |
-
if str(spec.get('古紙使用フラグ')) == '1':
|
| 71 |
-
findings.append("【環境対応見直し】古紙配合品からFSC認証紙へのスペック変更(▲18M事例準拠)。")
|
| 72 |
-
|
| 73 |
-
# C. 価値の再定義(一般品化)
|
| 74 |
-
if '1' in [str(spec.get('特抄フラグ')), str(spec.get('特寸フラグ'))]:
|
| 75 |
-
findings.append("【価値の再定義】特注仕様を廃止。汎用品への集約による相見積。")
|
| 76 |
-
|
| 77 |
-
# D. 仕様変更(薄物化)
|
| 78 |
-
gram = pd.to_numeric(spec['坪量 g/㎡'], errors='coerce')
|
| 79 |
-
if not np.isnan(gram) and gram > 70:
|
| 80 |
-
findings.append(f"【仕様変更】現行{gram}g/㎡。品質要件再精査による薄物化検討。")
|
| 81 |
-
|
| 82 |
-
if not findings:
|
| 83 |
-
return "💡 自動判定ロジックによる即効性のある施策は見つかりませんでした。個別交渉を検討してください。"
|
| 84 |
-
|
| 85 |
-
# OpenAIによる施策シート作成
|
| 86 |
-
prompt = f"""
|
| 87 |
-
あなたは製造業の調達部長です。以下のデータ分析結果から、実務用「CD施策シート」を作成してください。
|
| 88 |
-
|
| 89 |
-
対象銘柄: {target_brand} ({spec['製紙会社']} / {spec['仕入単価(新)']}円)
|
| 90 |
-
検出された論理: {" / ".join(findings)}
|
| 91 |
-
|
| 92 |
-
【出力項目】
|
| 93 |
-
各切り口ごとに以下を記載:
|
| 94 |
-
1. 切り口
|
| 95 |
-
2. 具体的な方法
|
| 96 |
-
3. 現状の課題(データ根拠)
|
| 97 |
-
4. 提案内容と期待効果(具体的アクション)
|
| 98 |
-
|
| 99 |
-
※挨拶、メール案、反論対策は一切不要です。
|
| 100 |
-
"""
|
| 101 |
-
|
| 102 |
-
response = client.chat.completions.create(
|
| 103 |
-
model="gpt-4o",
|
| 104 |
-
messages=[{"role": "system", "content": "あなたは論理的な調達分析官です。"},
|
| 105 |
-
{"role": "user", "content": prompt}],
|
| 106 |
-
temperature=0.3
|
| 107 |
-
)
|
| 108 |
-
return response.choices[0].message.content
|
| 109 |
-
|
| 110 |
-
except Exception as e:
|
| 111 |
-
return f"🚨 解析中にエラーが発生しました: {str(e)}"
|
| 112 |
-
|
| 113 |
-
# --- Gradio UI ---
|
| 114 |
-
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
| 115 |
-
gr.Markdown("# 🛡️ CDG AI Strategist (堅牢版)")
|
| 116 |
-
gr.Markdown("マスタデータを裏で自動参照し、利益貢献の切り口に基づいた具体的施策を立案します。")
|
| 117 |
-
|
| 118 |
-
with gr.Row():
|
| 119 |
-
with gr.Column():
|
| 120 |
-
brand_input = gr.Textbox(label="銘柄名 / 親品番 を入力", placeholder="例: Jウメ70AC")
|
| 121 |
-
btn = gr.Button("CD戦略を生成", variant="primary")
|
| 122 |
-
|
| 123 |
-
# デバッグ情報表示
|
| 124 |
-
if M_DF is not None:
|
| 125 |
-
gr.Markdown(f"✅ マスタ読み込み完了 ({len(M_DF)}行)")
|
| 126 |
-
else:
|
| 127 |
-
gr.Markdown(f"❌ マスタ読み込み失敗: {M_ERR}")
|
| 128 |
-
|
| 129 |
-
with gr.Column():
|
| 130 |
-
output = gr.Markdown(label="CD施策詳細レポート")
|
| 131 |
-
|
| 132 |
-
btn.click(fn=analyze_cd_logic, inputs=brand_input, outputs=output)
|
| 133 |
-
|
| 134 |
-
if __name__ == "__main__":
|
| 135 |
-
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|