Ryomaaa commited on
Commit
bc95072
·
verified ·
1 Parent(s): 52f8cc8

Upload app.py.txt

Browse files
Files changed (1) hide show
  1. app.py.txt +92 -151
app.py.txt CHANGED
@@ -1,171 +1,112 @@
1
- import streamlit as st
2
  import pandas as pd
3
  import numpy as np
4
  import os
5
  from openai import OpenAI
6
 
7
- # --- ページ設定 ---
8
- st.set_page_config(page_title="CDG AI Strategist - 論理分析特化版", layout="wide")
9
-
10
  # --- OpenAI設定 ---
11
  api_key = os.getenv("OPENAI_API_KEY")
12
- if api_key:
 
 
 
 
 
 
13
  client = OpenAI(api_key=api_key)
14
- else:
15
- st.error("🔑 OpenAI APIキーをSettings > Secretsに登録してください。")
16
 
17
- # --- データ読み込み ---
18
- def load_data(m_file, p_file):
19
  try:
20
- m_df = pd.read_csv(m_file)
 
21
  if '1' in m_df.columns:
22
  m_df.columns = m_df.iloc[0]
23
  m_df = m_df[1:].reset_index(drop=True)
24
- p_df = pd.read_csv(p_file)
 
25
  if '基礎情報' in p_df.columns:
26
  p_df.columns = p_df.iloc[0]
27
  p_df = p_df[1:].reset_index(drop=True)
28
- return m_df, p_df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  except Exception as e:
30
- st.error(f"データ読込エラー: {e}")
31
- return None, None
32
-
33
- # --- 徹底的な論理分析エンジン ---
34
- def run_deep_analysis(target, m_df, p_df):
35
- # 検索(親品番・銘柄名・一般名称)
36
- mask = (m_df['親品番'].astype(str) == target) | \
37
- (m_df['銘柄名'].astype(str) == target) | \
38
- (m_df.iloc[:, 2].astype(str) == target)
39
-
40
- brand_data = m_df[mask]
41
- if brand_data.empty: return None, None, []
42
-
43
- spec = brand_data.iloc[0]
44
- category = spec['分類']
45
- prods = p_df[p_df['代表銘柄'].astype(str) == target]
46
-
47
- findings = []
48
 
49
- # 1. 競争環境(メーカー集約・単価差)
50
- all_same_parent = m_df[m_df['親品番'] == spec['親品番']].copy()
51
- all_same_parent['price'] = pd.to_numeric(all_same_parent['仕入単価(新)'], errors='coerce')
52
- unique_mfrs = all_same_parent['製紙会社'].unique()
53
 
54
- if len(unique_mfrs) > 1:
55
- findings.append({
56
- "切り口": "共用銘柄化・メーカー集約",
57
- "具体的な方法": "最安値メーカーへの全量集約",
58
- "データ根拠": f"同一品番で{len(unique_mfrs)}社({', '.join(unique_mfrs)})が混在。単価差 {all_same_parent['price'].max() - all_same_parent['price'].min()}円/kgを確認。"
59
- })
60
-
61
- # 2. 一般品化(特抄・特寸フラグ)
62
- if str(spec.get('特抄フラグ')) == '1' or str(spec.get('特寸フラグ')) == '1':
63
- findings.append({
64
- "切り口": "価値の再定義・一般品化",
65
- "具体的な方法": "特殊仕様(特抄・特寸)の廃止と汎用品への切替",
66
- "データ根拠": f"現行品は特抄/特寸フラグが立っており、特定の供給元に固定。一般流通品への置換余地あり。"
67
- })
68
-
69
- # 3. 環境対応(1Gナレッジ:古紙→FSC)
70
- if str(spec.get('古紙使用フラグ')) == '1':
71
- findings.append({
72
- "切り口": "環境対応の見直し",
73
- "具体的な方法": "古紙配合品からFSC認証紙へのスペック変更",
74
- "データ根拠": "古紙配合によるコスト高(または供給不安)をFSC化(▲18Mの成功事例に準拠)で解消。"
75
- })
76
-
77
- # 4. 整理統合(寸法多寡)
78
- same_name_variants = m_df[m_df['銘柄名'] == spec['銘柄名']]
79
- dims = same_name_variants['寸法(幅) mm'].nunique()
80
- if dims >= 3:
81
- findings.append({
82
- "切り口": "仕様変更:整理統合(寸法)",
83
- "具体的な方法": "近似寸法の統合によるロット拡大",
84
- "データ根拠": f"同一銘柄名で{dims}種類の寸法が並立。標準寸法へ集約し、製造効率UPによる単価引き下げ交渉。"
85
- })
86
-
87
- # 5. 薄物化(坪量)
88
- gram = pd.to_numeric(spec['坪量 g/㎡'], errors='coerce')
89
- category_avg = pd.to_numeric(m_df[m_df['分類'] == category]['坪量 g/㎡'], errors='coerce').mean()
90
- if not np.isnan(gram) and gram > category_avg:
91
- findings.append({
92
- "切り口": "仕様変更:坪量変更(薄物化)",
93
- "具体的な方法": "現状の強度要件を再定義し、一段階下の米坪へ変更",
94
- "データ根拠": f"分類「{category}」の平均({category_avg:.1f}g/㎡)に対し、現行{gram}g/㎡と重厚。面積単価の低減が可能。"
95
- })
96
-
97
- # 6. 市場対応(商流整理)
98
- if '24年(重量)' in spec and pd.to_numeric(spec['24年(重量)'], errors='coerce') > 500000:
99
- findings.append({
100
- "切り口": "調達見直し:物流・商流",
101
- "具体的な方法": "代理店マージンの見直しまたは物流ルートの最短化",
102
- "データ根拠": f"年間購入重量 {spec['24年(重量)']}kgと大口。直送化または商流整理による手数料削減のインパクト大。"
103
- })
104
-
105
- return spec, prods, findings
106
-
107
- # --- UI構築 ---
108
- st.title("🛡️ CDG AI Strategist - 完全論理分析レポート")
109
- st.markdown("### 資材1G 利益貢献ロジックに基づくCD案生成")
110
-
111
- with st.sidebar:
112
- st.header("📁 データソース")
113
- m_file = st.file_uploader("原紙マスタ(CSV)", type="csv")
114
- p_file = st.file_uploader("製品マップ(CSV)", type="csv")
115
-
116
- target_brand = st.text_input("分析対象(親品番・銘柄名)", placeholder="Jウメ70AC")
117
-
118
- if st.button("CD戦略案を生成"):
119
- if m_file and p_file and target_brand:
120
- m_df, p_df = load_data(m_file, p_file)
121
- spec, prods, findings = run_deep_analysis(target_brand, m_df, p_df)
122
 
