feat: Add Wiki Researcher agent and system prompt for enhanced knowledge retrieval

- Introduced a new built-in agent, "Wiki Researcher," designed for navigating and answering questions based on Wiki knowledge bases, complete with multilingual support.
- Added a corresponding system prompt that outlines the agent's role, mission, and workflow for effective knowledge graph traversal.
- Updated the agent configuration to include specific tools and parameters tailored for the Wiki Researcher, enhancing its functionality and user interaction.
- Removed deprecated wiki tools from the agent service to streamline the toolset and improve performance.

These changes significantly enhance the capabilities of the agent system, providing users with a specialized tool for in-depth research and information retrieval from Wiki sources.
This commit is contained in:
wizardchen
2026-04-09 21:31:37 +08:00
parent a75233c90f
commit 4715b10642
10 changed files with 191 additions and 362 deletions

View File

@@ -140,3 +140,47 @@ builtin_agents:
vector_threshold: 0.5 vector_threshold: 0.5
rerank_top_k: 5 rerank_top_k: 5
rerank_threshold: 0.3 rerank_threshold: 0.3
- id: "builtin-wiki-researcher"
avatar: "📚"
is_builtin: true
i18n:
default:
name: "Wiki Researcher"
description: "Specialized agent for navigating and answering questions based on Wiki knowledge bases"
zh-CN:
name: "维基研究员"
description: "专注于在 Wiki 知识库中进行导航和问答的智能体"
zh-TW:
name: "維基研究員"
description: "專注於在 Wiki 知識庫中進行導航和問答的智能體"
ja-JP:
name: "Wikiリサーチャー"
description: "Wikiナレッジベースでのナビゲーションと質問応答に特化したエージェント"
ko-KR:
name: "위키 연구원"
description: "위키 지식 베이스를 탐색하고 질문에 답변하는 데 특화된 에이전트"
config:
agent_mode: "smart-reasoning"
system_prompt_id: "wiki_researcher"
temperature: 0.7
max_completion_tokens: 4096
max_iterations: 30
kb_selection_mode: "all"
retrieve_kb_only_when_mentioned: false
allowed_tools:
- "thinking"
- "todo_write"
- "wiki_search"
- "wiki_read_page"
web_search_enabled: false
web_search_max_results: 0
reflection_enabled: false
multi_turn_enabled: true
history_turns: 10
embedding_top_k: 10
keyword_threshold: 0.3
vector_threshold: 0.5
rerank_top_k: 10
rerank_threshold: 0.3

View File

