Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions .idea/_AI.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/inspectionProfiles/profiles_settings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 0 additions & 11 deletions app/pipeline/analysis_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,29 +52,20 @@ def run_analysis_pipeline(payload: AnalyzeRequest) -> Dict[str, object]:
for message in conversation
if message.sender.strip().upper() == "OTHER" and message.content.strip()
]
logger.info(
"Pipeline start: %d turns (other=%d)",
len(conversation),
len(other_contents),
)

# 1. 입력에서 대화 유형 수신 (유형별 신호 범위 결정을 위함)
conversation_type = payload.type
logger.info("Step 1 conversation_type(from input): %s", conversation_type)

# 2. 규칙 기반 신호 추출 (유형 기반 + 공통 신호) (위험 신호 후보 추출)
allowed_signals = resolve_risk_signals(conversation_type)
rule_signals = analyze_conversation(other_contents, allowed_signals=allowed_signals)
logger.info("Step 2 signals: %s", rule_signals)

# 3. RAG 쿼리 보강 (근거 자료 확보를 위한 검색 품질 향상)
signal_terms = signal_query_terms(rule_signals)
matched_phrases = extract_signal_phrases(other_contents, allowed_signals=allowed_signals)
logger.info("Step 3 query_terms=%s matched_phrases=%s", signal_terms, matched_phrases)

# 4. 결정 오케스트레이터 (위험 단계 산출)
risk_stage = decide_risk_stage(rule_signals)
logger.info("Step 4 risk_stage: %s", risk_stage)

# 5. RAG 검색 (근거 자료 확보)
retrieval_request = RetrievalRequest(
Expand All @@ -85,7 +76,6 @@ def run_analysis_pipeline(payload: AnalyzeRequest) -> Dict[str, object]:
matched_phrases=matched_phrases,
)
references = retrieve_evidence(retrieval_request)
logger.info("Step 5 references: %d", len(references))

# 6. 안전 행동 생성 (LLM: references + 대화 발췌 사용) (최종 응답 생성)
conversation_lines = _build_conversation_excerpt(
Expand All @@ -94,7 +84,6 @@ def run_analysis_pipeline(payload: AnalyzeRequest) -> Dict[str, object]:
safe_actions = generate_safe_actions(
risk_stage, conversation_type, references, conversation_lines, payload.platform
)
logger.info("Step 6 safe_actions generated")

rag_references = [
{"source": reference.source, "summary": reference.note} for reference in references
Expand Down
29 changes: 25 additions & 4 deletions app/schemas/request.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from datetime import datetime
from typing import List, Literal
from typing import List, Literal, Optional

from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, field_validator


class Message(BaseModel):
Expand All @@ -11,6 +11,20 @@ class Message(BaseModel):
content: str = Field(..., description="Message content")
sender: str = Field(..., description="Message sender (e.g. ME, OTHER)")
timestamp: datetime = Field(..., description="Message timestamp in ISO 8601")

@field_validator("type", mode="before")
@classmethod
def uppercase_type(cls, v):
if isinstance(v, str):
return v.upper()
return v

@field_validator("sender", mode="before")
@classmethod
def uppercase_sender(cls, v):
if isinstance(v, str):
return v.upper()
return v


class AnalyzeRequest(BaseModel):
Expand All @@ -19,9 +33,16 @@ class AnalyzeRequest(BaseModel):
platform: Literal["INSTAGRAM", "TELEGRAM"] = Field(
..., description="Platform name (INSTAGRAM or TELEGRAM)"
)
type: Literal["구직", "중고거래", "재테크", "부업"] = Field(
..., description="Conversation type (구직, 중고거래, 재테크, 부업)"
type: Optional[Literal["구직", "중고거래", "재테크", "부업"]] = Field(
None, description="Conversation type (구직, 중고거래, 재테크, 부업)"
)

@field_validator("platform", mode="before")
@classmethod
def uppercase_platform(cls, v):
if isinstance(v, str):
return v.upper()
return v


class ReportRequest(BaseModel):
Expand Down