123
- if spec is not None:
124
- # 1. 銘柄特定
125
- st.header("1. 銘柄・製品特定結果")
126
- col1, col2 = st.columns(2)
127
- with col1:
128
- st.info(f"**銘柄:** {spec['銘柄名']} / **分類:** {spec['分類']}")
129
- st.write(f"**メーカー:** {spec['製紙会社']} | **単価:** ¥{spec['仕入単価(新)']}/kg")
130
- with col2:
131
- st.info(f"**使用製品群**")
132
- st.write(", ".join(prods['品名'].unique()[:5]) if not prods.empty else "紐付けなし")
133
-
134
- # 2. CD案(AIによる具体化)
135
- st.header("2. 切り口別の具体的CD案")
136
- if findings:
137
- # 分析で見つかった「ネタ」をAIに渡して、各々の具体的なアクションプランを作成させる
138
- with st.spinner("論理的整合性を確認し、詳細案を作成中..."):
139
-
140
- data_summary = "\n".join([f"- {f['切り口']}: {f['具体的な方法']} (根拠: {f['データ根拠']})" for f in findings])
141
-
142
- prompt = f"""
143
- あなたは製造業の調達戦略スペシャリストです。
144
- 以下のデータ分析結果に基づき、各「切り口」と「具体的な方法」に沿った、論理的に完璧なCD案を作成してください。
145
-
146
- 【入力データ】
147
- 対象銘柄: {spec['銘柄名']} ({spec['分類']})
148
- 分析で見つかった事実:
149
- {data_summary}
150
-
151
- 【出力形式】
152
- 以下の項目に沿って、各切り口ごとに簡潔かつ鋭い論理で出力してください。
153
- 1. 切り口
154
- 2. 具体的な方法
155
- 3. 現状の課題(データからの根拠)
156
- 4. 提案内容と期待効果(具体的アクション)
157
-
158
- ※交渉メールや挨拶、反論対策はいりません。実務的な「CD施策シート」として出力してください。
159
- """
160
-
161
- response = client.chat.completions.create(
162
- model="gpt-4o",
163
- messages=[{"role": "system", "content": "あなたは論理的な調達分析官です。"},
164
- {"role": "user", "content": prompt}],
165
- temperature=0.4
166
- )
167
- st.markdown(response.choices[0].message.content)
168
- else:
169
- st.warning("現在のマスタデータからは、自動判定されるCDネタが見つかりませんでした。")
170
- else:
171
- st.error("銘柄が見つかりません。")
 
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
  # --- OpenAI設定 ---
8
  api_key = os.getenv("OPENAI_API_KEY")
9
+
10
+ def analyze_cd_logic(m_file, p_file, target_brand):
11
+ if not api_key:
12
+ return "❌ OpenAI APIキーが設定されていません。Settings > Secretsを確認してください。"
13
+ if m_file is None or p_file is None or not target_brand:
14
+ return "⚠️ 原紙マスタ、製品マップの両方をアップロードし、銘柄名を入力してください。"
15
+
16
  client = OpenAI(api_key=api_key)
 
 
17
 
 
 
18
  try:
19
+ # データ読み込み
20
+ m_df = pd.read_csv(m_file.name)
21
  if '1' in m_df.columns:
