Corin1998 commited on
Commit
95091a6
·
verified ·
1 Parent(s): 635b170

Update core/market_infer.py

Browse files
Files changed (1) hide show
  1. core/market_infer.py +46 -52
core/market_infer.py CHANGED
@@ -1,59 +1,69 @@
1
  # core/market_infer.py
2
  from __future__ import annotations
3
- import os, json, hashlib, time
4
- from typing import Dict, Any, List
5
  from openai import OpenAI
6
 
7
  OPENAI_MODEL_TEXT = os.environ.get("OPENAI_TEXT_MODEL", "gpt-4o-mini")
8
 
9
- # ---- simple memo cache (in-memory) ----
10
  _CACHE: Dict[str, Dict[str, Any]] = {}
11
 
12
  def _client() -> OpenAI:
13
  key = os.environ.get("OPENAI_API_KEY")
14
  if not key:
 
15
  raise RuntimeError("OPENAI_API_KEY が未設定です")
16
  return OpenAI(api_key=key, timeout=45)
17
 
18
  def _cache_key(industry: str, products: List[str], country: str, horizon_years: int) -> str:
19
- raw = json.dumps({
20
- "industry": industry.strip(),
21
- "products": [p.strip() for p in products if p.strip()],
22
- "country": country.strip(),
23
- "horizon": horizon_years
24
- }, ensure_ascii=False, sort_keys=True)
 
25
  return hashlib.sha256(raw.encode("utf-8")).hexdigest()
26
 
27
- def _to_float(x):
28
  if x is None: return None
29
- try: return float(str(x).replace(",",""))
30
  except Exception: return None
31
 
 
 
 
 
 
 
 
 
 
32
  def infer_market_metrics(industry: str, products: List[str], country: str = "JP", horizon_years: int = 3) -> Dict[str, Any]:
33
  """
34
- 事業領域・商品から市場メトリクスを推定して返す。
35
- 返り値は数値を極力float化した dict
36
  """
37
  key = _cache_key(industry, products, country, horizon_years)
38
  if key in _CACHE:
39
  return _CACHE[key]
40
 
41
  prompt = f"""
42
- あなたは投資銀行のセクターアナリストです。以下の事業領域・商品と地域を前提に、
43
- “定量”重視で市場メトリクスを推定してください。専門常識に基づく概算で構いません。
44
- 出力は **厳密なJSON** のみ、単位とキーは以下スキーマに合わせてください。
45
 
46
- 【入力】
47
  - 事業領域: {industry.strip() or "(未入力)"}
48
- - 商品: {", ".join([p.strip() for p in products if p.strip()]) or "(未入力)"}
49
- - 主対象国/地域: {country}
50
  - 予測期間: 直近→{horizon_years}年
51
 
52
- 【出力スキーマ(数値は可能な限り実数値、%は実数)】
53
  {{
54
  "市場の年成長率(%)": 0.0,
55
- "市場成熟度(0-1)": 0.0, // 0=黎明, 1=成熟
56
- "競争強度(0-10)": 0.0, // 大きいほど競争が激しい
57
  "参入障壁(0-10)": 0.0,
58
  "価格決定力(0-10)": 0.0,
59
  "サイクル感応度(0-10)": 0.0,
@@ -61,13 +71,9 @@ def infer_market_metrics(industry: str, products: List[str], country: str = "JP"
61
  "技術破壊リスク(0-10)": 0.0,
62
  "TAM_億円": 0.0, "SAM_億円": 0.0, "SOM_億円": 0.0,
63
  "製品別年成長率(%)": {{"<製品名>": 0.0}},
64
- "注記": ["定量根拠の観点や前提(簡潔に)"]
65
  }}
66
-
67
- 注意:
68
- - 未知は推定値で埋めて構いません(整合性重視)。
69
- - 根拠の“観点”のみ列挙し、URLは不要。
70
- - 回答はJSONのみ。
71
  """.strip()
72
 
73
  try:
