diff --git a/config/prompt_templates/agent_system_prompt.yaml b/config/prompt_templates/agent_system_prompt.yaml index 6d58e83fa..70159bc16 100644 --- a/config/prompt_templates/agent_system_prompt.yaml +++ b/config/prompt_templates/agent_system_prompt.yaml @@ -439,18 +439,30 @@ templates: 6. **Synthesize:** - Build an answer grounded in content actually retrieved this turn. - - Cite every source: wiki pages as `[[slug|display name]]`, chunks as `[knowledge_id#chunk_id]` (or the citation style expected by the product). + - Attach citations inline using the formats defined in `` below. - When only one surface was reachable, note it briefly ("The bound KB only exposes X, so this answer is drawn entirely from X"). + + Use EXACTLY these two formats (any other syntax renders as plain text): + - Chunk (verifiable facts: quotes, numbers, code, names): `` + - Wiki page (concepts / overviews / navigation): `[[slug|display name]]` + + All values MUST come from tool results of this turn — never fabricate a `chunk_id`, `kb_id`, or `slug`. + + Placement: inline on the same line as the sentence it supports, one per source per paragraph, never grouped at the end, never wrapped in backticks. + + Example: WeKnora 采用混合检索。 详见 [[hybrid-retrieval|混合检索]]。 + + ABSOLUTE RULES: 1. **Evidence-Based Facts:** Ground every factual claim in content you retrieved *this turn*, either from wiki pages or from source chunks. - 2. **Wiki Never Replaces RAG for Exact Quotes:** Wiki pages are summaries. If the user asks for a direct quote, numeric value, code, or table row, you MUST read the underlying chunks via `list_knowledge_chunks` or `wiki_read_source_doc`. + 2. **Wiki Never Replaces RAG for Exact Quotes:** Wiki pages are summaries. If the user asks for a direct quote, numeric value, code, or table row, you MUST read the underlying chunks via `list_knowledge_chunks` or `wiki_read_source_doc` and cite them with ``. 3. **Mandatory Deep Read:** Whenever `grep_chunks` or `knowledge_search` returns matched `knowledge_id`s / `chunk_id`s, you MUST immediately call `list_knowledge_chunks` to read the full content. Do NOT rely on search snippets. - 4. **Mandatory Wiki Read:** `wiki_search` returns summaries only. If a wiki hit is relevant, you MUST call `wiki_read_page` before citing it. + 4. **Mandatory Wiki Read:** `wiki_search` returns summaries only. If a wiki hit is relevant, you MUST call `wiki_read_page` before citing it via `[[slug|name]]`. 5. **Always Re-Retrieve:** Every new user question triggers fresh retrieval. Do not reuse retrieved content from earlier turns. - 6. **Cite Both Sides:** When your answer draws on wiki context plus chunk evidence, cite both (`[[wiki slug|name]]` + chunk refs). Make the evidence chain explicit. + 6. **Cite Both Sides When Both Contributed:** If a claim came from a wiki overview AND was verified against a source chunk, cite both. 7. **Flag Contradictions:** If wiki contradicts source chunks, call `wiki_flag_issue` on the wiki page before submitting the final answer. diff --git a/frontend/src/views/chat/components/AgentStreamDisplay.vue b/frontend/src/views/chat/components/AgentStreamDisplay.vue index 6347fe329..652a3216e 100644 --- a/frontend/src/views/chat/components/AgentStreamDisplay.vue +++ b/frontend/src/views/chat/components/AgentStreamDisplay.vue @@ -422,7 +422,7 @@ const DOMPurifyConfig = { ], ALLOWED_ATTR: [ 'href', 'title', 'target', 'rel', 'data-tooltip', 'data-url', 'data-kb-id', - 'data-chunk-id', 'data-doc', 'class', 'role', 'tabindex', 'src', 'alt', 'data-protected-src', + 'data-chunk-id', 'data-doc', 'data-slug', 'class', 'role', 'tabindex', 'src', 'alt', 'data-protected-src', 'width', 'height', 'style', 'id', // Mermaid SVG 支持的属性 'd', 'fill', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin', @@ -1647,9 +1647,27 @@ const preprocessMarkdown = (contentStr: string): string => { // as such, so the chat bubble must match. if (!slug) return match; + // Truncate long wiki labels the same way KB citations do, so both + // kinds of citation pills stay visually consistent in narrow bubbles. + const truncateMiddle = (text: string, maxLength = 13): string => { + if (!text) return ''; + if (text.length <= maxLength) return text; + const half = Math.floor((maxLength - 3) / 2); + const start = text.slice(0, half + ((maxLength - 3) % 2)); + const end = text.slice(-half); + return `${start}...${end}`; + }; + const safeSlug = escapeHtml(slug); - const safeDisplay = escapeHtml(display); - return `${safeDisplay}`; + const safeDisplay = escapeHtml(truncateMiddle(display)); + const tipSlug = escapeHtml(slug); + const tipDisplay = escapeHtml(display); + // Render as the same pill shape the KB citation uses (class + // `citation citation-wiki`) so the two retrieval sources share a + // single visual language. Anchor tag is kept for accessibility + // and keyboard handling; `data-slug` is preserved for the + // existing click/keyboard handlers above. + return `${safeDisplay}${tipDisplay}${tipSlug}`; } ); }; @@ -3047,6 +3065,13 @@ const handleAddToKnowledge = (answerEvent: any) => { background-image: url("../../../assets/img/zhishiku-thin.svg"); } +/* Wiki page icon — inline open-book SVG tinted brand blue (matches + .citation-wiki text color). Inlined as a data URI so no new asset + file is required. */ +:deep(.citation .citation-icon.wiki) { + background-image: url("data:image/svg+xml;utf8,"); +} + .kb-float-popup { position: absolute; z-index: 10000; @@ -3140,6 +3165,40 @@ const handleAddToKnowledge = (answerEvent: any) => { pointer-events: none; } +/* Wiki citation pill — same shape as KB / web citations but in brand blue + so the reader can tell at a glance whether evidence came from a + wiki-synthesized page or from a raw source chunk. */ +:deep(.citation.citation-wiki) { + background: var(--td-brand-color-light); + color: var(--td-brand-color); + border: 1px solid var(--td-brand-color-focus); + cursor: pointer; + white-space: nowrap; + position: relative; + text-decoration: none; + transition: all 0.2s ease; +} + +:deep(.citation.citation-wiki:hover) { + background: var(--td-brand-color-light); + border-color: var(--td-brand-color); + color: var(--td-brand-color); + text-decoration: none; +} + +:deep(.citation.citation-wiki:focus-visible) { + outline: 2px solid var(--td-brand-color); + outline-offset: 2px; +} + +/* Embedded tooltip bubble — hidden; relies on browser-native `title` + attribute for hover preview. Keeps parity with citation-kb, which + also hides its inline tip in favor of the floatPopup. */ +:deep(.citation.citation-wiki .citation-tip) { + display: none !important; + pointer-events: none; +} + .tool-arguments-wrapper { margin-top: 8px; padding: 0 10px;