feat: Enhance Wiki Fixer agent with improved issue handling and UI updates
- Updated the agent system prompt to refine the workflow for fixing issues, emphasizing the need to verify if issues still exist before making edits. - Introduced a new `embeddedMode` prop in the frontend components to manage UI behavior based on the context of use. - Simplified issue fix prompts in multiple languages for clarity, ensuring users receive concise instructions for resolving issues. - Enhanced the WikiBrowser component to improve the display of issues and actions, including updated icons and streamlined interaction elements. These changes significantly improve the user experience and functionality of the Wiki Fixer agent, fostering more efficient issue resolution and content management.
This commit is contained in:
@@ -301,45 +301,51 @@ templates:
|
||||
|
||||
<workflow>
|
||||
You must follow these steps for EVERY fixing task:
|
||||
1. **Understand the Issue:** The user will provide you with the target page slug, issue description, and optionally suspected knowledge IDs. Use `wiki_read_issue` if you need to list other pending issues.
|
||||
2. **Read the Current State:** Call `wiki_read_page` on the target slug to see the current content and related links. Use `wiki_search` if you need to find related entities or check if a page already exists for separated content.
|
||||
3. **Investigate Sources:** If the issue involves conflicting facts or mixed entities, the current wiki page might be poisoned. You MUST call `wiki_read_source_doc` using the `knowledge_id`s listed in the page's `<sources>` or provided in the issue description. Read the raw text to discover the truth.
|
||||
4. **Determine the Fix Strategy:**
|
||||
1. **Read the Issue:** The user will provide you with one or more issue IDs and a target page slug. Your FIRST action MUST be to call `wiki_read_issue` with the provided issue ID(s) to get the full issue details (type, description, suspected sources, etc.). Do NOT skip this step.
|
||||
2. **Read the Current State:** Call `wiki_read_page` on the target slug to see the current content and related links. You can call `wiki_read_issue` and `wiki_read_page` in parallel. Use `wiki_search` if you need to find related entities or check if a page already exists for separated content.
|
||||
3. **Verify the Issue Still Exists:** After reading the issue details and the current page content, CHECK whether the problem described in the issue actually exists in the current page. The issue might have already been fixed by a previous edit. If the issue no longer applies:
|
||||
- Inform the user that the issue appears to be already resolved.
|
||||
- Call `wiki_update_issue` to mark the issue as "resolved" with a note.
|
||||
- Do NOT make any edits to the page.
|
||||
- Stop here — do not continue to steps 4-7.
|
||||
4. **Investigate Sources:** If the issue is confirmed to still exist and involves conflicting facts or mixed entities, the current wiki page might be poisoned. You MUST call `wiki_read_source_doc` using the `knowledge_id`s listed in the page's `<sources>` or provided in the issue description. Read the raw text to discover the truth.
|
||||
5. **Determine the Fix Strategy:**
|
||||
- *Correction:* Fix minor errors efficiently using the `wiki_replace_text` tool, or rewrite the page using `wiki_write_page`.
|
||||
- *Renaming:* If the page title or slug is fundamentally wrong, use `wiki_rename_page` to change the slug. Incoming links will be updated automatically!
|
||||
- *Separation (Disambiguation):* Rewrite the target page to only focus on its true subject (using `wiki_write_page`), and remove the competitor's info.
|
||||
- *Creation:* Create a new page for the separated entity using `wiki_write_page`.
|
||||
- *Deletion:* If a page is completely redundant or should not exist, use `wiki_delete_page`. Incoming links will be cleaned up automatically!
|
||||
5. **Plan & Confirm:** Before calling any write/replace/rename/delete tool, you MUST present a plan of what you intend to change to the user and ask for their confirmation. Wait for the user to say "yes" or "approved" or express general agreement before making the actual edits. You should be robust to conversational affirmative responses (e.g. "好的", "ok", "go ahead", "没问题") instead of requiring exact phrase matches.
|
||||
6. **Apply the Fix:** After user confirmation, use the appropriate tool.
|
||||
6. **Announce & Execute:** Briefly announce what you are going to do (1-2 sentences), then IMMEDIATELY apply the fix using the appropriate tools in the SAME turn. Do NOT wait for user confirmation — the user has already requested the fix by clicking the "Fix" button. Execute everything in a single turn.
|
||||
- For `wiki_replace_text`, provide the exact `old_text` and the `new_text`.
|
||||
- For `wiki_rename_page`, provide the `new_slug`.
|
||||
- For `wiki_write_page`, provide the `title`, a concise 1-sentence `summary` for the index, the `page_type`, and the FULL, complete, corrected Markdown `content`. Do not output diffs in `content`.
|
||||
- For `wiki_delete_page`, just provide the `slug`.
|
||||
7. **Update Issue Status:** Use `wiki_update_issue` to mark the issue as "resolved" after successfully editing the page.
|
||||
7. **Update Issue Status:** After all edits are applied, use `wiki_update_issue` to mark each issue as "resolved".
|
||||
</workflow>
|
||||
|
||||
<constraints>
|
||||
ABSOLUTE RULES:
|
||||
1. **Never Guess:** Always base your fixes on evidence found in the raw source documents (`wiki_read_source_doc`).
|
||||
2. **Require Confirmation:** NEVER edit without first presenting your plan and getting explicit approval from the user in the conversation. Accept variations of affirmative responses (like "ok", "好的", "没问题", "可以").
|
||||
3. **Complete Content for Write:** When using `wiki_write_page`, you must provide the ENTIRE page content. Do not truncate or use placeholders like "...rest of the content...". Also, NEVER forget to provide the one-sentence `summary` field.
|
||||
4. **Exact Match for Replace:** When using `wiki_replace_text`, the `old_text` must EXACTLY match the text currently in the page.
|
||||
5. **Maintain Links:** When rewriting a page, try to preserve valid Wiki links `[[slug|Display Name]]`.
|
||||
6. **Writing Style:** You must strictly follow the standard Wiki writing style:
|
||||
2. **Read Issue First:** The user message only contains issue IDs. You MUST call `wiki_read_issue` to get the actual issue details before doing anything else.
|
||||
3. **Do Not Force Fix:** If after investigation the issue no longer exists in the current page (already fixed or no longer applicable), do NOT make any edits. Just mark the issue as resolved and inform the user.
|
||||
4. **No Confirmation Needed:** The user has explicitly requested the fix. Do NOT ask for confirmation or wait for a second message. Investigate → plan → execute → done, all in ONE turn.
|
||||
5. **Complete Content for Write:** When using `wiki_write_page`, you must provide the ENTIRE page content. Do not truncate or use placeholders like "...rest of the content...". Also, NEVER forget to provide the one-sentence `summary` field.
|
||||
6. **Exact Match for Replace:** When using `wiki_replace_text`, the `old_text` must EXACTLY match the text currently in the page.
|
||||
7. **Maintain Links:** When rewriting a page, try to preserve valid Wiki links `[[slug|Display Name]]`.
|
||||
8. **Writing Style:** You must strictly follow the standard Wiki writing style:
|
||||
- Use proper heading hierarchy (`##` for sections, `###` for subsections).
|
||||
- Include a "## Key Takeaways" section with bullet points at the end.
|
||||
- Preserve any valid image links ``.
|
||||
7. **Source Refs:** When calling `wiki_write_page` or `wiki_replace_text`, you MUST provide the `source_refs` array containing the `knowledge_id`s of the source documents you used to verify the information.
|
||||
9. **Source Refs:** When calling `wiki_write_page` or `wiki_replace_text`, you MUST provide the `source_refs` array containing the `knowledge_id`s of the source documents you used to verify the information.
|
||||
</constraints>
|
||||
|
||||
<tool_guidelines>
|
||||
* **wiki_read_page:** Use this first to see what the broken page looks like.
|
||||
* **wiki_read_issue:** Call this FIRST to read the full issue details from the provided issue ID(s). This is mandatory — the user message only contains IDs, not the full description.
|
||||
* **wiki_read_page:** Use this to see the current state of the broken page. Can be called in parallel with `wiki_read_issue`.
|
||||
* **wiki_search:** Use this to explore the wiki if you need to find related concepts or verify if another page already exists.
|
||||
* **wiki_read_source_doc:** Use this to find the ground truth. It is crucial for resolving "contradictory_facts" or "mixed_entities" issues.
|
||||
* **wiki_read_issue:** Use this to read specific issue details or list pending issues for a page.
|
||||
* **todo_write:** Use this to write down the plan and modifications you intend to make, so that you can remember them across conversation turns, and present them to the user.
|
||||
* **wiki_write_page / wiki_replace_text / wiki_rename_page / wiki_delete_page:** Use these to apply your fix ONLY AFTER the user confirms your plan.
|
||||
* **wiki_write_page / wiki_replace_text / wiki_rename_page / wiki_delete_page:** Use these to apply your fix directly after investigation. No user confirmation is needed.
|
||||
* **wiki_update_issue:** Use this to set the issue status to "resolved" after the page is fixed.
|
||||
</tool_guidelines>
|
||||
|
||||
|
||||
@@ -285,6 +285,10 @@ const props = defineProps({
|
||||
assistantMessageId: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
embeddedMode: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1950,7 +1954,7 @@ defineExpose({
|
||||
<!-- 控制栏 -->
|
||||
<div class="control-bar">
|
||||
<!-- 左侧控制按钮 -->
|
||||
<div class="control-left">
|
||||
<div class="control-left" v-if="!embeddedMode">
|
||||
<!-- Agent 模式切换按钮 -->
|
||||
<div
|
||||
ref="agentModeButtonRef"
|
||||
|
||||
@@ -1656,9 +1656,9 @@ export default {
|
||||
issueFixSuggestions: 'Content Optimization Suggestions ({count})',
|
||||
issueFixSingle: 'Fix',
|
||||
fixStartError: 'Failed to start fix assistant',
|
||||
issueFixPromptSingle: 'Please help me fix the following issue on page [[{slug}]]:\n\n- [{type}] {desc}\n\nOnce resolved, please call the `wiki_update_issue` tool to mark this issue (ID: {id}) as resolved.',
|
||||
issueFixPromptAutoStart: 'Please help me fix all pending issues on page [[{slug}]]:',
|
||||
issueFixPromptAutoEnd: 'Once resolved, please call the `wiki_update_issue` tool to mark these issues as resolved.',
|
||||
issueFixPromptSingle: 'Please fix the issue (ID: {id}) on page [[{slug}]].',
|
||||
issueFixPromptAutoStart: 'Please fix the following issues on page [[{slug}]]:',
|
||||
issueFixPromptAutoEnd: '',
|
||||
},
|
||||
buttons: {
|
||||
create: 'Create Knowledge Base',
|
||||
|
||||
@@ -2179,9 +2179,9 @@ export default {
|
||||
issueFixSuggestions: "콘텐츠 최적화 제안 ({count})",
|
||||
issueFixSingle: "수정",
|
||||
fixStartError: "수정 도우미 시작 실패",
|
||||
issueFixPromptSingle: "페이지 [[{slug}]] 의 다음 문제를 수정해 주세요:\n\n- [{type}] {desc}\n\n해결 완료 후 `wiki_update_issue` 도구를 호출하여 이 문제(ID: {id})를 해결됨으로 표시해 주세요.",
|
||||
issueFixPromptAutoStart: "페이지 [[{slug}]] 의 모든 보류 중인 문제를 수정해 주세요:",
|
||||
issueFixPromptAutoEnd: "해결 완료 후 `wiki_update_issue` 도구를 호출하여 이 문제들을 해결됨으로 표시해 주세요.",
|
||||
issueFixPromptSingle: "페이지 [[{slug}]] 의 문제(ID: {id})를 수정해 주세요.",
|
||||
issueFixPromptAutoStart: "페이지 [[{slug}]] 의 다음 문제들을 수정해 주세요:",
|
||||
issueFixPromptAutoEnd: "",
|
||||
},
|
||||
buttons: {
|
||||
create: "지식베이스 생성",
|
||||
|
||||
@@ -1858,9 +1858,9 @@ export default {
|
||||
issueFixSuggestions: 'Предложения по оптимизации ({count})',
|
||||
issueFixSingle: 'Исправить',
|
||||
fixStartError: 'Не удалось запустить помощник исправления',
|
||||
issueFixPromptSingle: 'Пожалуйста, помогите исправить следующую проблему на странице [[{slug}]]:\n\n- [{type}] {desc}\n\nПосле устранения проблемы, пожалуйста, вызовите инструмент `wiki_update_issue`, чтобы пометить эту проблему (ID: {id}) как решенную.',
|
||||
issueFixPromptAutoStart: 'Пожалуйста, помогите исправить все ожидающие проблемы на странице [[{slug}]]:',
|
||||
issueFixPromptAutoEnd: 'После устранения проблем, пожалуйста, вызовите инструмент `wiki_update_issue`, чтобы пометить эти проблемы как решенные.',
|
||||
issueFixPromptSingle: 'Пожалуйста, исправьте проблему (ID: {id}) на странице [[{slug}]].',
|
||||
issueFixPromptAutoStart: 'Пожалуйста, исправьте следующие проблемы на странице [[{slug}]]:',
|
||||
issueFixPromptAutoEnd: '',
|
||||
},
|
||||
buttons: {
|
||||
create: 'Создать базу знаний',
|
||||
|
||||
@@ -2164,9 +2164,9 @@ export default {
|
||||
issueFixSuggestions: "内容优化建议 ({count})",
|
||||
issueFixSingle: "修复",
|
||||
fixStartError: "启动修复助手失败",
|
||||
issueFixPromptSingle: "请帮我修复页面 [[{slug}]] 上的以下问题:\n\n- [{type}] {desc}\n\n解决完毕后,请调用 `wiki_update_issue` 工具将该问题 (ID: {id}) 标记为已解决。",
|
||||
issueFixPromptAutoStart: "请帮我修复页面 [[{slug}]] 上的所有待处理问题:",
|
||||
issueFixPromptAutoEnd: "解决完毕后,请调用 `wiki_update_issue` 工具将这些问题标记为已解决。",
|
||||
issueFixPromptSingle: "请修复页面 [[{slug}]] 上的问题 (ID: {id})。",
|
||||
issueFixPromptAutoStart: "请修复页面 [[{slug}]] 上的以下问题:",
|
||||
issueFixPromptAutoEnd: "",
|
||||
},
|
||||
buttons: {
|
||||
create: "创建知识库",
|
||||
|
||||
@@ -164,10 +164,10 @@ const fetchSuggestedQuestions = async () => {
|
||||
suggestedQuestionsLoading.value = true;
|
||||
// 加载期间保留旧数据,不清空,避免布局抖动
|
||||
try {
|
||||
const agentId = useSettingsStoreInstance.selectedAgentId;
|
||||
const agentId = props.embeddedMode ? props.agentId : useSettingsStoreInstance.selectedAgentId;
|
||||
if (!agentId) return;
|
||||
const selectedKBs = useSettingsStoreInstance.getSelectedKnowledgeBases();
|
||||
const selectedFiles = useSettingsStoreInstance.getSelectedFiles();
|
||||
const selectedKBs = props.embeddedMode ? props.kbIds : useSettingsStoreInstance.getSelectedKnowledgeBases();
|
||||
const selectedFiles = props.embeddedMode ? [] : useSettingsStoreInstance.getSelectedFiles();
|
||||
const res = await getSuggestedQuestions(agentId, {
|
||||
knowledge_base_ids: selectedKBs.length > 0 ? selectedKBs : undefined,
|
||||
knowledge_ids: selectedFiles.length > 0 ? selectedFiles : undefined,
|
||||
@@ -539,18 +539,18 @@ const sendMsg = async (value, modelId = '', mentionedItems = [], imageFiles = []
|
||||
scrollToBottom(true);
|
||||
|
||||
// Get agent mode status from settings store
|
||||
const agentEnabled = useSettingsStoreInstance.isAgentEnabled;
|
||||
const agentEnabled = props.embeddedMode ? (props.agentId && props.agentId !== 'builtin-quick-answer') : useSettingsStoreInstance.isAgentEnabled;
|
||||
|
||||
// Get web search status from settings store
|
||||
const webSearchEnabled = useSettingsStoreInstance.isWebSearchEnabled;
|
||||
const webSearchEnabled = props.embeddedMode ? false : useSettingsStoreInstance.isWebSearchEnabled;
|
||||
|
||||
// Get memory status from settings store
|
||||
const enableMemory = useSettingsStoreInstance.isMemoryEnabled;
|
||||
const enableMemory = props.embeddedMode ? false : useSettingsStoreInstance.isMemoryEnabled;
|
||||
|
||||
// Get knowledge_base_ids from settings store (selected by user via KnowledgeBaseSelector)
|
||||
// Merge @mentioned KB/file IDs so retrieval uses the same targets user @mentioned (including shared KBs)
|
||||
const sidebarKbIds = useSettingsStoreInstance.settings.selectedKnowledgeBases || [];
|
||||
const sidebarFileIds = useSettingsStoreInstance.settings.selectedFiles || [];
|
||||
const sidebarKbIds = props.embeddedMode ? props.kbIds : (useSettingsStoreInstance.settings.selectedKnowledgeBases || []);
|
||||
const sidebarFileIds = props.embeddedMode ? [] : (useSettingsStoreInstance.settings.selectedFiles || []);
|
||||
const kbIdSet = new Set(sidebarKbIds);
|
||||
const fileIdSet = new Set(sidebarFileIds);
|
||||
for (const item of mentionedItems || []) {
|
||||
@@ -565,13 +565,13 @@ const sendMsg = async (value, modelId = '', mentionedItems = [], imageFiles = []
|
||||
const knowledgeIds = [...fileIdSet];
|
||||
|
||||
// Get selected agent ID (backend resolves shared agent and its tenant from share relation)
|
||||
const selectedAgentId = useSettingsStoreInstance.selectedAgentId || '';
|
||||
const selectedAgentId = props.embeddedMode ? props.agentId : (useSettingsStoreInstance.selectedAgentId || '');
|
||||
|
||||
// Use agent-chat endpoint when agent is enabled, otherwise use knowledge-chat
|
||||
const endpoint = agentEnabled ? '/api/v1/agent-chat' : '/api/v1/knowledge-chat';
|
||||
|
||||
// Get selected MCP services from settings store (if available)
|
||||
const mcpServiceIds = useSettingsStoreInstance.settings.selectedMCPServices || [];
|
||||
const mcpServiceIds = props.embeddedMode ? [] : (useSettingsStoreInstance.settings.selectedMCPServices || []);
|
||||
|
||||
await startStream({
|
||||
session_id: session_id.value,
|
||||
|
||||
@@ -232,14 +232,12 @@
|
||||
trigger="click"
|
||||
:overlayInnerStyle="{ padding: 0, boxShadow: 'var(--td-shadow-3)', borderRadius: '8px', width: '560px', maxWidth: '90vw' }"
|
||||
>
|
||||
<t-tag
|
||||
theme="warning"
|
||||
variant="light-outline"
|
||||
<span
|
||||
class="wiki-issue-trigger"
|
||||
:title="$t('knowledgeEditor.wikiBrowser.issueTitle', { count: pageIssues.length })"
|
||||
>
|
||||
<template #icon><t-icon name="error-circle" /></template>
|
||||
{{ $t('knowledgeEditor.wikiBrowser.issueTitle', { count: pageIssues.length }) }}
|
||||
</t-tag>
|
||||
<t-icon name="error-circle-filled" style="color: var(--td-warning-color);" />
|
||||
</span>
|
||||
|
||||
<template #content>
|
||||
<div class="wiki-issue-popup-content">
|
||||
@@ -248,28 +246,29 @@
|
||||
<span>{{ $t('knowledgeEditor.wikiBrowser.issueFixSuggestions', { count: pageIssues.length }) }}</span>
|
||||
</div>
|
||||
<t-button size="small" theme="primary" variant="base" @click="triggerAutoFix">
|
||||
<template #icon><t-icon name="magic" /></template>
|
||||
<template #icon><t-icon name="tools" /></template>
|
||||
{{ $t('knowledgeEditor.wikiBrowser.issueFixBtn') }}
|
||||
</t-button>
|
||||
</div>
|
||||
<div class="wiki-issue-popup-list">
|
||||
<div v-for="issue in pageIssues" :key="issue.id" class="wiki-issue-popup-item">
|
||||
<div class="wiki-issue-popup-main">
|
||||
<div class="wiki-issue-popup-tags">
|
||||
<t-tag v-if="issue.issue_type === 'mixed_entities'" theme="warning" variant="light" size="small">{{ $t('knowledgeEditor.wikiBrowser.issueMixed') }}</t-tag>
|
||||
<t-tag v-else-if="issue.issue_type === 'contradictory_facts'" theme="danger" variant="light" size="small">{{ $t('knowledgeEditor.wikiBrowser.issueConflict') }}</t-tag>
|
||||
<t-tag v-else-if="issue.issue_type === 'out_of_date'" theme="default" variant="light" size="small">{{ $t('knowledgeEditor.wikiBrowser.issueOutdated') }}</t-tag>
|
||||
<t-tag v-else theme="primary" variant="light" size="small">{{ $t('knowledgeEditor.wikiBrowser.issueAttention') }}</t-tag>
|
||||
</div>
|
||||
<div class="wiki-issue-popup-desc">
|
||||
{{ issue.description }}
|
||||
</div>
|
||||
<div class="wiki-issue-popup-meta">
|
||||
<span class="wiki-issue-popup-reporter">
|
||||
{{ issue.reported_by === 'wiki-researcher-agent' ? $t('knowledgeEditor.wikiBrowser.issueAiLinter') : $t('knowledgeEditor.wikiBrowser.issueReportedBy', { reporter: issue.reported_by }) }}
|
||||
</span>
|
||||
<div class="wiki-issue-popup-list">
|
||||
<div v-for="issue in pageIssues" :key="issue.id" class="wiki-issue-popup-item">
|
||||
<div class="wiki-issue-popup-main">
|
||||
<div class="wiki-issue-popup-tags">
|
||||
<t-tag v-if="issue.issue_type === 'mixed_entities'" theme="warning" variant="light" size="small">{{ $t('knowledgeEditor.wikiBrowser.issueMixed') }}</t-tag>
|
||||
<t-tag v-else-if="issue.issue_type === 'contradictory_facts'" theme="danger" variant="light" size="small">{{ $t('knowledgeEditor.wikiBrowser.issueConflict') }}</t-tag>
|
||||
<t-tag v-else-if="issue.issue_type === 'out_of_date'" theme="default" variant="light" size="small">{{ $t('knowledgeEditor.wikiBrowser.issueOutdated') }}</t-tag>
|
||||
<t-tag v-else theme="primary" variant="light" size="small">{{ $t('knowledgeEditor.wikiBrowser.issueAttention') }}</t-tag>
|
||||
</div>
|
||||
<div class="wiki-issue-popup-desc">
|
||||
{{ issue.description }}
|
||||
</div>
|
||||
<div class="wiki-issue-popup-meta">
|
||||
<span class="wiki-issue-popup-reporter">
|
||||
{{ issue.reported_by === 'wiki-researcher-agent' ? $t('knowledgeEditor.wikiBrowser.issueAiLinter') : $t('knowledgeEditor.wikiBrowser.issueReportedBy', { reporter: issue.reported_by }) }}
|
||||
</span>
|
||||
<div class="wiki-issue-popup-actions">
|
||||
<span class="wiki-issue-popup-action" @click="triggerFixIssue(issue)" style="margin-right: 12px; font-weight: 500;">
|
||||
<t-icon name="magic" style="margin-right: 2px;" />{{ $t('knowledgeEditor.wikiBrowser.issueFixSingle') }}
|
||||
<t-icon name="tools" style="margin-right: 4px;" />{{ $t('knowledgeEditor.wikiBrowser.issueFixSingle') }}
|
||||
</span>
|
||||
<span class="wiki-issue-popup-action" style="color: var(--td-text-color-placeholder);" @click="handleIssueIgnore(issue.id)">{{ $t('knowledgeEditor.wikiBrowser.issueIgnore') }}</span>
|
||||
</div>
|
||||
@@ -277,6 +276,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</t-popup>
|
||||
</h2>
|
||||
@@ -377,10 +377,12 @@
|
||||
<span class="wiki-issue-popup-reporter">
|
||||
{{ issue.reported_by === 'wiki-researcher-agent' ? $t('knowledgeEditor.wikiBrowser.issueAiLinter') : $t('knowledgeEditor.wikiBrowser.issueReportedBy', { reporter: issue.reported_by }) }}
|
||||
</span>
|
||||
<span class="wiki-issue-popup-action" @click="navigateToSlugAndFix(issue.slug)" style="margin-right: 12px; font-weight: 500;">
|
||||
<t-icon name="arrow-right-circle" style="margin-right: 2px;" />{{ $t('knowledgeEditor.wikiBrowser.issueGoFix') }}
|
||||
</span>
|
||||
<span class="wiki-issue-popup-action" style="color: var(--td-text-color-placeholder);" @click="handleGlobalIssueIgnore(issue.id)">{{ $t('knowledgeEditor.wikiBrowser.issueIgnore') }}</span>
|
||||
<div class="wiki-issue-popup-actions">
|
||||
<span class="wiki-issue-popup-action" @click="navigateToSlugAndFix(issue.slug)" style="margin-right: 12px; font-weight: 500;">
|
||||
<t-icon name="arrow-right-circle" style="margin-right: 4px;" />{{ $t('knowledgeEditor.wikiBrowser.issueGoFix') }}
|
||||
</span>
|
||||
<span class="wiki-issue-popup-action" style="color: var(--td-text-color-placeholder);" @click="handleGlobalIssueIgnore(issue.id)">{{ $t('knowledgeEditor.wikiBrowser.issueIgnore') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -882,21 +884,8 @@ async function handleIssueIgnore(issueId: string) {
|
||||
}
|
||||
|
||||
async function startFixSession(prompt: string) {
|
||||
settingsStore.selectAgent("builtin-wiki-fixer", null)
|
||||
|
||||
const sessionData: any = {
|
||||
agent_config: {
|
||||
enabled: true,
|
||||
max_iterations: 30,
|
||||
temperature: 0.7,
|
||||
knowledge_bases: [props.knowledgeBaseId],
|
||||
knowledge_ids: [],
|
||||
allowed_tools: ["thinking", "wiki_read_page", "wiki_search", "wiki_read_source_doc", "wiki_edit_page", "wiki_read_issue", "wiki_update_issue"]
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await createSessions(sessionData)
|
||||
const res = await createSessions({})
|
||||
if (res && (res as any).data && (res as any).data.id) {
|
||||
const sessionId = (res as any).data.id
|
||||
const now = new Date().toISOString()
|
||||
@@ -930,8 +919,6 @@ function triggerFixIssue(issue: WikiPageIssue) {
|
||||
if (!selectedPage.value) return
|
||||
const prompt = t('knowledgeEditor.wikiBrowser.issueFixPromptSingle', {
|
||||
slug: selectedPage.value.slug,
|
||||
type: issue.issue_type,
|
||||
desc: issue.description,
|
||||
id: issue.id
|
||||
})
|
||||
startFixSession(prompt)
|
||||
@@ -942,10 +929,9 @@ function triggerAutoFix() {
|
||||
let prompt = t('knowledgeEditor.wikiBrowser.issueFixPromptAutoStart', { slug: selectedPage.value.slug }) + '\n\n'
|
||||
|
||||
pageIssues.value.forEach((issue, idx) => {
|
||||
prompt += `${idx + 1}. [${issue.issue_type}] ${issue.description} (ID: ${issue.id})\n`
|
||||
prompt += `${idx + 1}. Issue ID: ${issue.id}\n`
|
||||
})
|
||||
|
||||
prompt += '\n' + t('knowledgeEditor.wikiBrowser.issueFixPromptAutoEnd')
|
||||
startFixSession(prompt)
|
||||
}
|
||||
|
||||
@@ -2446,13 +2432,16 @@ onUnmounted(() => {
|
||||
|
||||
// ── Issues Popup ──
|
||||
.wiki-issue-trigger {
|
||||
margin-left: 12px;
|
||||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
transition: opacity 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
filter: brightness(0.95);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2492,21 +2481,21 @@ onUnmounted(() => {
|
||||
flex-direction: column;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
gap: 12px;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.wiki-issue-popup-item {
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
gap: 12px;
|
||||
border-bottom: 1px solid var(--td-component-stroke);
|
||||
transition: background-color 0.2s ease;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
border: 1px solid var(--td-component-border);
|
||||
border-radius: 6px;
|
||||
transition: box-shadow 0.2s ease, border-color 0.2s ease;
|
||||
background: var(--td-bg-color-container);
|
||||
|
||||
&:hover {
|
||||
background: var(--td-bg-color-container-hover);
|
||||
border-color: var(--td-brand-color-light);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2550,12 +2539,20 @@ onUnmounted(() => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-top: 4px;
|
||||
margin-top: 8px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px dashed var(--td-component-stroke);
|
||||
}
|
||||
|
||||
.wiki-issue-popup-reporter {
|
||||
font-size: 12px;
|
||||
color: var(--td-text-color-placeholder);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.wiki-issue-popup-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.wiki-issue-popup-action {
|
||||
|
||||
@@ -65,7 +65,7 @@ If previous slugs are provided above, you MUST follow these rules:
|
||||
Each entity should have:
|
||||
- "name": The entity name in {{.Language}} (human-readable)
|
||||
- "slug": URL-friendly slug, format "entity/<lowercase-hyphenated-name>" (use romanized/pinyin form for non-Latin names). **Reuse previous slug if the entity was extracted before.**
|
||||
- "aliases": An array of strings representing alternative names, abbreviations, acronyms or translations of the entity found in the document. Provide [] if none.
|
||||
- "aliases": An array of strings representing names that refer to THE EXACT SAME entity. Only include: official abbreviations (e.g. "IBM" for "International Business Machines"), full/short name variants (e.g. "腾讯" for "腾讯控股有限公司"), translations (e.g. "Apple" for "苹果公司"), and well-known alternate names (e.g. "Alphabet" for "Google母公司"). Do NOT include parent categories, related products, generic terms, or broader concepts. Provide [] if none.
|
||||
- "description": **Index listing summary** — one sentence, 15-40 words, in {{.Language}}. Describes WHAT this entity IS and its role in the document. Must be self-contained (understandable without reading the full page). This will be displayed in the wiki index.
|
||||
- "details": A 2-5 sentence summary in {{.Language}} of key facts from the document. **Image rule**: If the document contains relevant <image> elements in an <images> tag, include them in the details using Markdown syntax: .
|
||||
|
||||
@@ -75,7 +75,7 @@ Only include entities that are substantively discussed (mentioned at least twice
|
||||
Each concept should have:
|
||||
- "name": The concept name in {{.Language}} (human-readable)
|
||||
- "slug": URL-friendly slug, format "concept/<lowercase-hyphenated-name>" (use romanized/pinyin form for non-Latin names). **Reuse previous slug if the concept was extracted before.**
|
||||
- "aliases": An array of strings representing alternative names, abbreviations, acronyms or translations of the concept found in the document. Provide [] if none.
|
||||
- "aliases": An array of strings representing names that refer to THE EXACT SAME concept. Only include: official abbreviations (e.g. "RAG" for "Retrieval-Augmented Generation"), full/short name variants, and well-known synonyms used interchangeably in the field. Do NOT include sub-topics, related techniques, broader categories, or implementation details. Provide [] if none.
|
||||
- "description": **Index listing summary** — one sentence, 15-40 words, in {{.Language}}. Defines WHAT this concept IS. Must be self-contained (understandable without reading the full page). This will be displayed in the wiki index.
|
||||
- "details": A 2-5 sentence explanation in {{.Language}} as discussed in the document. **Image rule**: If the document contains relevant <image> elements in an <images> tag, include them in the details using Markdown syntax: .
|
||||
|
||||
|
||||
@@ -12,12 +12,13 @@ import (
|
||||
|
||||
type wikiReplaceTextTool struct {
|
||||
BaseTool
|
||||
wikiPageService interfaces.WikiPageService
|
||||
kbIDs []string
|
||||
wikiPageService interfaces.WikiPageService
|
||||
knowledgeService interfaces.KnowledgeService
|
||||
kbIDs []string
|
||||
}
|
||||
|
||||
// NewWikiReplaceTextTool creates a new wiki_replace_text tool
|
||||
func NewWikiReplaceTextTool(wikiPageService interfaces.WikiPageService, kbIDs []string) types.Tool {
|
||||
func NewWikiReplaceTextTool(wikiPageService interfaces.WikiPageService, kbIDs []string, knowledgeService interfaces.KnowledgeService) types.Tool {
|
||||
return &wikiReplaceTextTool{
|
||||
BaseTool: NewBaseTool(
|
||||
ToolWikiReplaceText,
|
||||
@@ -40,14 +41,15 @@ func NewWikiReplaceTextTool(wikiPageService interfaces.WikiPageService, kbIDs []
|
||||
"source_refs": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"description": "An optional list of source knowledge IDs that justify this change. If provided, these will COMPLETELY REPLACE the existing source_refs of the page."
|
||||
"description": "An optional list of source knowledge IDs (UUIDs only) that justify this change. If provided, these will COMPLETELY REPLACE the existing source_refs of the page."
|
||||
}
|
||||
},
|
||||
"required": ["slug", "old_text", "new_text"]
|
||||
}`),
|
||||
),
|
||||
wikiPageService: wikiPageService,
|
||||
kbIDs: kbIDs,
|
||||
wikiPageService: wikiPageService,
|
||||
knowledgeService: knowledgeService,
|
||||
kbIDs: kbIDs,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,13 +87,13 @@ func (t *wikiReplaceTextTool) Execute(ctx context.Context, args json.RawMessage)
|
||||
existingPage.Content = strings.Replace(existingPage.Content, params.OldText, params.NewText, 1)
|
||||
|
||||
if len(params.SourceRefs) > 0 {
|
||||
existingPage.SourceRefs = params.SourceRefs
|
||||
existingPage.SourceRefs = resolveSourceRefs(ctx, t.knowledgeService, params.SourceRefs)
|
||||
}
|
||||
|
||||
_, err = t.wikiPageService.UpdatePage(ctx, existingPage)
|
||||
if err != nil {
|
||||
return &types.ToolResult{Success: false, Error: "Failed to update page: " + err.Error()}, nil
|
||||
}
|
||||
|
||||
|
||||
return &types.ToolResult{Success: true, Output: fmt.Sprintf("Successfully replaced text on page %s.", params.Slug)}, nil
|
||||
}
|
||||
|
||||
@@ -137,6 +137,7 @@ func (t *wikiReadPageTool) Execute(ctx context.Context, args json.RawMessage) (*
|
||||
<title>%s</title>
|
||||
<slug>%s</slug>
|
||||
<type>%s</type>
|
||||
<aliases>%s</aliases>
|
||||
</metadata>
|
||||
<relationships>
|
||||
<links_to>%s</links_to>
|
||||
@@ -153,6 +154,7 @@ func (t *wikiReadPageTool) Execute(ctx context.Context, args json.RawMessage) (*
|
||||
</content>
|
||||
</wiki_page>`,
|
||||
page.Title, page.Slug, page.PageType,
|
||||
strings.Join(page.Aliases, ", "),
|
||||
strings.Join(outLinksDesc, ", "),
|
||||
strings.Join(inLinksDesc, ", "),
|
||||
strings.Join(sourcesDesc, "\n"),
|
||||
@@ -278,10 +280,15 @@ func (t *wikiSearchTool) Execute(ctx context.Context, args json.RawMessage) (*ty
|
||||
snippetTag = fmt.Sprintf("\n<match_snippet>%s</match_snippet>", snippet)
|
||||
}
|
||||
|
||||
aliasesTag := ""
|
||||
if len(p.Aliases) > 0 {
|
||||
aliasesTag = fmt.Sprintf("\n<aliases>%s</aliases>", strings.Join(p.Aliases, ", "))
|
||||
}
|
||||
|
||||
if seen {
|
||||
fmt.Fprintf(&sb, "<page>\n<title>%s</title>\n<slug>%s</slug>\n<type>%s</type>\n<summary>(summary omitted, already seen in previous search)</summary>%s\n</page>\n", p.Title, p.Slug, p.PageType, snippetTag)
|
||||
fmt.Fprintf(&sb, "<page>\n<title>%s</title>\n<slug>%s</slug>\n<type>%s</type>%s\n<summary>(summary omitted, already seen in previous search)</summary>%s\n</page>\n", p.Title, p.Slug, p.PageType, aliasesTag, snippetTag)
|
||||
} else {
|
||||
fmt.Fprintf(&sb, "<page>\n<title>%s</title>\n<slug>%s</slug>\n<type>%s</type>\n<summary>%s</summary>%s\n</page>\n", p.Title, p.Slug, p.PageType, p.Summary, snippetTag)
|
||||
fmt.Fprintf(&sb, "<page>\n<title>%s</title>\n<slug>%s</slug>\n<type>%s</type>%s\n<summary>%s</summary>%s\n</page>\n", p.Title, p.Slug, p.PageType, aliasesTag, p.Summary, snippetTag)
|
||||
}
|
||||
}
|
||||
sb.WriteString("</search_results>")
|
||||
@@ -327,6 +334,36 @@ func parseStringOrArray(val any) []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
// resolveSourceRefs enriches plain knowledge UUIDs to "uuid|title" format.
|
||||
// Refs already in "uuid|title" format are left unchanged.
|
||||
func resolveSourceRefs(ctx context.Context, knowledgeService interfaces.KnowledgeService, refs []string) []string {
|
||||
if len(refs) == 0 || knowledgeService == nil {
|
||||
return refs
|
||||
}
|
||||
resolved := make([]string, 0, len(refs))
|
||||
for _, ref := range refs {
|
||||
if strings.Contains(ref, "|") {
|
||||
resolved = append(resolved, ref)
|
||||
continue
|
||||
}
|
||||
kn, err := knowledgeService.GetKnowledgeByIDOnly(ctx, ref)
|
||||
if err != nil || kn == nil {
|
||||
resolved = append(resolved, ref)
|
||||
continue
|
||||
}
|
||||
title := kn.Title
|
||||
if title == "" {
|
||||
title = kn.FileName
|
||||
}
|
||||
if title != "" {
|
||||
resolved = append(resolved, ref+"|"+title)
|
||||
} else {
|
||||
resolved = append(resolved, ref)
|
||||
}
|
||||
}
|
||||
return resolved
|
||||
}
|
||||
|
||||
func extractSnippet(content string, query string) string {
|
||||
if content == "" || query == "" {
|
||||
return ""
|
||||
|
||||
@@ -13,12 +13,13 @@ import (
|
||||
|
||||
type wikiWritePageTool struct {
|
||||
BaseTool
|
||||
wikiPageService interfaces.WikiPageService
|
||||
kbIDs []string
|
||||
wikiPageService interfaces.WikiPageService
|
||||
knowledgeService interfaces.KnowledgeService
|
||||
kbIDs []string
|
||||
}
|
||||
|
||||
// NewWikiWritePageTool creates a new wiki_write_page tool
|
||||
func NewWikiWritePageTool(wikiPageService interfaces.WikiPageService, kbIDs []string) types.Tool {
|
||||
func NewWikiWritePageTool(wikiPageService interfaces.WikiPageService, kbIDs []string, knowledgeService interfaces.KnowledgeService) types.Tool {
|
||||
return &wikiWritePageTool{
|
||||
BaseTool: NewBaseTool(
|
||||
ToolWikiWritePage,
|
||||
@@ -54,14 +55,15 @@ func NewWikiWritePageTool(wikiPageService interfaces.WikiPageService, kbIDs []st
|
||||
"source_refs": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"description": "A list of source knowledge IDs that contributed to this page. If provided, these will COMPLETELY REPLACE the existing source_refs of the page."
|
||||
"description": "A list of source knowledge IDs (UUIDs only) that contributed to this page. If provided, these will COMPLETELY REPLACE the existing source_refs of the page."
|
||||
}
|
||||
},
|
||||
"required": ["slug", "title", "summary", "content", "page_type"]
|
||||
}`),
|
||||
),
|
||||
wikiPageService: wikiPageService,
|
||||
kbIDs: kbIDs,
|
||||
wikiPageService: wikiPageService,
|
||||
knowledgeService: knowledgeService,
|
||||
kbIDs: kbIDs,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +97,8 @@ func (t *wikiWritePageTool) Execute(ctx context.Context, args json.RawMessage) (
|
||||
return &types.ToolResult{Success: false, Error: "Failed to check existing page: " + err.Error()}, nil
|
||||
}
|
||||
|
||||
resolvedRefs := resolveSourceRefs(ctx, t.knowledgeService, params.SourceRefs)
|
||||
|
||||
var action string
|
||||
if existingPage != nil {
|
||||
// Update
|
||||
@@ -104,8 +108,8 @@ func (t *wikiWritePageTool) Execute(ctx context.Context, args json.RawMessage) (
|
||||
existingPage.PageType = params.PageType
|
||||
existingPage.Aliases = params.Aliases
|
||||
|
||||
if len(params.SourceRefs) > 0 {
|
||||
existingPage.SourceRefs = params.SourceRefs
|
||||
if len(resolvedRefs) > 0 {
|
||||
existingPage.SourceRefs = resolvedRefs
|
||||
}
|
||||
|
||||
_, err = t.wikiPageService.UpdatePage(ctx, existingPage)
|
||||
@@ -123,7 +127,7 @@ func (t *wikiWritePageTool) Execute(ctx context.Context, args json.RawMessage) (
|
||||
Content: params.Content,
|
||||
PageType: params.PageType,
|
||||
Aliases: params.Aliases,
|
||||
SourceRefs: params.SourceRefs,
|
||||
SourceRefs: resolvedRefs,
|
||||
}
|
||||
_, err = t.wikiPageService.CreatePage(ctx, newPage)
|
||||
if err != nil {
|
||||
|
||||
@@ -461,9 +461,9 @@ func (s *agentService) registerTools(
|
||||
case tools.ToolWikiUpdateIssue:
|
||||
toolToRegister = tools.NewWikiUpdateIssueTool(s.wikiPageService, wikiKBIDs)
|
||||
case tools.ToolWikiWritePage:
|
||||
toolToRegister = tools.NewWikiWritePageTool(s.wikiPageService, wikiKBIDs)
|
||||
toolToRegister = tools.NewWikiWritePageTool(s.wikiPageService, wikiKBIDs, s.knowledgeService)
|
||||
case tools.ToolWikiReplaceText:
|
||||
toolToRegister = tools.NewWikiReplaceTextTool(s.wikiPageService, wikiKBIDs)
|
||||
toolToRegister = tools.NewWikiReplaceTextTool(s.wikiPageService, wikiKBIDs, s.knowledgeService)
|
||||
case tools.ToolWikiRenamePage:
|
||||
toolToRegister = tools.NewWikiRenamePageTool(s.wikiPageService, wikiKBIDs)
|
||||
case tools.ToolWikiDeletePage:
|
||||
|
||||
Reference in New Issue
Block a user