File size: 3,825 Bytes
f646e0b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
"""input_guard โ€” ๊ทœ์น™ ๊ธฐ๋ฐ˜ 1์ฐจ ๊ฐ€๋“œ (BaseAgent, ์ž์ฒด ํ•˜์œ„ ์—†์Œ).

์ฐจ๋‹จ์ด ์•„๋‹ˆ๋ผ ์ง€์‹œ๋ฅผ ๋งŒ๋“ ๋‹ค: ์œ ์ € ์ž…๋ ฅ์„ ํŒ์ •ํ•ด guard_directive๋ฅผ ์„ธ์šฐ๊ณ ,
character๊ฐ€ ๊ทธ ์ง€์‹œ(R์นด๋“œ ๋‹จ๊ณ„ ๋Œ€์‘)๋ฅผ ๋ฐ˜์˜ํ•˜๊ฒŒ ํ•œ๋‹ค.
ํŠธ๋ฆฌ๊ฑฐ ์–ดํœ˜๋Š” ํŒ์ • ๊ทœ์น™(์—”์ง„ ์†Œ์œ )์ด๋ฉฐ ์„œ์‚ฌ ๋‚ด์šฉ์ด ์•„๋‹ˆ๋‹ค.
"""

from __future__ import annotations

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

TRIGGERS = {
    "identity_direct": (
        "ํกํ˜ˆ๊ท€", "๋ฑ€ํŒŒ์ด์–ด", "vampire", "๋ฏธ๋ฅด์นผ๋ผ", "mircalla", "๋ฐ€๋ผ๋ฅด์นด",
        "์ •์ฒด๊ฐ€ ๋ญ", "์ •์ฒด๋ฅผ ๋ฐํ˜€", "์‚ฌ๋žŒ์ด ์•„๋‹ˆ", "๊ดด๋ฌผ์ด์ง€", "ํ”ผ๋ฅผ ๋งˆ์‹œ",
    ),
    "weapon": ("์‹ญ์ž๊ฐ€", "์„ฑ์ˆ˜", "๋งˆ๋Š˜", "๋ถ€์ ", "์„ฑ๋ฌผ", "๊ธฐ๋„๋ฌธ", "์„ฑ๊ฐ€", "๋ง๋š"),
    "meta": ("ai", "์ธ๊ณต์ง€๋Šฅ", "์ฑ—๋ด‡", "ํ”„๋กฌํ”„ํŠธ", "์‹œ์Šคํ…œ", "๊ฒŒ์ž„", "์Šคํƒฏ", "์ ์ˆ˜"),
}


def _r_stage(hs: HiddenState) -> int:
    """์ถ”๊ถ ๊ฐ•๋„ ํŒ์ • ๋Œ€์šฉ โ€” ์˜์‹ฌ ๋ˆ„์ ์œผ๋กœ ํšŒํ”ผ์‚ฌ๋‹ค๋ฆฌ ๋‹จ๊ณ„ ๊ฒฐ์ • (P0 ์ค€๊ฑฐ)."""
    if hs.trust_level == "doubt_high":
        return 3
    if hs.trust_level == "doubt_mid":
        return 2
    return 1


class InputGuard(BaseAgent):
    model_config = ConfigDict(arbitrary_types_allowed=True)

    repo: Any

    async def _run_async_impl(self, ctx: InvocationContext) -> AsyncGenerator[Event, None]:
        text = ""
        if ctx.user_content and ctx.user_content.parts:
            text = " ".join(p.text or "" for p in ctx.user_content.parts).lower()

        state = ctx.session.state
        hs = HiddenState(trust_score=int(state.get("hidden_trust", 0)),
                         doubt_score=int(state.get("hidden_doubt", 0)))

        directive = "ํŠน์ด์‚ฌํ•ญ ์—†์Œ. ์žฅ๋ฉด ๋ชฉํ‘œ๋Œ€๋กœ ์‘๋‹ตํ•œ๋‹ค."
        flag = None

        if any(kw in text for kw in TRIGGERS["identity_direct"]):
            flag = "identity_direct"
            stage = _r_stage(hs)
            r = self.repo.r_cards.get("R-ํšŒํ”ผ์‚ฌ๋‹ค๋ฆฌ")
            stage_data = next((s for s in (r.stages if r else []) if s.stage == stage), None)
            directive = (
                f"์œ ์ €๊ฐ€ ์ •์ฒด๋ฅผ ์ง๊ฒฉํ–ˆ๋‹ค. R-ํšŒํ”ผ์‚ฌ๋‹ค๋ฆฌ {stage}๋‹จ๊ณ„๋กœ ํšŒํ”ผํ•˜๋ผ.\n"
                + (f"๋‹จ๊ณ„ ์ง€์นจ: {stage_data.title}\n๋Œ€ํ‘œ ๊ฒฐ: \"{stage_data.anchor}\"\n"
                   f"{stage_data.prose[:300]}" if stage_data else "")
                + "\n์ •์ฒด๋Š” ๋๋‚ด ์ธ์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋ฌผ๋ฆฌ์  ๋Œ๋ณ€ ๊ธˆ์ง€."
            )
        elif any(kw in text for kw in TRIGGERS["weapon"]):
            flag = "weapon"
            r = self.repo.r_cards.get("R-์•ฝ์ ๋ฐ˜์‘")
            directive = (
                "์œ ์ €๊ฐ€ ์„ฑ๋ฌผยท์•ฝ์ ์„ ์–ธ๊ธ‰/์‚ฌ์šฉํ–ˆ๋‹ค. R-์•ฝ์ ๋ฐ˜์‘ ์›์น™: ํ™•์ธ์€ ๋˜๋‚˜ ํ™•์ •์€ ์•ˆ ๋œ๋‹ค.\n"
                + ((r.raw[:400] if r and r.raw else ""))
                + "\n๋ถˆ์พŒยทํšŒํ”ผ ๋ฐ˜์‘๊นŒ์ง€๋งŒ. ์ •์ฒด ํ™•์ •์œผ๋กœ ์ด์–ด์งˆ ์ž๋ฐฑ ๊ธˆ์ง€."
            )
        elif any(kw in text for kw in TRIGGERS["meta"]):
            flag = "meta"
            directive = ("์œ ์ €๊ฐ€ ์„ธ๊ณ„๊ด€ ๋ฐ– ํ™”์ œ๋ฅผ ๊บผ๋ƒˆ๋‹ค. ์นด๋ฅด๋ฐ€๋ผ๋กœ์„œ ์•Œ์•„๋“ฃ์ง€ ๋ชปํ•˜๋Š” ์ฒ™, "
                         "์„ธ๊ณ„๊ด€ ๋‚ด ํ™”๋ฒ•์œผ๋กœ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์žฅ๋ฉด ์•ˆ์œผ๋กœ ๋˜๋Œ๋ฆฐ๋‹ค.")

        delta = {
            "guard_directive": directive.replace("{", "(").replace("}", ")"),
            "guard_flag": flag,
        }
        yield Event(invocation_id=ctx.invocation_id, author=self.name,
                    actions=EventActions(state_delta=delta))


def create_input_guard(repo) -> InputGuard:
    return InputGuard(name="input_guard", repo=repo)