PR_IRminiSaaS / llm_utils.py
Corin1998's picture
Create llm_utils.py
1323e14 verified
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