22
  m_df.columns = m_df.iloc[0]
23
  m_df = m_df[1:].reset_index(drop=True)
24
+
25
+ p_df = pd.read_csv(p_file.name)
26
  if '基礎情報' in p_df.columns:
27
  p_df.columns = p_df.iloc[0]
28
  p_df = p_df[1:].reset_index(drop=True)
29
+
30
+ # 1. 銘柄特定ロジック
31
+ mask = (m_df['親品番'].astype(str) == target_brand) | \
32
+ (m_df['銘柄名'].astype(str) == target_brand) | \
33
+ (m_df.iloc[:, 2].astype(str) == target_brand)
34
+
35
+ brand_data = m_df[mask]
36
+ if brand_data.empty:
37
+ return f"❌ 銘柄「{target_brand}」は見つかりませんでした。"
38
+
39
+ spec = brand_data.iloc[0]
40
+ findings = []
41
+
42
+ # --- ロジック抽出 ---
43
+ # A. 共用銘柄化(単価差)
44
+ all_same_parent = m_df[m_df['親品番'] == spec['親品番']].copy()
45
+ all_same_parent['price'] = pd.to_numeric(all_same_parent['仕入単価(新)'], errors='coerce')
46
+ if all_same_parent['製紙会社'].nunique() > 1:
47
+ findings.append(f"【共用銘柄化】同一品番内で単価乖離あり(最大差:{all_same_parent['price'].max() - all_same_parent['price'].min()}円)。安値メーカーへの集約。")
48
+
49
+ # B. 環境対応(1Gナレッジ)
50
+ if str(spec.get('古紙使用フラグ')) == '1':
51
+ findings.append("【環境対応見直し】古紙配合品からFSC認証紙へのスペック変更。1G成功事例(▲18M)に基づくコストダウン。")
52
+
53
+ # C. 一般品化
54
+ if '1' in [str(spec.get('特抄フラグ')), str(spec.get('特寸フラグ'))]:
55
+ findings.append("【価値の再定義】特抄・特寸仕様を廃止し、汎用JIS規格品へ切り替え。競争環境の再構築。")
56
+
57
+ # D. 坪量適正化
58
+ gram = pd.to_numeric(spec['坪量 g/㎡'], errors='coerce')
59
+ if not np.isnan(gram) and gram > 70:
60
+ findings.append(f"【仕様変更】現行{gram}g/㎡。製品要件を再定義し薄物化による面積単価低減。")
61
+
62
+ if not findings:
63
+ return "💡 明確な自動判定ロジックに該当する施策が見つかりませんでした。個別交渉を検討してください。"
64
+
65
+ # OpenAIによる詳細化
66
+ prompt = f"""
67
+ あなたは調達戦略スペシャリストです。以下の分析結果を、実務的な「CD施策シート」に変換してください。
68
+ 交渉メール、挨拶、反論対策は一切不要です。論理性と具体的アクションのみを出力してください。
69
+
70
+ 【対象銘柄】: {target_brand} ({spec['製紙会社']} / {spec['仕入単価(新)']}円)
71
+ 【抽出された事実】:
72
+ {"/".join(findings)}
73
+
74
+ 【出力項目】
75
+ 各項目について以下を記載:
76
+ 1. 切り口
77
+ 2. 具体的な方法
78
+ 3. 現状の課題(データ根拠)
79
+ 4. 提案内容と期待効果(具体的アクション)
80
+ """
81
+
82
+ response = client.chat.completions.create(
83
+ model="gpt-4o",
84
+ messages=[{"role": "system", "content": "論理的な調達分析官として振る舞ってください。"},
85
+ {"role": "user", "content": prompt}],
86
+ temperature=0.3
87
+ )
88
+ return response.choices[0].message.content
89
+
90
  except Exception as e:
91
+ return f"解析エラー: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
+ # --- Gradio UI ---
94
+ with gr.Blocks(title="CDG AI Strategist") as demo:
95
+ gr.Markdown("# 🛡️ CDG AI Strategist - 資材1G論理分析")
96
+ gr.Markdown("原紙マスタと製品マップから、利益貢献の切り口に基づいた具体的CD案を生成します。")
97
 
98
+ with gr.Row():
99
+ with gr.Column():
100
+ m_input = gr.File(label="1. 原紙マスタ(CSV)をアップロード")
101
+ p_input = gr.File(label="2. 製品マップ(CSV)をアップロード")
102
+ brand_input = gr.Textbox(label="3. 分析対象の銘柄名を入力", placeholder="例: Jウメ70AC")
103
+ btn = gr.Button("分析実行", variant="primary")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
 
105
+ with gr.Column():
106
+ output = gr.Markdown(label="CD施策詳細レポート")
107
+
108
+ btn.click(fn=analyze_cd_logic, inputs=[m_input, p_input, brand_input], outputs=output)
109
+
110
+ # HuggingFace用の起動設定
111
+ if __name__ == "__main__":
112
+ demo.launch()