๊ธฐ์ฌ ๊ฐ์ด๋¶
VLLM ๋ชจ๋ธ ์ฑ๋ฅ ์๋ ํ๊ฐ ์์คํ ์ ๊ธฐ์ฌํ๋ ๋ฐฉ๋ฒ
๐ ๊ธฐ์ฌํด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค!¶
์ด ํ๋ก์ ํธ์ ๊ด์ฌ์ ๊ฐ์ ธ์ฃผ์๊ณ ๊ธฐ์ฌํ๊ณ ์ ํด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค. ์ด ๋ฌธ์๋ ํจ๊ณผ์ ์ด๊ณ ์ผ๊ด์ฑ ์๋ ๊ธฐ์ฌ๋ฅผ ์ํ ๊ฐ์ด๋๋ผ์ธ์ ์ ๊ณตํฉ๋๋ค.
๐ ๋ชฉ์ฐจ¶
- ๊ฐ๋ฐ ํ๊ฒฝ ์ค์
- ๊ธฐ์ฌ ํ๋ก์ธ์ค
- ์ฝ๋ฉ ํ์ค
- ํ ์คํ ๊ฐ์ด๋๋ผ์ธ
- ๋ฌธ์ํ
- ์ด์ ๋ฆฌํฌํ
- Pull Request ๊ฐ์ด๋๋ผ์ธ
- ์ฝ๋ ๋ฆฌ๋ทฐ ํ๋ก์ธ์ค
๐ ๏ธ ๊ฐ๋ฐ ํ๊ฒฝ ์ค์ ¶
ํ์ ์๊ตฌ์ฌํญ¶
- Python: 3.9+
- Docker: 24.0+
- Kubernetes: 1.26+ (๋ก์ปฌ ํ ์คํธ์ฉ)
- Kind: 0.20+ (๋ก์ปฌ ํด๋ฌ์คํฐ)
- Helm: 3.12+
- Git: 2.30+
ํ๊ฒฝ ๊ตฌ์ฑ¶
# 1. ์ ์ฅ์ ํด๋ก
git clone https://github.com/your-org/vllm-eval.git
cd vllm-eval
# 2. Python ๊ฐ์ํ๊ฒฝ ์์ฑ ๋ฐ ํ์ฑํ
python3.11 -m venv venv
source venv/bin/activate
# 3. ๊ฐ๋ฐ ์์กด์ฑ ์ค์น
pip install -r requirements-dev.txt
pip install -r requirements-test.txt
# 4. Pre-commit ํ
์ค์น
pre-commit install
# 5. ํ
์คํธ ์คํ์ผ๋ก ์ค์ ํ์ธ
pytest eval/deepeval_tests/
IDE ์ค์ ¶
- Interpreter:
./venv/bin/python
- Code style & Linter: Ruff
- Test runner: pytest
๐ ๊ธฐ์ฌ ํ๋ก์ธ์ค¶
1. ์ด์ ํ์ธ ๋๋ ์์ฑ¶
- ๊ธฐ์กด ์ด์๋ฅผ ๊ฒ์ํด๋ณด์ธ์
- ์๋ก์ด ๊ธฐ๋ฅ์ด๋ ๋ฒ๊ทธ๋ ์ด์๋ฅผ ๋จผ์ ์์ฑํด์ฃผ์ธ์
- ์ด์ ํ ํ๋ฆฟ์ ์ฌ์ฉํด์ฃผ์ธ์
2. ๋ธ๋์น ์์ฑ¶
# ๋ฉ์ธ ๋ธ๋์น์์ ์์
git checkout main
git pull origin main
# ๊ธฐ๋ฅ ๋ธ๋์น ์์ฑ
git checkout -b feature/your-feature-name
# ๋๋ ๋ฒ๊ทธ ์์
git checkout -b fix/bug-description
3. ๊ฐ๋ฐ ๋ฐ ํ ์คํธ¶
4. ์ปค๋ฐ¶
# ๋ณ๊ฒฝ์ฌํญ ์ปค๋ฐ (Conventional Commits ๊ท์น ์ค์)
git add .
git commit -m "feat: add new evaluation metric for hallucination detection"
5. Pull Request ์์ฑ¶
๐จ ์ฝ๋ฉ ํ์ค¶
Python ์ฝ๋ ์คํ์ผ¶
Ruff๋ฅผ ์ฌ์ฉํ ์๋ ๋ฆฐํ ๋ฐ ํฌ๋งทํ ์ ๋ฐ๋ฆ ๋๋ค:
# โ
์ข์ ์
from typing import Dict, List, Optional
import torch
from deepeval import BaseMetric
class HallucinationMetric(BaseMetric):
"""ํ๊ฐ ํ์ง๋ฅผ ์ํ ์ปค์คํ
๋ฉํธ๋ฆญ.
Args:
threshold: ํ๊ฐ ํ์ ์๊ณ๊ฐ (0.0-1.0)
model_name: ์ฌ์ฉํ ๋ชจ๋ธ๋ช
Returns:
HallucinationScore: ํ๊ฐ ์ ์ ๊ฒฐ๊ณผ
"""
def __init__(self, threshold: float = 0.5, model_name: str = "gpt-4") -> None:
self.threshold = threshold
self.model_name = model_name
def measure(self, test_case: Dict[str, str]) -> float:
"""ํ๊ฐ ์ ์๋ฅผ ์ธก์ ํฉ๋๋ค."""
# ๊ตฌํ ๋ด์ฉ
pass
๋ช ๋ช ๊ท์น¶
- ํ์ผ๋ช
:
snake_case.py
- ํด๋์ค๋ช
:
PascalCase
- ํจ์/๋ณ์๋ช
:
snake_case
- ์์๋ช
:
UPPER_SNAKE_CASE
- Private ๋ฉค๋ฒ:
_leading_underscore
ํ์ ํํ ¶
๋ชจ๋ ํจ์์๋ ํ์ ํํ ์ ํฌํจํด์ผ ํฉ๋๋ค:
from typing import Dict, List, Optional, Union
def evaluate_model(
model_endpoint: str,
dataset_path: str,
metrics: List[str],
config: Optional[Dict[str, Union[str, int]]] = None
) -> Dict[str, float]:
"""๋ชจ๋ธ์ ํ๊ฐํฉ๋๋ค."""
pass
์๋ฌ ์ฒ๋ฆฌ¶
import logging
from typing import Optional
logger = logging.getLogger(__name__)
class EvaluationError(Exception):
"""ํ๊ฐ ๊ณผ์ ์์ ๋ฐ์ํ๋ ์๋ฌ."""
pass
def safe_evaluate(model_endpoint: str) -> Optional[Dict[str, float]]:
"""์์ ํ ๋ชจ๋ธ ํ๊ฐ."""
try:
result = evaluate_model(model_endpoint)
return result
except ConnectionError as e:
logger.error(f"๋ชจ๋ธ ์ฐ๊ฒฐ ์คํจ: {e}")
return None
except EvaluationError as e:
logger.error(f"ํ๊ฐ ์คํจ: {e}")
raise
๐งช ํ ์คํ ๊ฐ์ด๋๋ผ์ธ¶
ํ ์คํธ ๊ตฌ์กฐ¶
- Deepeval ํ
์คํธ:
eval/deepeval_tests/
๋๋ ํ ๋ฆฌ ์์ Pytest ๊ธฐ๋ฐ์ ํ ์คํธ ์ฝ๋๊ฐ ์์นํฉ๋๋ค.- ์ปค์คํ
๋ฉํธ๋ฆญ ํ
์คํธ:
eval/deepeval_tests/test_custom_metric.py
- RAG ํ๊ฐ ํ
์คํธ:
eval/deepeval_tests/test_llm_rag.py
- ์ปค์คํ
๋ฉํธ๋ฆญ ํ
์คํธ:
ํ ์คํธ ์์ฑ ๊ท์น¶
import pytest
from unittest.mock import Mock, patch
from eval.deepeval_tests.metrics.rag_precision import RAGPrecisionMetric
class TestRAGPrecisionMetric:
"""RAG Precision ๋ฉํธ๋ฆญ ํ
์คํธ."""
@pytest.fixture
def metric(self) -> RAGPrecisionMetric:
"""ํ
์คํธ์ฉ ๋ฉํธ๋ฆญ ์ธ์คํด์ค."""
return RAGPrecisionMetric(threshold=0.8)
def test_init_with_default_params(self) -> None:
"""๊ธฐ๋ณธ ํ๋ผ๋ฏธํฐ๋ก ์ด๊ธฐํ ํ
์คํธ."""
metric = RAGPrecisionMetric()
assert metric.threshold == 0.7
assert metric.name == "rag_precision"
@patch('eval.deepeval_tests.metrics.rag_precision.openai_client')
def test_measure_high_precision(self, mock_client: Mock, metric: RAGPrecisionMetric) -> None:
"""๋์ ์ ๋ฐ๋ ์ผ์ด์ค ํ
์คํธ."""
# Given
mock_client.return_value = {"score": 0.9}
test_case = {
"query": "ํ
์คํธ ์ง๋ฌธ",
"context": "๊ด๋ จ ๋ฌธ๋งฅ",
"answer": "์ ๋ต"
}
# When
score = metric.measure(test_case)
# Then
assert score > 0.8
mock_client.assert_called_once()
ํ ์คํธ ์คํ¶
# ๋ชจ๋ ํ
์คํธ ์คํ
pytest
# ํน์ ํ
์คํธ ํ์ผ
pytest tests/unit/test_metrics.py
# ์ปค๋ฒ๋ฆฌ์ง ํฌํจ
pytest --cov=eval --cov-report=html
# ๋ณ๋ ฌ ์คํ
pytest -n auto
๐ ๋ฌธ์ํ¶
Docstring ๊ท์น¶
Google ์คํ์ผ docstring์ ์ฌ์ฉํฉ๋๋ค:
def evaluate_dataset(
dataset_path: str,
metrics: List[str],
batch_size: int = 32
) -> Dict[str, float]:
"""๋ฐ์ดํฐ์
์ ํ๊ฐํฉ๋๋ค.
Args:
dataset_path: ๋ฐ์ดํฐ์
ํ์ผ ๊ฒฝ๋ก
metrics: ์ฌ์ฉํ ๋ฉํธ๋ฆญ ๋ฆฌ์คํธ
batch_size: ๋ฐฐ์น ํฌ๊ธฐ
Returns:
๋ฉํธ๋ฆญ๋ณ ์ ์ ๋์
๋๋ฆฌ
Raises:
FileNotFoundError: ๋ฐ์ดํฐ์
ํ์ผ์ด ์๋ ๊ฒฝ์ฐ
EvaluationError: ํ๊ฐ ์ค ์ค๋ฅ ๋ฐ์
Example:
>>> results = evaluate_dataset("data.json", ["accuracy", "f1"])
>>> print(results["accuracy"])
0.85
"""
pass
README ๋ฐ ๋ฌธ์ ์ ๋ฐ์ดํธ¶
์๋ก์ด ๊ธฐ๋ฅ์ ์ถ๊ฐํ ๋๋ ๊ด๋ จ ๋ฌธ์๋ ํจ๊ป ์ ๋ฐ์ดํธํด์ฃผ์ธ์:
README.md
: ์ฃผ์ ๊ธฐ๋ฅ ๋ณ๊ฒฝdocs/
: ์์ธ ๊ฐ์ด๋- API ๋ฌธ์: ์๋ก์ด API ์ถ๊ฐ ์
๐ ์ด์ ๋ฆฌํฌํ ¶
๋ฒ๊ทธ ๋ฆฌํฌํธ¶
์ด์ ํ ํ๋ฆฟ์ ์ฌ์ฉํ์ฌ ๋ค์ ์ ๋ณด๋ฅผ ํฌํจํด์ฃผ์ธ์:
- ํ๊ฒฝ ์ ๋ณด: OS, Python ๋ฒ์ , ์์กด์ฑ ๋ฒ์
- ์ฌํ ๋จ๊ณ: ๋จ๊ณ๋ณ ์์ธ ์ค๋ช
- ์์ ๋์: ์ด๋ป๊ฒ ๋์ํด์ผ ํ๋์ง
- ์ค์ ๋์: ์ค์ ๋ก ๋ฌด์์ด ์ผ์ด๋ฌ๋์ง
- ๋ก๊ทธ: ๊ด๋ จ ์๋ฌ ๋ก๊ทธ๋ ์คํ ํธ๋ ์ด์ค
๊ธฐ๋ฅ ์์ฒญ¶
- ๋๊ธฐ: ์ ์ด ๊ธฐ๋ฅ์ด ํ์ํ์ง
- ์ ์: ์ด๋ป๊ฒ ๊ตฌํ๋์ด์ผ ํ๋์ง
- ๋์: ๊ณ ๋ ค๋ ๋ค๋ฅธ ๋ฐฉ๋ฒ๋ค
- ์ํฅ๋: ๊ธฐ์กด ๊ธฐ๋ฅ์ ๋ฏธ์น๋ ์ํฅ
๐ Pull Request ๊ฐ์ด๋๋ผ์ธ¶
PR ์ฒดํฌ๋ฆฌ์คํธ¶
- [ ] ์ด์์ ์ฐ๊ฒฐ๋์ด ์์ต๋๋ค
- [ ] ํ ์คํธ๊ฐ ํต๊ณผํฉ๋๋ค
- [ ] ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๊ฐ ์ ์ง๋๊ฑฐ๋ ๊ฐ์ ๋ฉ๋๋ค
- [ ] ๋ฌธ์๊ฐ ์ ๋ฐ์ดํธ๋์์ต๋๋ค
- [ ] CHANGELOG๊ฐ ์ ๋ฐ์ดํธ๋์์ต๋๋ค (ํ์์)
- [ ] Breaking changes๊ฐ ๋ช ์๋์์ต๋๋ค (ํด๋น์)
PR ํ ํ๋ฆฟ¶
## ๋ณ๊ฒฝ์ฌํญ ์์ฝ
<!-- ๋ฌด์์ ๋ณ๊ฒฝํ๋์ง ๊ฐ๋จํ ์ค๋ช
-->
## ๊ด๋ จ ์ด์
<!-- ํด๊ฒฐํ๋ ์ด์ ๋ฒํธ -->
Closes #123
## ๋ณ๊ฒฝ ์ ํ
- [ ] ๋ฒ๊ทธ ์์
- [ ] ์ ๊ธฐ๋ฅ
- [ ] Breaking change
- [ ] ๋ฌธ์ ์
๋ฐ์ดํธ
## ํ
์คํธ
<!-- ์ด๋ป๊ฒ ํ
์คํธํ๋์ง ์ค๋ช
-->
## ์ฒดํฌ๋ฆฌ์คํธ
- [ ] ํ
์คํธ ์ถ๊ฐ/์
๋ฐ์ดํธ
- [ ] ๋ฌธ์ ์
๋ฐ์ดํธ
- [ ] ๋ฆฐํ
ํต๊ณผ
์ปค๋ฐ ๋ฉ์์ง ๊ท์น¶
Conventional Commits์ ๋ฐ๋ฆ ๋๋ค:
Types:
- feat
: ์๋ก์ด ๊ธฐ๋ฅ
- fix
: ๋ฒ๊ทธ ์์
- docs
: ๋ฌธ์ ๋ณ๊ฒฝ
- style
: ์ฝ๋ ์คํ์ผ ๋ณ๊ฒฝ
- refactor
: ๋ฆฌํฉํ ๋ง
- test
: ํ
์คํธ ์ถ๊ฐ/์์
- chore
: ๊ธฐํ ๋ณ๊ฒฝ์ฌํญ
Examples:
feat(metrics): add hallucination detection metric
fix(deepeval): resolve memory leak in evaluation loop
docs(readme): update installation instructions
test(rag): add integration tests for RAG metrics
๐ฅ ์ฝ๋ ๋ฆฌ๋ทฐ ํ๋ก์ธ์ค¶
๋ฆฌ๋ทฐ์ด ๊ฐ์ด๋๋ผ์ธ¶
๋ฆฌ๋ทฐ ํฌ์ธํธ: - ์ฝ๋ ํ์ง ๋ฐ ๊ฐ๋ ์ฑ - ํ ์คํธ ์ปค๋ฒ๋ฆฌ์ง - ์ฑ๋ฅ ์ํฅ - ๋ณด์ ๊ณ ๋ ค์ฌํญ - API ์ผ๊ด์ฑ
๋ฆฌ๋ทฐ ์์:
### ๐ก ์ ์
`evaluate_model` ํจ์์์ ์๋ฌ ์ฒ๋ฆฌ๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์ด ์ข๊ฒ ์ต๋๋ค.
### ๐ ์ด์
L45: ์ด ๋ถ๋ถ์์ ๋ฉ๋ชจ๋ฆฌ ๋์๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
### โ
์น์ธ
LGTM! ํ
์คํธ๋ ์ ์์ฑ๋์๋ค์.
์์ฑ์ ๊ฐ์ด๋๋ผ์ธ¶
- ๋ฆฌ๋ทฐ ํผ๋๋ฐฑ์ ์ ์ํ๊ฒ ์๋ต
- ๋ณ๊ฒฝ์ฌํญ์ ๋ํ ๋ช ํํ ์ค๋ช
- ํ ์คํธ ๊ฒฐ๊ณผ ๊ณต์
- ์๊ฒฌ ๋ถ์ผ์น ์ ๊ฑด์ค์ ํ ๋ก
๐ท๏ธ ๋ฆด๋ฆฌ์ค ํ๋ก์ธ์ค¶
๋ฒ์ ๊ด๋ฆฌ¶
- Semantic Versioning ์ฌ์ฉ:
MAJOR.MINOR.PATCH
- Pre-release:
1.0.0-alpha.1
,1.0.0-beta.1
,1.0.0-rc.1
๋ฆด๋ฆฌ์ค ๋จ๊ณ¶
- Feature freeze: ์ ๊ธฐ๋ฅ ์ถ๊ฐ ์ค๋จ
- ํ ์คํ : ์ ์ฒด ํ ์คํธ ์ค์ํธ ์คํ
- ๋ฌธ์ ์ ๋ฐ์ดํธ: README, CHANGELOG ์ ๋ฐ์ดํธ
- ํ๊ทธ ์์ฑ:
git tag v1.0.0
- ๋ฆด๋ฆฌ์ค ๋ ธํธ: GitHub ๋ฆด๋ฆฌ์ค ํ์ด์ง ์์ฑ
๐ ๋ณด์ ๊ฐ์ด๋๋ผ์ธ¶
๋ณด์ ์ด์ ๋ฆฌํฌํ ¶
๋ณด์ ์ทจ์ฝ์ ์ public ์ด์๊ฐ ์๋ ์ด๋ฉ์ผ๋ก ์ ๊ณ ํด์ฃผ์ธ์: - ์ด๋ฉ์ผ: security@company.com - PGP Key: [๊ณต๊ฐํค ๋งํฌ]
๋ณด์ ์ฒดํฌ๋ฆฌ์คํธ¶
- ๋ฏผ๊ฐํ ์ ๋ณด ํ๋์ฝ๋ฉ ๊ธ์ง
- ์์กด์ฑ ์ทจ์ฝ์ ์ ๊ธฐ ๊ฒ์ฌ
- API ์๋ํฌ์ธํธ ์ธ์ฆ/์ธ๊ฐ ํ์ธ
- ๋ก๊ทธ์ ๋ฏผ๊ฐํ ์ ๋ณด ์ถ๋ ฅ ๊ธ์ง
๐ ์ธ์ ๊ณผ ๊ฐ์ฌ¶
๋ชจ๋ ๊ธฐ์ฌ์๋ README์ Contributors ์น์ ์ ๋ฑ๋ก๋ฉ๋๋ค. ๋ํ ๋ฆด๋ฆฌ์ค ๋ ธํธ์์ ๊ธฐ์ฌ ๋ด์ฉ์ด ์ธ๊ธ๋ฉ๋๋ค.
๐ ๋์ ๋ฐ ๋ฌธ์¶
- ์ผ๋ฐ ์ง๋ฌธ: GitHub Discussions
- ๊ธฐ์ ์ง์: #vllm-eval Slack ์ฑ๋
- ๊ธด๊ธ ๋ฌธ์: tech-support@company.com
๋ค์ ํ๋ฒ ๊ธฐ์ฌํด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค! ๐
์ง๋ฌธ์ด๋ ์ ์์ฌํญ์ด ์์ผ์๋ฉด ์ธ์ ๋ ์ง ์ฐ๋ฝํด์ฃผ์ธ์.