Corin1998's picture
Update app/llm.py
e22b238 verified
import os
from typing import Literal
from openai import OpenAI
from app.config import settings
# 遅延初期化:インポート時にHTTPクライアントを作らない(環境差異の影響を減らす)
_client = None
def _get_client() -> OpenAI:
global _client
if _client is None:
api_key = settings.OPENAI_API_KEY or os.getenv("OPENAI_API_KEY", "")
if not api_key:
# 起動時ではなく呼び出し時に明示エラー
raise RuntimeError("OPENAI_API_KEY is not set. Please add it to HF Spaces Secrets or .env.")
_client = OpenAI(api_key=api_key)
return _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 = _get_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=settings.OPENAI_MODEL,
temperature=settings.OPENAI_TEMPERATURE,
messages=messages,
)
text = resp.choices[0].message.content
# 件名(Subject)候補A/B
subj_resp = client.chat.completions.create(
model=settings.OPENAI_MODEL,
temperature=0.3,
messages=[
{"role": "system", "content": "日本語のPRメール件名ライター。50文字以内、事実に忠実。"},
{"role": "user", "content": f"次のMarkdownドラフトから、差分のある件名案を2つ出して。\n---\n{text[:6000]}"},
],
)
subs = subj_resp.choices[0].message.content.splitlines()
subj_a = next((s for s in subs if s.strip()), "お知らせ")
subj_b = next((s for s in subs[1:] if s.strip()), 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.strip("- ・* "), subj_b.strip("- ・* ")