์ธํ ๋ฆฌ์ด ๋ฆฌ๋ชจ๋ธ๋ง ๊ฐ๊ฒฌ์ ์ฐ์ถ + ๊ณ์ฝ์ ๋ฆฌ์คํฌ ์ง๋จ API ์๋ฒ.
์ค์ ์๊ณต ์ฌ๋ก ํต๊ณ๋ฅผ ๊ทผ๊ฑฐ๋ก ๊ฐ๊ฒฌ์ ์ ์ฐ์ถํ๊ณ , ์ ๋ก๋๋ ๊ฒฌ์ ์ ์ด๋ฏธ์ง๋ฅผ ๋ถ์ํด ๊ณ์ฝ ๋ฆฌ์คํฌ์ ๊ฐ๊ฒฉ ์ด์์น๋ฅผ ํ์งํ๋ค. ๊ฐ๊ฒฌ์ "์ซ์" ์ฐ์ถ์๋ LLM์ ์ฐ์ง ์๋๋ค โ ์ ๋ขฐ์ฑ๊ณผ ๊ฐ๊ฒฉ ํ ๋ฃจ์๋ค์ด์ ๋ฐฉ์ง๋ฅผ ์ํ ์๋์ ์ค๊ณ ๊ฒฐ์ ์ด๋ค.
์ธํ ๋ฆฌ์ด ๊ฒฌ์ ์ ํ์ค ๋จ๊ฐ๊ฐ ๊ณต๊ฐ๋ผ ์์ง ์๊ณ ์ ์ฒด๋ณ ํธ์ฐจ๊ฐ ์ปค์, ์๋น์๊ฐ "์ด ๊ฐ๊ฒฉ์ด ํฉ๋ฆฌ์ ์ธ์ง" ํ๋จํ ๊ธฐ์ค์ด ์๋ค. ์ด ํ๋ก์ ํธ๋ ๋ ๊ฐ์ง ๋ฐฉ์์ผ๋ก ๊ทธ ๊ธฐ์ค์ ์ ๊ณตํ๋ค.
- ๊ฐ๊ฒฌ์ : ์ค์ ๊ณ์ฝ ์ฌ๋ก ์๋ฐฑ ๊ฑด์ ํฌ๋กค๋งํด ์ง์ญยทํ์ยท์์ฌ๋ฑ๊ธ ๋ฑ ์กฐ๊ฑด์ด ๋น์ทํ ์ฌ๋ก๋ฅผ ๊ฒ์ํ๊ณ , ๊ทธ ์ฌ๋ก๋ค์ ํต๊ณ(์ด์์น ์ ๊ฑฐ ํ ๋ถํฌ)๋ก ์์ ๋น์ฉ ๋ฒ์๋ฅผ ๊ณ์ฐํ๋ค.
- ๋ฆฌ์คํฌ ์ง๋จ: ์ฌ์ฉ์๊ฐ ๋ฐ์ ๊ฒฌ์ ์ ์ด๋ฏธ์ง๋ฅผ ์ ๋ก๋ํ๋ฉด Vision ํ์ฑ์ผ๋ก ํญ๋ชฉ์ ๊ตฌ์กฐํํ๊ณ , ๊ฐ์ ์ฝํผ์ค ๊ธฐ์ค ๊ฐ๊ฒฉ๋(P10~P90)๋ฅผ ๋ฒ์ด๋๋ ํญ๋ชฉ์ ์ด์์น๋ก ํ์ํ๋ค.
| ๊ฒฐ์ | ์ด์ |
|---|---|
| ๊ฐ๊ฒฌ์ ์ซ์ ์ฐ์ถ์ LLM ๋ฏธ์ฌ์ฉ | ํต๊ณ ์ง๊ณ๋ ๊ฒฐ์ ๋ก ์ ์ด๋ผ ์ฌํ ๊ฐ๋ฅํ๊ณ ๊ฐ์ฌ ๊ฐ๋ฅํ๋ค. LLM์ ๊ฐ๊ฒฉ ์ฐ์ถ์ ๋งก๊ธฐ๋ฉด ๊ทธ๋ด๋ฏํ์ง๋ง ํ๋ฆฐ ์ซ์(ํ ๋ฃจ์๋ค์ด์ )๋ฅผ ๋ผ ์ํ์ด ์๊ณ , ๊ฒฌ์ ์์คํ ์์ ์ด๋ ๊ฐ์ฅ ์น๋ช ์ ์ธ ์คํจ ๋ชจ๋๋ค. LLM์ ์ด๋ฏธ์ง ํ์ฑยท์์ฐ์ด ๋ณด์กฐ ์ญํ ๋ก๋ง ๊ด์ฌํ๋ค. |
| ์ ์ํ(adaptive) RAG โ Stage 1~5 ์ ์ง์ ํํฐ ์ํ | ํ์ยท์ง์ญยท์์ฌ๋ฑ๊ธยท๊ณต์ข ์กฐ๊ฑด์ ์ ๋ถ ๋ง์กฑํ๋ ์ฌ๋ก๊ฐ ๋ถ์กฑํ๋ฉด ์กฐ๊ฑด์ ๋จ๊ณ์ ์ผ๋ก ์ํํด ์ฌ๊ฒ์ํ๋ค(self-querying ํจํด). ์กฐ๊ฑด์ ์ฒ์๋ถํฐ ๋์จํ๊ฒ ์ก์ผ๋ฉด ์ ํ๋๊ฐ ๋จ์ด์ง๊ณ , ๋๋ฌด ์๊ฒฉํ๋ฉด ๊ฒ์ ๊ฒฐ๊ณผ๊ฐ ์์ด์ง๋ ๋ฌธ์ ๋ฅผ ๋์์ ํด๊ฒฐํ๋ค. |
| Chroma + MongoDB ์ด์ค ๊ตฌ์กฐ โ Atlas Vector Search ๋จ์ผํ | ๋ฒกํฐ ์ธ๋ฑ์ค์ ์๋ณธ ๋ฐ์ดํฐ๊ฐ ๋ถ๋ฆฌ๋ผ ์์ผ๋ฉด ํ์ชฝ๋ง ๊ฐฑ์ ๋ ๋ ๊ณ ์ ๋ฐ์ดํฐ๊ฐ ์๊ธด๋ค(์ฌ๋ก๋ฅผ Mongo์์ ์ง์๋ Chroma ๋ฒกํฐ๋ ๋จ์ ๊ฒ์๋๋ ๋ฑ). ์๋ฒ ๋ฉ์ ์๋ณธ ๋ฌธ์์ ๊ฐ์ ์ปฌ๋ ์ ยท๊ฐ์ write์ ์ ์ฅํด ๋๊ธฐํ ๋ฌธ์ ๋ฅผ ๊ตฌ์กฐ์ ์ผ๋ก ์ ๊ฑฐํ๋ค. |
ํ์ด๋ธ๋ฆฌ๋ ๊ฒ์ยท๋ฆฌ๋ญํน์ ๋ถ์ธ ๋ค, ์ค์ ๋ก ํ์ง์ด ๊ฐ์ ๋๋์ง ground truth๋ฅผ ์ง์ ๊ตฌ์ถํด ์ธก์ ํ๋ค.
TREC ์คํ์ผ pooling์ผ๋ก 24๊ฐ ์ฟผ๋ฆฌ์ ๋ํ ๊ด๋ จ์ฑ ๋ผ๋ฒจ(ํ์ ยฑ5ํยท์ธ์ ์ง์ญยท๊ณต์ข 1๊ฐ ์ด์ ๊ฒน์นจ์ ์ ๋ถ ๋ง์กฑํด์ผ relevant)์ ๋ง๋ค๊ณ ์ฌ๋ฌ ๋จ๊ณ๋ฅผ ๊ฐ์ ๊ธฐ์ค์ผ๋ก ๋น๊ตํ๋ค.
| ๋จ๊ณ | precision@15 | ๋น๊ณ |
|---|---|---|
| ๋ฒกํฐ ๊ฒ์ ๋จ๋ (ํํฐ ์์) | 17.2% | ์์ ํ ์คํธ ์ ์ฌ๋๋ง |
| + ํ์ด๋ธ๋ฆฌ๋(BM25+RRF) + Cross-Encoder ๋ฆฌ๋ญํน (ํํฐ ์์) | 20.6% (+3.4%p) | |
| + Stage ํํฐ(ํ์ยท์ง์ญยท๊ณต์ข , ์ ์ง์ ์ํ) ์ ์ฉ, ๋ฒกํฐ ๋จ๋ | 78.0% (+57.4%p) | |
| + Stage ํํฐ + ํ์ด๋ธ๋ฆฌ๋ + ๋ฆฌ๋ญํน (์ค์ ์๋น์ค ๊ฒฝ๋ก) | 82.5% (+4.5%p) | |
| + ์คํจ ์ฟผ๋ฆฌ ๋ถ์ ๊ธฐ๋ฐ ํ์ ํํฐ ํ๋(ยฑ7ํโยฑ5ํ) | 93.2% (+10.7%p) | |
| + ํ์ ํํฐ ์ฌ์กฐ์ (ยฑ5ํโยฑ6ํ), ๋น์ฃผ๊ฑฐ ์ ์ธ ๋ฐ์ | 93.2% (๊ฐ์ ์กฐ๊ฑด ยฑ5ํ 93.1%) | ์ต์ข โ ๋น๊ต |
ํต์ฌ ์ง๋ ๋๋ ๊ตฌ์กฐ์ ํํฐ๋ง์ด๋ค. ๋ฒกํฐ ์ ์ฌ๋๋ง์ผ๋ก๋ ์์ธ ์ฟผ๋ฆฌ์ ๋ถ์ฐ ์ฌ๋ก๊ฐ ์์ด๋ ๋ฑ
๊ด๋ จ ์๋ ๊ฒฐ๊ณผ๊ฐ ๋ง์ด ์์ฌ ์ ๋ฐ๋๊ฐ 17.2%์ ๊ทธ์ณค๋๋ฐ, ๊ฒ์ ์ ์ ํ์ยท์ง์ญยท๊ณต์ข
์กฐ๊ฑด์ผ๋ก ํ๋ณด๋ฅผ
๋ฏธ๋ฆฌ ์ขํ๋ Stage ํํฐ(์กฐ๊ฑด์ ๋ค ๋ง์กฑํ๋ ์ฌ๋ก๊ฐ 3๊ฑด ๋ฏธ๋ง์ด๋ฉด ๋จ๊ณ์ ์ผ๋ก ์ํ, SIZE_RANGEโ
์ง์ญโํ์ ์์ผ๋ก relax) ํ๋๋ง์ผ๋ก 78.0%๊น์ง ๋ด๋ค. ํ์ด๋ธ๋ฆฌ๋+๋ฆฌ๋ญํน์ ์ด ์์ ์น์ด๋
๊พธ์คํ ์ ์๋ฏธํ ๊ฐ์ (+4.5%p)์ ๋๋ค.
๊ทธ ๋ค์, ์ ๋ฐ๋๊ฐ ๊ฐ์ฅ ๋ฎ์ ์ฟผ๋ฆฌ๋ค์ ์ง์ ๊น๋ณธ ์คํจ ๋ถ์์์ "ํ์์ฐจ์ด=7ํ(ํํฐ ์ต๋ ๊ด์ฉ์น)"์ธ ํ๋ณด์ relevant ๋น์จ๋ง ์ ๋ 22.2%๋ก ์ ๋ฒฝ์ฒ๋ผ ๋จ์ด์ง๋ ๊ฑธ ๋ฐ๊ฒฌํ๋ค โ ํํฐ ๊ด์ฉ์น(ยฑ7ํ)๊ฐ ์ค์ ๋ผ๋ฒจ ๊ธฐ์ค(ยฑ5ํ)๋ณด๋ค ๋์จํ๊ฒ ์กํ ์๋ ๋ถ์ผ์น์๋ค. ํํฐ๋ฅผ ๋ผ๋ฒจ ๊ธฐ์ค์ ๋ง์ถฐ ์ขํ์ 82.5%โ93.2%๋ก ์ถ๊ฐ ๊ฐ์ ๋๊ณ , Stage ์ฑํ ๋ถํฌ(24๊ฐ ์ฟผ๋ฆฌ ์ค ์ง์ญ ์กฐ๊ฑด๊น์ง ์ํ๋ ๊ฑด 1๊ฐ๋ฟ)๋ก ๋ถ์์ฉ์ด ์์์ ๊ฐ์ด ๊ฒ์ฆํ๋ค.
์ดํ ๊ธ๋ฝ ์ง์ ์ด ์ ํํ 6ํ๊ณผ 7ํ ์ฌ์ด๋ผ๋ ์ ์ ๋ค์ ํ์ธํ๋ ค๊ณ ยฑ5/ยฑ6/ยฑ7์ ๊ฐ์ ๋ผ๋ฒจ์
ยท๊ฐ์ ํ์ดํ๋ผ์ธ์ผ๋ก
๋น๊ตํ๋ค(size_range_comparison.md). ยฑ6๊ณผ ยฑ5์ precision@15๋
93.2% vs 93.1%๋ก ์ฐจ์ด๊ฐ ๋
ธ์ด์ฆ ๋ฒ์(์ฟผ๋ฆฌ ๋จ์ ๋ถํธ์คํธ๋ฉ 95% ๊ตฌ๊ฐ โ5.7~+7.1%p)์์ง๋ง, ยฑ6์ ์ฟผ๋ฆฌ๋น
ํ๋ณด๋ฅผ ๋ ํ๋ณดํ๊ณ (์ค์๊ฐ 15โ19๊ฑด) ์ฌ๋ก ๋ถ์กฑ์ผ๋ก ์ง์ญ ์กฐ๊ฑด๊น์ง ํ๋ฆฌ๋ ์ฟผ๋ฆฌ๋ฅผ ์์ ์ SIZE_RANGE๋ฅผ 6์ผ๋ก
์กฐ์ ํ๋ค. ยฑ7์ 82.1%๋ก ์ฌ์ ํ 11%p ๋ฎ๋ค.
์ด ๊ณผ์ ์์ ์ฝํผ์ค ์์ฒด์ ๋ฐ์ดํฐ ํ์ง ๋ฌธ์ (ํฌ๋กค๋ฌ race condition์ผ๋ก ๊ฒ์๊ธ 693/855๊ฑด์ด ์ ๋ชฉ๊ณผ ์ค์ ์ ์ฅ ๋ด์ฉ์ด ๋ค๋ฅธ ์ํ์์)์, ๊ธฐ์กด ํ๊ฐ ์คํฌ๋ฆฝํธ๊ฐ ์ค์ ์๋น์ค๊ฐ ์ฐ๋ Stage ํํฐ๋ฅผ ์ ํ ํ ์คํธํ์ง ์๊ณ ์๋ ๋ฐฉ๋ฒ๋ก ๊ฒฐํจ๋ ํจ๊ป ์ฐพ์ ๊ณ ์ณค๋ค โ ์ ํ์ ์ ๋ ํ(ํํฐ ์์)๊ณผ ๋ค ์ธ ํ (Stage ํํฐ ํฌํจ, ์ค์ production ๊ฒฝ๋ก)์ด ๊ฐ๋ผ์ง๋ ์ง์ ์ด ๊ทธ ๊ฒฐํจ์ ๋ฐ๋ก์ก์ ๊ฒฐ๊ณผ๋ค.
์ ๋ ์งํ๋ง์ผ๋ก๋ ์ ์กํ๋ ๋ฌธ์ ๋ ์์๋ค. ์ค์ API ์๋ต์ Swagger๋ก ์ง์ ๊น๋ณด๋ค๊ฐ "18ํ
์๋๊ถ ์ ์ฒด๋ฆฌ๋ชจ๋ธ๋ง" ์์ฒญ์ ์ฐธ๊ณ ์ฌ๋ก ํ๋๊ฐ ํ๋น 396๋ง์(๋ค๋ฅธ ์ฌ๋ก ๋๋น 2~4๋ฐฐ)์ผ๋ก ํ๋ ๊ฑธ
๋ฐ๊ฒฌํ๋ค โ DB๋ฅผ ์ด์ด๋ณด๋ ํฌ๋กค๋ง ์ค๋ฅ๊ฐ ์๋๋ผ ํ์ฌ ํผํด ์๊ฐ์ฃผํ ์ ๋ฉด ์ฒ ๊ฑฐยท๋ณต๊ตฌ ๊ณต์ฌ์๋ค.
DB์ ๊ณต๊ฐ์ ํ ํ๋๊ฐ ์์ด ํ์ยท์ง์ญยท๊ณต์ข
๋ง์ผ๋ก๋ ์ด๋ฐ ์ผ์ด์ค๋ฅผ ๊ฑธ๋ฌ๋ผ ๋ฐฉ๋ฒ์ด ์์๋ ๊ฒ.
์ฝํผ์ค 709๊ฑด์ ํค์๋๋ก ์ค์บํด ์๊ฐ/๊ณต์ฅ ๋ฑ ๋น์ฃผ๊ฑฐ ์ฌ๋ก 9๊ฑด์ ํ์ธํ๊ณ ๊ฒ์์์ ํญ์ ์ ์ธํ๋๋ก
๊ณ ์ณค๋ค(app/repositories/case_repository.py). ๊ฐ์ ์๋ฆฌ์์ "ํ๋น ๋จ๊ฐ๊ฐ 0์ผ๋ก ๋์ค๋" ๋ณ๊ฐ
๋ฒ๊ทธ๋ ์ฐพ์๋ค โ ingest ์์ ์ ๋ฏธ๋ฆฌ ๊ณ์ฐํด ์ ์ฅํด๋ ํ๋๋ฅผ ๊ทธ๋๋ก ์ฝ์ด์ค๋ ๊ตฌ์กฐ๋ผ, size_pyeong์ด
๋์ค์ ๋ณด์ ๋ 116๊ฑด(16%)์ ์ด๊ธ์กยทํ์๋ ์ ์์ธ๋ฐ ํ๋น๋ง 0์ผ๋ก ๋จ์ ์์๋ค. ์ ์ฅ๊ฐ์ ์ ๋ขฐํ์ง
์๊ณ ๋งค๋ฒ total_cost/size_pyeong์ผ๋ก ๋ค์ ๊ณ์ฐํ๋๋ก ๊ณ ์ณค๋ค.
๊ฐ์ ์์ฒญ์ ์์ ์ /ํ๋ก ๋น๊ตํ๋ฉด ์ฐจ์ด๊ฐ ๊ทธ๋๋ก ๋๋ฌ๋๋ค(POST /estimates/generate, 18ํยท์๋๊ถยท
์ ์ฒด๋ฆฌ๋ชจ๋ธ๋งยท์์ฌ๋ฑ๊ธ ์ผ๋ฐ):
์ ์ด_๊ฒฌ์ _๋ฒ์๋ ์์ ๋น์(์์ง 1.1.0 ์ด์ )์ ์ถ๋ ฅ์ด๋ค. ์์ง 1.2.0๋ถํฐ ์ค๊ฐ์ ๋ฒ์์ ์ค์ ์ด ์๋๋ผ
์ฐธ๊ณ ์ฌ๋ก ์ด์ก์ ์ค์๊ฐ์ด๋ผ, ๊ฐ์ ์์ฒญ์ ์ง๊ธ ์คํํ๋ฉด ์ค๊ฐ์ด ๋ ๋ฎ๊ฒ ๋์จ๋ค.
์ฐธ๊ณ _์ฌ๋ก์ article_id๋ก ์ค์ ์๋ฌธ(ํฌ๋กค๋ง ๋์ ๊ธ)์ ๋ฐ๋ก ํ์ธํ ์ ์๊ฒ ํด๋ ๊ฒ๋ ์๋์
์ค๊ณ๋ค โ ๊ฒ์๋ ๊ทผ๊ฑฐ๋ฅผ ๊ฐ์ถ ์ฑ ์ต์ข
์ซ์๋ง ๋ณด์ฌ์ฃผ๋ฉด ์ด๋ฐ ์ค์ผ์ ๋ฐ๊ฒฌํ ๋ฐฉ๋ฒ์ด ์๋ค.
๊ฒ์ฆ ๊ณผ์ ์์ "cross-encoder๊ฐ ๊ธด ํ
์คํธ์ ๋ ๋์ ์ ์๋ฅผ ์ฃผ๋ ํธํฅ"์ ํ๋ฌ ํ
์คํธ ํจ๋ฉ ์คํ์ผ๋ก
์ธ๊ณผ๊ด๊ณ๊น์ง ํ์ธํ๊ณ , ์ด๊ธฐ ์คํ ๊ฒฐ๊ณผ๊ฐ ๋ค์งํ ์์ธ(pooling ์ค๊ณ ๊ฒฐํจ, ์ํ ๋
ผ๋ฆฌ ๋ผ๋ฒจ๋ง ์ค์)๋
์ฐพ์์ ์ ์ ํ๋ค. ์คํจํ ์๋(ํต๊ณ์ de-biasing ๋ฑ)๋ฅผ ํฌํจํ ์ ์ฒด ์กฐ์ฌ ๊ณผ์ ์
eval/results/reranker_hybrid_eval.md ์ฐธ๊ณ .
๊ฐ๊ฒฌ์ ์ ํ๋(๊ณต์ข
๋ณ ๊ฐ๊ฒฉ ์์ธก vs ์ค์ ์ฌ๋ก) ์ง๋จ์ eval/results/pricing_gap_diagnostic.md ์ฐธ๊ณ .
/risk-detector/analyze๋ ์
๋ก๋๋ ๊ฒฌ์ ์ ์ด๋ฏธ์ง๋ฅผ ์ฒญํฌ ๋จ์๋ก ๋๋ Vision API์ ์์ฐจ ํธ์ถํ๋
๊ตฌ์กฐ๋ผ, ์ด๋ฏธ์งยท์ฒญํฌ ์๊ฐ ๋์๋ก ์๋ต ์๊ฐ์ด ๊ทธ๋๋ก ์ ํ์ผ๋ก ๋์ด๋ฌ๋ค. ์ค์ API๋ก ๋จ๊ฑด ์๋ต์๊ฐ์,
k6๋ก ๋์ ์ฌ์ฉ์ ๋ถํ๋ฅผ ์ธก์ ํ ๋ค ํธ์ถ์ ๋ณ๋ ฌํํ๋ค.
| ์ผ์ด์ค (์ฒญํฌ ์) | ๋ณ๋ ฌํ ์ wall p50 | ๋ณ๋ ฌํ ํ wall p50 |
|---|---|---|
| 5์ฒญํฌ (์ค์ API) | 196.4s | 73.8s (โ62%) |
| 8์ฒญํฌ (์ค์ API) | 264.5s | 47.9s (โ82%) |
| ๋์ ์ฌ์ฉ์ (Vision ์๋ต์ ์ค์ธก ์ง์ฐ ๋ถํฌ๋ก ์ฌํํ mock ๊ธฐ์ค ๋ถํ ํ ์คํธ) | ๋ณ๋ ฌํ ์ p50 | ๋ณ๋ ฌํ ํ p50 |
|---|---|---|
| 1๋ช | 185.5s | 57.6s |
| 5๋ช | 167.6s | 53.9s |
๋ฐ๋ ๊ฒ
- ๋ชจ๋ ์ด๋ฏธ์งยท์ฒญํฌ Vision ํธ์ถ์ ์์ฐจ ๋์
asyncio.gather๋ก ๋์ ์คํํ๊ณ , ๊ฒฐ๊ณผ๋ ์๋ (์ด๋ฏธ์ง, ์ฒญํฌ) ์์๋ก ์ฌ์กฐํฉํด ๊ธฐ์กด ๋ณํฉ ๋ก์ง์ ๊ทธ๋๋ก ์ ์ง - ๋๊ธฐ ํด๋ผ์ด์ธํธ+์ค๋ ๋ํ ๋์
AsyncAnthropic์ผ๋ก ๊ต์ฒด โ ํธ์ถ๋น ์ค๋ ๋๋ฅผ ์ ์ ํ๋ ๊ตฌ์กฐ๋ผ ๋์ ์์ฒญ์ด ๋๋ฉด API ์๋ต๋ณด๋ค ์ค๋ ๋ํ ํฌํ๊ฐ ๋จผ์ ๋ณ๋ชฉ์ด ๋์ - ๋์ ํธ์ถ ์ํ์ ํ๋ก์ธ์ค ์ ์ฒด/์์ฒญ๋น ๋ ๋จ๊ณ๋ก ๋ฌ(
risk_vision_max_concurrency,risk_vision_max_concurrency_per_request) ์๋ฅ API ๋ ์ดํธ๋ฆฌ๋ฐ ์ด๊ณผ๋ฅผ ๋ฐฉ์ง - ์ฒญํฌ ํ๋๋ผ๋ ์คํจํ๋ฉด ๋๋จธ์ง ํธ์ถ์ ์ทจ์ํด ๋ถํ์ํ API ๋น์ฉ ์ง์ถ์ ๋ง์
- ์ด๋ฏธ์ง ๋ฆฌ์ฌ์ด์ฆยท์ธ์ฝ๋ฉ(CPU ๋ฐ์ด๋ ์์ )์ ์ค๋ ๋ํ๋ก ์ฎ๊ฒจ ์ด๋ฒคํธ ๋ฃจํ ๋ธ๋กํน ์ ๊ฑฐ
๋์ 10๋ช ์ด์๋ถํฐ๋ ์ ์ญ ๋์ ํธ์ถ ์ํ(๊ธฐ๋ณธ 20)์ด ์ฒ๋ฆฌ๋์ ๊ฒฐ์ ํ๋ฏ๋ก, ์ค์ ํธ๋ํฝ ๊ท๋ชจ์ ๋ง์ถฐ ์กฐ์ ์ด ํ์ํ๋ค.
์ธก์ ๋ฐฉ๋ฒ(๋ฒ ์ด์ค๋ผ์ธ ๊ณ์ธก, ํ
์คํธ์
์ ์ , k6 ๋ถํ ์ค๊ณ)๊ณผ ์ ์ฒด ๊ฒฐ๊ณผ๋
pipeline/results/risk_detector_latency_optimization.md ์ฐธ๊ณ .
๊ณ์ธกํด ๋ณด๋ ๋ฆฌ์คํฌ ์ง๋จ ๋น์ฉ์ ์ฝ 80%๊ฐ Vision ์ถ๋ ฅ ํ ํฐ์ด์๋ค. ๋ฃฐ์ด ์ฐ์ง ์๋ ํ๋๋ฅผ ์ถ๋ ฅ์์ ๋นผ๋ ๋ฆฌ์คํฌ ์ง๋จ ์ ์ฉ ์คํค๋ง๋ฅผ ๋ง๋ค๊ณ , ์ค์ธก ์๋ณธ ์ถ๋ ฅ์ ์บก์ฒํด ์ ํ๋ ๋ณํ๋ฅผ ์ถ์ ํ๋ค.
| ์ผ์ด์ค (์ฒญํฌ ์, ์ค์ API) | ์์ฒญ๋น ๋น์ฉ | wall p50 |
|---|---|---|
| 1์ฒญํฌ | $0.047 โ $0.034 (โ29%) | 34.1s โ 25.5s |
| 2์ฒญํฌ | $0.125 โ $0.097 (โ23%) | 58.8s โ 47.8s |
| 5์ฒญํฌ | $0.252 โ $0.206 (โ18%) | 71.5s โ 62.9s |
๋ฐ๋ ๊ฒ
- ํ๋๋ณ ์ฌ์ฉ์ฒ๋ฅผ ์กฐ์ฌํ๊ณ ์ฝํผ์ค 571๊ฑด์ผ๋ก ์๋ฎฌ๋ ์ด์
ํด
unitยทquantity๋ง ์ ๊ฑฐ.code(์ฒญํฌ ์ค๋ณต ์ ๊ฑฐ์ ํ ์๋ณ์)์unit_price(๋จ๊ฐ ๋๋ฝ ๋ฃฐ์ ํธ๋ฆฌ๊ฑฐ)๋ ๋นผ๋ฉด ๊ฒฐ๊ณผ๊ฐ ๊นจ์ ธ์ ์ ์ง - ํฌ๋กค๋ง ์์ง ๊ฒฝ๋ก๋ ๊ธฐ์กด ์คํค๋ง ๊ทธ๋๋ก โ ์์ง ๋ฐ์ดํฐ๋ ๊ฐ๊ฒฌ์ ์ฝํผ์ค๊ฐ ๋๋ฏ๋ก
- ํ๋๋ฅผ ๋นผ์ ๋ชจ๋ธ์ด ํ ์ฒญํฌ์
code๋ฅผ ์๋ตํด ์ค๋ณต์ด ์๊ณ , ์ ๋งคํ ํญ๋ชฉ์ ๊ณต์ข ๋ถ๋ฅ๊ฐ ๋ค์งํ๋ ๊ฒ์ ์๋ณธ ์ถ๋ ฅ ์บก์ฒ๋ก ์ถ์ โcodeํ์ํ์ ํ๋ฆ ๋ถ๋ฅ ๊ท์น ํ ์ค๋ก ๋์ - ๋ถ๋ฅ ์ ํ๋๋ "๊ธฐ์กด๊ณผ ๊ฐ์๊ฐ"๊ฐ ์๋๋ผ ์ ๋ต์ ๋ถ์ฌ ์ฑ์ : ํ๋ค๋ฆฐ 40๊ฐ ํญ๋ชฉ ๊ธฐ์ค 60% โ 82%
์ธก์ ๊ณผ์ ยทํ๋จ ๊ทผ๊ฑฐยทํ๊ณ๋
pipeline/results/risk_detector_cost_optimization.md ์ฐธ๊ณ .
flowchart LR
Client -->|"POST /estimates/generate"| API[app/api/routers]
API --> Domain[app/domain\nEstimateEngine]
Domain -->|"Stage 1~5 ํํฐ ์ํ\n$vectorSearch"| Repo[app/repositories\ncase_repository]
Domain -->|"BM25+RRF โ Cross-Encoder"| Rerank[ํ์ด๋ธ๋ฆฌ๋ ๋ฆฌ๋ญํน]
Repo --> Mongo[(MongoDB Atlas\nVector Search)]
Client2[Client] -->|"POST /risk-detector/analyze\n(๊ฒฌ์ ์ ์ด๋ฏธ์ง)"| API2[app/api/routers]
API2 --> RiskSvc[app/domain\nRiskDetectorService]
RiskSvc -->|Vision ํ์ฑ| Anthropic[Anthropic API]
RiskSvc -->|"์ฝํผ์ค ๊ธฐ์ค P10~P90"| Repo
app/
โโโ api/routers/ # HTTP ์์ฒญ/์๋ต (์๊ฒ ์ ์ง)
โโโ domain/ # ๋น์ฆ๋์ค ๋ก์ง โ DB ํด๋ผ์ด์ธํธ๋ฅผ ๋ชจ๋ฅธ๋ค
โโโ repositories/ # DB ์ ๊ทผ ์บก์ํ ($vectorSearch ๋ฑ)
โโโ schemas/ # Pydantic ์์ฒญ/์๋ต ๋ชจ๋ธ
โโโ core/ # ์ค์ (pydantic-settings), DI(lifespan), ๋ก๊น
, rate limit
pipeline/ # ํฌ๋กค๋งยท์ด๋ฏธ์ง ํ์ฑยท๋ฐ์ดํฐ ์ ์ฌ
eval/ # ๊ฒ์ ํ์ง/๊ฐ๊ฒฌ์ ์ ํ๋ ์ ๋ ํ๊ฐ (ํ๋ก๋์
eval๊ณผ ๋ถ๋ฆฌ)
scripts/ # ๋ฐฑํ, ์ธ๋ฑ์ค ์ธํ
๋ฑ ์ด์ ์คํฌ๋ฆฝํธ
tests/ # pytest ๋จ์/ํตํฉ ํ
์คํธ (154๊ฐ)
- DB: MongoDB Atlas ํ๋๋ก ํตํฉ (Vector Search๋ก ๋ฒกํฐ ๊ฒ์๊น์ง ์ฒ๋ฆฌ, Chroma ์์)
- ๊ฒ์:
$vectorSearchํํฐ ๊ธฐ๋ฐ ์ ์ง์ ํด๋ฐฑ(Stage 1~5) โ BM25+RRF ํ์ด๋ธ๋ฆฌ๋ โ (์ ํ) Cross-encoder(Dongjin-kr/ko-reranker) ๋ฆฌ๋ญํน โUSE_RERANKER๋ก ์ผ๊ณ ๋๋ค - ๋ณด์ ๊ณ์:
correction_coefficients์ปฌ๋ ์ ์์ ๋ฒ์ ๊ด๋ฆฌ (์ฑ ์์ ์ 1ํ ๋ก๋) - LLM: ๊ฐ๊ฒฌ์ ์ซ์ ์ฐ์ถ์ ๊ด์ฌํ์ง ์์. ๊ฒฌ์ ์ ์ด๋ฏธ์ง Vision ํ์ฑ, ํฅํ ์์ฐ์ด ์์ฝ ๋ฑ ๋ณด์กฐ ์ญํ ๋ก๋ง ํ์ฅ ์์
- ๊ณต์ข (๋๋ฐฐ/๋ง๋ฃจ/์์ค/์ฃผ๋ฐฉ ๋ฑ)ยทํ์ยท์ง์ญยท์์ฌ๋ฑ๊ธ ๋ฑ ๊ตฌ์กฐํ ์ ๋ ฅ โ ์ ์ฌ ์๊ณต ์ฌ๋ก ๊ฒ์ โ ํต๊ณ ๊ธฐ๋ฐ ์์ ๋น์ฉ ๋ฒ์ ์ฐ์ถ
- ๊ฒฌ์ ์ ์ฅ/์กฐํ/์ญ์ , ์ค์ ๊ณ์ฝ ๊ธ์ก ํผ๋๋ฐฑ ๊ธฐ๋ก
- ๊ฒฌ์ ์ ์ด๋ฏธ์ง ์ ๋ก๋ โ Vision API๋ก ํญ๋ชฉ ํ์ฑ โ ์ฝํผ์ค ๊ธฐ์ค ๊ฐ๊ฒฉ ๋ถํฌ(P10~P90) ๋๋น ์ด์์น ํญ๋ชฉ ํ์ง
- ์ง๋จ ๊ฒฐ๊ณผ ์ ์ฅ/์กฐํ/์ญ์
- ์์ฒญ ID ๊ธฐ๋ฐ ๊ตฌ์กฐํ ๋ก๊น , ์ ์ญ ์์ธ ํธ๋ค๋ฌ
- ์๋ํฌ์ธํธ๋ณ rate limit (
slowapi)
| ์์ญ | ์ ํ |
|---|---|
| API | FastAPI, Pydantic v2, pydantic-settings |
| DB | MongoDB Atlas ($vectorSearch, motor/pymongo) |
| ๊ฒ์ | sentence-transformers(์๋ฒ ๋ฉ), rank-bm25(RRF), Dongjin-kr/ko-reranker(Cross-Encoder) |
| LLM | Anthropic API (๊ฒฌ์ ์ ์ด๋ฏธ์ง Vision ํ์ฑ ์ ์ฉ) |
| ํฌ๋กค๋ง | Selenium, BeautifulSoup |
| ํ ์คํธ/๋ฆฐํธ | pytest, pytest-asyncio, httpx, ruff |
| ์ธํ๋ผ | Docker, docker-compose, GitHub Actions |
| Method | Path | ์ค๋ช |
|---|---|---|
| POST | /estimates/generate |
์กฐ๊ฑด ์ ๋ ฅ โ ๊ฐ๊ฒฌ์ ์ฐ์ถ |
| POST | /estimates/save |
๊ฐ๊ฒฌ์ ๊ฒฐ๊ณผ ์ ์ฅ |
| GET | /estimates |
์ ์ฅ๋ ๊ฐ๊ฒฌ์ ๋ชฉ๋ก |
| DELETE | /estimates/{id} |
๊ฐ๊ฒฌ์ ์ญ์ |
| POST | /estimates/{id}/feedback |
์ค์ ๊ณ์ฝ ๊ธ์ก ํผ๋๋ฐฑ ๊ธฐ๋ก |
| POST | /risk-detector/analyze |
๊ฒฌ์ ์ ์ด๋ฏธ์ง โ ๋ฆฌ์คํฌ ์ง๋จ |
| POST | /risk-detector/save |
์ง๋จ ๊ฒฐ๊ณผ ์ ์ฅ |
| GET | /risk-detector |
์ ์ฅ๋ ์ง๋จ ๋ชฉ๋ก |
| DELETE | /risk-detector/{id} |
์ง๋จ ์ญ์ |
| GET | /health |
ํฌ์ค์ฒดํฌ |
์ ์ฒด Swagger ๋ฌธ์๋ ์๋ฒ ์คํ ํ http://localhost:8000/docs. ์์ฒญ ์์๋
docs/API_TEST_EXAMPLES.md ์ฐธ๊ณ .
curl -X POST http://localhost:8000/estimates/generate \
-H "Content-Type: application/json" \
-H "x-user-id: test-user-1" \
-d '{"๊ณต์ข
": ["์ ๊ธฐ/์กฐ๋ช
", "๋์ฅ"], "ํ์": 20}'cp .env.example .env # ANTHROPIC_API_KEY, MONGO_URI(Atlas) ์ฑ์ฐ๊ธฐ
docker compose up --build
docker-compose.yml์mongo์๋น์ค๋ ๋ก์ปฌ ๊ฐ๋ฐ ํธ์์ฉ ์ปจํ ์ด๋๋ค. Vector Search ๊ธฐ๋ฅ์ ์ฐ๋ ค๋ฉดMONGO_URI๊ฐ ์ค์ Atlas ํด๋ฌ์คํฐ(Search Index ์ค์ ์๋ฃ)๋ฅผ ๊ฐ๋ฆฌ์ผ์ผ ํ๋ค.
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install torch --index-url https://download.pytorch.org/whl/cpu
pip install -r requirements.txt
cp .env.example .env
uvicorn app.main:app --reload| ๋ณ์ | ์ค๋ช |
|---|---|
ANTHROPIC_API_KEY |
๊ฒฌ์ ์ ์ด๋ฏธ์ง Vision ํ์ฑ์ฉ |
MONGO_URI |
MongoDB Atlas ์ฐ๊ฒฐ ๋ฌธ์์ด (Vector Search Index ํ์) |
USE_RERANKER |
Cross-encoder ๋ฆฌ๋ญํน ์ฌ์ฉ ์ฌ๋ถ(๊ธฐ๋ณธ false). true๋ฉด ์์ปค๋น RAM +2.2GB ํ์ |
ruff check app tests scripts eval
pytest -qtests/ 154๊ฐ ๋จ์/ํตํฉ ํ
์คํธ, GitHub Actions์์ push/PR๋ง๋ค lint + test ์๋ ์คํ
(.github/workflows/ci.yml). ๊ฒ์ ํ์ง/๊ฐ๊ฒฌ์ ์ ํ๋ ํ๊ฐ๋
eval/์ ๋ณ๋๋ก ๋ถ๋ฆฌ(ํ๋ก๋์
์ฝ๋ ํ๊ท ํ
์คํธ๊ฐ ์๋๋ผ ๋ชจ๋ธยทํ์ดํ๋ผ์ธ ์ฑ๋ฅ ์ธก์ ์ฉ).
- ์ค์ ๊ณ์ฝ ๊ธ์ก ํผ๋๋ฐฑ ๋ฃจํ๋ก ์จ๋ผ์ธ ์ ํ๋(MAPE) ์ธก์
- ๋ณด์ ๊ณ์(
correction_coefficients) ๋ฒ์ ์ถ์ + ์ฌํ์ฑ ํ๋(engine_version) - BM25/๋ฆฌ๋ญํน ์ํ์ฑ (ํ ์คํธ ์บก ์ถ์ ๋ฑ) ํ๋ก๋์ ๋ฐ์ ์ฌ๋ถ ๊ฒฐ์