import os from typing import Literal from openai import OpenAI _OPENAI_CLIENT = None def _client() -> OpenAI: global _OPENAI_CLIENT if _OPENAI_CLIENT is None: api_key = os.getenv("OPENAI_API_KEY", "") if not api_key: raise RuntimeError("OPENAI_API_KEY is not set in Secrets.") _OPENAI_CLIENT = OpenAI(api_key=api_key) return _OPENAI_CLIENT TONE_MAP = { "neutral": "ニュートラルで事実中心", "formal": "フォーマルで簡潔", "friendly": "親しみやすく平易", "investor": "投資家向け、KPI/財務メトリクスを強調", "pr_bold": "PR向け、ヘッドラインを強調し勢いある表現", } TYPE_GUIDE = { "press_release": "ニュースリリース: タイトル/リード/本文/会社概要/問い合わせ先", "ir_letter": "IRレター: タイトル/ご挨拶/ハイライト/今後の見通し/お問い合わせ", "investor_summary": "投資家向けサマリー: 3-7箇条書き、KPI・成長要因・リスク要因", } SYSTEM_TMPL = """ あなたは日本語のPR/IRライターです。事実の正確性、簡潔さ、再利用しやすいMarkdown構成を重視します。 - 出力は必ずMarkdown。 - センセーショナルすぎる表現や根拠のない主張は避ける。 - プレス/IRのルールに配慮し、誤解を招かない文に整える。 """ PROMPT_TMPL = """ 【目的】{ctype}のドラフトを{tone_ja}で作成。 【入力テキスト要約】以下の素材から、要点を抽出し、{ctype_guide}に沿ってMarkdownドラフトを生成。 --- {source_text} --- 出力要件: - 1行目に短いタイトル(H1) - 適切な小見出し(H2/H3) - 箇条書きは短文で - ファクトと数値はそのまま - 最後に「メタデータ」セクションを追加(推奨タグ・要約140字・推奨URLスラッグ) """ def generate_draft(source_text: str, content_type: Literal["press_release","ir_letter","investor_summary"], tone: str): client = _client() tone_ja = TONE_MAP.get(tone, TONE_MAP["neutral"]) guide = TYPE_GUIDE[content_type] messages = [ {"role": "system", "content": SYSTEM_TMPL}, {"role": "user", "content": PROMPT_TMPL.format( ctype=content_type, tone_ja=tone_ja, ctype_guide=guide, source_text=source_text[:12000] )} ] resp = client.chat.completions.create( model=os.getenv("OPENAI_MODEL","gpt-4o-mini"), temperature=float(os.getenv("OPENAI_TEMPERATURE","0.2")), messages=messages, ) text = resp.choices[0].message.content subj_resp = client.chat.completions.create( model=os.getenv("OPENAI_MODEL","gpt-4o-mini"), temperature=0.3, messages=[ {"role":"system","content":"日本語のPRメール件名ライター。50文字以内、事実に忠実。"}, {"role":"user","content": f"次のMarkdownドラフトから、差分のある件名案を2つ出して。\n---\n{text[:6000]}"}, ] ) subs = [s.strip("- ・* ") for s in subj_resp.choices[0].message.content.splitlines() if s.strip()] subj_a = subs[0] if subs else "お知らせ" subj_b = subs[1] if len(subs) > 1 else subj_a + "【続報】" title = None for line in text.splitlines(): if line.strip().startswith("# "): title = line.strip().lstrip("# ").strip() break return title or "ドラフト", text, subj_a, subj_b