meeting-103 R1: 5조합 (Pony·Flux·PuLID·Qwen) — 4/5 / Qwen 4차 실패

 4 정상 (시각 검수 대기):
- 1-1 Pony V6 XL A 별이 (best pastel 42.5%, 검정 0.61%)
- 1-2 Flux Dev A 별이 (검정 3.11% 한계 근접)
- 1-3 Flux Dev + PuLID A 별이 (별이 ID matching 핵심)
- 1-4 Flux Dev B 한옥 (검정 0%, 일본/한국 시각 평가 필수)

 1 실패:
- 1-5 Qwen-2512 C 한자카드 (5.5KB)
  v6 R1 3차 + v2.1 R1 v4 = 총 4차 연속
  패턴: EmptyLatent / EmptyQwenLayered / ModelSamplingAuraFlow+SD3Latent / SamplerCustomAdvanced
  hypothesis: GGUF Q4 + CUDA 13 + PyTorch 2.12 호환성 또는 non-GGUF 필요

자산 라이브러리 갱신:
- models-performance.json: 4 모델 등재 + Pony 파스텔 42.5 / Flux 평균 24.9
- failed-patterns.json: qwen-2512-4-attempts 패턴 등재
- 다음 시도: non-GGUF / Lightning LoRA / 다른 sampler/shift

scripts/round_pipeline.py 신설 (R2 부터 재사용)
eval/meeting-103-grid.html + meta.json 생성
썸네일 의무 충족 (최대 540KB, 모두 1MB 미만)
This commit is contained in:
choijaewook
2026-05-19 21:59:02 +09:00
parent 540a9d0aa6
commit 58016478ec
17 changed files with 609 additions and 2 deletions

View File

@@ -27,5 +27,78 @@
"illustrious", "illustrious",
"lightning-lora-qwen" "lightning-lora-qwen"
], ],
"models": {} "models": {
"pony-v6": {
"rounds_used": 1,
"dimensions": {
"byeolyi": [],
"hanja": [],
"hanok": [],
"illustration": [],
"korean_traditional": [],
"auto_pastel": [
42.5
],
"auto_black": [
0.61
]
},
"avg_scores": {},
"best_combinations": [],
"verdict": ""
},
"flux-dev": {
"rounds_used": 1,
"dimensions": {
"byeolyi": [],
"hanja": [],
"hanok": [],
"illustration": [],
"korean_traditional": [],
"auto_pastel": [
20.1,
29.7
],
"auto_black": [
3.11,
0.0
]
},
"avg_scores": {},
"best_combinations": [],
"verdict": ""
},
"pulid-flux": {
"rounds_used": 1,
"dimensions": {
"byeolyi": [],
"hanja": [],
"hanok": [],
"illustration": [],
"korean_traditional": [],
"auto_pastel": [
30.2
],
"auto_black": [
0.73
]
},
"avg_scores": {},
"best_combinations": [],
"verdict": ""
},
"qwen-image-2512": {
"rounds_used": 1,
"dimensions": {
"byeolyi": [],
"hanja": [],
"hanok": [],
"illustration": [],
"korean_traditional": []
},
"avg_scores": {},
"best_combinations": [],
"verdict": "워크플로우 결함 — 5KB 검정 latent (v1~v4 4차 시도 모두 실패)"
}
}
} }

View File

@@ -1 +1,19 @@
{} {
"qwen-image-2512-GGUF-Q4-SamplerCustomAdvanced-v4": {
"attempts": 4,
"patterns": [
"EmptyLatentImage",
"EmptyQwenImageLayeredLatentImage",
"ModelSamplingAuraFlow+EmptySD3LatentImage",
"SamplerCustomAdvanced+ModelSamplingAuraFlow"
],
"all_results": "5KB 검정 latent",
"hypothesis": "Qwen-Image GGUF Q4_K_S + CUDA 13.0 + PyTorch 2.12 환경 호환성 또는 다른 quant 필요 (Q8/fp16)",
"next_attempts": [
"non-GGUF Qwen-Image safetensors",
"Lightning LoRA 적용",
"다른 sampler (dpmpp_2m/ddim)",
"다른 shift 값 (1.73/5.0)"
]
}
}

159
eval/meeting-103-grid.html Normal file
View File

