전체 NotiLLM Schedule 및 Architecture 설계

Aug 11, 2026·
In Hyuk
In Hyuk
· 3 min read
blog

Schedule

단순히 기능을 추가하는 방식이 아니라, 측정 가능한 기준을 세우고 반복적으로 개선하는 과정을 중심으로 프로젝트를 진행했습니다.

아래와 같이 단순한 LLM 기능 구현을 넘어, Evaluation - Optimization - Validation - Production 으로 이어지는 AI Engineering 전 과정을 경험하는 것을 목표로 합니다.

순서할 일핵심 산출물키우는 역량
1현재 시스템 Baseline 고정v1 구조/성능 기준System Design
2Evaluation Dataset 구축Ground Truth DatasetLLM Evaluation
3자동 Evaluation 구현평가 코드 + 지표AI Engineering
4Error Analysis실패 유형/원인Problem Solving
5구조/Prompt 개선v2LLM Engineering
6LangGraph 도입 검토Stateful AgentAgent Engineering
7재평가 + 통계 검증v1 vs v2 결과Experimentation
8Reliability 강화Retry/Fallback/ValidationProduction AI
9실제 사용자 배포테스트 사용자Product Engineering
10Analytics/Crashlytics 분석실사용 데이터Product Analytics
11사용자 실험/A-B TestProduct 효과 검증Experiment Design
12최종 포트폴리오 정리GitHub/Portfolio취업

본격적인 Evaluation을 진행하기에 앞서서, 전체 Architecture를 먼저 설계해 어느 부분을 평가할 지에 대한 계획을 수립했습니다.

Baseline Ver.1 Architecture

  • Mermaid 목적 : Evaluation 시, 어느 Component에서 문제가 발생하는지 특정 하기 위함
  • ChatGPT-4o 모델로 Function Calling 전체 처리
flowchart TB user((User)) router["Router
GPT-4o w/
function calling"] user -->|chat command| router router -->|reply| user parse[Parse User Intent] choose[Choose mute / allow tool] router --> parse --> choose cond["extract_notification_condition
(time / recurrence)"] mute["extract_mute_target
(name / content)"] allow["extract_allow_target
(name / content)"] choose -->|function call| cond choose -->|function call| mute choose -->|function call| allow c1[Infer delivery / expires] c2[Set recurrence / window] m1[Extract mute apps / keywords] m2[Normalize app names] a1[Extract mute apps / keywords] a2[Normalize app names] cond --> c1 --> c2 mute --> m1 --> m2 allow --> a1 --> a2 c2 -.->|tool result| router m2 -.->|tool result| router a2 -.->|tool result| router merge["PromptEngine Merge
(target + condition)"] inj[Inject mute flag] build[Build target JSON] cm[ContextManager] mapPkg[Map app name to package] validate[Validate delivery before expires] save[Save rule to SQLite] db[(SQLite)] router -->|two JSONs| merge merge --> inj --> build --> cm --> mapPkg --> validate --> save save -->|Persist mode / apps / window| db classDef userNode fill:#7eb8da,stroke:#4a90b8,color:#fff classDef routerNode fill:#e8a0a0,stroke:#c07070,color:#222 classDef stepRed fill:#fff5f5,stroke:#c07070,color:#b33 classDef toolNode fill:#90c9a0,stroke:#5a9a6a,color:#222 classDef stepGreen fill:#f3faf5,stroke:#5a9a6a,color:#2a7a3a classDef dbNode fill:#a8d5b5,stroke:#5a9a6a,color:#222 class user userNode class router routerNode class parse,choose,c1,c2,m1,m2,a1,a2 stepRed class cond,mute,allow toolNode class merge,cm stepGreen class inj,build,mapPkg,validate,save stepGreen class db dbNode

평가 방식

각 Tools (extract_notification_condition, extract_mute_target, extract_allow_target) 에 따라 어떤 평가 방식을 사용해야 할까?

지금 평가 대상이 자유로운 자연어 답변이 아니라 스키마가 정해진 구조화된 JSON이고, 이미 Ground Truth를 만들 수 있다. 그래서 굳이 또 다른 LLM의 주관적 판단을 끼워 넣을 필요가 없다고 판단했습니다.

평가 방식 선정

Code-based evals 선정

Function Calling의 출력이 정해진 JSON Schema를 따르는 구조화된 데이터이므로, 각 필드를 Ground Truth와 직접 비교하는 Code-based Eval 방식 을 사용하기로 결정했습니다.

Function Calling이 현재 출력하는 JSON 필드

extract_notification_condition
{
  "name": "extract_notification_condition",
  "parameters": {
    "type": "object",
    "additionalProperties": false,
    "properties": {
      "delivery": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "absolute": { "type": "string" }
        },
        "required": ["absolute"]
      },
      "activity": {
        "type": ["string", "null"]
      },
      "location": {
        "type": ["string", "null"]
      },
      "expires": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "absolute": { "type": "string" }
        },
        "required": ["absolute"]
      },
      "recurrence": {
        "type": "string",
        "enum": ["none", "daily", "weekly"]
      },
      "days_of_week": {
        "type": "array",
        "items": {
          "type": "integer"
        }
      },
      "window_start": {
        "type": ["string", "null"]
      },
      "window_end": {
        "type": ["string", "null"]
      }
    },
    "required": [
      "delivery",
      "activity",
      "location",
      "expires",
      "recurrence",
      "days_of_week",
      "window_start",
      "window_end"
    ]
  }
}
extract_mute_target
{
  "name": "extract_mute_target",
  "parameters": {
    "type": "object",
    "additionalProperties": false,
    "properties": {
      "name": {
        "type": "array",
        "items": {
          "type": ["string", "null"]
        }
      },
      "content": {
        "type": "array",
        "items": {
          "type": ["string", "null"]
        }
      }
    },
    "required": [
      "name",
      "content"
    ]
  }
}
extract_allow_target
{
  "name": "extract_allow_target",
  "parameters": {
    "type": "object",
    "additionalProperties": false,
    "properties": {
      "name": {
        "type": "array",
        "items": {
          "type": ["string", "null"]
        }
      },
      "content": {
        "type": "array",
        "items": {
          "type": ["string", "null"]
        }
      }
    },
    "required": [
      "name",
      "content"
    ]
  }
}

References

In Hyuk
Authors
Data Analyst
데이터AI를 통해 가치를 만드는 것을 좋아합니다.
LLM을 활용한 서비스로 가치를 창출하고 유저 데이터 분석에 관심이 있습니다.