<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Evaluation |</title><link>https://inhyuk2000.github.io/tags/evaluation/</link><atom:link href="https://inhyuk2000.github.io/tags/evaluation/index.xml" rel="self" type="application/rss+xml"/><description>Evaluation</description><generator>HugoBlox Kit (https://hugoblox.com)</generator><language>en-us</language><lastBuildDate>Fri, 21 Aug 2026 00:00:00 +0900</lastBuildDate><image><url>https://inhyuk2000.github.io/media/icon_hu_eee4a95885829ab2.png</url><title>Evaluation</title><link>https://inhyuk2000.github.io/tags/evaluation/</link></image><item><title>최종 버전 확정 및 초안 배포</title><link>https://inhyuk2000.github.io/blog/notillm/%EA%B0%9C%EB%B0%9C_0821/</link><pubDate>Fri, 21 Aug 2026 00:00:00 +0900</pubDate><guid>https://inhyuk2000.github.io/blog/notillm/%EA%B0%9C%EB%B0%9C_0821/</guid><description>&lt;h2 id="version-03-architecture"&gt;Version 03 Architecture&lt;/h2&gt;
&lt;div class="mermaid"&gt;flowchart TD
START([사용자 입력&lt;br/&gt;prompt + pending + pendingOriginal&lt;br/&gt;+ installedApps]) --&gt; P{pending=true&lt;br/&gt;AND pendingOriginal?}
P --&gt;|Yes| PR[Pending Resolver&lt;br/&gt;gpt-4o-mini&lt;br/&gt;LangChain]
P --&gt;|No| IC[Intent Classifier&lt;br/&gt;ST + LogisticRegression&lt;br/&gt;또는 heuristic&lt;br/&gt;LLM 없음]
PR --&gt; A{5액션}
A --&gt;|CANCEL| C1([END&lt;br/&gt;ok=false&lt;br/&gt;failReason=cancelled&lt;br/&gt;needsSupplement=false])
A --&gt;|UNRESOLVED| C2([END&lt;br/&gt;ok=false&lt;br/&gt;needsSupplement=true&lt;br/&gt;pending 유지])
A --&gt;|CONTINUE / UPDATE| M[이전 명령 + 이번 입력&lt;br/&gt;merge prompt]
A --&gt;|NEW_REQUEST| N[이번 prompt만]
IC --&gt;|reject&lt;br/&gt;잡담| R([END&lt;br/&gt;ok=false&lt;br/&gt;failReason=chitchat&lt;br/&gt;dialogIntent=reject&lt;br/&gt;LLM 0회])
IC --&gt;|extract| E
M --&gt; E[LLM Extractor&lt;br/&gt;gpt-4o&lt;br/&gt;function calling]
N --&gt; E
E --&gt; V[Rule Validator&lt;br/&gt;코드]
V --&gt;|incomplete| ASK([END&lt;br/&gt;ok=false&lt;br/&gt;needsSupplement=true&lt;br/&gt;재질문 / pending])
V --&gt;|complete| MAP[App Mapper&lt;br/&gt;embedding cosine&lt;br/&gt;name → packageName]
MAP --&gt;|미매칭| U([END&lt;br/&gt;ok=false&lt;br/&gt;app_unresolved])
MAP --&gt;|성공| OK([END&lt;br/&gt;ok=true&lt;br/&gt;규칙 저장])
&lt;/div&gt;
&lt;h3 id="docker"&gt;Docker&lt;/h3&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;앱 + 라이브러리 + 실행 방법을 상자에 넣어, 어디든 같은 방식으로 돌리게 하는 도구임.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;현재 V3는 &lt;code&gt;sentence-transformers&lt;/code&gt;와 같은 것들로 인해 &lt;span class="high-mark"&gt;&lt;strong&gt;의존성이 무거워&lt;/strong&gt;&lt;/span&gt;
, EC2에 수동 pip보다는 &lt;strong&gt;Docker로 묶는 것&lt;/strong&gt;이 좋다고 결정함.&lt;/p&gt;
&lt;p&gt;우선 &lt;code&gt;Docker&lt;/code&gt; + &lt;code&gt;AWS ECR&lt;/code&gt; + &lt;code&gt;App Runner&lt;/code&gt;를 활용해서, 1차적으로 배포를 먼저 진행함.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이후 &lt;strong&gt;부하 테스트&lt;/strong&gt; 진행 후, 수정 사항 반영 후 재배포할 예정임.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="aws-app-runner-운영-종료-이슈"&gt;AWS App Runner 운영 종료 이슈&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;이로 인해, &lt;strong&gt;Amazon Elastic Container Service로 배포&lt;/strong&gt;를 진행함. (Tokyo)&lt;/li&gt;
&lt;li&gt;배포 이후, &lt;code&gt;/health&lt;/code&gt; 확인 완료&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ok&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;promptEngine&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;v3&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="중간-점검"&gt;중간 점검&lt;/h2&gt;
&lt;h3 id="notillm-관련"&gt;NotiLLM 관련&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;전체 소스코드에 주석 남기면서 과정을 하나하나 다시 뜯어보는 과정&lt;/strong&gt;이 필요함&lt;/li&gt;
&lt;li&gt;&lt;span class="high-mark"&gt;&lt;strong&gt;부하 테스트&lt;/strong&gt;&lt;/span&gt;
반드시 진행할 것.&lt;/li&gt;
&lt;li&gt;아직, &lt;strong&gt;눈에 띄는 인사이트를 기반으로 한 발전 과정이 안보임&lt;/strong&gt;. 너무 뻔하고 흔한 과정들로 발전시킨 것 같아, 해당 과정에 대한 고민도 필요할 것 같음.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="기타"&gt;기타&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;포스터 홀 개선 및 추가&lt;/strong&gt;할 것.&lt;/li&gt;
&lt;li&gt;코딩테스트 및 서류 및 면접 준비&lt;/li&gt;
&lt;li&gt;통계 및 머신러닝 관련 공부도 지속적으로 진행.&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Version 간 결과 비교/분석</title><link>https://inhyuk2000.github.io/blog/notillm/%EA%B8%B0%ED%9A%8D_0820/</link><pubDate>Thu, 20 Aug 2026 00:00:00 +0900</pubDate><guid>https://inhyuk2000.github.io/blog/notillm/%EA%B8%B0%ED%9A%8D_0820/</guid><description>&lt;p&gt;&lt;strong&gt;LangGraph를 사용하기 이전(전부 4o모델) vs 모델 분기 비교 실험&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="결과"&gt;결과&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th style="text-align: right"&gt;Baseline&lt;/th&gt;
&lt;th style="text-align: right"&gt;Routing V2&lt;/th&gt;
&lt;th style="text-align: right"&gt;Change (%)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Runs&lt;/td&gt;
&lt;td style="text-align: right"&gt;85.0000&lt;/td&gt;
&lt;td style="text-align: right"&gt;85.0000&lt;/td&gt;
&lt;td style="text-align: right"&gt;0.0000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total Cost ($)&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;strong&gt;0.8520&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;strong&gt;0.7277&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;-14.5903&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avg Cost ($)&lt;/td&gt;
&lt;td style="text-align: right"&gt;0.0100&lt;/td&gt;
&lt;td style="text-align: right"&gt;0.0086&lt;/td&gt;
&lt;td style="text-align: right"&gt;-14.5903&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mean Latency (s)&lt;/td&gt;
&lt;td style="text-align: right"&gt;4.6433&lt;/td&gt;
&lt;td style="text-align: right"&gt;7.5294&lt;/td&gt;
&lt;td style="text-align: right"&gt;+62.1563&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P50 Latency (s)&lt;/td&gt;
&lt;td style="text-align: right"&gt;4.2594&lt;/td&gt;
&lt;td style="text-align: right"&gt;7.3486&lt;/td&gt;
&lt;td style="text-align: right"&gt;+72.5282&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P95 Latency (s)&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;strong&gt;7.2314&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;strong&gt;9.9255&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;+37.2561&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P99 Latency (s)&lt;/td&gt;
&lt;td style="text-align: right"&gt;9.4063&lt;/td&gt;
&lt;td style="text-align: right"&gt;13.0560&lt;/td&gt;
&lt;td style="text-align: right"&gt;+38.8006&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;LLM-based routing은 비용을 감소시켰지만 sequential LLM calls로 P95 latency가 너무 증가하는 문제점이 생겼다. &lt;strong&gt;Total Cost와 Latency의 문제를 둘 다 잡는 다른 방법&lt;/strong&gt;에 대한 고민이 필요해보였다..&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;LLM Router 까지 두어서 &lt;strong&gt;LLM 모델을 두 번 처리하는 게 문제&lt;/strong&gt;였기에, &lt;span class="high-mark"&gt;&lt;strong&gt;LLM Router를 제거하고 lightweight intent classifier로 대체&lt;/strong&gt;&lt;/span&gt;
하려고 함.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;기존 &lt;code&gt;LLM Router&lt;/code&gt; 대신 &lt;code&gt;sentenceTransformer&lt;/code&gt; + &lt;code&gt;LogisticRegression&lt;/code&gt; 기반 classifier로 구현하고자 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="v1-vs-v2-vs-v3"&gt;&lt;code&gt;v1&lt;/code&gt; vs &lt;code&gt;v2&lt;/code&gt; vs &lt;code&gt;v3&lt;/code&gt;&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th style="text-align: right"&gt;V1 Baseline&lt;/th&gt;
&lt;th style="text-align: right"&gt;V2 LLM Router&lt;/th&gt;
&lt;th style="text-align: right"&gt;V3 Classifier&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Runs&lt;/td&gt;
&lt;td style="text-align: right"&gt;85&lt;/td&gt;
&lt;td style="text-align: right"&gt;85&lt;/td&gt;
&lt;td style="text-align: right"&gt;85&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total Cost ($)&lt;/td&gt;
&lt;td style="text-align: right"&gt;0.8557&lt;/td&gt;
&lt;td style="text-align: right"&gt;0.7401&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;strong&gt;0.7224&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avg Cost / Run ($)&lt;/td&gt;
&lt;td style="text-align: right"&gt;0.0101&lt;/td&gt;
&lt;td style="text-align: right"&gt;0.0087&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;strong&gt;0.0085&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mean Latency (s)&lt;/td&gt;
&lt;td style="text-align: right"&gt;4.1386&lt;/td&gt;
&lt;td style="text-align: right"&gt;6.2194&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;strong&gt;3.3433&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P50 Latency (s)&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;strong&gt;3.1119&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;5.4087&lt;/td&gt;
&lt;td style="text-align: right"&gt;3.1164&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P95 Latency (s)&lt;/td&gt;
&lt;td style="text-align: right"&gt;7.4149&lt;/td&gt;
&lt;td style="text-align: right"&gt;11.0379&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;strong&gt;7.0132&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P99 Latency (s)&lt;/td&gt;
&lt;td style="text-align: right"&gt;12.9387&lt;/td&gt;
&lt;td style="text-align: right"&gt;11.9233&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;strong&gt;8.7200&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Calls&lt;/td&gt;
&lt;td style="text-align: right"&gt;85&lt;/td&gt;
&lt;td style="text-align: right"&gt;85&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;strong&gt;67&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Call Rate (%)&lt;/td&gt;
&lt;td style="text-align: right"&gt;100.00&lt;/td&gt;
&lt;td style="text-align: right"&gt;100.00&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;strong&gt;78.82&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extract Rule Correctness&lt;/td&gt;
&lt;td style="text-align: right"&gt;0.8941&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;strong&gt;0.9048&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;0.9024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extract Rule Correctness (if routed)&lt;/td&gt;
&lt;td style="text-align: right"&gt;-&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;strong&gt;0.9048&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;0.8780&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Route Correctness&lt;/td&gt;
&lt;td style="text-align: right"&gt;-&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;strong&gt;1.0000&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;0.9756&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;V3는 V1 대비 비용 15.58%, 평균 Latency 19.22% 감소했고, LLM API 호출도 21.18% 절감함.&lt;/li&gt;
&lt;li&gt;정확도는 V1 0.8941 → V3 0.9024로 유지·소폭 개선되어, 성능 저하 없이 비용과 응답 속도를 함께 개선함.&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>LangGraph 도입 검토 및 데이터셋 설계</title><link>https://inhyuk2000.github.io/blog/notillm/%EA%B8%B0%ED%9A%8D_0817/</link><pubDate>Mon, 17 Aug 2026 00:00:00 +0900</pubDate><guid>https://inhyuk2000.github.io/blog/notillm/%EA%B8%B0%ED%9A%8D_0817/</guid><description>&lt;h2 id="langgraph-도입하는-게-맞을까"&gt;&lt;code&gt;LangGraph&lt;/code&gt; 도입하는 게 맞을까?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;LangGraph&lt;/code&gt;를 &lt;span class="high-mark"&gt;&lt;strong&gt;비용 최적화&lt;/strong&gt;&lt;/span&gt;
를 위해 도입하는 게 좋을 거 같음.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;현재 &lt;code&gt;version&lt;/code&gt;은 ChatGPT 4o 모델을 활용해 사용자 자연어 처리를 함.
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;현재 얼마의 비용이 청구되는 지에 대한 정확한 판단이 필요함. &lt;span class="high-mark"&gt;&lt;strong&gt;문제 설정 필요&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;하지만 사용자 명령이 만약 &lt;strong&gt;안녕&lt;/strong&gt;, &lt;strong&gt;ㅎ2&lt;/strong&gt;, &lt;strong&gt;ㅎㅎ&lt;/strong&gt; 라면? &lt;span class="high-mark"&gt;&lt;strong&gt;굳이 모든 명령들을 전부 &lt;code&gt;4o&lt;/code&gt; 모델로 처리할 필요가 있을까??&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;사용자의 명령을 &lt;code&gt;중요도 낮음&lt;/code&gt;, &lt;code&gt;약간 중요&lt;/code&gt;, &lt;code&gt;매우 중요&lt;/code&gt; 등으로 나눠서, 이에 맞는 수준의 모델로 라우팅해주는 게 좋을 거 같음. &lt;span class="high-mark"&gt;&lt;strong&gt;나중에 구현할 기능&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="version-01"&gt;Version 01.&lt;/h3&gt;
&lt;div class="mermaid"&gt;flowchart TD
START([사용자 입력&lt;br/&gt;prompt + pending 플래그]) --&gt; R{의도 라우터&lt;br/&gt;gpt-4o-mini}
R --&gt;|잡담 / 인사 / ㅎㅎ| S[단순 응답&lt;br/&gt;gpt-4o-mini&lt;br/&gt;tool 없음]
S --&gt; END1([END&lt;br/&gt;채팅 응답만])
R --&gt;|알림 규칙 관련| P{pending?}
P --&gt;|false| E[규칙 추출&lt;br/&gt;gpt-4o&lt;br/&gt;extract_notification_rule]
P --&gt;|true| C[재질문 후속 분류&lt;br/&gt;gpt-4o-mini]
C --&gt;|supplement&lt;br/&gt;보충| M[이전 명령 + 보충&lt;br/&gt;합친 prompt]
C --&gt;|new_command&lt;br/&gt;새 명령| N[이번 prompt만]
M --&gt; E
N --&gt; E
E --&gt;|tool 성공| OK[confirm 문장&lt;br/&gt;gpt-4o]
E --&gt;|정보 부족| ASK[재질문 메시지&lt;br/&gt;ok=false]
OK --&gt; END2([END&lt;br/&gt;규칙 저장])
ASK --&gt; END3([END&lt;br/&gt;앱이 pending 유지])
&lt;/div&gt;
&lt;h3 id="version-02"&gt;Version 02.&lt;/h3&gt;
&lt;div class="mermaid"&gt;flowchart TD
START([사용자 입력&lt;br/&gt;prompt + pending + pendingOriginal]) --&gt; ENTRY{pending=true&lt;br/&gt;AND pendingOriginal 있음?}
ENTRY --&gt;|Yes| RP[resolve_pending]
ENTRY --&gt;|No| R[의도 라우터&lt;br/&gt;gpt-4o-mini&lt;br/&gt;route_intent]
R --&gt;|chitchat&lt;br/&gt;잡담/인사/ㅎㅎ| S[단순 응답&lt;br/&gt;gpt-4o-mini&lt;br/&gt;tool 없음]
S --&gt; END1([END&lt;br/&gt;ok=false&lt;br/&gt;failReason=chitchat&lt;br/&gt;needsSupplement=false])
R --&gt;|extract&lt;br/&gt;알림 규칙 관련| RP
RP --&gt; C{pending 분류?&lt;br/&gt;gpt-4o-mini}
C --&gt;|pending 아님| N[이번 prompt만]
C --&gt;|supplement&lt;br/&gt;보충| M[이전 명령 + 보충&lt;br/&gt;합친 prompt]
C --&gt;|new_command&lt;br/&gt;새 명령| N
M --&gt; E[규칙 추출&lt;br/&gt;gpt-4o&lt;br/&gt;function calling]
N --&gt; E
E --&gt;|성공| OK[confirm 문장&lt;br/&gt;gpt-4o]
E --&gt;|정보 부족| ASK[재질문&lt;br/&gt;ok=false&lt;br/&gt;needsSupplement=true]
E --&gt;|충돌/앱 미해결 등| FAIL[실패&lt;br/&gt;ok=false&lt;br/&gt;needsSupplement=false]
OK --&gt; END2([END&lt;br/&gt;규칙 저장])
ASK --&gt; END3([END&lt;br/&gt;앱이 pending 유지])
FAIL --&gt; END4([END&lt;br/&gt;pending 해제])
&lt;/div&gt;
&lt;h3 id="langgraph-도입-시-routing-버전을-추가-확인-사항"&gt;LangGraph 도입 시 (&lt;code&gt;Routing 버전&lt;/code&gt;을 추가), 확인 사항&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;A버전에 비해 B버전에서, &lt;strong&gt;정확도&lt;/strong&gt;가 많이 떨어지지 않는지&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;비용 및 속도&lt;/strong&gt;가 얼마나 줄어드는지&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="evaluation-dataset-구성-기준"&gt;Evaluation Dataset 구성 기준&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Evaluation Dataset&lt;/code&gt;이 특정 유형에 편중되지 않도록, 주요 평가 축을 &lt;code&gt;intent&lt;/code&gt;, &lt;code&gt;time&lt;/code&gt;, &lt;code&gt;target&lt;/code&gt;으로 나누고 각 카테고리 별 최소 케이스 수를 정의했습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;전체 Dataset: &lt;strong&gt;85 cases&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;주요 카테고리 별 최소 5 cases 이상&lt;/strong&gt; 확보&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ok=true&lt;/code&gt; 케이스에서는 &lt;code&gt;time&lt;/code&gt;, &lt;code&gt;target&lt;/code&gt; 세부 유형이 모두 포함되도록 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="coverage-확인"&gt;Coverage 확인&lt;/h3&gt;
&lt;div class="callout flex px-4 py-3 mb-6 rounded-md border-l-4 bg-blue-100 dark:bg-blue-900 border-blue-500"
data-callout="note"
data-callout-metadata=""&gt;
&lt;span class="callout-icon pr-3 pt-1 text-blue-600 dark:text-blue-300"&gt;
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="m16.862 4.487l1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8l.8-2.685a4.5 4.5 0 0 1 1.13-1.897zm0 0L19.5 7.125"/&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="callout-content dark:text-neutral-300"&gt;
&lt;div class="callout-title font-semibold mb-1"&gt;모든 주요 카테고리에서 최소 5개 이상의 평가 케이스를 확보하여, 특정 입력 유형이 Dataset에서 완전히 누락되는 것을 방지했습니다.&lt;/div&gt;
&lt;div class="callout-body"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;축&lt;/th&gt;
&lt;th&gt;값&lt;/th&gt;
&lt;th style="text-align: right"&gt;개수&lt;/th&gt;
&lt;th style="text-align: right"&gt;기준&lt;/th&gt;
&lt;th style="text-align: center"&gt;상태&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;A. intent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mute&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;44&lt;/td&gt;
&lt;td style="text-align: right"&gt;≥5&lt;/td&gt;
&lt;td style="text-align: center"&gt;OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;A. intent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;allow&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;14&lt;/td&gt;
&lt;td style="text-align: right"&gt;≥5&lt;/td&gt;
&lt;td style="text-align: center"&gt;OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;A. intent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;reject&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;27&lt;/td&gt;
&lt;td style="text-align: right"&gt;≥5&lt;/td&gt;
&lt;td style="text-align: center"&gt;OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;B. time (ok=true 58건)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;duration&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;38&lt;/td&gt;
&lt;td style="text-align: right"&gt;≥5&lt;/td&gt;
&lt;td style="text-align: center"&gt;OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;B. time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;until_absolute&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;5&lt;/td&gt;
&lt;td style="text-align: right"&gt;≥5&lt;/td&gt;
&lt;td style="text-align: center"&gt;OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;B. time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;relative_delay&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;5&lt;/td&gt;
&lt;td style="text-align: right"&gt;≥5&lt;/td&gt;
&lt;td style="text-align: center"&gt;OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;B. time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;daily&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;5&lt;/td&gt;
&lt;td style="text-align: right"&gt;≥5&lt;/td&gt;
&lt;td style="text-align: center"&gt;OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;B. time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;weekly&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;5&lt;/td&gt;
&lt;td style="text-align: right"&gt;≥5&lt;/td&gt;
&lt;td style="text-align: center"&gt;OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;C. target (ok=true 58건)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;all&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;11&lt;/td&gt;
&lt;td style="text-align: right"&gt;≥5&lt;/td&gt;
&lt;td style="text-align: center"&gt;OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;C. target&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;single_app&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;29&lt;/td&gt;
&lt;td style="text-align: right"&gt;≥5&lt;/td&gt;
&lt;td style="text-align: center"&gt;OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;C. target&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;multi_app&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;5&lt;/td&gt;
&lt;td style="text-align: right"&gt;≥5&lt;/td&gt;
&lt;td style="text-align: center"&gt;OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;C. target&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;content&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;6&lt;/td&gt;
&lt;td style="text-align: right"&gt;≥5&lt;/td&gt;
&lt;td style="text-align: center"&gt;OK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;C. target&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;app_and_content&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;7&lt;/td&gt;
&lt;td style="text-align: right"&gt;≥5&lt;/td&gt;
&lt;td style="text-align: center"&gt;OK&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;LangGraph 도입해야 한다면 내 프로젝트 서비스를 최적화시켜줄 수 있는 최적의 참조 논문은..?&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Reasoning&lt;/code&gt;과 &lt;code&gt;Action&lt;/code&gt;을 번갈아 수행하면서 외부 환경 / 도구와 상호작용하는 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="2"&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;같은 LLM이 &lt;code&gt;Generator&lt;/code&gt;, &lt;code&gt;Feedback Provider&lt;/code&gt;, &lt;code&gt;Refiner&lt;/code&gt; 역할을 수행하면서 반복적으로 결과 개선&lt;/li&gt;
&lt;li&gt;별도의 &lt;code&gt;Supervised Training&lt;/code&gt;이나 &lt;code&gt;RL&lt;/code&gt; 없이도 &lt;strong&gt;여러 task에서 one-shot generation보다 성능 개선&lt;/strong&gt; 보고&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="3"&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;자연어 &lt;code&gt;reflection&lt;/code&gt; 형태로 &lt;code&gt;memory&lt;/code&gt;에 저장&lt;/strong&gt;하고 다음 &lt;code&gt;attempt&lt;/code&gt;에 활용하는 구조&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LangGraph&lt;/code&gt;에서 &lt;code&gt;state&lt;/code&gt; / &lt;code&gt;memory&lt;/code&gt; / &lt;code&gt;retry loop&lt;/code&gt;를 설계할 때 굉장히 자연스럽게 연결&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="4"&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;틀린 모델이 자기 답을 평가&lt;/strong&gt;하면 틀릴 수 있음.&lt;/li&gt;
&lt;li&gt;검색엔진, 코드 실행기 같은 &lt;strong&gt;외부 도구의 피드백을 사용해 검증&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="5"&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;여러 &lt;code&gt;reasoning candidate&lt;/code&gt;를 만들고 평가하면서 &lt;code&gt;search&lt;/code&gt; / &lt;code&gt;branching&lt;/code&gt; / &lt;code&gt;backtracking&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="6"&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;전체 문제를 먼저 작은 &lt;code&gt;subtask&lt;/code&gt;로 나눈 뒤 실행&lt;/li&gt;
&lt;li&gt;특히 &lt;code&gt;Zero-shot CoT&lt;/code&gt;에서 발생하는 &lt;code&gt;missing-step&lt;/code&gt; 등의 문제를 해결하려는 접근&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="7"&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;reasoning&lt;/code&gt;과 &lt;code&gt;tool observation&lt;/code&gt;을 &lt;strong&gt;분리&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="8"&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Tool Calling&lt;/code&gt;의 중요한 초기 연구&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LLM이 언제 어떤 API를 사용할지를 결정&lt;/strong&gt;하는 것 자체를 연구한 논문&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="9"&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;같은 여러 &lt;code&gt;Agent&lt;/code&gt; 간 &lt;code&gt;interaction&lt;/code&gt;을 일반화한 프레임워크 연구&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LLM&lt;/code&gt;, &lt;code&gt;human input&lt;/code&gt;, &lt;code&gt;tool&lt;/code&gt;을 조합한 &lt;code&gt;customizable/conversable agen&lt;/code&gt;t를 구성하고 다양한 &lt;code&gt;conversation pattern&lt;/code&gt;을 정의&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>사용자 재질문 처리 전략</title><link>https://inhyuk2000.github.io/blog/notillm/%EA%B8%B0%ED%9A%8D_0816/</link><pubDate>Sun, 16 Aug 2026 00:00:00 +0900</pubDate><guid>https://inhyuk2000.github.io/blog/notillm/%EA%B8%B0%ED%9A%8D_0816/</guid><description>&lt;p&gt;&lt;strong&gt;pending flag를 활용해 재질문 처리용 LLM 추가&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="backendapppendingpy-추가"&gt;backend/app/pending.py 추가&lt;/h2&gt;
&lt;p&gt;&lt;span class="high-mark"&gt;&lt;strong&gt;Sequence Diagram&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이전 대화에서 사용자 질문 의도 파악 불가로 &lt;u&gt;재질문&lt;/u&gt; + &lt;u&gt;사용자 보충 질문 추가&lt;/u&gt;하는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="mermaid"&gt;sequenceDiagram
participant U as 사용자
participant App as Android PromptEngine
participant API as FastAPI handle
participant C as gpt-4o-mini 분류
participant E as gpt-4o 추출
U-&gt;&gt;App: 카톡만 받아줘
App-&gt;&gt;API: pending=false
API-&gt;&gt;E: extract
E--&gt;&gt;API: ok=false + 재질문
API--&gt;&gt;App: ok=false
App-&gt;&gt;App: pendingOriginal 저장
U-&gt;&gt;App: 1시간
App-&gt;&gt;API: pending=true + pendingOriginal
API-&gt;&gt;C: 이전+이번 분류
C--&gt;&gt;API: supplement
API-&gt;&gt;E: 합친 prompt로 extract
E--&gt;&gt;API: ok=true + 규칙
API--&gt;&gt;App: ok=true
App-&gt;&gt;App: 규칙 저장, pending clear
&lt;/div&gt;
&lt;h3 id="classify_pending_turn-함수"&gt;&lt;code&gt;classify_pending_turn&lt;/code&gt; 함수&lt;/h3&gt;
&lt;p&gt;재질문 대기 중일 때, &lt;span class="high-mark"&gt;&lt;strong&gt;이번 입력이 보충인지 새 명령인지 LLM으로 판별&lt;/strong&gt;&lt;/span&gt;
한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pending_original&lt;/code&gt;: 재질문 전 사용자 명령&lt;/li&gt;
&lt;li&gt;&lt;code&gt;current_prompt&lt;/code&gt;: 재질문 후 사용자 입력&lt;/li&gt;
&lt;li&gt;&lt;code&gt;supplement&lt;/code&gt;: 이전 명령에 대한 보충 (예: 1시간)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;new_command&lt;/code&gt;: 이전과 무관한 새 규칙&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="resolve_extract_prompt-함수"&gt;&lt;code&gt;resolve_extract_prompt&lt;/code&gt; 함수&lt;/h3&gt;
&lt;p&gt;handle 앞에서 &lt;span class="high-mark"&gt;&lt;strong&gt;추출용 prompt 문자열을 만드는 진입점&lt;/strong&gt;&lt;/span&gt;
역할을 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;prompt&lt;/code&gt;: 이번 사용자 입력&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pending&lt;/code&gt;: 앱이 보낸 재질문 대기 여부&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pending_original&lt;/code&gt;: 재질문 전 원문&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(extract_prompt, classification)&lt;/code&gt;: 추출 LLM에 넣을 문장 + 분류 결과 [None: 분류 안함]&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="callout flex px-4 py-3 mb-6 rounded-md border-l-4 bg-blue-100 dark:bg-blue-900 border-blue-500"
data-callout="todo"
data-callout-metadata=""&gt;
&lt;span class="callout-icon pr-3 pt-1 text-blue-600 dark:text-blue-300"&gt;
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 12.75L11.25 15L15 9.75M21 12a9 9 0 1 1-18 0a9 9 0 0 1 18 0"/&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="callout-content dark:text-neutral-300"&gt;
&lt;div class="callout-title font-semibold mb-1"&gt;추가 재질문 (Pending 처리) LLM이 실 기기에서 동작하는 지에 대한 테스트 진행 필요&lt;/div&gt;
&lt;div class="callout-body"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>baseline_v1_auto_map Evaluation 및 개선</title><link>https://inhyuk2000.github.io/blog/notillm/%EA%B8%B0%ED%9A%8D_0814/</link><pubDate>Fri, 14 Aug 2026 00:00:00 +0900</pubDate><guid>https://inhyuk2000.github.io/blog/notillm/%EA%B8%B0%ED%9A%8D_0814/</guid><description>&lt;p&gt;&lt;strong&gt;Evaluation에서 확인된 부분을 기반으로 코드 수정&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;&lt;img src="./image.png" alt="LangSmith" loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
LangSmith를 기반으로 53문항의 테스트를 진행했고, 개선 수행.&lt;/p&gt;
&lt;p&gt;
에서는 여러 앱의 메타데이터도 같이 넣어서, context기반 cosine 유사도 계산 방식을 추천했으나, 메타데이터를 불러올 적절한 방법을 찾지 못함.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;앱 관련 시스템 정보에는 메타데이터가 존재하지 않았음. 전부 &lt;code&gt;null&lt;/code&gt; 필드.&lt;/li&gt;
&lt;li&gt;Google Play Store에서 API 형태로 앱의 Description을 가져오는 것도 생각해 보았으나, 서비스가 너무 무거워질 것으로 예상되어 &lt;span class="high-mark"&gt;&lt;strong&gt;유의어 사전을 만드는 방식을 채택함.&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="코드-개선-전-baseline-테스트-결과"&gt;코드 개선 전, Baseline 테스트 결과&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;총 53개 테스트 데이터&lt;/li&gt;
&lt;li&gt;정답률 : &lt;span class="high-mark"&gt;&lt;strong&gt;0.87&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="error-case-1"&gt;Error Case 1.&lt;/h2&gt;
&lt;div class="callout flex px-4 py-3 mb-6 rounded-md border-l-4 bg-green-100 dark:bg-green-900 border-green-500"
data-callout="success"
data-callout-metadata=""&gt;
&lt;span class="callout-icon pr-3 pt-1 text-green-600 dark:text-green-300"&gt;
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 12.75L11.25 15L15 9.75M21 12a9 9 0 1 1-18 0a9 9 0 0 1 18 0"/&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="callout-content dark:text-neutral-300"&gt;
&lt;div class="callout-title font-semibold mb-1"&gt;&amp;ldquo;카톡 2분만 받지마&amp;rdquo;&lt;/div&gt;
&lt;div class="callout-body"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;output
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;ok&amp;rdquo;: false&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ground truth
&lt;ul&gt;
&lt;li&gt;현재 시간부터 2분동안 카톡을 받지 않도록 설정함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;피드백
&lt;ul&gt;
&lt;li&gt;시스템 프롬프트에 현재 시간이 정의되어 있지 않으면 현재 시간 부터라는 내용을 추가해 넣어줘야 할 거 같음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="error-case-2"&gt;Error Case 2.&lt;/h2&gt;
&lt;div class="callout flex px-4 py-3 mb-6 rounded-md border-l-4 bg-green-100 dark:bg-green-900 border-green-500"
data-callout="success"
data-callout-metadata=""&gt;
&lt;span class="callout-icon pr-3 pt-1 text-green-600 dark:text-green-300"&gt;
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 12.75L11.25 15L15 9.75M21 12a9 9 0 1 1-18 0a9 9 0 0 1 18 0"/&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="callout-content dark:text-neutral-300"&gt;
&lt;div class="callout-title font-semibold mb-1"&gt;&amp;ldquo;지금부터 1시간 문자만 받아줘&amp;rdquo;&lt;/div&gt;
&lt;div class="callout-body"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;output
&lt;ul&gt;
&lt;li&gt;문자 앱이 아닌 com.samsung.android.mdecservice 다른 앱의 cosine 유사도가 가장 높았고, 이 마저도 0.5 이하여서 ok=false 반환.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ground truth
&lt;ul&gt;
&lt;li&gt;packages에 &amp;ldquo;com.samsung.android.messaging&amp;quot;이 담겨야 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;피드백
&lt;ul&gt;
&lt;li&gt;Cosine 유사도 판단 오류. &amp;ldquo;문자&amp;quot;와 &amp;ldquo;메시지&amp;quot;의 코사인 유사도가 낮게 나옴.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="error-case-3"&gt;Error Case 3.&lt;/h2&gt;
&lt;div class="callout flex px-4 py-3 mb-6 rounded-md border-l-4 bg-orange-100 dark:bg-orange-900 border-orange-500"
data-callout="warning"
data-callout-metadata=""&gt;
&lt;span class="callout-icon pr-3 pt-1 text-orange-600 dark:text-orange-300"&gt;
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0zM12 15.75h.007v.008H12z"/&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="callout-content dark:text-neutral-300"&gt;
&lt;div class="callout-title font-semibold mb-1"&gt;&amp;ldquo;지금부터 2시간 문자 스팸 알림 받지마&amp;rdquo;&lt;/div&gt;
&lt;div class="callout-body"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;output
&lt;ul&gt;
&lt;li&gt;name 필드는 비어있고, content=[&amp;ldquo;문자&amp;rdquo;, &amp;ldquo;스팸&amp;rdquo;]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ground truth
&lt;ul&gt;
&lt;li&gt;name=&amp;ldquo;메시지&amp;rdquo;, content=&amp;ldquo;스팸&amp;rdquo; 이 담겨야 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;피드백
&lt;ul&gt;
&lt;li&gt;스팸이 name인지 앞서 나온 &amp;ldquo;문자&amp;quot;에 대한 &amp;ldquo;스팸&amp;quot;인지 확인할 수 있어야 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="error-case-4"&gt;Error Case 4.&lt;/h2&gt;
&lt;div class="callout flex px-4 py-3 mb-6 rounded-md border-l-4 bg-green-100 dark:bg-green-900 border-green-500"
data-callout="success"
data-callout-metadata=""&gt;
&lt;span class="callout-icon pr-3 pt-1 text-green-600 dark:text-green-300"&gt;
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 12.75L11.25 15L15 9.75M21 12a9 9 0 1 1-18 0a9 9 0 0 1 18 0"/&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="callout-content dark:text-neutral-300"&gt;
&lt;div class="callout-title font-semibold mb-1"&gt;&amp;ldquo;지금부터 2시간 문자에서 &amp;lsquo;스팸&amp;rsquo;이라는 단어가 들어간 알림 받지마&amp;rdquo;&lt;/div&gt;
&lt;div class="callout-body"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;output
&lt;ul&gt;
&lt;li&gt;문자 앱이 아닌 com.samsung.android.mdecservice 다른 앱의 cosine 유사도가 가장 높았고, 이 마저도 0.5 이하여서 ok=false 반환.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ground truth
&lt;ul&gt;
&lt;li&gt;packages에 &amp;ldquo;com.samsung.android.messaging&amp;quot;이 담겨야 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;피드백
&lt;ul&gt;
&lt;li&gt;Cosine 유사도 판단 오류. &amp;ldquo;문자&amp;quot;와 &amp;ldquo;메시지&amp;quot;의 코사인 유사도가 낮게 나옴.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="error-case-5"&gt;Error Case 5.&lt;/h2&gt;
&lt;div class="callout flex px-4 py-3 mb-6 rounded-md border-l-4 bg-green-100 dark:bg-green-900 border-green-500"
data-callout="success"
data-callout-metadata=""&gt;
&lt;span class="callout-icon pr-3 pt-1 text-green-600 dark:text-green-300"&gt;
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 12.75L11.25 15L15 9.75M21 12a9 9 0 1 1-18 0a9 9 0 0 1 18 0"/&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="callout-content dark:text-neutral-300"&gt;
&lt;div class="callout-title font-semibold mb-1"&gt;&amp;ldquo;지금부터 2시간 플레이스토어 업데이트 알림 받지마&amp;rdquo;&lt;/div&gt;
&lt;div class="callout-body"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;output
&lt;ul&gt;
&lt;li&gt;com.android.vending 앱이 아닌 com.samsung.android.smartsuggestions 다른 앱의 cosine 유사도가 가장 높았음. 즉, 잘못된 앱의 이름을 설정함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ground truth
&lt;ul&gt;
&lt;li&gt;packages에 &amp;ldquo;com.android.vending&amp;quot;이 담겨야 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;피드백
&lt;ul&gt;
&lt;li&gt;Cosine 유사도 판단 오류.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="error-case-5-제외-케이스"&gt;Error Case 5. [제외 케이스]&lt;/h2&gt;
&lt;div class="callout flex px-4 py-3 mb-6 rounded-md border-l-4 bg-orange-100 dark:bg-orange-900 border-orange-500"
data-callout="warning"
data-callout-metadata=""&gt;
&lt;span class="callout-icon pr-3 pt-1 text-orange-600 dark:text-orange-300"&gt;
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0zM12 15.75h.007v.008H12z"/&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="callout-content dark:text-neutral-300"&gt;
&lt;div class="callout-title font-semibold mb-1"&gt;&amp;ldquo;지금부터 12시간 카톡 홍보, 프로모션이나 카드 알림은 띄우지마&amp;rdquo;&lt;/div&gt;
&lt;div class="callout-body"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;output
&lt;ul&gt;
&lt;li&gt;name=&amp;ldquo;카카오톡&amp;rdquo;, content=&amp;ldquo;홍보, 프로모션, 카드&amp;rdquo; 가 들어감.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ground truth
&lt;ul&gt;
&lt;li&gt;현재 구조로는 해결이 불가능. ok=false&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;피드백
&lt;ul&gt;
&lt;li&gt;아직 해결 불가능.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="error-case-6"&gt;Error Case 6.&lt;/h2&gt;
&lt;div class="callout flex px-4 py-3 mb-6 rounded-md border-l-4 bg-orange-100 dark:bg-orange-900 border-orange-500"
data-callout="warning"
data-callout-metadata=""&gt;
&lt;span class="callout-icon pr-3 pt-1 text-orange-600 dark:text-orange-300"&gt;
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0zM12 15.75h.007v.008H12z"/&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="callout-content dark:text-neutral-300"&gt;
&lt;div class="callout-title font-semibold mb-1"&gt;&amp;ldquo;지금부터 5분 카톡 메세지 알림 받지마&amp;rdquo;&lt;/div&gt;
&lt;div class="callout-body"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;output
&lt;ul&gt;
&lt;li&gt;name=&amp;ldquo;카카오톡&amp;rdquo;, content=&amp;ldquo;메세지&amp;rdquo; 가 들어감.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ground truth
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;카톡 메세지&amp;rdquo; 라고 말을 해도, 사실상 &amp;ldquo;카톡&amp;quot;을 의미하는 것이므로, name=&amp;ldquo;카카오톡&amp;rdquo; 이고 content는 비워야 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;피드백
&lt;ul&gt;
&lt;li&gt;추출 오류.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span class="high-mark"&gt;&lt;strong&gt;문제점&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;다른 기기에서도 전화/문자하기&amp;rdquo;(&amp;ldquo;com.samsung.android.mdecservice&amp;rdquo;) 와 &amp;ldquo;메시지&amp;rdquo;(&amp;ldquo;com.samsung.android.messaging&amp;rdquo;)가 있을 때, &amp;ldquo;문자&amp;quot;라는 단어가 &amp;ldquo;메시지&amp;quot;에 매핑되도록 하려면 어떻게 해야 하나?&lt;/li&gt;
&lt;li&gt;&amp;ldquo;퍼스널 데이터 인텔리전스&amp;rdquo;(&amp;ldquo;com.samsung.android.smartsuggestions&amp;rdquo;) 와 &amp;ldquo;Play 스토어&amp;rdquo;(&amp;ldquo;com.android.vending&amp;rdquo;)가 있을 때, &amp;ldquo;플레이스토어&amp;quot;라는 단어가 &amp;ldquo;Play 스토어&amp;quot;에 매핑되도록 하려면 어떻게 해야 하나?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span class="high-mark"&gt;&lt;strong&gt;나의 해결책&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;처음에는
사이트에서 관련 메타데이터를 찾을 수 있는 방법을 고민함.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;하지만 &lt;span class="high-mark"&gt;&lt;strong&gt;해당 필드가 전부 비어있는 값으로 나와 사용할 수 없었음.&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;따라서 &lt;span class="high-mark"&gt;&lt;strong&gt;유의어 사전&lt;/strong&gt;&lt;/span&gt;
을 만드는 방법을 채택함.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;LLM이 뽑은 **앱 이름(name, &amp;ldquo;문자&amp;rdquo;)**으로 찾음&lt;/li&gt;
&lt;li&gt;유의어 사전에 그 그룹 단어를 [&amp;ldquo;문자&amp;rdquo;, &amp;ldquo;메시지&amp;rdquo;, &amp;ldquo;SMS&amp;rdquo;, &amp;hellip;] 로 확장&lt;/li&gt;
&lt;li&gt;(확장된 각 단어) X (설치 앱 각 라벨) : Cosine&lt;/li&gt;
&lt;li&gt;각 앱마다 그 중 가장 높은 점수로 대표&lt;/li&gt;
&lt;li&gt;전 앱 중 최고 + Threshold 로 최종 package 결정&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;시스템 프롬프트 &lt;strong&gt;few-shot learning&lt;/strong&gt; 추가&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;N분(만/동안/간) …&amp;#34;&lt;/span&gt; &lt;span class="n"&gt;처럼&lt;/span&gt; &lt;span class="n"&gt;기간만&lt;/span&gt; &lt;span class="n"&gt;있고&lt;/span&gt; &lt;span class="n"&gt;시작&lt;/span&gt; &lt;span class="n"&gt;시각이&lt;/span&gt; &lt;span class="n"&gt;없으면&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;지금부터&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;delivery&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;현재&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expires&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;현재&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;N분&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;예&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;카톡 2분만 받지마&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;카톡 5분동안 받지마&amp;#34;&lt;/span&gt; &lt;span class="n"&gt;모두&lt;/span&gt; &lt;span class="n"&gt;현재부터&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="코드-개선-이후"&gt;코드 개선 이후&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;정답률 : &lt;span class="high-mark"&gt;&lt;strong&gt;0.91&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="callout flex px-4 py-3 mb-6 rounded-md border-l-4 bg-orange-100 dark:bg-orange-900 border-orange-500"
data-callout="warning"
data-callout-metadata=""&gt;
&lt;span class="callout-icon pr-3 pt-1 text-orange-600 dark:text-orange-300"&gt;
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0zM12 15.75h.007v.008H12z"/&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="callout-content dark:text-neutral-300"&gt;
&lt;div class="callout-title font-semibold mb-1"&gt;&amp;ldquo;매일 밤 10시부터 새벽 4시까지 조용히&amp;rdquo;&lt;/div&gt;
&lt;div class="callout-body"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout flex px-4 py-3 mb-6 rounded-md border-l-4 bg-orange-100 dark:bg-orange-900 border-orange-500"
data-callout="warning"
data-callout-metadata=""&gt;
&lt;span class="callout-icon pr-3 pt-1 text-orange-600 dark:text-orange-300"&gt;
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0zM12 15.75h.007v.008H12z"/&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="callout-content dark:text-neutral-300"&gt;
&lt;div class="callout-title font-semibold mb-1"&gt;&amp;ldquo;매일 밤 10시부터 아침 6시까지 모든 알림 받지마&amp;rdquo;&lt;/div&gt;
&lt;div class="callout-body"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;주기성 명령의 경우 delivery와 expires 시간 계산에서 예측값과 실제값이 다르게 나옴. &lt;span class="high-mark"&gt;&lt;strong&gt;하지만 이것이 결과에는 영향을 주지 않음&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;프롬프트 변경을 통해 해결함.
&lt;ul&gt;
&lt;li&gt;구간 전: 다음 회차&lt;/li&gt;
&lt;li&gt;구간 안: 진행 중 회차&lt;/li&gt;
&lt;li&gt;구간 끝남: 다음 밤 회차&lt;/li&gt;
&lt;li&gt;지난 회차 금지. &lt;code&gt;overrnight&lt;/code&gt;면 &lt;code&gt;expires = delivery + 1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>LangSmith를 활용한 Evaluation 설계</title><link>https://inhyuk2000.github.io/blog/notillm/%EA%B8%B0%ED%9A%8D_0812/</link><pubDate>Wed, 12 Aug 2026 00:00:00 +0900</pubDate><guid>https://inhyuk2000.github.io/blog/notillm/%EA%B8%B0%ED%9A%8D_0812/</guid><description>&lt;p&gt;&lt;strong&gt;Phoenix 기반 DeepLearningAI의 강의 코드를 참조해, 동일한 로직을 LangSmith로 구현함&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="evaluation-설명"&gt;Evaluation 설명&lt;/h2&gt;
&lt;p&gt;기존 시스템은 Kotlin 기반 Android 앱 내부의 PromptEngine.kt에서 자연어 입력을 LLM에 전달하고, Function Calling 결과를 바탕으로 알림 규칙을 생성하는 구조였다.&lt;/p&gt;
&lt;p&gt;하지만 Android 플랫폼 로직과 LLM Extraction 로직이 하나의 코드에 강하게 결합되어 있어, &lt;span class="high-mark"&gt;&lt;strong&gt;LLM 성능만 독립적으로 평가하거나 Prompt 변경에 따른 성능 변화를 반복적으로 검증하기 어려운 구조&lt;/strong&gt;&lt;/span&gt;
였다.&lt;/p&gt;
&lt;p&gt;이에 기존 PromptEngine.kt에서 수행하던 자연어 조건 추출 로직을 분리하여, &lt;span class="high-mark"&gt;&lt;strong&gt;FastAPI + LangChain 기반의 AI Backend&lt;/strong&gt;&lt;/span&gt;
로 이전하였다.&lt;/p&gt;
&lt;p&gt;이를 통해 Android 앱은 사용자 입력을 AI Backend로 전달하고 결과를 받아 실제 알림 규칙으로 저장하는 역할에 집중하고, AI Backend에서는 LLM 호출, Function Calling, 결과 후처리 및 Evaluation을 독립적으로 수행할 수 있도록 구조를 분리하였다.&lt;/p&gt;
&lt;h3 id="fastapi-기반-ai-backend-부분-설명"&gt;FastAPI 기반 AI Backend 부분 설명&lt;/h3&gt;
&lt;p&gt;FastAPI 기반 AI Backend의 핵심 로직은 크게 &lt;span class="high-mark"&gt;&lt;strong&gt;다음 4단계&lt;/strong&gt;&lt;/span&gt;
로 구성하였다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;시스템 프롬프트 few shot learning 관련 코드&lt;/li&gt;
&lt;li&gt;현재 시간을 지정해 function calling용 Tool Schema 리스트를 return 해주는 코드&lt;/li&gt;
&lt;li&gt;LLM의 추론으로 반환된 Function Calling 결과를 내 서비스에서 사용하기 쉽도록 후처리하는 코드&lt;/li&gt;
&lt;li&gt;&lt;span class="high-mark"&gt;&lt;strong&gt;메인 처리 로직&lt;/strong&gt;&lt;/span&gt;
: (사용자 프롬프트, 현재 시간) 입력 -&amp;gt; (targetFixed, condition 포함한 Response Json) 반환&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;AI Backend 코드에 대한 Kotlin 연결부 : FastAPI LLM 서버에 요청하고 결과를 기존 Android 규칙 시스템에 연결&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="langsmith를-활용한-llm-실행-추적"&gt;LangSmith를 활용한 LLM 실행 추적&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;LangSmith를 활용해서 &lt;span class="high-mark"&gt;&lt;strong&gt;현재 LangChain 기반 LLM Function Calling을 추적&lt;/strong&gt;&lt;/span&gt;
했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;&lt;img src="./image.png" alt="LangSmith 결과화면" loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;&lt;code&gt;handle&lt;/code&gt; 함수안의 두 &lt;code&gt;ChatOpenAI.invoke&lt;/code&gt; 함수를 자동으로 추적하고 있는 것을 확인할 수 있었다!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="code-based-evaluation-설계-과정"&gt;Code-based Evaluation 설계 과정&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;evals&lt;/code&gt; 폴더 생성 후, 평가에 사용할 샘플 5개 &lt;strong&gt;JSON 데이터셋(input, output)&lt;/strong&gt; 생성&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dataset_upload.py&lt;/code&gt; : Jsonl 확장자 파일에서 정의한 데이터셋을 &lt;strong&gt;LangSmith Dataset&lt;/strong&gt;에 업로드&lt;/li&gt;
&lt;li&gt;&lt;code&gt;System Few Shot&lt;/code&gt; 부분도 전체적으로 어떻게 프롬프팅 했는지 점검해봐야 함.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;evaluators.py&lt;/code&gt; 파일과 &lt;code&gt;run_experiment.py&lt;/code&gt; 파일 생성해서 LangSmith 기반 테스트 진행함. (정답률 : 100%, 샘플 5개 적용)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="evaluatorspy-코드에-대해서"&gt;Evaluators.py 코드에 대해서..&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;extract_rule_correctness&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;reference_outputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; ExtractRule JSON 정답 일치도 (binary code metric).
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; LangSmith evaluate()가 Example마다 호출:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; outputs ← Target(handle) 반환값
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; reference_outputs ← Dataset example.outputs
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Returns:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; key/score 형태 dict — UI 지표명과 0~1 점수
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; comment — 필드별 pass/fail (디버깅용)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; delivery/expires 는 currentTime이 고정이므로 ISO 문자열 exact match.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;outputs&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reference_outputs&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# ok (성공/실패) — router 성격&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ok&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pred&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ok&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ok&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 정답이 실패면 구조 필드 없음 → ok만 채점&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 예: &amp;#34;카톡만 받아줘&amp;#34; (시간 없음) → ok=false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ok&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;key&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;extract_rule_correctness&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;score&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ok&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;comment&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 성공 정답 → targetFixed / condition 필드 비교&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 정답에 있는 키만 검사 (부분 reference 허용)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pred&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;targetFixed&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;et&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;targetFixed&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pred&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;condition&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;condition&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;mute&amp;#34;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;et&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;mute&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;mute&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;et&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;mute&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;name&amp;#34;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;et&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_as_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;_as_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;et&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;content&amp;#34;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;et&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_as_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;_as_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;et&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;recurrence&amp;#34;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;recurrence&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;recurrence&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;recurrence&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;delivery&amp;#34;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;delivery&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;delivery&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;_abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;delivery&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;expires&amp;#34;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;expires&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;expires&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;_abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;expires&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;passed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;checks&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;key&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;extract_rule_correctness&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;score&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;passed&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;comment&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;LangSmith&lt;/code&gt;에서 &lt;code&gt;Target(handle)&lt;/code&gt; 반환값과 &lt;code&gt;Dataset example.outputs&lt;/code&gt; 의 결과값을 비교해 &lt;code&gt;0, 1&lt;/code&gt; 점(binary)으로 수치화함.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;handle&lt;/code&gt; 함수의 결과값 JSON 필드는 예상과 정확하게 나와야 하기 때문에, &lt;span class="high-mark"&gt;&lt;strong&gt;Code Based Evaluation&lt;/strong&gt;&lt;/span&gt;
을 진행함.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="dataset-생성"&gt;Dataset 생성&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;예전 학부연구생 생활하면서, 같은 연구실 동기들에게 받은 32개의 예시에는 &lt;code&gt;ok=False&lt;/code&gt;가 너무 많아 평가가 편향될 것으로 예상되어, 10개로 줄였다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ok = True&lt;/code&gt; 는 40개 / &lt;code&gt;ok = False&lt;/code&gt; 는 10개로 개수를 맞춰 테스트를 진행할 예정이다.&lt;/li&gt;
&lt;li&gt;본 Evaluation에서는 사용자 자연어 요청으로부터 앱, 콘텐츠, 알림 허용/차단 및 시간 조건을 구조화된 JSON으로 정확하게 추출하는지를 검증하며, &lt;span class="high-mark"&gt;&lt;strong&gt;콘텐츠의 의미적 분류 정확도는 평가 범위에서 제외&lt;/strong&gt;&lt;/span&gt;
한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;&lt;img src="./image2.png" alt="LangSmith 결과화면" loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;의미적 분류 정확도는 현재 오류로 검증하지 않았지만, &lt;strong&gt;개선 사항&lt;/strong&gt;임.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;extra_rule_correctness&lt;/code&gt; = 0.76 : 왜 이 테스트 결과값이 나왔는지 점검 해봐야 한다.&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>전체 NotiLLM Schedule 및 Architecture 설계</title><link>https://inhyuk2000.github.io/blog/notillm/%EA%B8%B0%ED%9A%8D/</link><pubDate>Tue, 11 Aug 2026 00:00:00 +0900</pubDate><guid>https://inhyuk2000.github.io/blog/notillm/%EA%B8%B0%ED%9A%8D/</guid><description>&lt;h2 id="schedule"&gt;Schedule&lt;/h2&gt;
&lt;p&gt;단순히 기능을 추가하는 방식이 아니라, &lt;strong&gt;측정 가능한 기준을 세우고 반복적으로 개선하는 과정&lt;/strong&gt;을 중심으로 프로젝트를 진행했습니다.&lt;/p&gt;
&lt;p&gt;아래와 같이 단순한 LLM 기능 구현을 넘어, &lt;code&gt;Evaluation&lt;/code&gt; - &lt;code&gt;Optimization&lt;/code&gt; - &lt;code&gt;Validation&lt;/code&gt; - &lt;code&gt;Production&lt;/code&gt; 으로 이어지는 AI Engineering 전 과정을 경험하는 것을 목표로 합니다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align:center;"&gt;순서&lt;/th&gt;
&lt;th&gt;할 일&lt;/th&gt;
&lt;th&gt;핵심 산출물&lt;/th&gt;
&lt;th&gt;키우는 역량&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr style="background-color: rgba(81, 255, 0, 0.18);"&gt;
&lt;td style="text-align:center;"&gt;&lt;small&gt;&lt;strong&gt;1&lt;/strong&gt;&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;현재 시스템 Baseline 고정&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;v1 구조/성능 기준&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;System Design&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style="background-color: rgba(81, 255, 0, 0.18);"&gt;
&lt;td style="text-align:center;"&gt;&lt;small&gt;&lt;strong&gt;2&lt;/strong&gt;&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;Evaluation Dataset 구축&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;Ground Truth Dataset&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;LLM Evaluation&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style="background-color: rgba(81, 255, 0, 0.18);"&gt;
&lt;td style="text-align:center;"&gt;&lt;small&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;자동 Evaluation 구현&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;평가 코드 + 지표&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;AI Engineering&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style="background-color: rgba(81, 255, 0, 0.18);"&gt;
&lt;td style="text-align:center;"&gt;&lt;small&gt;&lt;strong&gt;4&lt;/strong&gt;&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;Error Analysis&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;실패 유형/원인&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;Problem Solving&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style="background-color: rgba(255, 215, 0, 0.18);"&gt;
&lt;td style="text-align:center;"&gt;&lt;small&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;구조/Prompt 개선&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;v2&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;LLM Engineering&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style="background-color: rgba(255, 0, 0, 0.18);"&gt;
&lt;td style="text-align:center;"&gt;&lt;small&gt;&lt;strong&gt;6&lt;/strong&gt;&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;LangGraph 도입 검토&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;Stateful Agent&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;Agent Engineering&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style="background-color: rgba(255, 215, 0, 0.18);"&gt;
&lt;td style="text-align:center;"&gt;&lt;small&gt;&lt;strong&gt;7&lt;/strong&gt;&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;재평가 + 통계 검증&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;v1 vs v2 결과&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;Experimentation&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style="background-color: rgba(255, 0, 0, 0.18);"&gt;
&lt;td style="text-align:center;"&gt;&lt;small&gt;&lt;strong&gt;8&lt;/strong&gt;&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;Reliability 강화&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;Retry/Fallback/Validation&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;Production AI&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style="background-color: rgba(255, 215, 0, 0.18);"&gt;
&lt;td style="text-align:center;"&gt;&lt;small&gt;&lt;strong&gt;9&lt;/strong&gt;&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;실제 사용자 배포&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;테스트 사용자&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;Product Engineering&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;small&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;Analytics/Crashlytics 분석&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;실사용 데이터&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;Product Analytics&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;small&gt;&lt;strong&gt;11&lt;/strong&gt;&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;사용자 실험/A-B Test&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;Product 효과 검증&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;Experiment Design&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align:center;"&gt;&lt;small&gt;&lt;strong&gt;12&lt;/strong&gt;&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;최종 포트폴리오 정리&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;GitHub/Portfolio&lt;/small&gt;&lt;/td&gt;
&lt;td&gt;&lt;small&gt;취업&lt;/small&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;본격적인 &lt;code&gt;Evaluation&lt;/code&gt;을 진행하기에 앞서서, 전체 &lt;code&gt;Architecture&lt;/code&gt;를 먼저 설계해 어느 부분을 평가할 지에 대한 계획을 수립했습니다.&lt;/p&gt;
&lt;h2 id="baseline-ver1-architecture"&gt;Baseline Ver.1 Architecture&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="high-mark"&gt;&lt;strong&gt;Mermaid&lt;/strong&gt;&lt;/span&gt;
목적 : Evaluation 시, &lt;span class="high-mark"&gt;&lt;strong&gt;어느 Component에서 문제가 발생하는지 특정&lt;/strong&gt;&lt;/span&gt;
하기 위함&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ChatGPT-4o&lt;/code&gt; 모델로 &lt;code&gt;Function Calling&lt;/code&gt; 전체 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="mermaid"&gt;flowchart TB
user((User))
router["Router&lt;br/&gt;GPT-4o w/&lt;br/&gt;function calling"]
user --&gt;|chat command| router
router --&gt;|reply| user
parse[Parse User Intent]
choose[Choose mute / allow tool]
router --&gt; parse --&gt; choose
cond["extract_notification_condition&lt;br/&gt;(time / recurrence)"]
mute["extract_mute_target&lt;br/&gt;(name / content)"]
allow["extract_allow_target&lt;br/&gt;(name / content)"]
choose --&gt;|function call| cond
choose --&gt;|function call| mute
choose --&gt;|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 --&gt; c1 --&gt; c2
mute --&gt; m1 --&gt; m2
allow --&gt; a1 --&gt; a2
c2 -.-&gt;|tool result| router
m2 -.-&gt;|tool result| router
a2 -.-&gt;|tool result| router
merge["PromptEngine Merge&lt;br/&gt;(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 --&gt;|two JSONs| merge
merge --&gt; inj --&gt; build --&gt; cm --&gt; mapPkg --&gt; validate --&gt; save
save --&gt;|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
&lt;/div&gt;
&lt;h2 id="평가-방식"&gt;평가 방식&lt;/h2&gt;
&lt;div class="callout flex px-4 py-3 mb-6 rounded-md border-l-4 bg-yellow-100 dark:bg-yellow-900 border-yellow-500"
data-callout="question"
data-callout-metadata=""&gt;
&lt;span class="callout-icon pr-3 pt-1 text-yellow-700 dark:text-yellow-300"&gt;
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9.879 7.519c1.172-1.025 3.071-1.025 4.243 0c1.171 1.025 1.171 2.687 0 3.712q-.308.268-.67.442c-.746.361-1.452.999-1.452 1.827v.75M21 12a9 9 0 1 1-18 0a9 9 0 0 1 18 0m-9 5.25h.008v.008H12z"/&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="callout-content dark:text-neutral-300"&gt;
&lt;div class="callout-title font-semibold mb-1"&gt;각 Tools (&lt;code&gt;extract_notification_condition&lt;/code&gt;, &lt;code&gt;extract_mute_target&lt;/code&gt;, &lt;code&gt;extract_allow_target&lt;/code&gt;) 에 따라 &lt;strong&gt;어떤 평가 방식&lt;/strong&gt;을 사용해야 할까?&lt;/div&gt;
&lt;div class="callout-body"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;지금 평가 대상이 &lt;strong&gt;자유로운 자연어 답변이 아니라 스키마가 정해진 구조화된 JSON&lt;/strong&gt;이고, 이미 &lt;strong&gt;Ground Truth&lt;/strong&gt;를 만들 수 있다. 그래서 굳이 또 다른 LLM의 주관적 판단을 끼워 넣을 필요가 없다고 판단했습니다.&lt;/p&gt;
&lt;h3 id="평가-방식-선정"&gt;평가 방식 선정&lt;/h3&gt;
&lt;div class="callout flex px-4 py-3 mb-6 rounded-md border-l-4 bg-blue-100 dark:bg-blue-900 border-blue-500"
data-callout="check"
data-callout-metadata=""&gt;
&lt;span class="callout-icon pr-3 pt-1 text-blue-600 dark:text-blue-300"&gt;
&lt;svg height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"&gt;&lt;path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="m4.5 12.75l6 6l9-13.5"/&gt;&lt;/svg&gt;
&lt;/span&gt;
&lt;div class="callout-content dark:text-neutral-300"&gt;
&lt;div class="callout-title font-semibold mb-1"&gt;&lt;code&gt;Code-based evals&lt;/code&gt; 선정&lt;/div&gt;
&lt;div class="callout-body"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;Function Calling&lt;/code&gt;의 출력이 정해진 &lt;code&gt;JSON Schema&lt;/code&gt;를 따르는 &lt;strong&gt;구조화된 데이터&lt;/strong&gt;이므로, 각 필드를 &lt;code&gt;Ground Truth&lt;/code&gt;와 직접 비교하는 &lt;span class="high-mark"&gt;&lt;strong&gt;Code-based Eval 방식&lt;/strong&gt;&lt;/span&gt;
을 사용하기로 결정했습니다.&lt;/p&gt;
&lt;h3 id="function-calling이-현재-출력하는-json-필드"&gt;Function Calling이 현재 출력하는 JSON 필드&lt;/h3&gt;
&lt;div class="schema-tool-grid"&gt;
&lt;!-- LEFT : 50% WIDTH / 100% HEIGHT --&gt;
&lt;div class="schema-tool-panel"&gt;
&lt;div class="schema-tool-label"&gt;
extract_notification_condition
&lt;/div&gt;
&lt;pre class="schema-tool-pre"&gt;&lt;code&gt;{
"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"
]
}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- RIGHT : 50% WIDTH --&gt;
&lt;div class="schema-tool-stack"&gt;
&lt;!-- RIGHT TOP : 50% HEIGHT --&gt;
&lt;div class="schema-tool-panel"&gt;
&lt;div class="schema-tool-label"&gt;
extract_mute_target
&lt;/div&gt;
&lt;pre class="schema-tool-pre"&gt;&lt;code&gt;{
"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"
]
}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;!-- RIGHT BOTTOM : 50% HEIGHT --&gt;
&lt;div class="schema-tool-panel"&gt;
&lt;div class="schema-tool-label"&gt;
extract_allow_target
&lt;/div&gt;
&lt;pre class="schema-tool-pre"&gt;&lt;code&gt;{
"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"
]
}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>