interactive-chat / engine /agents /pocket_context_loader.py
Junhoee's picture
Upload 98 files
f646e0b verified
Raw
History Blame Contribute Delete
4.99 kB
"""pocket_context_loader โ€” ์นด๋“œ ๋ธŒ๋ฆฌํ”„ ๋กœ๋“œ (BaseAgent, LLM ์—†์Œ).
ํ˜„์žฌ ํฌ์ผ“ ์นด๋“œ์˜ ๋ชฉํ‘œ/ํ—ˆ์šฉ๋ฒ”์œ„ + ์ธ๋ฌผ ์‹œํŠธ + ์‹ ๋ขฐ ๋ ˆ๋ฒจ๋ณ„ ๊ฒฐ์„
session.state์— ์ ์žฌํ•œ๋‹ค. ์Šคํฌ์ผ๋Ÿฌ ํ•„ํ„ฐ: ํ˜„์žฌ ์นด๋“œ ์ดํ›„์˜ ์ฝ˜ํ…์ธ ๋Š” ์‹ฃ์ง€ ์•Š๋Š”๋‹ค.
"""
from __future__ import annotations
import os
from typing import Any, AsyncGenerator
from google.adk.agents import BaseAgent
from google.adk.agents.invocation_context import InvocationContext
from google.adk.events import Event, EventActions
from pydantic import ConfigDict
from engine.services.state import HiddenState
# P0 0-3 ํ™˜์‚ฐ ์ถœ๋ ฅ โ€” ์‹ ๋ขฐ ๋ ˆ๋ฒจ โ†’ ์นด๋ฅด๋ฐ€๋ผ์˜ '๊ฒฐ' (์ˆซ์ž ๋…ธ์ถœ ๊ธˆ์ง€์˜ ๊ตฌํ˜„๋ถ€)
TRUST_TEXTURES = {
"trust_high": "๋–จ๋ฆฌ๋Š” ์ง„์‹ฌ. ๊ฐ€์žฅ ๋‹ค์ •ํ•˜๊ณ , ๊พธ๋ฐˆ์ด ๋ฒ—๊ฒจ์ง„ ์ˆœ๊ฐ„์ด ์˜จ๋‹ค. "
"์กฐ๊ฑด์ด ๋˜๋ฉด ์‹ฌ์ธต์˜ ๋ง์„ ๊บผ๋‚ผ ์ˆ˜ ์žˆ๋‹ค.",
"trust_mid": "๋‚˜๋ฅธํ•œ ๋‹ค์ •. ์ผ์ƒ ํ™”์ œ๊ฐ€ ํ’๋ถ€ํ•˜๊ณ  ํŽธ์•ˆํ•˜๋‹ค. ํ‘œ๋ฉด์˜ ๊ณ ๋ฐฑ๊นŒ์ง€๋งŒ.",
"neutral": "์Šฌํ”ˆ ๋ฏธ์†Œ๋กœ ์ฆ‰๋‹ต์„ ํ”ผํ•˜๊ณ , ๊ด€์‹ฌ์„ ์ƒ๋Œ€์—๊ฒŒ ๋˜๋Œ๋ฆฐ๋‹ค.",
"doubt_mid": "์–ธ์งข์Œ๊ณผ ๊ฒฝ๊ณ„. ์˜› ์ƒ์ฒ˜๊ฐ€ ์šฑ์‹ ๊ฑฐ๋ฆฐ๋‹ค. '๋‚˜๋ฅผ ๋ฏฟ์ง€ ๋ชปํ•˜๋‹ˆ'๊ฐ€ ๋ฐฐ์–ด ๋‚˜์˜จ๋‹ค.",
"doubt_high": "์ง‘์ฐฉ๊ณผ ๋ถˆ์•ˆ. ๊ฐ•์••์  ์„ค๋“ ์ง์ „๊นŒ์ง€ ๊ฐ€๋˜, ๊ทธ ๋ฐ‘์˜ ์™ธ๋กœ์›€์ด ์ƒˆ์–ด ๋‚˜์˜จ๋‹ค. "
"๋ฌผ๋ฆฌ์  ๋Œ๋ณ€์€ ์ ˆ๋Œ€ ์—†๋‹ค.",
}
DEFAULT_POCKET_CARD = os.environ.get("POCKET_CARD", "E06-09")
def _clean(text: str) -> str:
"""instruction ์น˜ํ™˜๊ฐ’ ์œ„์ƒ โ€” ์ค‘๊ด„ํ˜ธ ์ œ๊ฑฐ (state ๋ณ€์ˆ˜ ์žฌํ•ด์„ ๋ฐฉ์ง€)."""
return text.replace("{", "(").replace("}", ")")
class PocketContextLoader(BaseAgent):
model_config = ConfigDict(arbitrary_types_allowed=True)
repo: Any # ContentRepository โ€” services ๊ฒฝ์œ  ์—†์ด ์ฝ๊ธฐ ์ „์šฉ ์กฐํšŒ๋งŒ
async def _run_async_impl(self, ctx: InvocationContext) -> AsyncGenerator[Event, None]:
state = ctx.session.state
card_id = state.get("pocket_card_id", DEFAULT_POCKET_CARD)
card = self.repo.get_card(card_id) # ํฌ์ผ“ ์นด๋“œ ์ž์‹  = ํ˜„์žฌ ์‹œ์ 
# ์‹ ๋ขฐ ๋ ˆ๋ฒจ ํ™˜์‚ฐ (session.state์˜ ์ˆจ์€ ๊ฐ’ ๊ธฐ์ค€)
hs = HiddenState(trust_score=int(state.get("hidden_trust", 0)),
doubt_score=int(state.get("hidden_doubt", 0)))
# ์นด๋“œ ๋ธŒ๋ฆฌํ”„: ์ง€๋ฌธ(๊ฒŒ์ดํŠธ ๋ฐ˜์˜) + ๋ชฉํ‘œ(๋…ธํŠธ) + ์•ต์ปค ๋Œ€์‚ฌ
visible = [b for b in card.narration
if b.gate == "all" or (b.gate == "trust_high" and hs.deep_unlock)]
# ๋Œ€์‚ฌ(์ค‘์•™ ๋Œ€์‚ฌยท์•ต์ปค)๋Š” ์ž๋ฅด์ง€ ์•Š๋Š”๋‹ค โ€” ๋ณ€์ฃผ์˜ ๊ธฐ์ค€์ด๋ฏ€๋กœ ์›๋ฌธ ๊ทธ๋Œ€๋กœ
lines = []
for b in visible[:10]:
if b.type == "dialogue_center":
lines.append(f' ํ•ต์‹ฌ ๋Œ€์‚ฌ: โ{b.text}โž')
elif b.type == "character_dialogue":
lines.append(f' {b.speaker or "์นด๋ฅด๋ฐ€๋ผ"}: "{b.text}"')
else:
lines.append(f" {b.text[:150]}")
anchors = [f"- {b.speaker or '์นด๋ฅด๋ฐ€๋ผ'}: \"{b.text}\"" for b in visible if b.anchor]
# ์บ๋ฆญํ„ฐ์šฉ ๋ธŒ๋ฆฌํ”„ โ€” ๋…ธํŠธ(์—ฐ์ถœ ๋ฉ”ํƒ€ยทํ•ด๊ธˆ ์กฐ๊ฑดยท๋ถ„๊ธฐ ์ •๋ณด)๋Š” ์ ˆ๋Œ€ ์‹ฃ์ง€ ์•Š๋Š”๋‹ค.
# ๋…ธํŠธ์—๋Š” ๊ฒŒ์ดํŠธ ๋ฐ– ๋Œ€์‚ฌ๊ฐ€ ์ธ์šฉ๋  ์ˆ˜ ์žˆ์–ด ์บ๋ฆญํ„ฐ์—๊ฒŒ ์ฃผ๋ฉด ์Šคํฌ์ผ๋Ÿฌ ๋ˆ„์ˆ˜๊ฐ€ ๋œ๋‹ค
brief = (
f"์žฅ๋ฉด: {card.id} ยท {card.title}\n"
f"์žฅ๋ฉด ์ƒํ™ฉ(๊ณ ์ • ์ง€๋ฌธ):\n" + "\n".join(lines)
+ ("\n์ฐธ๊ณ  ๋Œ€์‚ฌ ํŒจํ„ด(๊ทธ๋Œ€๋กœ ์“ฐ์ง€ ๋ง๊ณ  ๋ณ€์ฃผ):\n" + "\n".join(anchors) if anchors else "")
)
# ๋””๋ ‰ํ„ฐ์šฉ ๋…ธํŠธ โ€” ํŒ์ • ๊ธฐ์ค€(๋ชฉํ‘œ ๋น„ํŠธยท์ดํƒˆ ๋ฒ”์œ„)์ด๋ฏ€๋กœ ๋””๋ ‰ํ„ฐ์—๊ฒŒ๋งŒ ๊ฐ„๋‹ค
note = f"์žฅ๋ฉด ๋ชฉํ‘œ(์—ฐ์ถœ ๋…ธํŠธ): {card.note[:500]}" if card.note else "๋ช…์‹œ๋œ ๋…ธํŠธ ์—†์Œ."
carmilla = self.repo.characters.get("carmilla")
sheet = ""
if carmilla:
sheet = (f"{carmilla.display_name} โ€” {carmilla.description}\n"
f"๊ธฐ์งˆ: {', '.join(carmilla.personality.traits)}\n"
f"ํ™”๋ฒ•: {', '.join(carmilla.speech.style + carmilla.speech.tone)}\n"
+ "\n".join(f"์ œ์•ฝ: {k.value}" for k in carmilla.immutable_constraints
if k.value))
delta = {
"pocket_card_id": card_id,
"pocket_brief": _clean(brief),
"pocket_note": _clean(note),
"carmilla_sheet": _clean(sheet),
"trust_texture": TRUST_TEXTURES[hs.trust_level],
"trust_level": hs.trust_level,
"deep_unlock": hs.deep_unlock,
"guard_directive": state.get("guard_directive", "ํŠน์ด์‚ฌํ•ญ ์—†์Œ."),
}
yield Event(invocation_id=ctx.invocation_id, author=self.name,
actions=EventActions(state_delta=delta))
def create_pocket_context_loader(repo) -> PocketContextLoader:
return PocketContextLoader(name="pocket_context_loader", repo=repo)