@@ -75,43 +81,31 @@ def infer_market_metrics(industry: str, products: List[str], country: str = "JP"
75
  resp = cli.chat.completions.create(
76
  model=OPENAI_MODEL_TEXT,
77
  messages=[
78
- {"role": "system", "content": "出力は厳密なJSONのみ。説明やマークダウンは不要。"},
79
  {"role": "user", "content": prompt},
80
  ],
81
  response_format={"type": "json_object"},
82
  temperature=0.3,
83
  )
84
- data = json.loads(resp.choices[0].message.content)
85
 
86
- # 型の安全化
87
  out: Dict[str, Any] = {}
88
  for k in [
89
  "市場の年成長率(%)","市場成熟度(0-1)","競争強度(0-10)","参入障壁(0-10)",
90
  "価格決定力(0-10)","サイクル感応度(0-10)","規制リスク(0-10)","技術破壊リスク(0-10)",
91
  "TAM_億円","SAM_億円","SOM_億円"
92
  ]:
93
- out[k] = _to_float(data.get(k))
94
- # 製品別成長
95
- prod = {}
96
- for name, val in (data.get("製品別年成長率(%)") or {}).items():
97
  fv = _to_float(val)
98
  if name and fv is not None:
99
- prod[str(name)] = fv
100
- out["製品別年成長率(%)"] = prod
101
- # 注記
102
- notes = data.get("注記") or []
103
  out["注記"] = [str(x) for x in notes][:5]
 
 
104
 
105
- _CACHE[key] = out
106
- return out
107
- except Exception as e:
108
- # フォールバック(無難値)
109
- fallback = {
110
- "市場の年成長率(%)": 4.0, "市場成熟度(0-1)": 0.6, "競争強度(0-10)": 6.0, "参入障壁(0-10)": 5.0,
111
- "価格決定力(0-10)": 5.0, "サイクル感応度(0-10)": 5.0, "規制リスク(0-10)": 4.0, "技術破壊リスク(0-10)": 5.0,
112
- "TAM_億円": 1000.0, "SAM_億円": 300.0, "SOM_億円": 60.0,
113
- "製品別年成長率(%)": {p: 5.0 for p in products if p.strip()},
114
- "注記": [f"推定失敗のため既定値を使用: {type(e).__name__}"]
115
- }
116
- _CACHE[key] = fallback
117
- return fallback
 
1
  # core/market_infer.py
2
  from __future__ import annotations
3
+ import os, json, hashlib
4
+ from typing import Dict, Any, List, Optional
5
  from openai import OpenAI
6
 
7
  OPENAI_MODEL_TEXT = os.environ.get("OPENAI_TEXT_MODEL", "gpt-4o-mini")
8
 
9
+ # シンプルなオンメモリキャッシュ
10
  _CACHE: Dict[str, Dict[str, Any]] = {}
11
 
12
  def _client() -> OpenAI:
13
  key = os.environ.get("OPENAI_API_KEY")
14
  if not key:
15
+ # UI側で拾えるよう例外にせずフォールバックを使わせる
16
  raise RuntimeError("OPENAI_API_KEY が未設定です")
17
  return OpenAI(api_key=key, timeout=45)
18
 
19
  def _cache_key(industry: str, products: List[str], country: str, horizon_years: int) -> str:
20
+ raw = json.dumps(
21
+ {"industry": industry.strip(),
22
+ "products": [p.strip() for p in products if p.strip()],
23
+ "country": country.strip(),
24
+ "horizon": horizon_years},
25
+ ensure_ascii=False, sort_keys=True
26
+ )
27
  return hashlib.sha256(raw.encode("utf-8")).hexdigest()
28
 
29
+ def _to_float(x: Any) -> Optional[float]:
30
  if x is None: return None
31
+ try: return float(str(x).replace(",", ""))
32
  except Exception: return None
33
 
34
+ def _fallback(products: List[str]) -> Dict[str, Any]:
35
+ return {
36
+ "市場の年成長率(%)": 4.0, "市場成熟度(0-1)": 0.6, "競争強度(0-10)": 6.0, "参入障壁(0-10)": 5.0,
37
+ "価格決定力(0-10)": 5.0, "サイクル感応度(0-10)": 5.0, "規制リスク(0-10)": 4.0, "技術破壊リスク(0-10)": 5.0,
38
+ "TAM_億円": 1000.0, "SAM_億円": 300.0, "SOM_億円": 60.0,
39
+ "製品別年成長率(%)": {p: 5.0 for p in products if p.strip()},
40
+ "注記": ["フォールバックを使用(鍵未設定/通信失敗など)"]
41
+ }
42
+
43
  def infer_market_metrics(industry: str, products: List[str], country: str = "JP", horizon_years: int = 3) -> Dict[str, Any]:
44
  """
45
+ 事業領域+製品名群から、市場メトリクスをJSONで返す。
46
+ 例外時はフォールバックを返し、必ず dict を返す(UIを落とさない)。
47
  """
48
  key = _cache_key(industry, products, country, horizon_years)
49
  if key in _CACHE:
50
  return _CACHE[key]
51
 
52
  prompt = f"""
53
+ あなたは投資銀行のセクターアナリストです。以下の前提から“定量重視”で市場メトリクスを推定。
54
+ **厳密JSONのみ**で、下スキーマのキー/単位に合わせてください。
 
55
 
56
+ [前提]
57
  - 事業領域: {industry.strip() or "(未入力)"}
58
+ - 製品: {", ".join([p.strip() for p in products if p.strip()]) or "(未入力)"}
59
+ - 地域: {country}
60
  - 予測期間: 直近→{horizon_years}年
61
 
62
+ [JSONスキーマ]
63
  {{
64
  "市場の年成長率(%)": 0.0,
65
+ "市場成熟度(0-1)": 0.0,
66
+ "競争強度(0-10)": 0.0,
67
  "参入障壁(0-10)": 0.0,
68
  "価格決定力(0-10)": 0.0,
69
  "サイクル感応度(0-10)": 0.0,
 
71
  "技術破壊リスク(0-10)": 0.0,
72
  "TAM_億円": 0.0, "SAM_億円": 0.0, "SOM_億円": 0.0,
73
  "製品別年成長率(%)": {{"<製品名>": 0.0}},
74
+ "注記": ["数値根拠の観点を簡潔に(URL不要)"]
75
  }}
76
+ 注意: 回答はJSONのみ。未知は一貫性ある推定値で可。
 
 
 
 
77
  """.strip()
78
 
79
  try:
 
81
  resp = cli.chat.completions.create(
82
  model=OPENAI_MODEL_TEXT,
83
  messages=[
84
+ {"role": "system", "content": "出力は厳密JSONのみ。説明やマークダウンは禁止。"},
85
  {"role": "user", "content": prompt},
86
  ],
87
  response_format={"type": "json_object"},
88
  temperature=0.3,
89
  )
90
+ raw = json.loads(resp.choices[0].message.content)
91
 
 
92
  out: Dict[str, Any] = {}
93
  for k in [
94
  "市場の年成長率(%)","市場成熟度(0-1)","競争強度(0-10)","参入障壁(0-10)",
95
  "価格決定力(0-10)","サイクル感応度(0-10)","規制リスク(0-10)","技術破壊リスク(0-10)",
96
  "TAM_億円","SAM_億円","SOM_億円"
97
  ]:
98
+ out[k] = _to_float(raw.get(k))
99
+ prods = {}
100
+ for name, val in (raw.get("製品別年成長率(%)") or {}).items():
 
101
  fv = _to_float(val)
102
  if name and fv is not None:
103
+ prods[str(name)] = fv
104
+ out["製品別年成長率(%)"] = prods
105
+ notes = raw.get("注記") or []
 
106
  out["注記"] = [str(x) for x in notes][:5]
107
+ except Exception:
108
+ out = _fallback(products)
109
 
110
+ _CACHE[key] = out
111
+ return out