fix(wiki): internal link navigation and source citation guidance

This commit is contained in:
matevip 2026-04-24 06:55:20 +08:00
parent 4be867a3e6
commit c50977785a
4 changed files with 22 additions and 6 deletions

View File

@ -49,7 +49,9 @@ public class WikiContextService {
}
StringBuilder sb = new StringBuilder("<wiki-relevant>\n");
sb.append("[Relevant wiki pages for this query. Use wiki_read_page(slug) for full content.]\n\n");
sb.append("[Relevant wiki pages for this query. Use wiki_read_page(slug) for full content. " +
"When using information from these pages in your answer, always cite the source page title, " +
"e.g. 「来源:[[页面标题]]」or「(来源:页面标题)」.]\n\n");
int totalChars = 0;
int maxChars = properties.getMaxContextChars();

View File

@ -72,6 +72,8 @@ public class WikiTool {
@Tool(description = """
Read a wiki page. Use maxChars to limit size (recommended: 3000-6000 for most tasks).
Use sectionHeading to read only one section by its heading text.
The result includes a "sourceFiles" field listing the source documents this page was derived from.
When using content from this page in your answer, cite the page title and source files.
""")
public String wiki_read_page(
@ToolParam(description = "Agent ID") Long agentId,
@ -167,6 +169,8 @@ public class WikiTool {
@Tool(description = """
Search wiki pages. Returns snippet so you can judge relevance without reading the full page.
Default topK=5 is sufficient for most queries.
Each result includes "slug" and "title" use wiki_read_page to get full content.
When using wiki information in your answer, always cite the source page title.
""")
public String wiki_search_pages(
@ToolParam(description = "Agent ID") Long agentId,
@ -214,8 +218,9 @@ public class WikiTool {
@Tool(description = """
Chunk-level semantic search in the wiki knowledge base.
Returns raw text fragments closest to the query with similarity scores.
Returns raw text fragments closest to the query with similarity scores and source page title.
Use when wiki_search_pages results are not specific enough.
When using retrieved content in your answer, cite the source page title shown in each result.
""")
public String wiki_semantic_search(
@ToolParam(description = "Agent ID") Long agentId,

View File

@ -88,7 +88,7 @@ const markedInstance = new Marked({
// 配置 DOMPurify — 允许 Markdown + 代码块复制按钮的标签和属性
const purifyConfig = {
ADD_ATTR: ['target', 'rel', 'class', 'data-code', 'data-echarts-option', 'data-wiki-title', 'type', 'viewBox', 'fill', 'stroke', 'stroke-width', 'd', 'x', 'y', 'width', 'height', 'rx', 'ry', 'points'],
ADD_ATTR: ['target', 'rel', 'class', 'data-code', 'data-echarts-option', 'data-wiki-title', 'data-slug', 'type', 'viewBox', 'fill', 'stroke', 'stroke-width', 'd', 'x', 'y', 'width', 'height', 'rx', 'ry', 'points'],
ADD_TAGS: ['input', 'button', 'svg', 'path', 'rect', 'polyline', 'circle', 'line', 'span'],
}

View File

@ -99,9 +99,18 @@ const enrichToast = ref('')
const renderedContent = computed(() => {
if (!store.currentPage?.content) return ''
const content = store.currentPage.content.replace(/\[\[([^\]]+)\]\]/g, (_match, title) => {
const slug = title.trim().toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff\s-]/g, '').replace(/\s+/g, '-')
return `<a class="wiki-link" data-slug="${slug}" onclick="return false">${title}</a>`
// Build a lookup map: title (normalized) slug, for resolving [[Title]] links
const titleToSlug = new Map<string, string>()
for (const p of store.pages) {
if (p.title && p.slug) {
titleToSlug.set(p.title.trim().toLowerCase(), p.slug)
}
}
const content = store.currentPage.content.replace(/\[\[([^\]]+)\]\]/g, (_match, raw) => {
const title = raw.trim()
// Prefer exact title match; fall back to slug-style guess
const slug = titleToSlug.get(title.toLowerCase()) ?? title.toLowerCase().replace(/\s+/g, '-')
return `<a class="wiki-link" data-slug="${slug}">${title}</a>`
})
return renderMarkdown(content)
})