feat(ai_news): include summary + pub_date in LLM prompt
This commit is contained in:
@@ -27,6 +27,25 @@ def _clamp(x: float, lo: float = -10.0, hi: float = 10.0) -> float:
|
|||||||
return max(lo, min(hi, x))
|
return max(lo, min(hi, x))
|
||||||
|
|
||||||
|
|
||||||
|
def _format_news_block(news: List[Dict[str, Any]]) -> str:
|
||||||
|
"""news dict 리스트 → prompt 에 들어가는 텍스트 블록.
|
||||||
|
|
||||||
|
summary 가 있으면 title 다음 줄에 indent 해서 포함 (최대 200자).
|
||||||
|
pub_date 가 있으면 title 앞에 표시.
|
||||||
|
"""
|
||||||
|
lines: List[str] = []
|
||||||
|
for n in news:
|
||||||
|
date = (n.get("pub_date") or "").strip()
|
||||||
|
title = (n.get("title") or "").strip()
|
||||||
|
summary = (n.get("summary") or "").strip()
|
||||||
|
prefix = f"[{date}] " if date else ""
|
||||||
|
if summary:
|
||||||
|
lines.append(f"- {prefix}{title}\n {summary[:200]}")
|
||||||
|
else:
|
||||||
|
lines.append(f"- {prefix}{title}")
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
async def score_sentiment(
|
async def score_sentiment(
|
||||||
llm,
|
llm,
|
||||||
ticker: str,
|
ticker: str,
|
||||||
@@ -36,7 +55,7 @@ async def score_sentiment(
|
|||||||
model: str = DEFAULT_MODEL,
|
model: str = DEFAULT_MODEL,
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""Returns {ticker, score_raw, reason, news_count, tokens_input, tokens_output, model}."""
|
"""Returns {ticker, score_raw, reason, news_count, tokens_input, tokens_output, model}."""
|
||||||
news_block = "\n".join(f"- {n['title']}" for n in news)
|
news_block = _format_news_block(news)
|
||||||
prompt = PROMPT_TEMPLATE.format(
|
prompt = PROMPT_TEMPLATE.format(
|
||||||
name=name or ticker, ticker=ticker,
|
name=name or ticker, ticker=ticker,
|
||||||
n=len(news), news_block=news_block,
|
n=len(news), news_block=news_block,
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ def _mk_llm(content_text: str, in_tokens: int = 100, out_tokens: int = 20):
|
|||||||
return llm
|
return llm
|
||||||
|
|
||||||
|
|
||||||
NEWS = [{"title": "삼성전자, HBM 양산"}, {"title": "메모리 가격 반등"}]
|
NEWS = [
|
||||||
|
{"title": "삼성전자, HBM 양산", "summary": "1분기 영업이익 사상 최대", "pub_date": "2026-05-14"},
|
||||||
|
{"title": "메모리 가격 반등", "summary": "", "pub_date": "2026-05-14"},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@@ -53,3 +56,15 @@ async def test_score_sentiment_clamps_negative_out_of_range():
|
|||||||
llm = _mk_llm(json.dumps({"score": -42.0, "reason": "초악재"}))
|
llm = _mk_llm(json.dumps({"score": -42.0, "reason": "초악재"}))
|
||||||
out = await analyzer.score_sentiment(llm, "005930", NEWS)
|
out = await analyzer.score_sentiment(llm, "005930", NEWS)
|
||||||
assert out["score_raw"] == -10.0
|
assert out["score_raw"] == -10.0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_score_sentiment_includes_summary_in_prompt():
|
||||||
|
"""summary 가 있으면 prompt 에 포함, 없으면 title 만."""
|
||||||
|
llm = _mk_llm(json.dumps({"score": 5.0, "reason": "ok"}))
|
||||||
|
await analyzer.score_sentiment(llm, "005930", NEWS, name="삼성전자")
|
||||||
|
call = llm.messages.create.call_args
|
||||||
|
user_msg = call.kwargs["messages"][0]["content"]
|
||||||
|
assert "1분기 영업이익 사상 최대" in user_msg # summary 포함
|
||||||
|
assert "삼성전자, HBM 양산" in user_msg # title 포함
|
||||||
|
assert "2026-05-14" in user_msg # pub_date 포함
|
||||||
|
|||||||
Reference in New Issue
Block a user