Add style override to the debug chat probe.

- 디버그 chatProbe·chatProbeSheet에 style 쿼리 파라미터를 추가해, prod config(config/chat.stylePack)를 바꾸지 않고도 스타일을 A/B로 검증할 수 있게 했습니다.
- chatProbeService의 buildProbeEnv/runChatProbe/runChatProbeBatch에 styleOverride를 연결하고, 프로브 시트 메타·마크다운에 적용 스타일을 표기했습니다.
This commit is contained in:
윤정민 2026-06-23 13:27:17 +09:00
parent 4e98532f6c
commit 9f7666a73f
2 changed files with 26 additions and 13 deletions

View File

@ -98,8 +98,11 @@ export const debug = onRequest({ timeoutSeconds: 300 }, async (req, res) => {
return; return;
} }
const model = typeof req.query.model === "string" ? req.query.model : undefined; const model = typeof req.query.model === "string" ? req.query.model : undefined;
logger.info(`debug.chatProbe triggered (uid=${uid}, model=${model ?? "default"})`); const style = typeof req.query.style === "string" ? req.query.style : undefined;
const result = await runChatProbe(uid, q, model); logger.info(
`debug.chatProbe triggered (uid=${uid}, model=${model ?? "default"}, style=${style ?? "default"})`,
);
const result = await runChatProbe(uid, q, model, style);
res.status(200).json(result); res.status(200).json(result);
return; return;
} }
@ -115,8 +118,11 @@ export const debug = onRequest({ timeoutSeconds: 300 }, async (req, res) => {
} }
const format = typeof req.query.format === "string" ? req.query.format : "json"; const format = typeof req.query.format === "string" ? req.query.format : "json";
const model = typeof req.query.model === "string" ? req.query.model : undefined; const model = typeof req.query.model === "string" ? req.query.model : undefined;
logger.info(`debug.chatProbeSheet triggered (uid=${uid}, model=${model ?? "default"})`); const style = typeof req.query.style === "string" ? req.query.style : undefined;
const sheet = await runChatProbeBatch(uid, undefined, model); logger.info(
`debug.chatProbeSheet triggered (uid=${uid}, model=${model ?? "default"}, style=${style ?? "default"})`,
);
const sheet = await runChatProbeBatch(uid, undefined, model, style);
if (format === "md" || format === "markdown") { if (format === "md" || format === "markdown") {
res.status(200).type("text/markdown; charset=utf-8").send(renderProbeMarkdown(sheet)); res.status(200).type("text/markdown; charset=utf-8").send(renderProbeMarkdown(sheet));
return; return;

View File

@ -56,6 +56,7 @@ export interface ChatProbeMeta {
date: string; date: string;
provider: string; provider: string;
model: string; model: string;
stylePack: string;
} }
export interface ChatProbeSheet { export interface ChatProbeSheet {
@ -92,15 +93,18 @@ interface ProbeEnv {
} }
/** uid 1명 기준으로 시스템 프롬프트·도구 컨텍스트·provider를 한 번만 준비한다. */ /** uid 1명 기준으로 시스템 프롬프트·도구 컨텍스트·provider를 한 번만 준비한다. */
async function buildProbeEnv(uid: string, modelOverride?: string): Promise<ProbeEnv> { async function buildProbeEnv(
uid: string,
modelOverride?: string,
styleOverride?: string,
): Promise<ProbeEnv> {
// 프로브는 검수 도구이므로 항상 최신 config를 읽는다 — 5분 캐시 우회. // 프로브는 검수 도구이므로 항상 최신 config를 읽는다 — 5분 캐시 우회.
invalidateChatConfigCache(); invalidateChatConfigCache();
const base = await getChatConfig(); const base = await getChatConfig();
// A/B용 모델 오버라이드 — 모델만 바꾸고 나머지 provider 설정(공평 타임아웃 등)은 공유. // A/B 오버라이드 — 모델·스타일만 바꾸고 나머지 설정은 공유. 미지정 시 라이브 기본.
// 미지정 시 라이브 기본(lite). let config: ChatConfig = base;
const config: ChatConfig = modelOverride ? if (modelOverride) config = { ...config, provider: { ...config.provider, model: modelOverride } };
{ ...base, provider: { ...base.provider, model: modelOverride } } : if (styleOverride) config = { ...config, stylePack: styleOverride };
base;
const user = await getUser(uid); const user = await getUser(uid);
if (!user) { if (!user) {
throw new HttpError(404, `user not found: ${uid}`, "USER_NOT_FOUND"); throw new HttpError(404, `user not found: ${uid}`, "USER_NOT_FOUND");
@ -126,6 +130,7 @@ async function buildProbeEnv(uid: string, modelOverride?: string): Promise<Probe
date, date,
provider: config.provider.name, provider: config.provider.name,
model: config.provider.model, model: config.provider.model,
stylePack: config.stylePack,
}, },
}; };
} }
@ -189,8 +194,9 @@ export async function runChatProbe(
uid: string, uid: string,
message: string, message: string,
modelOverride?: string, modelOverride?: string,
styleOverride?: string,
): Promise<ProbeResult> { ): Promise<ProbeResult> {
const env = await buildProbeEnv(uid, modelOverride); const env = await buildProbeEnv(uid, modelOverride, styleOverride);
return runOne(env, message); return runOne(env, message);
} }
@ -199,8 +205,9 @@ export async function runChatProbeBatch(
uid: string, uid: string,
prompts?: Array<{ q: string; expect?: string }>, prompts?: Array<{ q: string; expect?: string }>,
modelOverride?: string, modelOverride?: string,
styleOverride?: string,
): Promise<ChatProbeSheet> { ): Promise<ChatProbeSheet> {
const env = await buildProbeEnv(uid, modelOverride); const env = await buildProbeEnv(uid, modelOverride, styleOverride);
const set = prompts ?? DEFAULT_PROBE_PROMPTS; const set = prompts ?? DEFAULT_PROBE_PROMPTS;
// 순차 실행 — 동시 호출 부하로 인한 예산 초과 아티팩트를 피하고 실제 채팅(1건씩)에 // 순차 실행 — 동시 호출 부하로 인한 예산 초과 아티팩트를 피하고 실제 채팅(1건씩)에
// 가깝게 둔다. 각 프로브가 25초 예산을 온전히 쓰도록 보장. // 가깝게 둔다. 각 프로브가 25초 예산을 온전히 쓰도록 보장.
@ -233,7 +240,7 @@ export function renderProbeMarkdown(sheet: ChatProbeSheet): string {
lines.push(`- 생성: ${meta.generatedAt}`); lines.push(`- 생성: ${meta.generatedAt}`);
lines.push(`- uid: \`${meta.uid}\` / 유저: ${meta.displayName}`); lines.push(`- uid: \`${meta.uid}\` / 유저: ${meta.displayName}`);
lines.push(`- 응원팀: ${meta.favoriteTeamName} (${meta.favoriteTeamCode ?? "—"})`); lines.push(`- 응원팀: ${meta.favoriteTeamName} (${meta.favoriteTeamCode ?? "—"})`);
lines.push(`- 모델: ${meta.provider}/${meta.model} / 기준 날짜(KST): ${meta.date}`); lines.push(`- 모델: ${meta.provider}/${meta.model} / 스타일: ${meta.stylePack} / 기준 날짜(KST): ${meta.date}`);
lines.push( lines.push(
`- 토큰 합계: 입력 ${sheet.totals.inputTokens.toLocaleString()} / ` + `- 토큰 합계: 입력 ${sheet.totals.inputTokens.toLocaleString()} / ` +
`출력 ${sheet.totals.outputTokens.toLocaleString()} / ` + `출력 ${sheet.totals.outputTokens.toLocaleString()} / ` +