|
|
나는 생성형 인공지능과 같이 공동 생성한 자산을 사용해서 생성형 인공지능 플랫폼 개발 기술 자문 용역을 수행하고 있다.
2025년 9월 8일 계약을 체결했고 2025년 12월 15일까지 수행기간이다.
그 결과로 나의 자산의 신뢰성, 효율성, 책임성을 매 순간마다 생성형 인공지능과 같이 판별하고 있다. 외부 검증을 기다리지 않고 나와 생성형 인공지능이 상호 시이소오처럼 견제와 균형을 통해 나를 발견하고 발명하고 있다.
아래 내가 발명한 작품을 공개한다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>REE-JSON 체크리스트 · Human-in-the-Loop 비교</title>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<style>
:root {
--bg:#f7f8fa; --card:#ffffff; --text:#1f2937; --sub:#6b7280; --brand:#2563eb; --ok:#059669; --warn:#d97706; --bad:#dc2626; --line:#e5e7eb;
}
* { box-sizing: border-box; }
body { margin:0; font-family: "Noto Sans KR", system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; background:var(--bg); color:var(--text);}
h1 { text-align:center; margin:18px 8px 6px; color:#1e40af; }
p.lead { text-align:center; color:var(--sub); margin:0 8px 18px; }
.wrap { max-width:1080px; margin:0 auto; padding:16px; }
.card { background:var(--card); border:1px solid var(--line); border-radius:14px; box-shadow:0 2px 6px rgba(0,0,0,.06); padding:18px; margin-bottom:16px;}
.row { display:grid; grid-template-columns: 1fr 1fr; gap:12px; }
.row-3 { display:grid; grid-template-columns: 1fr 1fr 1fr; gap:12px; }
label { font-weight:700; font-size:.95rem; margin-top:8px; display:block;}
input[type="text"], input[type="date"], input[type="number"], textarea {
width:100%; padding:10px 12px; border:1px solid var(--line); border-radius:10px; font-size:.95rem; background:#fff;
}
textarea { min-height:90px; resize:vertical; }
.muted { color:var(--sub); font-size:.9rem; }
.grid { display:grid; grid-template-columns: repeat(6, 1fr); gap:8px; }
.btns { display:flex; flex-wrap:wrap; gap:8px; margin-top:14px; }
button {
border:0; background:var(--brand); color:#fff; padding:10px 14px; border-radius:10px; font-weight:700; cursor:pointer;
}
button.secondary { background:#374151; }
button.ghost { background:#fff; color:var(--brand); border:1px solid var(--brand); }
.two { display:grid; grid-template-columns: 1fr 1fr; gap:12px; }
.tag { display:inline-block; padding:2px 8px; border-radius:999px; border:1px solid var(--line); font-size:.8rem; background:#f8fafc; }
.table { width:100%; border-collapse:collapse; margin-top:8px; }
.table th, .table td { border-bottom:1px solid var(--line); padding:8px 10px; font-size:.92rem; vertical-align:top; }
.table th { text-align:left; background:#f9fafb; }
code, pre { background:#f3f4f6; border:1px solid var(--line); border-radius:10px; padding:10px; }
.ok { color:var(--ok); font-weight:700; }
.warn { color:var(--warn); font-weight:700; }
.bad { color:var(--bad); font-weight:700; }
.pill { padding:2px 8px; border-radius:999px; font-size:.8rem; border:1px solid var(--line); }
.delta { font-variant-numeric: tabular-nums; }
@media (max-width:900px){ .row, .row-3, .two { grid-template-columns:1fr; } }
</style>
</head>
<body>
<h1>REE-JSON 체크리스트 (무료 계정) · Human-in-the-Loop</h1>
<p class="lead">HandLoop™ + Local Storage First + JSON 구조화 · 사용자 평가 vs. 모델 자평 병렬 비교</p>
<div class="wrap">
<!-- 세션 메타 -->
<div class="card">
<div class="row">
<div>
<label>사용자 ID</label>
<input id="userId" type="text" value="free-tier-user-01" />
</div>
<div>
<label>세션 날짜</label>
<input id="sessionDate" type="date" />
</div>
</div>
<p class="muted" style="margin-top:8px;">
매 세션의 맥락은 JSON으로 로컬 저장합니다. 다음 세션에서 <strong>불러오기</strong>로 이어붙이세요.
</p>
</div>
<!-- 입력/출력 -->
<div class="card">
<div class="row">
<div>
<label>Step 번호</label>
<input id="step" type="number" value="1" />
</div>
<div>
<label>도메인/태스크 라벨(선택)</label>
<input id="label" type="text" placeholder="예: ifrs-s2 / esg-assurance / csr-summary" />
</div>
</div>
<div class="row" style="margin-top:8px;">
<div>
<label>입력 (Input)</label>
<textarea id="inputText" placeholder="질문 또는 업로드 파일 설명"></textarea>
</div>
<div>
<label>출력 요약 (Output)</label>
<textarea id="outputText" placeholder="모델이 생성한 답변 요약 또는 산출물"></textarea>
</div>
</div>
</div>
<!-- REE: 사용자 평가 vs 모델 자평 -->
<div class="card">
<div class="two">
<div>
<h3>👤 사용자 평가 (Human)</h3>
<div class="row-3">
<div>
<label>신뢰성 R (0~1)</label>
<input id="hR" type="number" min="0" max="1" step="0.01" />
</div>
<div>
<label>효율성 E (0~1)</label>
<input id="hE" type="number" min="0" max="1" step="0.01" />
</div>
<div>
<label>책임성 R (0~1)</label>
<input id="hRes" type="number" min="0" max="1" step="0.01" />
</div>
</div>
<label style="margin-top:10px;">사용자 코멘트/NOTE</label>
<textarea id="humanNote" placeholder="근거, 개선 포인트, 다음 프롬프트 방향 등"></textarea>
</div>
<div>
<h3>🤖 모델 자평 (Model Self-Assessment)</h3>
<div class="row-3">
<div>
<label>신뢰성 R (0~1)</label>
<input id="mR" type="number" min="0" max="1" step="0.01" />
</div>
<div>
<label>효율성 E (0~1)</label>
<input id="mE" type="number" min="0" max="1" step="0.01" />
</div>
<div>
<label>책임성 R (0~1)</label>
<input id="mRes" type="number" min="0" max="1" step="0.01" />
</div>
</div>
<label style="margin-top:10px;">모델 자기 설명 (선택)</label>
<textarea id="modelExplain" placeholder="모델이 스스로 제시한 근거/한계/추가 조치 등 (복붙)"></textarea>
</div>
</div>
<!-- 비교와 사용자 NOTE -->
<div class="card" style="margin-top:12px;">
<h3>🔀 Human vs Model 비교(Δ = Human − Model)</h3>
<table class="table">
<thead><tr><th>지표</th><th>Human</th><th>Model</th><th>Δ(H−M)</th><th>판독</th></tr></thead>
<tbody>
<tr><td>신뢰성</td><td id="vhR">–</td><td id="vmR">–</td><td id="vdR" class="delta">–</td><td id="viR">–</td></tr>
<tr><td>효율성</td><td id="vhE">–</td><td id="vmE">–</td><td id="vdE" class="delta">–</td><td id="viE">–</td></tr>
<tr><td>책임성</td><td id="vhRes">–</td><td id="vmRes">–</td><td id="vdRes" class="delta">–</td><td id="viRes">–</td></tr>
<tr><td><b>REE 평균</b></td><td id="vhAvg">–</td><td id="vmAvg">–</td><td id="vdAvg" class="delta">–</td><td id="viAvg">–</td></tr>
</tbody>
</table>
<div class="row" style="margin-top:8px;">
<div>
<label>비교 소견 / 사용자 NOTE</label>
<textarea id="compareNote" placeholder="차이 원인, 보정 계획(프롬프트 수정/지식팩 갱신/스키머 조정 등)"></textarea>
</div>
<div>
<label>권고 액션(선택)</label>
<textarea id="actionPlan" placeholder="예: 근거 인용 의무화, JSON 스키마 필드 추가, 문맥 분할 전략 적용 등"></textarea>
</div>
</div>
<div class="btns">
<button type="button" xxonclick="calcDiff()">비교/판독 갱신</button>
</div>
</div>
</div>
<!-- 스텝 누적 / JSON 미리보기 -->
<div class="card">
<div class="btns">
<button type="button" xxonclick="addStep()">스텝 추가(누적)</button>
<button type="button" xxonclick="generateJSON()">세션 JSON 생성</button>
<button type="button" xxonclick="downloadJSON()">로컬 저장</button>
<button type="button" class="secondary" xxonclick="resetAll()">초기화</button>
<input type="file" id="fileInput" xxonchange="loadJSON(event)" title="JSON 불러오기" />
</div>
<h3 style="margin-top:12px;">📑 누적 스텝</h3>
<table class="table" id="stepsTable">
<thead>
<tr>
<th>#</th><th>라벨</th><th>Human REE</th><th>Model REE</th><th>Δ 평균</th><th>NOTE</th>
</tr>
</thead>
<tbody></tbody>
</table>
<h3 style="margin-top:12px;">📦 JSON 미리보기</h3>
<pre id="jsonOutput">여기에 세션 JSON이 표시됩니다.</pre>
<p class="muted">구조: { user_id, session_date, context: [ { step, label, input, output, metrics: {human:{}, model:{}}, deltas:{}, notes:{}, model_explain } ], storage, handloop }</p>
</div>
</div>
<script>
const ctx = { user_id:"", session_date:"", context:[], storage:"local/json", handloop:true };
function val(id){ return document.getElementById(id).value; }
function num(id){ const v=parseFloat(val(id)); return isNaN(v)?null:v; }
function clamp01(x){ if(x==null||isNaN(x)) return null; return Math.max(0, Math.min(1, x)); }
function fmt(x){
if(x==null || isNaN(x)) return "–";
return Number(x).toFixed(2);
}
function verdict(d){
if(d==null) return "–";
if(d >= 0.10) return "✅ Human↑ (모델 과소평가 가능)";
if(d <= -0.10) return "⚠️ Model↑ (자가평가 과대 가능)";
return "≈ 유사 (±0.10)";
}
function calcDiff(){
const hR = clamp01(num('hR')), hE = clamp01(num('hE')), hRes = clamp01(num('hRes'));
const mR = clamp01(num('mR')), mE = clamp01(num('mE')), mRes = clamp01(num('mRes'));
const hAvg = avg3(hR,hE,hRes), mAvg = avg3(mR,mE,mRes);
const dR = diff(hR,mR), dE = diff(hE,mE), dRes = diff(hRes,mRes), dAvg = diff(hAvg,mAvg);
setCell('vhR',fmt(hR)); setCell('vmR',fmt(mR)); setCell('vdR',deltaFmt(dR)); setCell('viR', verdict(dR));
setCell('vhE',fmt(hE)); setCell('vmE',fmt(mE)); setCell('vdE',deltaFmt(dE)); setCell('viE', verdict(dE));
setCell('vhRes',fmt(hRes)); setCell('vmRes',fmt(mRes)); setCell('vdRes',deltaFmt(dRes)); setCell('viRes', verdict(dRes));
setCell('vhAvg',fmt(hAvg)); setCell('vmAvg',fmt(mAvg)); setCell('vdAvg',deltaFmt(dAvg)); setCell('viAvg', verdict(dAvg));
}
function deltaFmt(d){
const el = document.createElement('span');
el.textContent = (d==null? "–" : (d>=0? "+"+d.toFixed(2): d.toFixed(2)));
el.className = "delta " + (d==null? "" : (d>=0.10? "ok": (d<=-0.10? "bad":"")));
return el.outerHTML;
}
function setCell(id, html){
const el = document.getElementById(id);
el.innerHTML = html;
}
function avg3(a,b,c){
const arr=[a,b,c].filter(v=>v!=null);
if(arr.length===0) return null;
return arr.reduce((s,v)=>s+v,0)/arr.length;
}
function diff(a,b){
if(a==null || b==null) return null;
return a-b;
}
function addStep(){
const step = parseInt(val('step'))|| (ctx.context.length+1);
const label = val('label');
const input = val('inputText');
const output = val('outputText');
const human = {
Reliability: clamp01(num('hR')),
Efficiency: clamp01(num('hE')),
Responsibility: clamp01(num('hRes')),
Avg: null
};
human.Avg = avg3(human.Reliability, human.Efficiency, human.Responsibility);
const model = {
Reliability: clamp01(num('mR')),
Efficiency: clamp01(num('mE')),
Responsibility: clamp01(num('mRes')),
Avg: null
};
model.Avg = avg3(model.Reliability, model.Efficiency, model.Responsibility);
const deltas = {
dR: diff(human.Reliability, model.Reliability),
dE: diff(human.Efficiency, model.Efficiency),
dRes: diff(human.Responsibility, model.Responsibility),
dAvg: diff(human.Avg, model.Avg)
};
const notes = {
human: val('humanNote') || "",
compare: val('compareNote') || "",
action: val('actionPlan') || ""
};
const model_explain = val('modelExplain') || "";
ctx.user_id = val('userId') || ctx.user_id || "free-tier-user-01";
ctx.session_date = val('sessionDate') || ctx.session_date || new Date().toISOString().slice(0,10);
ctx.context.push({ step, label, input, output, metrics:{ human, model }, deltas, notes, model_explain });
renderSteps();
calcDiff();
toast("스텝이 누적되었습니다.");
}
function renderSteps(){
const tb = document.querySelector('#stepsTable tbody');
tb.innerHTML = "";
ctx.context.forEach((c, i)=>{
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${c.step}</td>
<td>${c.label || ""}</td>
<td>
R:${fmt(c.metrics.human.Reliability)} · E:${fmt(c.metrics.human.Efficiency)} · R:${fmt(c.metrics.human.Responsibility)}
<div class="muted">Avg:${fmt(c.metrics.human.Avg)}</div>
</td>
<td>
R:${fmt(c.metrics.model.Reliability)} · E:${fmt(c.metrics.model.Efficiency)} · R:${fmt(c.metrics.model.Responsibility)}
<div class="muted">Avg:${fmt(c.metrics.model.Avg)}</div>
</td>
<td class="delta">${fmt(c.deltas.dAvg)}</td>
<td>
<span class="tag">H:</span> ${escapeHtml(c.notes.human)}<br/>
<span class="tag">Δ:</span> ${escapeHtml(c.notes.compare)}
</td>
`;
tb.appendChild(tr);
});
}
function generateJSON(){
const out = JSON.stringify(ctx, null, 2);
document.getElementById('jsonOutput').textContent = out;
}
function downloadJSON(){
if(!ctx.user_id){ ctx.user_id = "free-tier-user-01"; }
if(!ctx.session_date){ ctx.session_date = new Date().toISOString().slice(0,10); }
const blob = new Blob([JSON.stringify(ctx,null,2)], {type:"application/json"});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `ree-checklist_${ctx.session_date}.json`;
a.click();
URL.revokeObjectURL(url);
}
function loadJSON(ev){
const file = ev.target.files[0];
if(!file) return;
const reader = new FileReader();
reader.xxonload = e=>{
try{
const obj = JSON.parse(e.target.result);
if(!obj || !obj.context){ alert("올바른 REE JSON이 아닙니다."); return; }
// 병합: 같은 세션으로 간주하고 컨텍스트 이어붙이기
ctx.user_id = obj.user_id || ctx.user_id;
ctx.session_date = obj.session_date || ctx.session_date || new Date().toISOString().slice(0,10);
ctx.storage = obj.storage || ctx.storage;
ctx.handloop = (typeof obj.handloop==="boolean")? obj.handloop : ctx.handloop;
ctx.context = (ctx.context||[]).concat(obj.context);
renderSteps();
generateJSON();
toast("JSON을 불러와 병합했습니다.");
}catch(err){
alert("JSON 파싱 실패: "+err.message);
}
};
reader.readAsText(file);
}
function resetAll(){
if(!confirm("모든 입력과 누적 스텝을 초기화할까요?")) return;
ctx.user_id = ""; ctx.session_date = ""; ctx.context = [];
document.getElementById('jsonOutput').textContent = "여기에 세션 JSON이 표시됩니다.";
document.querySelector('#stepsTable tbody').innerHTML = "";
['hR','hE','hRes','mR','mE','mRes','humanNote','compareNote','actionPlan','modelExplain','inputText','outputText','label']
.forEach(id=>document.getElementById(id).value="");
calcDiff();
toast("초기화 완료");
}
function toast(msg){
console.log(msg);
}
function escapeHtml(s){
if(s==null) return "";
return s.replace(/[&<>"']/g, m=>({ "&":"&","<":"<",">":">",'"':""","'":"'" }[m]));
}
// 초기 표 갱신
calcDiff();
</script>
</body>
</html>