@@ -0,0 +1,159 @@
<!DOCTYPE html>
<html lang="ko"><head><meta charset="utf-8">
<title>meeting-103 v2.1 — ComfyUI 자율 R&D + 자산 라이브러리</title>
<style>
body { font-family: -apple-system, "Pretendard", system-ui, sans-serif; padding: 24px;
background: linear-gradient(135deg, #FAF7FF 0%, #FFF0F5 100%); color: #4A3F5C; }
h1 { color: #4A3F5C; margin-bottom: 6px; font-size: 24px; }
.subtitle { color: #8B7AAE; font-size: 13px; margin-bottom: 24px; }
h2 { color: #6B5B8E; margin-top: 32px; border-bottom: 2px solid #DCC9F3; padding-bottom: 6px; font-size: 18px; }
.summary-box { background: white; border-radius: 14px; padding: 18px 20px; margin: 16px 0;
box-shadow: 0 2px 8px rgba(180,140,200,0.15); }
.summary-box h3 { margin-top: 0; font-size: 15px; }
.summary-box ul { font-size: 12px; line-height: 1.7; margin: 6px 0; }
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; margin: 14px 0; }
.cell { background: white; border-radius: 14px; overflow: hidden; box-shadow: 0 4px 12px rgba(180,140,200,0.18); cursor: pointer; transition: all 0.2s; }
.cell:hover { transform: translateY(-2px); box-shadow: 0 6px 18px rgba(180,140,200,0.28); }
.cell img { width: 100%; display: block; aspect-ratio: 9/16; object-fit: cover; }
.cell.failed { background: linear-gradient(135deg, #fde2e4, #f3e8ff); display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 200px; padding: 16px; text-align: center; font-size: 12px; color: #6F2D2D; }
.label { padding: 10px 12px; font-size: 12px; }
.label-title { font-weight: 600; color: #4A3F5C; margin-bottom: 4px; }
.label-meta { color: #8B7AAE; font-size: 11px; line-height: 1.5; }
.label-auto-eval { color: #4A3F5C; font-size: 11px; margin-top: 6px; padding: 6px; background: #F5F0FF; border-radius: 6px; line-height: 1.5; }
.label-flags { margin-top: 6px; }
.flag { display: inline-block; padding: 2px 8px; border-radius: 10px; font-size: 10px; margin-right: 4px; }
.flag-vision { background: #FFF4D0; color: #8B6F1A; }
.flag-suspicious { background: #FFD9D9; color: #8B1A1A; }
.flag-pass-auto { background: #D5F0D5; color: #1A6F1A; }
.modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(74,63,92,0.92); z-index: 999; cursor: zoom-out; }
.modal img { max-width: 92%; max-height: 92%; margin: 4% auto; display: block; border-radius: 8px; }
</style></head>
<body>
<h1>meeting-103 v2.1 — ComfyUI 자율 R&D</h1>
<div class="subtitle">
3-stage 파이프라인 · 14 모델 풀 (9 설치 / 4 미설치) · 자산 라이브러리 7종 누적 ·
합격 기준: 별이 ≥8 · 한자 ≥4.5 · 한옥 ≥4.5
</div>
<div class="summary-box">
<h3>📊 R1 진행 상태</h3>
<ul>
<li>✅ 4/5 정상: Pony A · Flux Dev A · Flux+PuLID A · Flux Dev B</li>
<li>❌ 1/5 실패: <strong>Qwen-2512 C 한자카드 (5.5KB)</strong> — SamplerCustomAdvanced v4 도 실패, 4차 연속</li>
<li>자동 best pastel: 1-1 (Pony) 42.5%</li>
<li>시각 검수 필수 (별이 정합성·한자·한옥 자동 평가 미커버)</li>
</ul>
</div>
<div class="summary-box">
<h3>🎯 자산 라이브러리 R1 누적</h3>
<ul>
<li>models-performance.json: 4 모델 등재 (pony-v6 / flux-dev / pulid-flux / qwen-image-2512)</li>
<li>workflows: 시각 평가 후 winner/partial 분류 (현재 0)</li>
<li>prompts/failed-patterns: <strong>qwen-2512 4차 실패 패턴 등재</strong> (다음 시도: non-GGUF / Lightning LoRA / 다른 sampler)</li>
<li>design-tokens: 합격 시 추출 (현재 0)</li>
<li>loras: 별이 ≥7 + 3라운드+ + 8 미달 시 자동 학습</li>
</ul>
</div>
<h2>라운드 1 — A 별이 (3 모델 비교)</h2>
<div class="grid">
<div class="cell" onclick="zoom(this)"><img src="../results/meeting-103/round1/1-1_thumb.png" data-original="../results/meeting-103/round1/1-1.png" alt="1-1">
<div class="label">
<div class="label-title">1-1: Pony V6 XL A 별이</div>
<div class="label-meta">model=pony-v6, steps=28, cfg=7, sampler=dpmpp_2m karras, seed=42, 768×1344</div>
<div class="label-auto-eval">
자동 평가:<br>
- 크기: 1501KB ✓<br>
- 검정: <strong>0.61%</strong><br>
- 파스텔: <strong>42.5%</strong> (R1 1위)<br>
- 별이 8/10 · 한복 정합성 · Cute Horror — 시각 평가 필요
</div>
<div class="label-flags">
<span class="flag flag-pass-auto">auto-pass</span>
<span class="flag flag-vision">vision-check</span>
</div>
</div></div>
<div class="cell" onclick="zoom(this)"><img src="../results/meeting-103/round1/1-2_thumb.png" data-original="../results/meeting-103/round1/1-2.png" alt="1-2">
<div class="label">
<div class="label-title">1-2: Flux Dev A 별이 (text only)</div>
<div class="label-meta">model=flux-dev Q5 GGUF, steps=25, cfg=1, guidance=3.5, FluxGuidance, seed=42</div>
<div class="label-auto-eval">
자동 평가:<br>
- 크기: 1121KB ✓<br>
- 검정: <strong>3.11%</strong> ⚠️ (DESIGN.md §10 한계 근접)<br>
- 파스텔: 20.1%<br>
- 별이 정합성 — 시각 평가
</div>
<div class="label-flags">
<span class="flag flag-suspicious">black-3pct</span>
<span class="flag flag-vision">vision-check</span>
</div>
</div></div>
<div class="cell" onclick="zoom(this)"><img src="../results/meeting-103/round1/1-3_thumb.png" data-original="../results/meeting-103/round1/1-3.png" alt="1-3">
<div class="label">
<div class="label-title">1-3: Flux Dev + PuLID A 별이 ⭐</div>
<div class="label-meta">model=flux-dev + PuLID weight=0.85 fusion=mean, ref=byeolyi-default-768x1344, end_at=0.8</div>
<div class="label-auto-eval">
자동 평가:<br>
- 크기: 1065KB ✓<br>
- 검정: 0.73% ✓<br>
- 파스텔: 30.2%<br>
- <strong>별이 얼굴 ID matching 핵심</strong> — 시각 평가 필수
</div>
<div class="label-flags">
<span class="flag flag-pass-auto">auto-pass</span>
<span class="flag flag-vision">vision-id-check</span>
</div>
</div></div>
</div>
<h2>라운드 1 — B 한옥</h2>
<div class="grid">
<div class="cell" onclick="zoom(this)"><img src="../results/meeting-103/round1/1-4_thumb.png" data-original="../results/meeting-103/round1/1-4.png" alt="1-4">
<div class="label">
<div class="label-title">1-4: Flux Dev B 한옥 (text only)</div>
<div class="label-meta">model=flux-dev Q5 GGUF, NOT japanese / pagoda / wabi-sabi negative, seed=42</div>
<div class="label-auto-eval">
자동 평가:<br>
- 크기: 1724KB ✓<br>
- 검정: <strong>0.0%</strong> ✓ (완벽)<br>
- 파스텔: 29.7%<br>
- 한옥 vs 일본 구분 — 시각 평가 필수
</div>
<div class="label-flags">
<span class="flag flag-pass-auto">auto-pass</span>
<span class="flag flag-vision">korean-vs-japanese</span>
</div>
</div></div>
</div>
<h2>라운드 1 — C 60갑자 한자카드</h2>
<div class="grid">
<div class="cell failed">
<strong>1-5: Qwen-2512 C 한자카드 ❌</strong>
<br>5.5KB 검정 latent 실패<br>(v6 R1 3차 + 본 R1 v4 = 총 4차 연속)
<br><br>워크플로우 패턴:<br>SamplerCustomAdvanced + ModelSamplingAuraFlow + EmptySD3LatentImage
<br><br>다음 시도 (R2):<br>non-GGUF safetensors / Lightning LoRA / 다른 sampler/shift
</div>
</div>
<div class="summary-box">
<h3>🔗 검수 채널</h3>
<ul>
<li>1순위 Vault: D:\Vault\8460s-image-rd\eval\meeting-103-grid.html (Syncthing 동기화 후)</li>
<li>2순위 Gitea raw: <code>https://kakao-kakao2-server.tail31bd37.ts.net/choijaewook/8460s-image-rd/raw/branch/main/eval/meeting-103-grid.html</code></li>
<li>썸네일 의무 충족 (모든 cell 1MB 미만, 최대 540KB)</li>
</ul>
</div>
<div class="modal" id="modal" onclick="this.style.display='none'"><img id="modal-img"></div>
<script>
function zoom(el) {
const orig = el.querySelector('img').dataset.original || el.querySelector('img').src;
document.getElementById('modal-img').src = orig;
document.getElementById('modal').style.display = 'block';
}
</script>
</body></html>

View File

@@ -0,0 +1,84 @@
{
"session": "meeting-103-v2.1",
"started_at": "2026-05-19T21:47:00+09:00",
"current_round": 1,
"rounds": [
{
"round": 1,
"name": "초기 매트릭스 (5 조합)",
"status": "completed",
"combinations": [
{
"id": "1-1",
"model": "pony-v6",
"prompt_set": "A",
"status": "OK",
"auto_eval": {
"size_kb": 1501,
"black_pct": 0.61,
"pastel_pct": 42.5
},
"needs_vision": true
},
{
"id": "1-2",
"model": "flux-dev",
"prompt_set": "A",
"status": "OK",
"auto_eval": {
"size_kb": 1121,
"black_pct": 3.11,
"pastel_pct": 20.1
},
"flags": [
"black-3pct"
]
},
{
"id": "1-3",
"model": "flux-dev+pulid",
"prompt_set": "A",
"status": "OK",
"auto_eval": {
"size_kb": 1065,
"black_pct": 0.73,
"pastel_pct": 30.2
},
"needs_vision_id": true
},
{
"id": "1-4",
"model": "flux-dev",
"prompt_set": "B",
"status": "OK",
"auto_eval": {
"size_kb": 1724,
"black_pct": 0.0,
"pastel_pct": 29.7
},
"needs_vision": true
},
{
"id": "1-5",
"model": "qwen-image-2512",
"prompt_set": "C",
"status": "FAILED",
"size_bytes": 5540,
"issue": "5KB 검정 latent (4차 연속)"
}
],
"best_pastel": "1-1",
"learning": {
"qwen_workflow_3rd_attempt_failed": true,
"next_round_hint": "R2 — Qwen 비-GGUF + Stage 3 Flux Refiner (Pony 1-1 → refiner) + 다른 PuLID fusion"
}
}
],
"thresholds": {
"byeolyi": 8,
"hanja": 4.5,
"hanok": 4.5
},
"raw_html_url": "https://kakao-kakao2-server.tail31bd37.ts.net/choijaewook/8460s-image-rd/raw/branch/main/eval/meeting-103-grid.html",
"vault_path": "D:\\Vault\\8460s-image-rd\\eval\\meeting-103-grid.html"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,99 @@
{
"round": 1,
"captured_at": "2026-05-19T21:57:10.975065",
"combos": [
{
"id": "1-1",
"prompt_id": "3b6d1ef1-163c-484b-93bd-1965a5dd59b3",
"models_used": [
"pony-v6"
],
"prompt_set": "A",
"stage": "1-only",
"status": "OK",
"size_bytes": 1537272,
"width": 768,
"height": 1344,
"aspect": 0.5714,
"black_pct": 0.61,
"pastel_pct": 42.5,
"thumb_size_kb": 540
},
{
"id": "1-2",
"prompt_id": "472d7fc4-0234-40f9-be7d-3e25e3d04b32",
"models_used": [
"flux-dev"
],
"prompt_set": "A",
"stage": "1-only",
"status": "OK",
"size_bytes": 1148193,
"width": 768,
"height": 1344,
"aspect": 0.5714,
"black_pct": 3.11,
"pastel_pct": 20.1,
"thumb_size_kb": 374
},
{
"id": "1-3",
"prompt_id": "117742cb-1615-44ec-9f7b-5c558af278f8",
"models_used": [
"flux-dev",
"pulid-flux"
],
"prompt_set": "A",
"stage": "1+pulid",
"status": "OK",
"size_bytes": 1090948,
"width": 768,
"height": 1344,
"aspect": 0.5714,
"black_pct": 0.73,
"pastel_pct": 30.2,
"thumb_size_kb": 377
},
{
"id": "1-4",
"prompt_id": "b4ec98ac-2f7f-4dd5-9e47-5bfdcc78cf3c",
"models_used": [
"flux-dev"
],
"prompt_set": "B",
"stage": "1-only",
"status": "OK",
"size_bytes": 1765589,
"width": 768,
"height": 1344,
"aspect": 0.5714,
"black_pct": 0.0,
"pastel_pct": 29.7,
"thumb_size_kb": 512
},
{
"id": "1-5",
"prompt_id": "54ee2273-7585-4618-8b51-b74ded23cf05",
"models_used": [
"qwen-image-2512"
],
"prompt_set": "C",
"stage": "1-only",
"workflow_variant": "SamplerCustomAdvanced v4",
"status": "FAILED_SMALL",
"size_bytes": 5540,
"width": 768,
"height": 1344,
"aspect": 0.5714,
"black_pct": 100.0,
"pastel_pct": 0.0,
"thumb_size_kb": 1
}
],
"learning": {
"passed_count": 4,
"failed_count": 1,
"best_pastel": "1-1",
"next_round_hint": "auto-determine based on round learning"
}
}

Binary file not shown.

174
scripts/round_pipeline.py Normal file
View File

@@ -0,0 +1,174 @@
#!/usr/bin/env python3
"""
meeting-103 v2.1 라운드 파이프라인
- 결과 회수 (HTTP /view)
- 썸네일 생성 (1MB 미만)
- 자동 평가 (black/pastel/aspect)
- 자산 라이브러리 7종 누적
"""
import json
import os
import sys
import urllib.request
from pathlib import Path
from PIL import Image
REPO_ROOT = Path("/mnt/ssd1/dev/projects/8460s-image-rd")
COMFY = "http://100.123.6.62:8188"
THRESHOLDS = {"byeolyi": 8, "hanja": 4.5, "hanok": 4.5}
PROTECTED = {"qwen-image-2.0", "qwen-image-2512", "qwen-image-layered", "qwen-edit-2511",
"flux-dev", "flux-schnell", "flux-kontext", "flux-fill", "flux-redux",
"pulid-flux", "pony-v6", "illustrious", "lightning-lora-qwen"}
def http_view(filename):
"""ComfyUI HTTP /view 으로 PNG 다운로드 (bytes 반환)"""
url = f"{COMFY}/view?filename={filename}&type=output"
with urllib.request.urlopen(url, timeout=20) as r:
return r.read()
def history_outputs(prompt_id):
"""prompt_id → 출력 파일명 목록"""
url = f"{COMFY}/history/{prompt_id}"
with urllib.request.urlopen(url, timeout=10) as r:
data = json.load(r)
if not data:
return []
info = list(data.values())[0]
outs = info.get("outputs", {})
return [f["filename"] for n, i in outs.items() for f in i.get("images", [])]
def fetch_round_results(round_n, combos):
"""combos = [{"id": "1-1", "prompt_id": "...", "expected_filename": "..."}]"""
round_dir = REPO_ROOT / "results" / "meeting-103" / f"round{round_n}"
round_dir.mkdir(parents=True, exist_ok=True)
results = []
for c in combos:
fns = history_outputs(c["prompt_id"]) if c.get("prompt_id") else []
fn = fns[0] if fns else c.get("expected_filename")
if not fn:
results.append({**c, "status": "no_filename"})
continue
try:
data = http_view(fn)
out_png = round_dir / f"{c['id']}.png"
out_png.write_bytes(data)
size = len(data)
# 썸네일 (1MB 미만 의무)
img = Image.open(out_png).convert("RGB")
thumb = img.copy()
thumb.thumbnail((512, 768))
thumb_path = round_dir / f"{c['id']}_thumb.png"
thumb.save(thumb_path, "PNG", optimize=True)
assert thumb_path.stat().st_size < 1_000_000, "thumbnail >1MB"
# 자동 평가
w, h = img.size
pixels = list(img.getdata())
total = len(pixels)
near_black = sum(1 for r, g, b in pixels if r < 30 and g < 30 and b < 30)
pastel = sum(1 for r, g, b in pixels if (r > 150 and b > 150) or (r > 200 and g > 150 and b > 150))
results.append({
**c, "status": "OK" if size > 50000 else "FAILED_SMALL",
"size_bytes": size, "width": w, "height": h,
"aspect": round(w / h, 4),
"black_pct": round(near_black / total * 100, 2),
"pastel_pct": round(pastel / total * 100, 1),
"thumb_size_kb": thumb_path.stat().st_size // 1024,
})
except Exception as e:
results.append({**c, "status": f"ERROR:{type(e).__name__}", "error": str(e)[:120]})
return results
def update_models_performance(round_n, combo_results):
"""자산 라이브러리 1 — 모델 차원별 점수 누적"""
path = REPO_ROOT / "assets-library" / "models-performance.json"
data = json.loads(path.read_text())
for c in combo_results:
if c.get("status") != "OK":
continue
for model in c.get("models_used", []):
mdata = data["models"].setdefault(model, {
"rounds_used": 0,
"dimensions": {dim: [] for dim in data["_dimensions"]},
"avg_scores": {}, "best_combinations": [], "verdict": ""
})
mdata["rounds_used"] = max(mdata["rounds_used"], round_n)
scores = c.get("scores", {})
for dim, score in scores.items():
if score is not None and dim in mdata["dimensions"]:
mdata["dimensions"][dim].append(score)
for dim, arr in mdata["dimensions"].items():
if arr:
mdata["avg_scores"][dim] = round(sum(arr) / len(arr), 2)
path.write_text(json.dumps(data, indent=2, ensure_ascii=False))
return path
def save_workflow_to_library(combo, workflow_json, round_n):
"""자산 라이브러리 2 — winner/partial 워크플로 보존"""
scores = combo.get("scores", {})
if not scores:
return None
if all(scores.get(d, 0) >= t for d, t in THRESHOLDS.items()):
category = "winner"
elif max(scores.values()) >= 5:
category = "partial"
else:
return None
best_dim = max(scores, key=scores.get)
model = (combo.get("models_used") or ["unknown"])[0]
name = f"{category}-R{round_n}-{combo['id']}-{model}.json"
path = REPO_ROOT / "assets-library" / "workflows" / name
path.write_text(json.dumps(workflow_json, indent=2, ensure_ascii=False))
# 인덱스 갱신
idx_path = REPO_ROOT / "assets-library" / "workflows" / "_index.json"
idx = json.loads(idx_path.read_text())
entry = {"name": name, "category": category, "best_dimension": best_dim,
"scores": scores, "round": round_n, "model": model}
idx.setdefault(category, []).append(entry)
idx["count"] = len(idx.get("winner", [])) + len(idx.get("partial", []))
idx_path.write_text(json.dumps(idx, indent=2, ensure_ascii=False))
return path
def save_results_meta(round_n, results, learning=None):
"""라운드 메타 JSON 저장 (그리드 보조)"""
path = REPO_ROOT / "results" / "meeting-103" / f"round{round_n}" / "meta.json"
payload = {
"round": round_n,
"captured_at": __import__("datetime").datetime.now().isoformat(),
"combos": results,
"learning": learning or {}
}
path.write_text(json.dumps(payload, indent=2, ensure_ascii=False))
return path
def extract_learning(results):
"""라운드 학습 추출 (RAG-style)"""
passed = []
failed = []
for r in results:
if r.get("status") != "OK":
failed.append({"id": r["id"], "reason": r.get("status")})
continue
if r.get("black_pct", 0) > 5:
failed.append({"id": r["id"], "reason": "검정 5%+", "black_pct": r["black_pct"]})
else:
passed.append({"id": r["id"], "pastel_pct": r.get("pastel_pct"), "size": r.get("size_bytes")})
best = sorted([r for r in results if r.get("status") == "OK"],
key=lambda x: x.get("pastel_pct", 0), reverse=True)
return {
"passed_count": len(passed),
"failed_count": len(failed),
"best_pastel": best[0]["id"] if best else None,
"next_round_hint": "auto-determine based on round learning"
}
if __name__ == "__main__":
print("round_pipeline.py — 라이브러리, import 후 사용")