feat: v2.2.83 → v2.2.91 — info prompt 강화 + 사용자 노출 설정 + 답변 포맷 정리
[v2.2.83] /youtube info 프롬프트 강화 - 비유 방향 보존 룰 (Hugging Face=자료실 같은 짝 뒤집기 방지) - 신뢰도 라벨 4종 ([근거 명시] / [화자 주장] / [가정] / [정리자 추론]) - 타임스탬프 fail 룰 (인용·구간 요약 모두 mm:ss 필수) - "정리자 노트" 별도 섹션으로 추론 격리 [v2.2.85] polishPersona self-check 5가지 - 정리·리뷰·요약 답변 출력 직전 머릿속 체크: (1) 사실 오류 (2) 없는 내용 추가 (3) 뉘앙스 유지 (4) 중요도 비례 (5) 중복 제거 [v2.2.86] chunkedSwitchTokens 절대 임계값 게이트 - 입력 < 50k 토큰이면 키워드·길이 트리거 무시하고 단일 호출 - 큰 컨텍스트 모델(131k+)에서 chunked 과잉 발동 방지 [v2.2.87] MAX_SECTIONS 5→3 cap - 총 호출 7회 → 5회 (outline + 3 section + polish) - 사용자 피드백 "6+회는 과하다" [v2.2.88] 이모지 사용 금지 룰 - polishPersona / directPersona / sectionPersona 모두 적용 - 사용자 피드백 "이모지는 시각 노이즈" [v2.2.89] 사용자 노출 설정 두 항목 - chunkedMaxSections config 신규 (default 3, 1~10 clamp) - MAX_SECTIONS_HARD_CEILING (10) 으로 안전망 격상 - Astra Settings 패널 "고급" 섹션에 두 슬라이더 노출 [v2.2.90] 가이드 문구 단순화 - "작은 모델은 낮추라" 문구 빼고 일관되게 50000 권장으로 [v2.2.91] 답변 포맷 가독성 fix - persona 의 "TL;DR" 표현 전부 "한 줄 요약" 으로 단일화 - stripMarkdownFormatting 에 헤더 후 빈 줄 강제 삽입 (marked.parse 가 라벨·본문을 별도 단락으로 인식 → 시각 분리) [테스트] 400/400 통과 (resilience_stress + chunked flow + MAX_SECTIONS cap 등) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+59
-18
@@ -138,11 +138,15 @@ export interface SectionOutline {
|
||||
* prompt picked here based on `options.config.role`.
|
||||
*/
|
||||
export class ChunkedWriter extends BaseAgent {
|
||||
/** Hard cap on section count regardless of what the outline model returns. */
|
||||
static readonly MAX_SECTIONS = 5;
|
||||
/**
|
||||
* Hard ceiling — *사용자 config 가 어떤 값이든 이걸 넘을 수 없다*. 안전망 의미.
|
||||
* 실제 사용 상한은 `getConfig().chunkedMaxSections` (default 3). 사용자가
|
||||
* Astra Settings 에서 1~10 사이 조정 가능, 이 상수가 그 위 절대 한도.
|
||||
*/
|
||||
static readonly MAX_SECTIONS_HARD_CEILING = 10;
|
||||
|
||||
private readonly outlinePersona = `You are a concise editor planning the structure of a Korean answer.
|
||||
Decide how many sections the answer needs (0..${ChunkedWriter.MAX_SECTIONS}). Pick the *smallest* number that still covers the user's request well — a short factual question should be 0-1 section, a meaty analysis 3-5.
|
||||
Decide how many sections the answer needs. The exact upper bound (MAX_N) is given in the user message below — never exceed it. Pick the *smallest* count that still covers the request well — a short factual question should be 0-1 section, a meaty analysis up to MAX_N.
|
||||
|
||||
Output STRICTLY a JSON array of objects: \`[{"heading": "...", "scope": "..."}]\`. No prose, no fences, no leading text.
|
||||
- 🟢 **빈 배열 \`[]\`** = "쪼갤 필요 없음". 사용자 질문이 간단해서 단일 LLM 호출로 즉답이 더 빠르고 자연스러울 때 (예: 단순 사실 질문, 짧은 코드 한 줄, 정의 묻기). 시스템이 이걸 받으면 outline·section 단계 건너뛰고 1회 직답으로 처리한다.
|
||||
@@ -151,7 +155,7 @@ Output STRICTLY a JSON array of objects: \`[{"heading": "...", "scope": "..."}]\
|
||||
|
||||
판단 기준:
|
||||
- 답변이 한 단락 (대략 3~5문장) 이내로 완결 가능 → \`[]\`
|
||||
- 본문 분석·여러 측면 비교·구조화된 보고서가 필요 → N개 섹션
|
||||
- 본문 분석·여러 측면 비교·구조화된 보고서가 필요 → N개 섹션 (단, MAX_N 절대 초과 금지)
|
||||
|
||||
If the user attached source content (article/code/log) the sections must cover *that content*, not analysis methodology.`;
|
||||
|
||||
@@ -164,24 +168,52 @@ If the user attached source content (article/code/log) the sections must cover *
|
||||
Rules:
|
||||
- Stay strictly inside this section's scope. Do NOT cover other outline entries.
|
||||
- Korean, plain markdown (no top-level "#" — the heading will be added by the joiner).
|
||||
- 이모지 / 이모티콘 사용 금지 (📌 🎯 💡 ✅ 등). 사용자 명시 피드백.
|
||||
- Pack facts. Avoid filler / executive summaries / closing remarks (the polish pass adds those).
|
||||
- If the user attached source content, cite from it; do not invent facts.
|
||||
- Do NOT output the heading itself — only the body of this section.`;
|
||||
|
||||
private readonly polishPersona = `You are the final editor producing the user-facing Korean answer from a sectioned draft.
|
||||
|
||||
Job:
|
||||
[Job]
|
||||
1. Fix typos, broken markdown, inconsistent terminology.
|
||||
2. Remove unsupported claims / hallucinations: if a sentence asserts a fact that isn't grounded in the user's request (or the earlier sections themselves), delete it. Better to be short than wrong.
|
||||
3. Smooth section transitions and remove duplicated information across sections.
|
||||
4. Open with the conclusion / key takeaway in the first sentence (no "분석해보겠습니다", no preamble).
|
||||
5. Preserve every factually grounded claim from the draft. Don't invent new facts.
|
||||
4. Preserve every factually grounded claim from the draft. Don't invent new facts.
|
||||
|
||||
Output rules:
|
||||
- Korean. Plain markdown. Section labels as plain text on their own line — no "#", "##".
|
||||
- Bullets with "- " only. No tables, no HTML, no triple-bar separators.
|
||||
- If the draft already has a sensible structure, keep it; only refactor when sections clearly overlap or contradict.
|
||||
- DO NOT emit hidden reasoning (<think>, "Thinking:", etc.).`;
|
||||
[정리·리뷰·요약 self-check — 출력 직전에 반드시 머릿속으로 통과]
|
||||
사용자가 원본을 첨부했거나 draft 가 원본 자료를 다루고 있을 때, 답변 출력 전에 다음 5가지를
|
||||
스스로 점검. 어기면 그 부분 삭제·수정 후 출력.
|
||||
(1) **사실 오류** — 원본의 고유명사·수치·비유·대응 관계가 정확히 옮겨졌나? 비유는
|
||||
방향이 뒤집히기 쉬움 (예: "A=자료실, B=공부방" 을 "B=자료실, A=공부방" 으로 뒤집기).
|
||||
(2) **없는 내용 추가 금지** — 원본에 없는 *인과·순서·단계 구분* 을 만들지 말 것. "따라서",
|
||||
"그러므로", "단계별로", "A → B → C 순으로" 같은 표현이 답변에 들어가려 하면, 원본에
|
||||
그 흐름이 *명시* 돼 있는지 확인. 없으면 그 표현 빼거나 "(정리자 추론)" 로 라벨링.
|
||||
(3) **원본 뉘앙스 유지** — "A 와 B 를 *동시에* 하라" 를 "A 후 B *순서로*" 로 바꾸는 식의
|
||||
양상(동시/순차/선택/필수) 변형 금지. 원본 표현 그대로 따옴표 인용 권장.
|
||||
(4) **중요도 비례** — 원본의 핵심이 답변에서도 부각되고, 부가 디테일은 그에 비례한 분량만.
|
||||
본문 길이가 아니라 중요도에 비례해서 요약.
|
||||
(5) **중복 제거** — 마지막 단락에서 앞 내용을 다시 요약·반복하지 말 것. 한 줄 요약이 있으면
|
||||
그 역할은 거기서 끝.
|
||||
|
||||
[답변 포맷 — Readability / Visibility]
|
||||
사용자가 명시 피드백을 줘서 다음 포맷을 따른다. 답변 *복잡도* 에 따라 두 분기:
|
||||
|
||||
A. **본문이 길거나 여러 단위의 정보를 다룰 때** (대략 본문 250자 이상 / 비교·분석·계획·리뷰 등):
|
||||
1. 답변 첫 섹션 헤더는 정확히 \`## 한 줄 요약\` 으로 시작 (한국어 사용자 친화 — "TL;DR", "Summary", "요약" 같은 다른 표현 금지). 결론·핵심을 1~3문장으로 압축. 사용자가 본문을 다 안 읽어도 take-away 가 잡혀야 함. **헤더에 이모지 절대 사용 금지**.
|
||||
2. 본문은 \`##\` 또는 \`###\` subheading 으로 시각 분할. 한 덩어리 prose 금지.
|
||||
3. 비교 가능한 정보(장단점·옵션·항목별 평가)는 마크다운 표로. 순서·체크리스트는 \`- \` 불릿.
|
||||
4. 첫 문장 자체가 결론이어야 한다는 룰은 유지 — 한 줄 요약 안에서 첫 문장이 결론.
|
||||
|
||||
B. **짧은 직답 (1~3문장 정도로 충분한 경우)**:
|
||||
1. 한 줄 요약 / subheading 강제 안 함. 그냥 결론으로 직답.
|
||||
2. 인사·서문 없이 첫 문장이 답. ("좋은 질문입니다" "분석해보겠습니다" 금지)
|
||||
|
||||
[공통 규칙]
|
||||
- 한국어 마크다운. 코드 블록은 실제 코드일 때만 (\`\`\`).
|
||||
- **이모지·이모티콘 절대 사용 금지** — 헤더든 본문이든 📌 🎯 💡 ✅ ⚠️ 🚀 ❓ 🧩 등 모두 금지. 사용자가 시각 노이즈로 느낀다고 명시 피드백. 정보는 텍스트·표·불릿으로만 전달.
|
||||
- 추론 과정·\`<think>\`·"Thinking Process:" 같은 hidden reasoning 절대 노출 금지.
|
||||
- 본문 분기를 LLM 자신이 판단 — 사용자가 모드 명시 안 함.`;
|
||||
|
||||
/**
|
||||
* Single-pass 직답 persona. 짧은 질문·정의 묻기·간단한 사실 확인처럼
|
||||
@@ -192,16 +224,25 @@ Output rules:
|
||||
|
||||
Rules:
|
||||
- 첫 문장이 결론 / 직답이다. "분석해보겠습니다" "좋은 질문입니다" 같은 서문 금지.
|
||||
- Korean. Plain markdown — "#", "##" 같은 헤더 금지, "- " bullet 만 허용. No tables, no HTML.
|
||||
- 짧은 질문엔 짧은 답. 한 문장으로 충분하면 한 문장.
|
||||
- Korean. Plain markdown.
|
||||
- **이모지 / 이모티콘 사용 금지** (📌 🎯 💡 ✅ ⚠️ 등 전부). 사용자 명시 피드백.
|
||||
- 짧은 질문엔 짧은 답. 한 문장으로 충분하면 한 문장. 1~3문장 직답이면 헤더·표 없이 그냥 prose 로.
|
||||
- 만약 답이 *예상보다 길어지거나* 여러 정보 단위를 다루게 되면 \`## 한 줄 요약\` 후 \`##\` subheading 으로 분할 (사용자가 Readability 위해 요청한 룰). 표·불릿도 활용. 헤더에 이모지 사용 금지.
|
||||
- 사용자가 본문(코드·기사·로그)을 첨부했으면 그 본문에서 인용. 본문에 없는 사실 지어내지 말 것.
|
||||
- 추론 과정·"Thinking:"·<think> 노출 금지.`;
|
||||
|
||||
async execute(input: string, context?: string, signal?: AbortSignal, options?: AgentExecuteOptions): Promise<string> {
|
||||
const role = (options?.config?.role as string | undefined) || 'section';
|
||||
switch (role) {
|
||||
case 'outline':
|
||||
return this.callLLM(this.outlinePersona, this.buildOutlinePrompt(input, context), signal);
|
||||
case 'outline': {
|
||||
// 호출자(AgentEngine)가 사용자 config 의 chunkedMaxSections 값을
|
||||
// options.config.maxSections 로 박아 넘긴다. 없으면 hard ceiling 사용
|
||||
// (실행 안 되어야 할 코드 경로 — 안전망).
|
||||
const maxN = (typeof options?.config?.maxSections === 'number' && options.config.maxSections > 0)
|
||||
? Math.min(ChunkedWriter.MAX_SECTIONS_HARD_CEILING, Math.floor(options.config.maxSections as number))
|
||||
: ChunkedWriter.MAX_SECTIONS_HARD_CEILING;
|
||||
return this.callLLM(this.outlinePersona, this.buildOutlinePrompt(input, context, maxN), signal);
|
||||
}
|
||||
case 'polish':
|
||||
return this.callLLM(this.polishPersona, this.buildPolishPrompt(input, options), signal);
|
||||
case 'direct':
|
||||
@@ -212,11 +253,11 @@ Rules:
|
||||
}
|
||||
}
|
||||
|
||||
private buildOutlinePrompt(userRequest: string, brainContext?: string): string {
|
||||
private buildOutlinePrompt(userRequest: string, brainContext?: string, maxN: number = ChunkedWriter.MAX_SECTIONS_HARD_CEILING): string {
|
||||
const ctx = brainContext && brainContext.trim().length > 0
|
||||
? `\n\n[보조 지식 컨텍스트 — 답변에 직접 인용하기보단 분할 결정에만 참고]\n${brainContext.substring(0, 1200)}`
|
||||
: '';
|
||||
return `[사용자 요청 — 본문이 포함돼 있다면 그게 1차 자료입니다]\n${userRequest}${ctx}\n\n위 요청에 대한 답변을 ${ChunkedWriter.MAX_SECTIONS}개 이내의 섹션으로 어떻게 나눌지 JSON 배열로만 출력하세요.`;
|
||||
return `[사용자 요청 — 본문이 포함돼 있다면 그게 1차 자료입니다]\n${userRequest}${ctx}\n\n[제약]\nMAX_N = ${maxN} — 절대 ${maxN}개 초과 금지.\n\n위 요청에 대한 답변을 ${maxN}개 이내의 섹션으로 어떻게 나눌지 JSON 배열로만 출력하세요.`;
|
||||
}
|
||||
|
||||
private buildSectionPrompt(input: string, brainContext?: string, options?: AgentExecuteOptions): string {
|
||||
|
||||
Reference in New Issue
Block a user