mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
feat(wiki): slug-first prompt contract and same-batch link safety
This commit is contained in:
parent
2b3c068db9
commit
105b075f13
@ -1172,10 +1172,20 @@ public class WikiProcessingService {
|
||||
if (wasCreated) {
|
||||
created.incrementAndGet();
|
||||
totalCreated++;
|
||||
// Append to liveIndex so next sub-batch can link to this page
|
||||
// Append to liveIndex so the NEXT sub-batch can link to this freshly
|
||||
// created page. Mirrors the slug-first row format produced by
|
||||
// {@link #buildExistingPagesIndex}: `[[slug]] — title — summary`.
|
||||
// Keeps the LLM's view of the index uniformly slug-first across
|
||||
// pre-existing rows and just-created rows.
|
||||
String briefSummary = pageSummary.length() > 100
|
||||
? pageSummary.substring(0, 100) : pageSummary;
|
||||
liveIndex.append("\n- ").append(slug).append(": ").append(briefSummary);
|
||||
liveIndex.append("\n- [[").append(slug).append("]]");
|
||||
if (title != null && !title.isBlank()) {
|
||||
liveIndex.append(" — ").append(title);
|
||||
}
|
||||
if (briefSummary != null && !briefSummary.isBlank()) {
|
||||
liveIndex.append(" — ").append(briefSummary);
|
||||
}
|
||||
}
|
||||
ok = true;
|
||||
} catch (RuntimeException e) {
|
||||
@ -1612,7 +1622,23 @@ public class WikiProcessingService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建已有 Wiki 页面索引(供 LLM 参考)
|
||||
* Build the "existing pages" index that the LLM consults when picking
|
||||
* cross-references during page generation / merge / compile.
|
||||
* <p>
|
||||
* Slug-first format — each line begins with {@code [[slug]]} so the
|
||||
* model has exactly one syntactically-valid target shape to copy. Title
|
||||
* and summary follow as semantic context, separated by em-dashes, so the
|
||||
* model can pick a relevant target without being confused about whether
|
||||
* to write the title or the slug. The earlier "**[[Title]]** (slug: `x`)"
|
||||
* format exposed two candidate target strings on every row, which let
|
||||
* the model write {@code [[Title]]} freely and produced the lint noise
|
||||
* this RFC exists to close.
|
||||
* <p>
|
||||
* Manual-edit and archived markers stay as plain-text trailing tags so
|
||||
* they don't drift into the link form. The lint downstream treats a
|
||||
* title-only match as a soft warning in v1; the long-term direction is
|
||||
* to delete the title-fallback once content has been regenerated under
|
||||
* the slug-first prompt.
|
||||
*/
|
||||
private String buildExistingPagesIndex(Long kbId) {
|
||||
List<WikiPageEntity> summaries = pageService.listSummaries(kbId);
|
||||
@ -1622,12 +1648,17 @@ public class WikiProcessingService {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (WikiPageEntity page : summaries) {
|
||||
sb.append("- **[[").append(page.getTitle()).append("]]** (slug: `").append(page.getSlug()).append("`");
|
||||
if ("manual".equals(page.getLastUpdatedBy())) {
|
||||
sb.append(", 手动编辑");
|
||||
sb.append("- [[").append(page.getSlug()).append("]]");
|
||||
if (page.getTitle() != null && !page.getTitle().isBlank()) {
|
||||
sb.append(" — ").append(page.getTitle());
|
||||
}
|
||||
if ("manual".equals(page.getLastUpdatedBy())) {
|
||||
sb.append(" (手动编辑)");
|
||||
}
|
||||
String summary = page.getSummary();
|
||||
if (summary != null && !summary.isBlank()) {
|
||||
sb.append(" — ").append(summary);
|
||||
}
|
||||
sb.append("): ");
|
||||
sb.append(page.getSummary() != null ? page.getSummary() : "无摘要");
|
||||
sb.append("\n");
|
||||
}
|
||||
return sb.toString().trim();
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
- 读取 `pages_to_create` 数组,里面包含若干页面的 slug / title / summary
|
||||
- 从原始材料里抽取与每个页面主题相关的信息
|
||||
- 为每个页面生成完整的 markdown 内容
|
||||
- 在内容里使用 [[页面标题]] 双向链接到其他相关页面(已有页面和同批次将创建的页面均可)
|
||||
- 在内容里用 [[slug]] 双向链接到其他相关页面
|
||||
|
||||
## 你不做什么
|
||||
|
||||
@ -17,10 +17,20 @@
|
||||
|
||||
- 内容开头先一段话摘要(与 metadata 的 summary 一致或更详细)
|
||||
- 使用 Markdown 标题(## / ###)组织章节
|
||||
- 使用 [[页面标题]] 链接到其他相关页面
|
||||
- 用 [[slug]] 链接到其他相关页面
|
||||
- 长度控制在 500~2000 字,不要为了凑字数而拖沓
|
||||
- 内容至少包含 3 句实质信息
|
||||
|
||||
## 链接(**单一契约,必须严格遵守**)
|
||||
|
||||
- 只允许两种形态:
|
||||
- `[[slug]]` —— 显示文本默认为目标页标题
|
||||
- `[[slug|显示文本]]` —— 显示文本自定义
|
||||
- slug **必须**来自以下两类来源之一:
|
||||
- **已有 Wiki 页面索引**(user prompt 中列出)—— 这些链接是**强保证**,slug 100% 可用
|
||||
- **本批次同时创建的页面**(即 `pages_to_create` 数组中的 slug)—— 这类链接**不保证成功**:本批次中的页面可能因去重 / 合并 / 失败而最终未落库,导致链接转为死链;这是预期行为,系统会在写入后由 lint 标记并由人工修复
|
||||
- 禁止发明上述两类来源之外的 slug;禁止写 `[[页面标题]]` 形态 —— 系统按 slug 严格匹配,写标题会被识别为死链
|
||||
|
||||
## 输出格式(严格遵守)
|
||||
|
||||
每个页面输出一个 FILE 块,格式如下:
|
||||
|
||||
@ -4,11 +4,13 @@
|
||||
|
||||
{document_map_section}
|
||||
|
||||
## 已有 Wiki 页面索引(用于建立 [[链接]])
|
||||
## 已有 Wiki 页面索引(强保证,可直接链接;每行格式 `[[slug]] — 标题 — 摘要`)
|
||||
|
||||
{existing_pages}
|
||||
|
||||
## 待生成页面列表
|
||||
## 本批次将一并创建的页面(计划中,可能可被链接;slug 见下方 pages_to_create)
|
||||
|
||||
链接到这一类的页面**不保证成功**:本批次中的页面可能因去重 / 合并 / 失败而最终未落库,对应的 `[[slug]]` 会在 lint 中被标记为死链,由人工后续修复。需要交叉引用时**优先**链接到上文「已有 Wiki 页面索引」中的页面。
|
||||
|
||||
```json
|
||||
{pages_to_create}
|
||||
@ -25,5 +27,5 @@
|
||||
请为上面 `pages_to_create` 数组中的**每一个页面**生成完整的 markdown 内容。
|
||||
- 按 system 中规定的 FILE 块格式输出,每个页面一个 FILE 块
|
||||
- 每个页面的内容必须基于原始材料中与该主题相关的信息
|
||||
- 适当使用 [[页面标题]] 链接到相关页面(同批次内其他页面也可链接)
|
||||
- 适当使用 [[slug]] 链接到相关页面(slug 必须出自上文「已有 Wiki 页面索引」或本批次 `pages_to_create` 中的 slug)
|
||||
- 不要遗漏任何一个页面
|
||||
|
||||
@ -9,7 +9,7 @@ Strict output contract — return ONLY this JSON object, nothing else:
|
||||
|
||||
Rules:
|
||||
- Do not invent facts beyond the supplied evidence. If something is not in the evidence, do not assert it.
|
||||
- Use [[wikilinks]] when an evidence breadcrumb names a related concept; alias form [[slug|display]] is fine.
|
||||
- Wikilink contract: use [[slug]] or [[slug|display text]]. The slug MUST come from the existing-pages index supplied in the user prompt. Never invent a slug that is not in that index. If the evidence names a concept that has no existing page, write it as plain text — do not guess a slug.
|
||||
- Keep summary concise (single paragraph, <= 300 characters).
|
||||
- content must be Markdown with at least one ## header.
|
||||
- Do not include any prose outside the JSON object.
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
- 读懂该页 metadata 描述的主题
|
||||
- 从原始材料里抽取与该主题相关的信息
|
||||
- 为这一个页面生成完整的 markdown 内容
|
||||
- 在内容里使用 [[页面标题]] 双向链接到其他相关页面(无论是新建中还是已有)
|
||||
- 在内容里用 [[slug]] 双向链接到其他相关页面
|
||||
|
||||
## 你不做什么
|
||||
|
||||
@ -17,19 +17,23 @@
|
||||
|
||||
- 内容开头先一段话摘要(与 metadata 的 summary 一致或更详细)
|
||||
- 使用 Markdown 标题(## / ###)组织
|
||||
- 使用 [[页面标题]] 双向链接到其他页面
|
||||
- 在正文中用 [[slug]] 链接到其他页面
|
||||
- 每个不同的子主题用 ### 章节区分
|
||||
- 末尾可附"参见"段落,列出相关 [[链接]]
|
||||
- 末尾可附"参见"段落,列出相关 [[slug]]
|
||||
|
||||
## 长度
|
||||
|
||||
- 单页内容控制在合理范围(一般 500~2000 字),不要为了凑长度而拖沓
|
||||
- 内容应有 3 句以上的实质信息
|
||||
|
||||
## 链接
|
||||
## 链接(**单一契约,必须严格遵守**)
|
||||
|
||||
- 已有页面索引中其他页的 slug 都可用 [[title]] 链接
|
||||
- 同批次将创建的其他页面也可以链接(按 metadata 中的 title)
|
||||
- 只允许两种形态:
|
||||
- `[[slug]]` —— 显示文本默认为目标页标题
|
||||
- `[[slug|显示文本]]` —— 显示文本自定义
|
||||
- `slug` **必须**来自上文"已有 Wiki 页面索引"段落中列出的 slug,**禁止发明**索引中不存在的 slug
|
||||
- 不要写 `[[页面标题]]`、`[[Title]]` 这种形态 —— 系统会按 slug 严格匹配,写标题会被识别为死链
|
||||
- 如果某个相关概念没有对应的页面,不要硬塞链接,直接用普通文本描述即可
|
||||
|
||||
## 语言
|
||||
|
||||
@ -42,7 +46,7 @@
|
||||
{
|
||||
"slug": "page-slug",
|
||||
"title": "页面标题",
|
||||
"content": "## 标题\n\n摘要段落...\n\n### 详细内容\n...\n\n参见:[[相关页面]]",
|
||||
"content": "## 标题\n\n摘要段落...\n\n### 详细内容\n...\n\n参见:[[related-slug]]",
|
||||
"summary": "一段话摘要"
|
||||
}
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
{config}
|
||||
|
||||
## 已有 Wiki 页面索引(用于建立 [[链接]])
|
||||
## 已有 Wiki 页面索引(用于建立 [[slug]] 链接;每行格式 `[[slug]] — 标题 — 摘要`)
|
||||
|
||||
{existing_pages}
|
||||
|
||||
@ -23,4 +23,4 @@ slug:`{page_slug}`
|
||||
请为上面 metadata 描述的**这一个页面**生成完整的 markdown 内容(按 system 中规定的 JSON 格式输出)。
|
||||
- 只生成这一个 slug 对应的页面,不要顺便生成其他页面
|
||||
- 内容必须基于原始材料中与该主题相关的信息
|
||||
- 适当使用 [[页面标题]] 链接到其他相关页面
|
||||
- 适当使用 [[slug]] 链接到其他相关页面(slug 必须来自上文索引)
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
1. **阅读并理解原始材料**
|
||||
2. **创建新的 Wiki 页面**:每个页面聚焦一个概念、实体或主题
|
||||
3. **更新已有 Wiki 页面**:当新材料包含已有页面的相关信息时,合并更新
|
||||
4. **建立双向链接**:使用 [[页面标题]] 语法在页面间建立交叉引用
|
||||
4. **建立双向链接**:用 [[slug]] 语法在页面间建立交叉引用(slug 必须存在于本批次的 pages 或上下文索引中)
|
||||
|
||||
## 页面质量标准
|
||||
|
||||
@ -19,7 +19,8 @@
|
||||
|
||||
- 每个页面以一段话摘要开头
|
||||
- 使用清晰的 Markdown 标题(## 和 ###)组织内容
|
||||
- 在提到相关概念时使用 [[链接标题]] 链接到其他页面
|
||||
- 引用相关概念时**只能**用 [[slug]] 或 [[slug|显示文本]] 形态;slug 必须是本批次输出中的 slug,或上文索引中已存在的 slug
|
||||
- 禁止写 [[页面标题]] 这种形态 —— 系统按 slug 严格匹配,写标题会被识别为死链
|
||||
- 页面标题应简洁准确,反映核心内容
|
||||
- slug(URL 标识符)使用小写字母、数字和连字符
|
||||
|
||||
@ -45,7 +46,7 @@
|
||||
{
|
||||
"slug": "concept-name",
|
||||
"title": "概念名称",
|
||||
"content": "## 概念名称\n\n一段话摘要...\n\n### 详细内容\n...\n\n参见:[[相关主题]]",
|
||||
"content": "## 概念名称\n\n一段话摘要...\n\n### 详细内容\n...\n\n参见:[[related-concept-slug]]",
|
||||
"summary": "一段话摘要"
|
||||
}
|
||||
],
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
{config}
|
||||
|
||||
## 已有 Wiki 页面索引
|
||||
## 已有 Wiki 页面索引(用于建立 [[slug]] 链接;每行格式 `[[slug]] — 标题 — 摘要`)
|
||||
|
||||
{existing_pages}
|
||||
|
||||
@ -17,5 +17,5 @@
|
||||
请根据以上原始材料:
|
||||
1. 根据材料内容的丰富程度,创建合适数量的高质量页面(不追求数量,宁少勿多)
|
||||
2. 如果已有页面与新材料相关,更新这些页面(不要重复创建已有概念的页面)
|
||||
3. 确保页面间有充分的 [[双向链接]]
|
||||
3. 确保页面间有充分的 [[slug]] 双向链接(slug 必须存在于本次输出的 pages 中,或上文索引中)
|
||||
4. 每个页面聚焦单一主题,内容完整且有实质价值
|
||||
|
||||
@ -20,10 +20,12 @@
|
||||
- 新材料对该页面无新增信息 → 输出原 content 即可(保持不变)
|
||||
- 已有页面 lastUpdatedBy=manual → 仍然合并,但优先保留手动编辑的措辞和结构,仅追加新事实
|
||||
|
||||
## 链接
|
||||
## 链接(**单一契约,必须严格遵守**)
|
||||
|
||||
- 沿用已有页面里的 [[页面标题]] 双向链接
|
||||
- 如果新材料引出了对其他已知概念的引用,新增 [[…]] 链接
|
||||
- 只允许两种形态:`[[slug]]` 与 `[[slug|显示文本]]`
|
||||
- 沿用已有页面里的所有 `[[slug]]` 链接(如果它们指向已存在的页面)
|
||||
- 如果新材料引出了对其他已知页面的引用,**只能**用上文"已有 Wiki 页面索引"中列出的 slug 来建立新链接
|
||||
- 禁止发明索引中不存在的 slug;禁止写 `[[页面标题]]` —— 系统按 slug 严格匹配,写标题会被识别为死链
|
||||
|
||||
## 语言
|
||||
|
||||
@ -36,7 +38,7 @@
|
||||
{
|
||||
"slug": "existing-slug",
|
||||
"title": "页面标题(可微调)",
|
||||
"content": "## 标题\n\n摘要...\n\n### 章节...\n\n参见:[[相关]]",
|
||||
"content": "## 标题\n\n摘要...\n\n### 章节...\n\n参见:[[related-slug]]",
|
||||
"summary": "更新后的一段话摘要"
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user