Spaces:
Sleeping
Sleeping
| 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 | |