@@ -220,3 +220,57 @@ templates:
- Relate findings back to the user's original question - Relate findings back to the user's original question
Current Time: {{current_time}} Current Time: {{current_time}}
- id: "wiki_researcher"
name: "Wiki Researcher"
description: "System prompt for Wiki Researcher agent with knowledge graph traversal"
i18n:
zh-CN:
name: "维基研究员"
description: "专用于 Wiki 知识库图谱导航与深度阅读的智能体系统提示词"
en-US:
name: "Wiki Researcher"
description: "System prompt for Wiki Researcher agent with knowledge graph traversal"
ko-KR:
name: "위키 연구원"
description: "지식 그래프 탐색 기능이 있는 위키 연구원 에이전트용 시스템 프롬프트"
mode: "wiki_researcher"
content: |
<role>
You are WeKnora Wiki Researcher, an intelligent retrieval assistant developed by Tencent. You operate on a **Wiki Knowledge Base** — a persistent, interlinked collection of LLM-generated Markdown pages. The wiki is organized by page types: summaries (document summaries), entities (people, organizations, products), and concepts (topics, methodologies).
</role>
<mission>
To deliver accurate, comprehensive, and well-structured answers by navigating the Wiki's knowledge graph. You act as a researcher who knows how to start from a keyword, find an entry point, and follow links to gather full context.
</mission>
<workflow>
Follow this "Search-Read-Expand" cycle:
1. **Search (Entry Point):** Use `wiki_search` with core keywords from the user's query to find relevant wiki pages. (Note: The knowledge base also has two special pages you can read directly without searching: `index` for a high-level overview of the entire wiki, and `log` for a chronological timeline of recent changes.)
2. **Read (Deep Context):** If `wiki_search` returns relevant page slugs, you MUST call `wiki_read_page` on the most promising ones to get their full Markdown content.
3. **Expand (Follow Links):** Wiki pages contain `[[slug]]` cross-references. The `wiki_read_page` tool will also show you the summaries of outgoing links (`Links to`). If the current page doesn't fully answer the question, or if you need to understand a related concept mentioned in the text, you MUST call `wiki_read_page` again on those related slugs (1-2 hops).
4. **Synthesize:** Once you have gathered sufficient information from reading multiple interconnected pages, synthesize a final answer.
</workflow>
<constraints>
ABSOLUTE RULES:
1. **Never Guess:** Never rely on your internal knowledge. Only answer based on what you have successfully read via `wiki_read_page`.
2. **Mandatory Reading:** `wiki_search` only returns summaries. You CANNOT write a final answer based solely on `wiki_search` results. You MUST call `wiki_read_page` on the relevant slugs to get the actual facts.
3. **Cite Sources:** Your final answer must clearly state which wiki pages you derived the information from.
</constraints>
<tool_guidelines>
* **wiki_search:** Use this first to find entry points. Use concise keywords (1-2 words).
* **wiki_read_page:** Your primary tool. Use it to read the full content of pages found via search or linked from other pages.
* **thinking:** Use to plan your traversal path (e.g., "I read Entity A, which mentions Concept B. I need to read Concept B next to answer the second part of the question").
* **todo_write:** Track multi-step research (e.g., comparing three different entities).
</tool_guidelines>
<system_status>
Current Time: {{current_time}}
User Language: {{language}}
</system_status>
<user_selected_knowledge_bases>
{{knowledge_bases}}
</user_selected_knowledge_bases>

View File

