- Published on
Chain-of-Thought 노출 없이 추론 정확도 올리기
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서론부터 결론까지 한 문장으로 요약하면, 추론은 내부에서 길게 하되 출력은 짧고 검증 가능하게 만들면 됩니다. CoT를 사용자에게 그대로 보여주면 보안·프라이버시·정책 리스크가 커지고, 제품 UX도 장황해질 수 있습니다. 반대로 CoT를 아예 억누르면 모델이 어려운 문제에서 성능이 떨어지는 경우가 있어 균형이 필요합니다.
이 글에서는 CoT를 노출하지 않으면서 정확도를 올리는 대표 전략을 출력 스키마, 자기검증 루프, 근거 기반 응답, 도구 호출, RAG 결합 관점에서 정리합니다.
참고로 RAG의 환각을 줄이는 쪽은 아래 글과 함께 보면 시너지가 큽니다.
왜 CoT를 숨기면서도 정확도를 올릴 수 있나
핵심은 모델이 추론을 하도록 유도하는 신호와 사용자에게 노출되는 텍스트를 분리하는 것입니다.
- 모델에게는
단계적 사고를 하라고 지시하되, 출력은결론 + 근거 요약 + 검증 가능한 증거로 제한 - 긴 추론은 내부에서 수행하고, 외부로는
짧은 근거(증거 조각, 인용, 계산 결과)만 제공 - 정답을 맞히는 과정에서 필요한 것은 장황한 CoT가 아니라
오류를 잡는 메커니즘(검증, 재시도, 제약)인 경우가 많음
즉, CoT 자체가 성능의 원인이라기보다 추론을 촉발하는 프롬프트 구조와 검증 루프가 성능을 좌우하는 경우가 많습니다.
전략 1: 출력 스키마로 “짧고 검증 가능하게” 강제
가장 쉬운 방법은 출력 형식을 강제해 장황한 사고 과정을 못 나오게 하는 것입니다. 대신 답, 근거 요약, 불확실성, 추가 질문 같은 필드를 두어 품질을 올립니다.
예시: JSON 스키마 기반 응답
아래는 OpenAI Responses API 스타일로 JSON만 반환하게 하는 예시입니다. 중요한 점은 CoT를 요구하지 않고도, 근거를 짧게 요약하게 만들어 품질을 유지한다는 것입니다.
import OpenAI from "openai";
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const response = await client.responses.create({
model: "gpt-4.1-mini",
input: [
{
role: "system",
content: [
{
type: "text",
text:
"너는 정확한 답변을 제공하는 어시스턴트다. " +
"내부적으로는 충분히 숙고하되, 출력에는 단계별 추론을 노출하지 마라. " +
"반드시 아래 JSON 스키마로만 답하라."
}
]
},
{
role: "user",
content: [
{ type: "text", text: "쿠버네티스에서 ImagePullBackOff가 뜰 때 가장 흔한 원인 3가지는?" }
]
}
],
text: {
format: {
type: "json_schema",
name: "answer_schema",
schema: {
type: "object",
additionalProperties: false,
properties: {
answer: { type: "string" },
key_reasons: {
type: "array",
items: { type: "string" },
minItems: 3,
maxItems: 3
},
confidence: { type: "number", minimum: 0, maximum: 1 },
followups: {
type: "array",
items: { type: "string" },
minItems: 0,
maxItems: 3
}
},
required: ["answer", "key_reasons", "confidence", "followups"]
}
}
}
});
console.log(response.output_text);
이 방식의 장점은 다음과 같습니다.
- 출력이 짧아지고 제품 UX가 안정적
- 서버에서
key_reasons를 로깅해 품질 분석 가능 - 프론트엔드에서
confidence기반으로 경고 UI나 재질문 유도 가능
인증 오류나 권한 문제로 API 호출이 흔들리면 정확도 이전에 신뢰성이 깨집니다. 운영 단계에서는 아래 체크리스트도 같이 참고하세요.
전략 2: “요약 근거”만 요구하고 CoT는 금지
CoT를 숨긴다고 해서 근거를 완전히 없애면 사용자가 불안해합니다. 그래서 근거는 주되, 추론과정은 주지 않는 형태가 실무에서 가장 많이 씁니다.
권장 출력 템플릿 예시는 다음과 같습니다.
- 결론: 한 문장
- 근거: 최대 3개 불릿(사실, 정책, 공식 문서, 로그 등)
- 확인 방법: 사용자가 재현 가능한 커맨드 또는 체크 포인트
예시 프롬프트(인라인)
아래처럼 단계별 추론 노출 금지를 명시하고, 대신 검증 포인트를 요구합니다.
규칙:
- 내부적으로는 충분히 숙고하되, 단계별 추론/사고과정은 출력하지 마라.
- 답변은 "결론 / 근거(최대 3개) / 확인방법(최대 3개)" 순서로 작성하라.
- 불확실하면 불확실하다고 말하고, 필요한 추가 정보를 질문하라.
질문: EKS에서 ALB Ingress가 403을 반환할 때 어디부터 봐야 해?
이 주제는 로그 기반으로 푸는 것이 핵심이라 아래 글을 내부 링크로 연결해 두면 독자가 바로 이동해 맥락을 이어갈 수 있습니다.
전략 3: 자기검증 루프(Verifier)로 정확도 올리기
CoT 노출 없이 성능을 끌어올리는 가장 강력한 패턴은 생성 모델과 검증 모델(또는 검증 프롬프트)을 분리하는 것입니다.
- 1차 생성: 답을 만든다(짧게)
- 2차 검증: 논리적 비약, 누락된 전제, 금지된 주장, 사실 오류를 체크한다
- 필요 시 재생성: 검증 피드백을 반영해 다시 생성
중요한 점은 검증 단계에서도 CoT를 사용자에게 보여줄 필요가 없다는 것입니다. 검증은 서버 내부에서만 수행하고, 최종 답만 노출하면 됩니다.
예시: 2단계 생성-검증 파이프라인
import OpenAI from "openai";
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
async function draftAnswer(question) {
const r = await client.responses.create({
model: "gpt-4.1-mini",
input: [
{
role: "system",
content: [{
type: "text",
text:
"내부적으로는 숙고하되 사고과정은 출력하지 마라. " +
"답변은 간결한 결론과 핵심 근거만 제시하라."
}]
},
{ role: "user", content: [{ type: "text", text: question }] }
]
});
return r.output_text;
}
async function verifyAnswer(question, answer) {
const r = await client.responses.create({
model: "gpt-4.1-mini",
input: [
{
role: "system",
content: [{
type: "text",
text:
"너는 엄격한 검증기다. 사고과정은 출력하지 말고, " +
"오류가 있으면 짧은 수정 지시만 bullet로 작성하라. " +
"오류가 없으면 'OK'만 출력하라."
}]
},
{
role: "user",
content: [{
type: "text",
text:
"질문: " + question + "\n\n" +
"답변: " + answer
}]
}
]
});
return r.output_text;
}
async function answerWithVerification(question) {
let answer = await draftAnswer(question);
const feedback = await verifyAnswer(question, answer);
if (feedback.trim() === "OK") return answer;
const revised = await client.responses.create({
model: "gpt-4.1-mini",
input: [
{
role: "system",
content: [{
type: "text",
text:
"내부적으로 숙고하되 사고과정은 출력하지 마라. " +
"검증 피드백을 반영해 답변을 수정하라."
}]
},
{
role: "user",
content: [{
type: "text",
text:
"질문: " + question + "\n\n" +
"초안: " + answer + "\n\n" +
"검증 피드백: " + feedback
}]
}
]
});
return revised.output_text;
}
console.log(await answerWithVerification("분산 트랜잭션에서 Saga 패턴의 보상 설계를 할 때 흔한 함정은?"));
이 패턴은 분산 시스템에서도 익숙한 형태입니다. 실패를 전제로 재시도/보상을 설계하듯, LLM도 오답을 전제로 검증/수정을 설계하면 안정성이 올라갑니다.
전략 4: 도구 호출로 추론을 “계산/검색”으로 치환
모델이 CoT로 버티려고 하는 영역(계산, 날짜/시간, 규칙 기반 판정, DB 조회)을 도구 호출로 빼면 정확도가 크게 오릅니다.
- 수학/통계: 코드 실행 도구
- 사실 확인: 검색 API, 사내 문서 검색
- 시스템 상태: 로그 조회, 메트릭 쿼리
도구 호출은 결과가 명시적이라, 사용자에게는 도구 결과 요약만 제공해도 납득 가능성이 높습니다.
예시: 함수 호출로 “검증 가능한 결과” 만들기
import OpenAI from "openai";
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
function calcVat({ amount, rate }) {
return { vat: amount * rate, total: amount * (1 + rate) };
}
const tools = [
{
type: "function",
function: {
name: "calc_vat",
description: "부가세를 계산한다",
parameters: {
type: "object",
properties: {
amount: { type: "number" },
rate: { type: "number" }
},
required: ["amount", "rate"]
}
}
}
];
const r1 = await client.responses.create({
model: "gpt-4.1-mini",
input: [{
role: "user",
content: [{ type: "text", text: "공급가 120000원, 세율 10%일 때 총액과 부가세를 알려줘" }]
}],
tools
});
// 실제 구현에서는 r1의 tool call을 파싱해 calcVat 실행 후
// tool 결과를 다시 모델에 넣어 최종 문장만 출력하게 만든다.
여기서 중요한 운영 팁은 도구 결과를 그대로 로그로 남기면, CoT 없이도 사후 분석이 가능해진다는 점입니다.
전략 5: RAG 결합 시 “출처 강제 + 재랭킹 + 인용”으로 CoT 대체
지식 기반 질의응답에서 CoT는 종종 그럴듯한 연결을 만들어 환각을 키웁니다. 대신 다음을 강제하면 정확도가 올라갑니다.
- 답은 반드시 제공된 컨텍스트에서만 작성
- 컨텍스트 문서 조각에 대한 인용 키를 포함
- 재랭킹으로 관련도 낮은 조각 제거
- 불충분하면
모른다또는추가 조회 필요로 종료
RAG 품질을 체계적으로 올리는 방법은 아래 글에서 더 깊게 다룹니다.
예시: 컨텍스트 기반 답변 스키마
규칙:
- 제공된 CONTEXT에 없는 내용은 추측하지 마라.
- 사고과정은 출력하지 마라.
- 출력은 JSON으로만:
- answer: 결론
- citations: 사용한 근거 조각의 id 배열
- gaps: 부족한 정보
CONTEXT:
[id=doc1#p2] ...
[id=doc3#p1] ...
QUESTION:
...
이렇게 하면 사용자는 CoT 대신 인용(citations)으로 신뢰를 얻고, 운영자는 어떤 문서가 품질에 기여했는지를 관측할 수 있습니다.
전략 6: 재시도 정책과 종료 조건을 제품 레벨에서 설계
정확도를 올리려다 보면 무한 재시도나 비용 폭증이 발생합니다. 그래서 LLM 호출도 분산 시스템처럼 실패/재시도를 설계하는 것이 중요합니다.
권장 정책 예시:
- 최대 2회 재생성
- 검증기 피드백이
치명적 오류일 때만 재생성 - 불확실성 높음이면
추가 질문으로 전환 - 타임아웃과 폴백 모델 지정
이 관점은 분산 트랜잭션의 실패 복구 설계와 유사합니다.
실무 체크리스트
CoT를 숨기면서 정확도를 올리려면 아래를 점검하면 됩니다.
- 출력 형식이 강제되는가:
JSON 스키마또는 엄격한 템플릿 - 근거가 검증 가능한가: 인용, 로그, 커맨드, 도구 결과
- 검증 루프가 있는가: 생성과 검증을 분리
- 불확실성 처리가 있는가: 모르면 모른다고, 추가 질문으로 전환
- 재시도/비용 통제가 있는가: 최대 횟수, 종료 조건
- 운영 로깅이 되는가: 최종 답, 사용 컨텍스트, 도구 결과, 검증 피드백
결론
CoT를 노출하지 않으면서도 추론 정확도를 올리는 방법은 의외로 정교한 프롬프트 한 줄이 아니라, 출력 제약 + 검증 + 근거화 + 도구화의 조합입니다. 사용자는 긴 사고 과정보다 짧고 신뢰 가능한 결론을 원하고, 운영자는 검증 가능한 로그와 재현 가능한 근거를 원합니다.
따라서 제품에서는 모델이 내부적으로는 충분히 생각하게 두되, 외부로는 짧은 답 + 근거 요약 + 확인 방법만 내보내는 구조를 기본값으로 두는 것이 가장 안전하고 성능도 잘 나옵니다.