feat(wiki): /wikify 포맷 정본 통일 + 채팅 URL 실데이터 주입 (v2.2.229)
[포맷 통일 — Datacollect 가 정본] /wikify 와 Datacollect research 가 각자 포맷 사본을 들고 어긋났던 문제. 더 최신인 Datacollect 포맷을 wiki_format.mjs 정본으로 추출(브리지 측)하고, /wikify 는 GET /api/wiki/template 로 받아 소비. 구버전 브리지면 내장 사본 fallback (정본 v3.1과 동일 내용). 포맷 수정은 이제 wiki_format.mjs 한 곳. /wikify 가 정본을 따르며 고쳐진 것: - category "10_Wiki/Topics"(물리 경로 버그) → 논리 도메인 규칙 - 고정 신뢰도 B/0.8 → 소스 평가 동적 부여 (충돌 신뢰도 권고의 입력 품질) - aliases 빈 배열 → 동의어 3-8개 강제 (어휘갭 검색 보완) - "## 🔗 관련 문서 링크" → "## 🔗 지식 그래프" + 고아 방지 up-link - 인라인 [S#] 출처 인용 + 📚 출처 섹션, 비교표·코드 패턴 조건 섹션 [채팅 URL 접근 — 강제 주입 패턴 4번째 적용] 일반 채팅에 URL 을 주면 "접근 불가"라고 답하던 공백: urlContext 가 URL 감지 시 브리지 /api/web-extract(기존 /wikify 인프라 재사용)로 본문 추출 → 컨텍스트 주입 (8K 캡, 잘림 시 /wikify 안내). 실패 시 정직 블록 (브리지 확인 안내 + 추측 금지). 슬래시 명령은 제외 (자체 처리). 주입 성공 로그 포함. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -59,6 +59,7 @@ export const BRIDGE_API = {
|
||||
},
|
||||
wiki: {
|
||||
save: '/api/wiki/save',
|
||||
template: '/api/wiki/template',
|
||||
},
|
||||
lm: {
|
||||
proxy: '/api/lm',
|
||||
|
||||
@@ -428,12 +428,21 @@ async function wikifyOne(url: string, userContent: string, view: Webview | undef
|
||||
}
|
||||
|
||||
const model = (cfg.get<string>('defaultModel', '') || 'gemma4:e2b').trim();
|
||||
const wikiSystem = '당신은 지식 큐레이터입니다. 제공된 웹사이트 본문을 P-Reinforce v3.0 규격의 고밀도 위키 문서로 정리합니다. 본문에 없는 내용은 절대 지어내지 않으며, 모든 문서는 한국어로 작성합니다.';
|
||||
const wikiSystem = '당신은 지식 큐레이터입니다. 제공된 웹사이트 본문을 P-Reinforce 규격의 고밀도 위키 문서로 정리합니다. 본문에 없는 내용은 절대 지어내지 않으며, 모든 문서는 한국어로 작성합니다.';
|
||||
// 포맷 정본을 브리지에서 가져온다 (wiki_format.mjs — research 와 동일 포맷 보장).
|
||||
// 구버전 브리지(엔드포인트 없음)면 wikifyPrompt 의 내장 사본 fallback.
|
||||
let canonicalFormat: import('./prompts/wikifyPrompt').CanonicalWikiFormat | null = null;
|
||||
try {
|
||||
canonicalFormat = await bridgeFetch<any>(BRIDGE_API.wiki.template, { method: 'GET' }, { timeoutMs: 5000 });
|
||||
chunk(view, `📐 포맷 정본 v${canonicalFormat?.version ?? '?'} (브리지)\n`);
|
||||
} catch {
|
||||
chunk(view, `📐 포맷 내장 사본 사용 (브리지 템플릿 미제공 — 브리지 갱신 시 정본 적용)\n`);
|
||||
}
|
||||
chunk(view, `🧪 P-Reinforce 위키 합성 (모델 \`${model}\`)…`);
|
||||
let report: string;
|
||||
try {
|
||||
const synthT0 = Date.now();
|
||||
report = await callLmSynthesis(buildWikifyPrompt(data, userContent), wikiSystem);
|
||||
report = await callLmSynthesis(buildWikifyPrompt(data, userContent, canonicalFormat), wikiSystem);
|
||||
if (!report) throw new Error('LLM 응답이 비어 있습니다.');
|
||||
report = report.replace(/\[\[([^\[\]]+?)\](?!\])/g, '[[$1]]');
|
||||
chunk(view, ` ✓ (${Math.round((Date.now() - synthT0) / 1000)}s)\n\n`);
|
||||
|
||||
@@ -1,8 +1,119 @@
|
||||
/**
|
||||
* 추출된 웹사이트 본문 → Datacollect Research(P-Reinforce v3.0)와 동일한 위키 문서
|
||||
* 프롬프트. Bridge의 /api/research/synthesize 템플릿을 웹 본문 소스용으로 이식.
|
||||
* 추출된 웹사이트 본문 → P-Reinforce 위키 문서 프롬프트.
|
||||
*
|
||||
* 포맷 정본은 Datacollect 브리지의 wiki_format.mjs — 핸들러가
|
||||
* GET /api/wiki/template 로 받아 `canonical` 로 주입한다 (양쪽 포맷 통일).
|
||||
* 브리지가 구버전(엔드포인트 없음)일 때만 아래 내장 사본(fallback)을 쓴다.
|
||||
* 내장 사본은 정본을 따라 갱신하되, 차이가 생기면 정본이 항상 옳다.
|
||||
*/
|
||||
export function buildWikifyPrompt(extracted: any, userContent: string): string {
|
||||
|
||||
export interface CanonicalWikiFormat {
|
||||
version: string;
|
||||
/** 규칙 7-15 (별칭·출처·동적 신뢰도·그래프·논리 도메인 분류…), {{ROOT}} placeholder. */
|
||||
commonRules: string;
|
||||
/** Frontmatter 골격 — {{ID}} {{TITLE}} {{TODAY}} {{TAGS}} {{SOURCES}}. */
|
||||
frontmatter: string;
|
||||
/** 본문 섹션 골격 — {{TOPIC}} {{ROOT}} {{TODAY}} {{ORIGIN}}. */
|
||||
sections: string;
|
||||
}
|
||||
|
||||
/** 정본 골격의 placeholder 치환. */
|
||||
function fill(tpl: string, vars: Record<string, string>): string {
|
||||
return tpl.replace(/\{\{(\w+)\}\}/g, (_, k) => vars[k] ?? '');
|
||||
}
|
||||
|
||||
// ── 내장 fallback (정본 wiki_format.mjs v3.1 사본 — 브리지 구버전 대비) ────────
|
||||
|
||||
const FALLBACK_VERSION = '3.1-embedded';
|
||||
|
||||
const FALLBACK_COMMON_RULES = `
|
||||
7. [별칭] Frontmatter 'aliases'에 동의어·약어·영문/국문 표기 3-8개를 YAML 리스트로 채우시오.
|
||||
8. [출처] '세부 내용' 주장 끝에 [S1],[S2] 인라인 표기 + 문서 끝 '## 📚 출처'에 번호별 제목/URL 나열, Frontmatter 'raw_sources'에도 동일 기입(placeholder 금지).
|
||||
9. [신뢰도] 소스 평가로 source_trust_level(S/A/B/C/D)·confidence_score(0.00-1.00) 부여(문서마다 다르게). 이 값은 지식 충돌 시 우선순위 판단에 사용되므로 정직하게 평가하시오.
|
||||
10. [그래프] '## 🔗 지식 그래프' 항상 작성: 루트 [[{{ROOT}}]] + 관련 개념 2개+ 링크.
|
||||
11. [분류] category는 폴더경로 말고 논리 도메인(AI_and_ML/Frontend/Architecture 등, 없으면 "Topic").
|
||||
12. [비교] 대안·경쟁 기술이 있으면 '## ⚖️ 비교 및 선택 기준'에 표(|항목|장점|단점|언제 선택|), 없으면 생략.
|
||||
13. [코드] 소스에 코드가 있으면 '## 💻 코드 패턴'에 최소 스니펫(언어/버전), 없으면 "소스에 코드 예시 없음".
|
||||
14. [완결성] 빈 섹션·(TODO)·플레이스홀더 금지. 근거 없으면 "소스에서 확인되지 않음".
|
||||
15. [인용정렬] 인라인 [S#]는 '## 📚 출처' 번호와 1부터 순차 일치.`;
|
||||
|
||||
const FALLBACK_FRONTMATTER = `---
|
||||
id: {{ID}}
|
||||
title: "{{TITLE}}"
|
||||
category: "Topic"
|
||||
status: "draft"
|
||||
verification_status: "conceptual"
|
||||
canonical_id: ""
|
||||
aliases: []
|
||||
duplicate_of: ""
|
||||
source_trust_level: "B"
|
||||
confidence_score: 0.85
|
||||
created_at: {{TODAY}}
|
||||
updated_at: {{TODAY}}
|
||||
review_reason: ""
|
||||
merge_history: []
|
||||
tags: [{{TAGS}}]
|
||||
raw_sources: {{SOURCES}}
|
||||
applied_in: []
|
||||
github_commit: ""
|
||||
---`;
|
||||
|
||||
const FALLBACK_SECTIONS = `# [[{{TOPIC}}]]
|
||||
|
||||
## 🎯 한 줄 통찰 (One-line insight)
|
||||
(이 주제의 핵심 가치를 관통하는 강력한 한 줄 정의)
|
||||
|
||||
## 🧠 핵심 개념 (Core concepts)
|
||||
(이 주제를 구성하는 가장 중요한 3-5가지 핵심 기둥/개념)
|
||||
|
||||
## 🧩 추출된 패턴 (Extracted patterns)
|
||||
(소스에서 발견된 반복되는 구조, 전략, 설계 패턴 또는 휴리스틱)
|
||||
|
||||
## ⚖️ 비교 및 선택 기준 (Comparison & decision criteria)
|
||||
(이 주제에 대안·경쟁 기술이 있을 때만 작성. 대안이 없으면 이 섹션 전체를 생략.)
|
||||
|
||||
| 항목 (Option) | 장점 | 단점 | 언제 선택 |
|
||||
|---|---|---|---|
|
||||
|
||||
## 📖 세부 내용 (Details)
|
||||
(소스에서 합성된 상세하고 전문적인 설명. 논리적 단락이나 글머리 기호로 구분하시오.)
|
||||
|
||||
## ⚖️ 모순 및 업데이트 (Contradictions & updates)
|
||||
(소스 내에서 상충되는 정보가 있거나, 기존 상식과 다른 최신 정보가 있다면 서술하시오.)
|
||||
|
||||
## 🛠️ 적용 사례 (Applied in summary)
|
||||
(소스에서 실제 적용된 코드·커밋·프로젝트·결정이 발견되면 요약. 없으면 "현재 발견된 실제 적용 사례가 없습니다.")
|
||||
|
||||
## 💻 코드 패턴 (Code patterns)
|
||||
(소스에 코드·구현·API 사용법이 있으면 최소 스니펫을 언어/버전과 함께. 없으면 "소스에 코드 예시 없음".)
|
||||
|
||||
## ✅ 검증 상태 및 신뢰도
|
||||
- **상태:** draft
|
||||
- **검증 단계:** conceptual (실제 적용 사례 발견 시 applied/validated로 승격 가능)
|
||||
- **출처 신뢰도:** (소스 평가에 따라 S/A/B/C/D 중 부여 — Frontmatter source_trust_level과 일치)
|
||||
- **신뢰 점수:** (0.00-1.00 평가값 — Frontmatter confidence_score와 일치)
|
||||
- **중복 검사 결과:** 신규 생성 (New discovery)
|
||||
|
||||
## 🔗 지식 그래프 (Knowledge Graph)
|
||||
(항상 작성 — 이 문서를 위키 그래프에 연결한다. 고아 문서 금지.)
|
||||
- **상위/루트:** [[{{ROOT}}]]
|
||||
- **관련 개념:** (직접 관련된 핵심 개념 2개 이상을 [[대괄호 두 개]]로.)
|
||||
- **참조 맥락:** (이 지식이 어떤 작업·주제·결정에서 참조될지 한 줄로.)
|
||||
|
||||
## 📚 출처 (Sources)
|
||||
('세부 내용'의 [S1], [S2] 인라인 표기와 번호를 일치시키시오.)
|
||||
- [S1]
|
||||
|
||||
## 📝 변경 이력 (Change history)
|
||||
- {{TODAY}}: {{ORIGIN}}`;
|
||||
|
||||
// ── 프롬프트 빌드 ────────────────────────────────────────────────────────────
|
||||
|
||||
export function buildWikifyPrompt(extracted: any, userContent: string, canonical?: CanonicalWikiFormat | null): string {
|
||||
const fmt: CanonicalWikiFormat = canonical && canonical.commonRules && canonical.frontmatter && canonical.sections
|
||||
? canonical
|
||||
: { version: FALLBACK_VERSION, commonRules: FALLBACK_COMMON_RULES, frontmatter: FALLBACK_FRONTMATTER, sections: FALLBACK_SECTIONS };
|
||||
|
||||
const today = new Date().toISOString().slice(0, 10);
|
||||
const topic = (userContent.trim() || extracted?.title || extracted?.url || '웹사이트 지식').trim();
|
||||
const url = extracted?.url || '';
|
||||
@@ -10,15 +121,23 @@ export function buildWikifyPrompt(extracted: any, userContent: string): string {
|
||||
const headings = Array.isArray(extracted?.headings) ? extracted.headings.slice(0, 40) : [];
|
||||
const body = String(extracted?.text || '').slice(0, 30000);
|
||||
|
||||
return `임무: 아래 웹사이트에서 추출한 본문을 근거로 P-Reinforce v3.0 규격에 맞춰 고밀도 지식 문서를 작성하시오.
|
||||
const vars = {
|
||||
ID: idSlug, TITLE: topic, TOPIC: topic, ROOT: topic, TODAY: today,
|
||||
TAGS: '"web", "wikify"', SOURCES: `["${url}"]`,
|
||||
ORIGIN: `Astra /wikify 로 ${url} 본문에서 초안 생성.`,
|
||||
};
|
||||
|
||||
return `임무: 아래 웹사이트에서 추출한 본문을 근거로 P-Reinforce v${fmt.version.replace('-embedded', '')} 규격에 맞춰 고밀도 지식 문서를 작성하시오.
|
||||
주제: '${topic}'
|
||||
|
||||
[필수 규칙]
|
||||
1. 반드시 아래 [웹사이트 본문]의 내용만을 근거로 작성하시오. 외부 지식을 절대 섞지 마시오.
|
||||
2. 반드시 아래 Markdown 템플릿과 Frontmatter 형식을 정확히 따르시오. 섹션 이름과 구조를 변경하지 마시오.
|
||||
3. 본문에 없는 정보는 지어내지 말고, 해당 섹션에 "본문에서 확인되지 않음"이라고 명시하시오.
|
||||
4. 한국어로 작성하시오. 관련 개념·고유명사는 [[대괄호 두 개]]로 감싸 위키 링크로 만드시오. 위키 링크는 반드시 \`[[\` 로 열고 \`]]\` 로 닫으시오 (닫는 대괄호를 빠뜨리지 마시오).
|
||||
5. 원문이 JSON Schema·API 명세·기술 스펙·설정 레퍼런스인 경우, '📖 세부 내용'에 등장하는 모든 필드·속성·파라미터를 **누락 없이** 마크다운 표로 정리하시오. 표 컬럼은 [필드 | 타입 | 필수/선택 | 제약·설명]. 원문의 \`required\` 배열에 있는 항목만 '필수'로 표기하고, 그 목록을 임의로 바꾸거나 추가하지 마시오. \`additionalProperties\`·\`const\`·\`enum\` 같은 제약과 중첩 객체 구조도 원문 그대로 반영하시오. 최상위 필드를 하나라도 빠뜨리지 마시오.
|
||||
3. 본문에 없는 정보는 지어내지 말고, 해당 섹션에 "소스에서 확인되지 않음"이라고 명시하시오.
|
||||
4. 한국어로 작성하시오. 관련 개념·고유명사는 [[대괄호 두 개]]로 감싸 위키 링크로 만드시오. 위키 링크는 반드시 \`[[\` 로 열고 \`]]\` 로 닫으시오.
|
||||
5. 원문이 JSON Schema·API 명세·기술 스펙·설정 레퍼런스인 경우, '📖 세부 내용'에 등장하는 모든 필드·속성·파라미터를 **누락 없이** 마크다운 표로 정리하시오. 표 컬럼은 [필드 | 타입 | 필수/선택 | 제약·설명]. 원문의 \`required\` 배열에 있는 항목만 '필수'로 표기하고, 그 목록을 임의로 바꾸거나 추가하지 마시오.
|
||||
6. 이 문서의 출처는 웹 본문 1건이므로 '## 📚 출처'의 [S1]은 위 URL을 가리키며, source_trust_level 은 사이트 성격(공식 문서/학술 S~A, 기술 블로그 B, 커뮤니티/익명 C~D)으로 평가하시오.
|
||||
${fill(fmt.commonRules, vars)}
|
||||
|
||||
[웹사이트 메타]
|
||||
- URL: ${url}
|
||||
@@ -33,56 +152,7 @@ ${body}
|
||||
|
||||
[출력 템플릿 - 이 형식을 정확히 따르시오]
|
||||
|
||||
---
|
||||
id: ${idSlug}
|
||||
title: "${topic}"
|
||||
category: "10_Wiki/Topics"
|
||||
status: "draft"
|
||||
verification_status: "conceptual"
|
||||
canonical_id: ""
|
||||
aliases: []
|
||||
duplicate_of: ""
|
||||
source_trust_level: "B"
|
||||
confidence_score: 0.8
|
||||
created_at: ${today}
|
||||
updated_at: ${today}
|
||||
review_reason: ""
|
||||
merge_history: []
|
||||
tags: ["web", "wikify"]
|
||||
raw_sources: ["${url}"]
|
||||
applied_in: []
|
||||
github_commit: ""
|
||||
---
|
||||
${fill(fmt.frontmatter, vars)}
|
||||
|
||||
# [[${topic}]]
|
||||
|
||||
## 🎯 한 줄 통찰 (One-line insight)
|
||||
(이 웹사이트/주제의 핵심 가치를 관통하는 강력한 한 줄 정의)
|
||||
|
||||
## 🧠 핵심 개념 (Core concepts)
|
||||
(본문을 구성하는 가장 중요한 3-5가지 핵심 개념/기둥)
|
||||
|
||||
## 🧩 추출된 패턴 (Extracted patterns)
|
||||
(본문에서 발견된 반복되는 구조, 전략, 주장 또는 접근법)
|
||||
|
||||
## 📖 세부 내용 (Details)
|
||||
(본문에서 합성된 상세하고 전문적인 설명. 논리적 단락이나 글머리 기호로 구분하시오. 원문이 명세·스키마·API 레퍼런스라면 위 규칙 5에 따라 모든 필드를 표로 빠짐없이 정리하시오.)
|
||||
|
||||
## ⚖️ 모순 및 업데이트 (Contradictions & updates)
|
||||
(본문 내에서 상충되는 정보나 주목할 최신 정보가 있다면 서술. 없으면 "본문에서 확인되지 않음".)
|
||||
|
||||
## 🛠️ 적용 사례 (Applied in summary)
|
||||
(본문에 구체적 사례·수치·제품·프로젝트·의사결정이 있으면 요약하여 기술. 없으면 "본문에서 확인되지 않음".)
|
||||
|
||||
## ✅ 검증 상태 및 신뢰도
|
||||
- **상태:** draft
|
||||
- **검증 단계:** conceptual
|
||||
- **출처 신뢰도:** B (Primary Source — 웹사이트 본문 직접 추출)
|
||||
- **중복 검사 결과:** 신규 생성 (New discovery)
|
||||
|
||||
## 🔗 관련 문서 링크 (Related document links)
|
||||
(이 주제와 직접 연결되는 핵심 개념 3-7개를 [[위키링크]]로 제시하고, 각 링크마다 연결 이유를 한 줄로 적으시오. 본문에 등장한 개념을 우선 사용.)
|
||||
|
||||
## 📝 변경 이력 (Change history)
|
||||
- ${today}: Astra /wikify 로 ${url} 본문에서 초안 생성.`;
|
||||
${fill(fmt.sections, vars)}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user