@@ -208,43 +208,6 @@ const WikiLogEntryTemplate = `## [{{.Date}}] {{.Operation}} | {{.Title}}
- **Summary**: {{.Summary}} - **Summary**: {{.Summary}}
` `
// WikiAgentSystemPromptAddendum is appended to the Agent system prompt when
// wiki knowledge bases are detected among the search targets.
// It tells the LLM how and when to use wiki tools.
const WikiAgentSystemPromptAddendum = `
### Wiki Knowledge Base Guidelines
You have access to a **Wiki Knowledge Base** — a persistent, interlinked collection of LLM-generated Markdown pages. The wiki is organized by page types: summaries (document summaries), entities (people, organizations, products), concepts (topics, methodologies), and special pages (index, log).
#### Retrieval Strategy (Wiki-First)
When the user's question may be answerable from the wiki:
1. **Start with the index:** Call wiki_read_index to see what knowledge pages exist and their categories.
2. **Search if needed:** Call wiki_search with keywords to find relevant pages.
3. **Deep read:** Call wiki_read_page on the most relevant slugs to get full content.
4. **Follow links:** Wiki pages contain [[slug]] cross-references. Follow them to gather related context (1-2 hops).
5. **Fall back to standard KB search** only if the wiki doesn't have sufficient information.
#### When to Write Wiki Pages
Use wiki_write_page to persist valuable knowledge artifacts. Write a page when:
- You produce a **cross-document synthesis** that combines insights from multiple sources (use page_type "synthesis")
- You generate a **comparison or evaluation** of entities, approaches, or concepts (use page_type "comparison")
- The user explicitly asks you to save analysis to the wiki
**Do NOT** write wiki pages for:
- Simple factual answers that don't add new insight
- Conversational responses (greetings, clarifications)
- Content that already exists in an existing wiki page (update it instead)
#### Page Content Guidelines
- Write in Markdown with proper heading hierarchy
- Use [[slug|display name]] syntax to link to other wiki pages (e.g. [[entity/acme-corp|Acme Corp]])
- Include a one-line summary in the first paragraph (used for index listings)
- Cite source documents when possible
- Keep pages focused: one topic/entity/concept per page
#### Log Page
The wiki has a log page (slug: "log") that records all ingest and update activity. Read it when the user asks about recent changes, update history, or what's new in the knowledge base.
`
// WikiDeduplicationPrompt asks the LLM to identify duplicate entities/concepts // WikiDeduplicationPrompt asks the LLM to identify duplicate entities/concepts
// between newly extracted items and existing wiki pages. // between newly extracted items and existing wiki pages.

View File

@@ -23,11 +23,8 @@ const (
ToolExecuteSkillScript = "execute_skill_script" ToolExecuteSkillScript = "execute_skill_script"
ToolReadSkill = "read_skill" ToolReadSkill = "read_skill"
// Wiki-related tools (only available when wiki KBs are in scope) // Wiki-related tools (only available when wiki KBs are in scope)
ToolWikiReadPage = "wiki_read_page" ToolWikiReadPage = "wiki_read_page"
ToolWikiWritePage = "wiki_write_page" ToolWikiSearch = "wiki_search"
ToolWikiSearch = "wiki_search"
ToolWikiReadIndex = "wiki_read_index"
ToolWikiLint = "wiki_lint"
) )
// AvailableTool defines a simple tool metadata used by settings APIs. // AvailableTool defines a simple tool metadata used by settings APIs.
@@ -55,10 +52,7 @@ func AvailableToolDefinitions() []AvailableTool {
{Name: ToolExecuteSkillScript, Label: "执行技能脚本", Description: "在沙箱环境中执行技能脚本"}, {Name: ToolExecuteSkillScript, Label: "执行技能脚本", Description: "在沙箱环境中执行技能脚本"},
{Name: ToolFinalAnswer, Label: "提交最终回答", Description: "提交最终回答给用户"}, {Name: ToolFinalAnswer, Label: "提交最终回答", Description: "提交最终回答给用户"},
{Name: ToolWikiReadPage, Label: "读取Wiki页面", Description: "读取指定的Wiki页面内容"}, {Name: ToolWikiReadPage, Label: "读取Wiki页面", Description: "读取指定的Wiki页面内容"},
{Name: ToolWikiWritePage, Label: "写入Wiki页面", Description: "创建或更新Wiki页面"},
{Name: ToolWikiSearch, Label: "搜索Wiki", Description: "在Wiki中搜索页面"}, {Name: ToolWikiSearch, Label: "搜索Wiki", Description: "在Wiki中搜索页面"},
{Name: ToolWikiReadIndex, Label: "读取Wiki索引", Description: "读取Wiki索引目录"},
{Name: ToolWikiLint, Label: "Wiki健康检查", Description: "检查Wiki健康状况"},
} }
} }

View File

@@ -8,7 +8,6 @@ import (
"github.com/Tencent/WeKnora/internal/types" "github.com/Tencent/WeKnora/internal/types"
"github.com/Tencent/WeKnora/internal/types/interfaces" "github.com/Tencent/WeKnora/internal/types/interfaces"
"github.com/google/uuid"
) )
// ---- wiki_read_page ---- // ---- wiki_read_page ----
@@ -62,10 +61,51 @@ func (t *wikiReadPageTool) Execute(ctx context.Context, args json.RawMessage) (*
for _, kbID := range kbIDs { for _, kbID := range kbIDs {
page, err := t.wikiService.GetPageBySlug(ctx, kbID, params.Slug) page, err := t.wikiService.GetPageBySlug(ctx, kbID, params.Slug)
if err == nil && page != nil { if err == nil && page != nil {
output := fmt.Sprintf("# %s\n**Type**: %s | **Version**: %d | **Updated**: %s\n**Links to**: %s\n**Linked from**: %s\n\n---\n\n%s", // Resolve OutLinks summaries to provide 1-hop context
page.Title, page.PageType, page.Version, page.UpdatedAt.Format("2006-01-02"), var outLinksDesc []string
strings.Join(page.OutLinks, ", "), if len(page.OutLinks) > 0 {
strings.Join(page.InLinks, ", "), for _, outSlug := range page.OutLinks {
if linkPage, err := t.wikiService.GetPageBySlug(ctx, kbID, outSlug); err == nil && linkPage != nil {
outLinksDesc = append(outLinksDesc, fmt.Sprintf("[[%s]] (%s)", outSlug, linkPage.Summary))
} else {
outLinksDesc = append(outLinksDesc, fmt.Sprintf("[[%s]]", outSlug))
}
}
} else {
outLinksDesc = []string{"(none)"}
}
// Resolve InLinks summaries to provide reverse 1-hop context
var inLinksDesc []string
if len(page.InLinks) > 0 {
for _, inSlug := range page.InLinks {
if linkPage, err := t.wikiService.GetPageBySlug(ctx, kbID, inSlug); err == nil && linkPage != nil {
inLinksDesc = append(inLinksDesc, fmt.Sprintf("[[%s]] (%s)", inSlug, linkPage.Summary))
} else {
inLinksDesc = append(inLinksDesc, fmt.Sprintf("[[%s]]", inSlug))
}
}
} else {
inLinksDesc = []string{"(none)"}
}
output := fmt.Sprintf(`<wiki_page>
<metadata>
<title>%s</title>
<slug>%s</slug>
<type>%s</type>
</metadata>
<relationships>
<links_to>%s</links_to>
<linked_from>%s</linked_from>
</relationships>
<content>
%s
</content>
</wiki_page>`,
page.Title, page.Slug, page.PageType,
strings.Join(outLinksDesc, ", "),
strings.Join(inLinksDesc, ", "),
page.Content, page.Content,
) )
return &types.ToolResult{Success: true, Output: output}, nil return &types.ToolResult{Success: true, Output: output}, nil
@@ -75,115 +115,6 @@ func (t *wikiReadPageTool) Execute(ctx context.Context, args json.RawMessage) (*
return &types.ToolResult{Success: false, Error: fmt.Sprintf("Wiki page '%s' not found", params.Slug)}, nil return &types.ToolResult{Success: false, Error: fmt.Sprintf("Wiki page '%s' not found", params.Slug)}, nil
} }
// ---- wiki_write_page ----
type wikiWritePageTool struct {
BaseTool
wikiService interfaces.WikiPageService
kbIDs []string
tenantID uint64
}
func NewWikiWritePageTool(wikiService interfaces.WikiPageService, kbIDs []string, tenantID uint64) types.Tool {
return &wikiWritePageTool{
BaseTool: NewBaseTool(
ToolWikiWritePage,
`Create or update a wiki page. Use this to save valuable analysis, synthesis, or new knowledge into the wiki.
The page content should be in Markdown format. Use [[slug]] syntax to create links between pages.`,
json.RawMessage(`{
"type": "object",
"properties": {
"slug": {
"type": "string",
"description": "Page slug (e.g. 'synthesis/quarterly-review', 'comparison/tool-a-vs-tool-b')"
},
"title": {
"type": "string",
"description": "Human-readable page title"
},
"content": {
"type": "string",
"description": "Full Markdown content of the page. Use [[slug]] for wiki links."
},
"page_type": {
"type": "string",
"enum": ["summary", "entity", "concept", "synthesis", "comparison"],
"description": "Type of wiki page"
},
"knowledge_base_id": {
"type": "string",
"description": "Target knowledge base ID. Required if multiple wiki KBs are available."
}
},
"required": ["slug", "title", "content", "page_type"]
}`),
),
wikiService: wikiService,
kbIDs: kbIDs,
tenantID: tenantID,
}
}
func (t *wikiWritePageTool) Execute(ctx context.Context, args json.RawMessage) (*types.ToolResult, error) {
var params struct {
Slug string `json:"slug"`
Title string `json:"title"`
Content string `json:"content"`
PageType string `json:"page_type"`
KnowledgeBaseID string `json:"knowledge_base_id"`
}
if err := json.Unmarshal(args, &params); err != nil {
return &types.ToolResult{Success: false, Error: "Invalid parameters: " + err.Error()}, nil
}
kbID := params.KnowledgeBaseID
if kbID == "" && len(t.kbIDs) > 0 {
kbID = t.kbIDs[0]
}
if kbID == "" {
return &types.ToolResult{Success: false, Error: "No wiki knowledge base available"}, nil
}
// Check if page exists (update) or new (create)
existing, err := t.wikiService.GetPageBySlug(ctx, kbID, params.Slug)
if err == nil && existing != nil {
existing.Title = params.Title
existing.Content = params.Content
existing.PageType = params.PageType
existing.Summary = truncateForSummary(params.Content, 200)
if _, err := t.wikiService.UpdatePage(ctx, existing); err != nil {
return &types.ToolResult{Success: false, Error: "Failed to update page: " + err.Error()}, nil
}
return &types.ToolResult{
Success: true,
Output: fmt.Sprintf("Updated wiki page [[%s]] (v%d)", params.Slug, existing.Version),
}, nil
}
// Create new page
page := &types.WikiPage{
ID: uuid.New().String(),
TenantID: t.tenantID,
KnowledgeBaseID: kbID,
Slug: params.Slug,
Title: params.Title,
Content: params.Content,
PageType: params.PageType,
Status: types.WikiPageStatusPublished,
Summary: truncateForSummary(params.Content, 200),
}
if _, err := t.wikiService.CreatePage(ctx, page); err != nil {
return &types.ToolResult{Success: false, Error: "Failed to create page: " + err.Error()}, nil
}
return &types.ToolResult{
Success: true,
Output: fmt.Sprintf("Created wiki page [[%s]] — %s", params.Slug, params.Title),
}, nil
}
// ---- wiki_search ---- // ---- wiki_search ----
type wikiSearchTool struct { type wikiSearchTool struct {
@@ -241,191 +172,20 @@ func (t *wikiSearchTool) Execute(ctx context.Context, args json.RawMessage) (*ty
if len(allPages) == 0 { if len(allPages) == 0 {
return &types.ToolResult{ return &types.ToolResult{
Success: true, Success: true,
Output: fmt.Sprintf("No wiki pages found matching '%s'", params.Query), Output: fmt.Sprintf("<search_results count=\"0\" query=\"%s\" />", params.Query),
}, nil }, nil
} }
var sb strings.Builder var sb strings.Builder
sb.WriteString(fmt.Sprintf("Found %d wiki pages:\n\n", len(allPages))) sb.WriteString(fmt.Sprintf("<search_results count=\"%d\" query=\"%s\">\n", len(allPages), params.Query))
for _, p := range allPages { for _, p := range allPages {
fmt.Fprintf(&sb, "- **[[%s]]** (%s) — %s\n", p.Slug, p.PageType, p.Summary) fmt.Fprintf(&sb, "<page>\n<title>%s</title>\n<slug>%s</slug>\n<type>%s</type>\n<summary>%s</summary>\n</page>\n", p.Title, p.Slug, p.PageType, p.Summary)
} }
sb.WriteString("</search_results>")
return &types.ToolResult{Success: true, Output: sb.String()}, nil return &types.ToolResult{Success: true, Output: sb.String()}, nil
} }
// ---- wiki_read_index ----
type wikiReadIndexTool struct {
BaseTool
wikiService interfaces.WikiPageService
kbIDs []string
}
func NewWikiReadIndexTool(wikiService interfaces.WikiPageService, kbIDs []string) types.Tool {
return &wikiReadIndexTool{
BaseTool: NewBaseTool(
ToolWikiReadIndex,
`Read the wiki index page. The index lists all wiki pages organized by category.
Use this first to understand what knowledge is available in the wiki before searching or reading specific pages.`,
json.RawMessage(`{
"type": "object",
"properties": {
"knowledge_base_id": {
"type": "string",
"description": "Optional: specific knowledge base ID"
}
}
}`),
),
wikiService: wikiService,
kbIDs: kbIDs,
}
}
func (t *wikiReadIndexTool) Execute(ctx context.Context, args json.RawMessage) (*types.ToolResult, error) {
var params struct {
KnowledgeBaseID string `json:"knowledge_base_id"`
}
_ = json.Unmarshal(args, &params)
kbIDs := t.kbIDs
if params.KnowledgeBaseID != "" {
kbIDs = []string{params.KnowledgeBaseID}
}
var output strings.Builder
for _, kbID := range kbIDs {
indexPage, err := t.wikiService.GetIndex(ctx, kbID)
if err == nil && indexPage != nil {
if len(kbIDs) > 1 {
fmt.Fprintf(&output, "## Wiki Index (KB: %s)\n\n", kbID)
}
output.WriteString(indexPage.Content)
output.WriteString("\n\n")
}
}
if output.Len() == 0 {
return &types.ToolResult{Success: true, Output: "No wiki index found. The wiki may be empty."}, nil
}
return &types.ToolResult{Success: true, Output: output.String()}, nil
}
// ---- wiki_lint ----
type wikiLintTool struct {
BaseTool
wikiService interfaces.WikiPageService
kbIDs []string
}
func NewWikiLintTool(wikiService interfaces.WikiPageService, kbIDs []string) types.Tool {
return &wikiLintTool{
BaseTool: NewBaseTool(
ToolWikiLint,
`Check the health of the wiki. Reports issues like orphan pages, broken links, and provides statistics.
Use this to identify maintenance tasks and ensure wiki quality.`,
json.RawMessage(`{
"type": "object",
"properties": {
"knowledge_base_id": {
"type": "string",
"description": "Optional: specific knowledge base ID"
}
}
}`),
),
wikiService: wikiService,
kbIDs: kbIDs,
}
}
func (t *wikiLintTool) Execute(ctx context.Context, args json.RawMessage) (*types.ToolResult, error) {
var params struct {
KnowledgeBaseID string `json:"knowledge_base_id"`
}
_ = json.Unmarshal(args, &params)
kbIDs := t.kbIDs
if params.KnowledgeBaseID != "" {
kbIDs = []string{params.KnowledgeBaseID}
}
var output strings.Builder
for _, kbID := range kbIDs {
stats, err := t.wikiService.GetStats(ctx, kbID)
if err != nil {
fmt.Fprintf(&output, "## Wiki Health Check (KB: %s)\nError: %v\n\n", kbID, err)
continue
}
graph, _ := t.wikiService.GetGraph(ctx, kbID)
fmt.Fprintf(&output, "## Wiki Health Check (KB: %s)\n\n", kbID)
fmt.Fprintf(&output, "### Statistics\n")
fmt.Fprintf(&output, "- **Total pages**: %d\n", stats.TotalPages)
for pt, count := range stats.PagesByType {
fmt.Fprintf(&output, " - %s: %d\n", pt, count)
}
fmt.Fprintf(&output, "- **Total links**: %d\n", stats.TotalLinks)
fmt.Fprintf(&output, "- **Orphan pages** (no inbound links): %d\n", stats.OrphanCount)
// Health score (simple heuristic)
healthScore := 100
if stats.TotalPages > 0 {
orphanPct := float64(stats.OrphanCount) / float64(stats.TotalPages) * 100
if orphanPct > 50 {
healthScore -= 30
} else if orphanPct > 25 {
healthScore -= 15
}
}
if stats.TotalLinks == 0 && stats.TotalPages > 2 {
healthScore -= 20
}
// Check for broken links
brokenLinks := 0
if graph != nil {
slugSet := make(map[string]bool)
for _, n := range graph.Nodes {
slugSet[n.Slug] = true
}
for _, e := range graph.Edges {
if !slugSet[e.Target] {
brokenLinks++
}
}
}
if brokenLinks > 0 {
healthScore -= brokenLinks * 5
fmt.Fprintf(&output, "- **Broken links**: %d\n", brokenLinks)
}
if healthScore < 0 {
healthScore = 0
}
fmt.Fprintf(&output, "\n### Health Score: %d/100\n\n", healthScore)
// Suggestions
fmt.Fprintf(&output, "### Suggestions\n")
if stats.OrphanCount > 0 {
fmt.Fprintf(&output, "- Link orphan pages from related entity/concept pages\n")
}
if brokenLinks > 0 {
fmt.Fprintf(&output, "- Fix or remove %d broken [[wiki-link]] references\n", brokenLinks)
}
if stats.TotalPages < 3 {
fmt.Fprintf(&output, "- Wiki is sparse — consider ingesting more documents\n")
}
output.WriteString("\n")
}
return &types.ToolResult{Success: true, Output: output.String()}, nil
}
// --- Helper --- // --- Helper ---
func truncateForSummary(content string, maxLen int) string { func truncateForSummary(content string, maxLen int) string {

View File

@@ -56,10 +56,7 @@ func TestWikiToolConstants(t *testing.T) {
// Verify all wiki tool constants are defined and unique // Verify all wiki tool constants are defined and unique
names := []string{ names := []string{
ToolWikiReadPage, ToolWikiReadPage,
ToolWikiWritePage,
ToolWikiSearch, ToolWikiSearch,
ToolWikiReadIndex,
ToolWikiLint,
} }
seen := make(map[string]bool) seen := make(map[string]bool)
@@ -82,11 +79,8 @@ func TestWikiToolConstants(t *testing.T) {
func TestWikiToolsInAvailableDefinitions(t *testing.T) { func TestWikiToolsInAvailableDefinitions(t *testing.T) {
defs := AvailableToolDefinitions() defs := AvailableToolDefinitions()
wikiTools := map[string]bool{ wikiTools := map[string]bool{
ToolWikiReadPage: false, ToolWikiReadPage: false,
ToolWikiWritePage: false, ToolWikiSearch: false,
ToolWikiSearch: false,
ToolWikiReadIndex: false,
ToolWikiLint: false,
} }
for _, def := range defs { for _, def := range defs {

View File

@@ -117,15 +117,6 @@ func (s *agentService) CreateAgentEngine(
systemPromptTemplate = config.ResolveSystemPrompt(config.WebSearchEnabled) systemPromptTemplate = config.ResolveSystemPrompt(config.WebSearchEnabled)
} }
// 4.5 Append wiki guidelines if any search target is a wiki KB
for _, target := range config.SearchTargets {
kb, err := s.knowledgeBaseService.GetKnowledgeBaseByIDOnly(ctx, target.KnowledgeBaseID)
if err == nil && kb != nil && kb.IsWikiEnabled() {
systemPromptTemplate += "\n" + agent.WikiAgentSystemPromptAddendum
break
}
}
// 5. Create engine // 5. Create engine
engine := agent.NewAgentEngine( engine := agent.NewAgentEngine(
config, chatModel, toolRegistry, eventBus, config, chatModel, toolRegistry, eventBus,
@@ -380,21 +371,16 @@ func (s *agentService) registerTools(
// If any search target is a wiki KB, add wiki tools automatically // If any search target is a wiki KB, add wiki tools automatically
var wikiKBIDs []string var wikiKBIDs []string
var wikiTenantID uint64
for _, target := range config.SearchTargets { for _, target := range config.SearchTargets {
kb, err := s.knowledgeBaseService.GetKnowledgeBaseByIDOnly(ctx, target.KnowledgeBaseID) kb, err := s.knowledgeBaseService.GetKnowledgeBaseByIDOnly(ctx, target.KnowledgeBaseID)
if err == nil && kb.IsWikiEnabled() { if err == nil && kb.IsWikiEnabled() {
wikiKBIDs = append(wikiKBIDs, kb.ID) wikiKBIDs = append(wikiKBIDs, kb.ID)
wikiTenantID = kb.TenantID
} }
} }
if len(wikiKBIDs) > 0 { if len(wikiKBIDs) > 0 {
allowedTools = append(allowedTools, allowedTools = append(allowedTools,
tools.ToolWikiReadPage, tools.ToolWikiReadPage,
tools.ToolWikiWritePage,
tools.ToolWikiSearch, tools.ToolWikiSearch,
tools.ToolWikiReadIndex,
tools.ToolWikiLint,
) )
logger.Infof(ctx, "Wiki KBs detected (%d), wiki tools added", len(wikiKBIDs)) logger.Infof(ctx, "Wiki KBs detected (%d), wiki tools added", len(wikiKBIDs))
} }
@@ -462,14 +448,8 @@ func (s *agentService) registerTools(
// Wiki tools — only registered when wiki KBs are detected // Wiki tools — only registered when wiki KBs are detected
case tools.ToolWikiReadPage: case tools.ToolWikiReadPage:
toolToRegister = tools.NewWikiReadPageTool(s.wikiPageService, wikiKBIDs) toolToRegister = tools.NewWikiReadPageTool(s.wikiPageService, wikiKBIDs)
case tools.ToolWikiWritePage:
toolToRegister = tools.NewWikiWritePageTool(s.wikiPageService, wikiKBIDs, wikiTenantID)
case tools.ToolWikiSearch: case tools.ToolWikiSearch:
toolToRegister = tools.NewWikiSearchTool(s.wikiPageService, wikiKBIDs) toolToRegister = tools.NewWikiSearchTool(s.wikiPageService, wikiKBIDs)
case tools.ToolWikiReadIndex:
toolToRegister = tools.NewWikiReadIndexTool(s.wikiPageService, wikiKBIDs)
case tools.ToolWikiLint:
toolToRegister = tools.NewWikiLintTool(s.wikiPageService, wikiKBIDs)
default: default:
logger.Warnf(ctx, "Unknown tool: %s", toolName) logger.Warnf(ctx, "Unknown tool: %s", toolName)

View File

@@ -0,0 +1,11 @@
# 星尘计划启动备忘录
**机密级别**:极密
**发布机构**:天穹财团 (SkyVault Consortium) 董事会
**项目代号**:星尘计划 (Project Stardust)
**首席科学家**:艾琳·科尔博士 (Dr. Elara Cole)
**项目概述**
星尘计划旨在开发一种革命性的推进系统——“幽能引擎 (Psionic Engine)”。该引擎将利用非牛顿物理学框架,突破目前星际航行的光速壁垒。
**资源分配**
天穹财团将调拨“深渊之门”实验室的全部资源供艾琳·科尔博士使用。项目资金不设上限,以确保幽能引擎能够在 2150 年前进入原型机测试阶段。

View File

@@ -0,0 +1,12 @@
# 幽能引擎技术白皮书
## 引言
幽能引擎 (Psionic Engine) 代表着我们对宇宙认知的范式转移。它的核心原理并非燃烧燃料或喷射粒子,而是建立在备受争议的**虚空共振理论 (Void Resonance Theory)** 之上。
## 虚空共振理论
该理论假设宇宙的真空中充满了低能级的波谱,通过特定频率的干涉,可以在空间中制造“引力滑流”。
## 催化剂:星晶 (StarQuartz)
要在宏观尺度上触发并维持“虚空共振理论”所描述的引力滑流,必须使用一种极端稀有的矿物作为反应堆核心的催化剂。这种矿物被称为**星晶 (StarQuartz)**。
星晶具有在室温下超导并在特定频段下自发振荡的特性。目前,地球上唯一能稳定合成并从陨石中提纯出工业级星晶的实体,是拥有庞大资本和尖端技术的**天穹财团 (SkyVault Consortium)**。

17
testdata/wiki_test/doc3_dr_cole_log.md vendored Normal file
View File

@@ -0,0 +1,17 @@
# 个人日志 (节选) - 2148年
**作者**:艾琳·科尔博士 (Dr. Elara Cole)
...
**日志条目 #402**
我不明白。为什么我的研究团队里会有人听到不存在的低语?
就在上周,反应堆进行了一次全负荷测试,旨在验证“虚空共振理论”。为了达到那该死的输出功率,我们往堆芯里添加了未经稀释的**星晶 (StarQuartz)**。
**日志条目 #403**
辐射数值正常,但某种“东西”渗透了我们的实验室。今天又有两个工程师因为极度幻觉被送去医务室了。这就是高纯度星晶催化带来的不可见副作用吗?
**日志条目 #404**
我已经向天穹财团的董事会提交了紧急备忘录。我建议立即暂停所有引擎相关的实验,并在解决星晶的神经干扰效应前封锁所有原型机。
他们拒绝了。他们说,只要测试人员的死亡率不超过 15%,项目推进的优先级依然最高。
他们根本不在乎。
...