fix(vision): 본문 빈 케이스 prompt 강화 — null title/summary 회귀
gemma4:26b 가 본문 없이 이미지만 있을 때 title=null/summary=null 반환. prompt 가 "(이미지만 있음)" 만 던지는 게 신호 약함. 본문 비었으면 이미지 내용으로 한국어 채우라고 명시 + "null 반환 금지" 규칙 추가. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,16 +7,21 @@ export function buildVisionPrompt(
|
|||||||
// v0.3.11 — vision model 이 'format:json' constraint 를 부분적으로만 따르는 경우가
|
// v0.3.11 — vision model 이 'format:json' constraint 를 부분적으로만 따르는 경우가
|
||||||
// 잦음 (특히 gemma3 vision). title 한국어 + JSON only 를 prompt 에서 명시 강조,
|
// 잦음 (특히 gemma3 vision). title 한국어 + JSON only 를 prompt 에서 명시 강조,
|
||||||
// markdown fence 금지 표기로 schema parse 통과율 개선.
|
// markdown fence 금지 표기로 schema parse 통과율 개선.
|
||||||
return `다음 메모와 첨부 이미지를 종합 분석해 한국어로 요약하세요.
|
// 본문 비었으면 이미지만 보고 title/summary 채우도록 명시. 그냥 "(이미지만 있음)" 만
|
||||||
|
// 던지면 모델이 null 반환 (gemma4:26b 확인). 본문 있으면 본문 우선, 없으면 이미지 묘사.
|
||||||
|
const bodySection = text
|
||||||
|
? `메모 본문:\n${text}\n\n첨부 이미지도 함께 분석해 요약에 반영하세요.`
|
||||||
|
: `본문이 없으니 첨부 이미지의 내용 (텍스트/사람/장면/문서 등) 을 보고 title 과 summary 를 한국어로 작성하세요. null 반환 금지.`;
|
||||||
|
|
||||||
메모 본문 (비어 있을 수 있음):
|
return `다음 메모를 한국어로 분석해 JSON 으로 정리하세요.
|
||||||
${text || '(이미지만 있음)'}
|
|
||||||
|
|
||||||
규칙:
|
${bodySection}
|
||||||
- "title" 은 반드시 한국어로 (영어 금지). 60자 이내.
|
|
||||||
- "summary" 는 3줄. 이미지 시각 정보 (텍스트/사람/장면) 포함.
|
규칙 (위반 시 재시도):
|
||||||
- "tags" 는 영문 kebab-case (예: meeting-notes), 최대 3개. 한국어 태그 금지.
|
- "title": 한국어 문자열 필수, null 금지. 60자 이내. 영어 단독 금지.
|
||||||
- "due_date" 는 ISO 형식 YYYY-MM-DD 또는 null.
|
- "summary": 한국어 문자열 필수, null 금지. 3줄. 이미지 시각 정보 (텍스트/사람/장면) 포함.
|
||||||
|
- "tags": 영문 kebab-case 배열 (예: ["meeting-notes"]), 최대 3개. 한국어 태그 금지. 없으면 [].
|
||||||
|
- "due_date": ISO YYYY-MM-DD 또는 null. 빈 문자열 금지.
|
||||||
|
|
||||||
오직 JSON 객체 하나만 출력. markdown 코드 펜스 (\`\`\`) / 설명 prose 금지.
|
오직 JSON 객체 하나만 출력. markdown 코드 펜스 (\`\`\`) / 설명 prose 금지.
|
||||||
출력 형식: {"title":"...","summary":"...","tags":[],"due_date":null}
|
출력 형식: {"title":"...","summary":"...","tags":[],"due_date":null}
|
||||||
|
|||||||
@@ -15,9 +15,16 @@ describe('buildVisionPrompt', () => {
|
|||||||
expect(result).toContain('work, meeting, project, todo');
|
expect(result).toContain('work, meeting, project, todo');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('uses (이미지만 있음) placeholder when text is empty', () => {
|
it('본문 빈 경우 이미지 묘사 + null 금지 명시 (v0.3.14+)', () => {
|
||||||
const result = buildVisionPrompt('', '2026-05-09', [], []);
|
const result = buildVisionPrompt('', '2026-05-09', [], []);
|
||||||
expect(result).toContain('(이미지만 있음)');
|
expect(result).toContain('본문이 없으니');
|
||||||
expect(result).not.toContain('\n\n\n'); // no double-blank from empty text
|
expect(result).toContain('null 반환 금지');
|
||||||
|
expect(result).not.toContain('메모 본문:\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('본문 있는 경우 본문 우선 + 이미지 함께 분석 명시', () => {
|
||||||
|
const result = buildVisionPrompt('회의 메모', '2026-05-09', [], []);
|
||||||
|
expect(result).toContain('메모 본문:\n회의 메모');
|
||||||
|
expect(result).toContain('첨부 이미지도 함께 분석');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user