Corin1998 commited on
Commit
1323e14
·
verified ·
1 Parent(s): 7d17d15

Create llm_utils.py

Browse files
Files changed (1) hide show
  1. llm_utils.py +89 -0
llm_utils.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from typing import Literal
3
+ from openai import OpenAI
4
+
5
+ _OPENAI_CLIENT = None
6
+
7
+ def _client() -> OpenAI:
8
+ global _OPENAI_CLIENT
9
+ if _OPENAI_CLIENT is None:
10
+ api_key = os.getenv("OPENAI_API_KEY", "")
11
+ if not api_key:
12
+ raise RuntimeError("OPENAI_API_KEY is not set in Secrets.")
13
+ _OPENAI_CLIENT = OpenAI(api_key=api_key)
14
+ return _OPENAI_CLIENT
15
+
16
+ TONE_MAP = {
17
+ "neutral": "ニュートラルで事実中心",
18
+ "formal": "フォーマルで簡潔",
19
+ "friendly": "親しみやすく平易",
20
+ "investor": "投資家向け、KPI/財務メトリクスを強調",
21
+ "pr_bold": "PR向け、ヘッドラインを強調し勢いある表現",
22
+ }
23
+
24
+ TYPE_GUIDE = {
25
+ "press_release": "ニュースリリース: タイトル/リード/本文/会社概要/問い合わせ先",
26
+ "ir_letter": "IRレター: タイトル/ご挨拶/ハイライト/今後の見通し/お問い合わせ",
27
+ "investor_summary": "投資家向けサマリー: 3-7箇条書き、KPI・成長要因・リスク要因",
28
+ }
29
+
30
+ SYSTEM_TMPL = """
31
+ あなたは日本語のPR/IRライターです。事実の正確性、簡潔さ、再利用しやすいMarkdown構成を重視します。
32
+ - 出力は必ずMarkdown。
33
+ - センセーショナルすぎる表現や根拠のない主張は避ける。
34
+ - プレス/IRのルールに配慮し、誤解を招かない文に整える。
35
+ """
36
+
37
+ PROMPT_TMPL = """
38
+ 【目的】{ctype}のドラフトを{tone_ja}で作成。
39
+ 【入力テキスト要約】以下の素材から、要点を抽出し、{ctype_guide}に沿ってMarkdownドラフトを生成。
40
+ ---
41
+ {source_text}
42
+ ---
43
+ 出力要件:
44
+ - 1行目に短いタイトル(H1)
45
+ - 適切な小見出し(H2/H3)
46
+ - 箇条書きは短文で
47
+ - ファクトと数値はそのまま
48
+ - 最後に「メタデータ」セクションを追加(推奨タグ・要約140字・推奨URLスラッグ)
49
+ """
50
+
51
+ def generate_draft(source_text: str,
52
+ content_type: Literal["press_release","ir_letter","investor_summary"],
53
+ tone: str):
54
+ client = _client()
55
+ tone_ja = TONE_MAP.get(tone, TONE_MAP["neutral"])
56
+ guide = TYPE_GUIDE[content_type]
57
+
58
+ messages = [
59
+ {"role": "system", "content": SYSTEM_TMPL},
60
+ {"role": "user", "content": PROMPT_TMPL.format(
61
+ ctype=content_type, tone_ja=tone_ja, ctype_guide=guide,
62
+ source_text=source_text[:12000]
63
+ )}
64
+ ]
65
+ resp = client.chat.completions.create(
66
+ model=os.getenv("OPENAI_MODEL","gpt-4o-mini"),
67
+ temperature=float(os.getenv("OPENAI_TEMPERATURE","0.2")),
68
+ messages=messages,
69
+ )
70
+ text = resp.choices[0].message.content
71
+
72
+ subj_resp = client.chat.completions.create(
73
+ model=os.getenv("OPENAI_MODEL","gpt-4o-mini"),
74
+ temperature=0.3,
75
+ messages=[
76
+ {"role":"system","content":"日本語のPRメール件名ライター。50文字以内、事実に忠実。"},
77
+ {"role":"user","content": f"次のMarkdownドラフトから、差分のある件名案を2つ出して。\n---\n{text[:6000]}"},
78
+ ]
79
+ )
80
+ subs = [s.strip("- ・* ") for s in subj_resp.choices[0].message.content.splitlines() if s.strip()]
81
+ subj_a = subs[0] if subs else "お知らせ"
82
+ subj_b = subs[1] if len(subs) > 1 else subj_a + "【続報】"
83
+
84
+ title = None
85
+ for line in text.splitlines():
86
+ if line.strip().startswith("# "):
87
+ title = line.strip().lstrip("# ").strip()
88
+ break
89
+ return title or "ドラフト", text, subj_a, subj_b