Files
Enterprise-Knowledge-Base/internal/agent/tools/list_knowledge_chunks.go
T
wizardchen 3bf2a14499 feat: Enhance agent configuration and localization for web search prompts
- Updated agent configuration to support separate system prompts for web search enabled and disabled states.
- Removed deprecated agent configuration parameters to streamline settings management.
- Enhanced UI components in AgentSettings.vue to allow configuration of custom prompts based on web search status.
- Improved localization in English, Russian, and Chinese for new prompt settings and UI elements.
- Refactored related API and service logic to accommodate changes in agent configuration structure.
2025-11-19 19:27:25 +08:00

231 lines
6.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package tools
import (
"context"
"fmt"
"strings"
"github.com/Tencent/WeKnora/internal/types"
"github.com/Tencent/WeKnora/internal/types/interfaces"
)
// ListKnowledgeChunksTool retrieves chunk snapshots for a specific knowledge document.
type ListKnowledgeChunksTool struct {
BaseTool
tenantID uint
chunkService interfaces.ChunkService
knowledgeService interfaces.KnowledgeService
}
// NewListKnowledgeChunksTool creates a new tool instance.
func NewListKnowledgeChunksTool(
tenantID uint,
knowledgeService interfaces.KnowledgeService,
chunkService interfaces.ChunkService,
) *ListKnowledgeChunksTool {
description := `Retrieve paged chunks for a document (knowledge) by knowledge_id.
## When to Use
- Need deterministic chunk previews for a known document
- Want to quickly confirm how many chunks a document contains
- Require surrounding context around a chunk_index returned by search results
- Need content snippets without running an additional search query
Avoid when:
- You don't know the knowledge_id (use knowledge_search first)
## Parameters
- knowledge_id (required): Target document/knowledge ID
- limit (optional): Number of chunks to fetch (default 20, max 100).
- offset (optional): Offset to start fetching chunks from (default 0).`
return &ListKnowledgeChunksTool{
BaseTool: NewBaseTool("list_knowledge_chunks", description),
tenantID: tenantID,
chunkService: chunkService,
knowledgeService: knowledgeService,
}
}
// Parameters returns the JSON schema describing accepted arguments.
func (t *ListKnowledgeChunksTool) Parameters() map[string]interface{} {
return map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"knowledge_id": map[string]interface{}{
"type": "string",
"description": "Knowledge/document ID to inspect",
},
"limit": map[string]interface{}{
"type": "integer",
"description": "Number of chunks to fetch (default 20, max 100)",
"default": 20,
"minimum": 1,
"maximum": 100,
},
"offset": map[string]interface{}{
"type": "integer",
"description": "Offset to start fetching chunks from (default 0)",
"default": 0,
"minimum": 0,
},
},
"required": []string{"knowledge_id", "limit", "offset"},
}
}
// Execute performs the chunk fetch against the chunk service.
func (t *ListKnowledgeChunksTool) Execute(ctx context.Context, args map[string]interface{}) (*types.ToolResult, error) {
knowledgeID, ok := args["knowledge_id"].(string)
if !ok || strings.TrimSpace(knowledgeID) == "" {
return &types.ToolResult{
Success: false,
Error: "knowledge_id is required",
}, fmt.Errorf("knowledge_id is required")
}
knowledgeID = strings.TrimSpace(knowledgeID)
chunkLimit := 20
offset := 0
if rawLimit, exists := args["limit"]; exists {
switch v := rawLimit.(type) {
case float64:
chunkLimit = int(v)
case int:
chunkLimit = v
}
}
if rawOffset, exists := args["offset"]; exists {
switch v := rawOffset.(type) {
case float64:
offset = int(v)
case int:
offset = v
}
}
if offset < 0 {
offset = 0
}
pagination := &types.Pagination{
Page: offset/chunkLimit + 1,
PageSize: chunkLimit,
}
chunks, total, err := t.chunkService.GetRepository().ListPagedChunksByKnowledgeID(ctx,
t.tenantID, knowledgeID, pagination, []types.ChunkType{types.ChunkTypeText}, "")
if err != nil {
return &types.ToolResult{
Success: false,
Error: fmt.Sprintf("failed to list chunks: %v", err),
}, err
}
if chunks == nil {
return &types.ToolResult{
Success: false,
Error: "chunk query returned no data",
}, fmt.Errorf("chunk query returned no data")
}
totalChunks := total
fetched := len(chunks)
knowledgeTitle := t.lookupKnowledgeTitle(ctx, knowledgeID)
output := t.buildOutput(knowledgeID, knowledgeTitle, totalChunks, fetched, chunkLimit, chunks)
formattedChunks := make([]map[string]interface{}, 0, len(chunks))
for idx, c := range chunks {
formattedChunks = append(formattedChunks, map[string]interface{}{
"seq": idx + 1,
"chunk_id": c.ID,
"chunk_index": c.ChunkIndex,
"content": c.Content,
"chunk_type": c.ChunkType,
"knowledge_id": c.KnowledgeID,
"knowledge_base": c.KnowledgeBaseID,
"start_at": c.StartAt,
"end_at": c.EndAt,
"parent_chunk_id": c.ParentChunkID,
})
}
return &types.ToolResult{
Success: true,
Output: output,
Data: map[string]interface{}{
"knowledge_id": knowledgeID,
"knowledge_title": knowledgeTitle,
"total_chunks": totalChunks,
"fetched_chunks": fetched,
"page": pagination.Page,
"page_size": pagination.PageSize,
"chunks": formattedChunks,
},
}, nil
}
func (t *ListKnowledgeChunksTool) lookupKnowledgeTitle(ctx context.Context, knowledgeID string) string {
if t.knowledgeService == nil {
return ""
}
knowledge, err := t.knowledgeService.GetKnowledgeByID(ctx, knowledgeID)
if err != nil || knowledge == nil {
return ""
}
return strings.TrimSpace(knowledge.Title)
}
func (t *ListKnowledgeChunksTool) buildOutput(
knowledgeID string,
knowledgeTitle string,
total int64,
fetched int,
chunkLimit int,
chunks []*types.Chunk,
) string {
builder := &strings.Builder{}
builder.WriteString("=== 知识文档分块 ===\n\n")
if knowledgeTitle != "" {
builder.WriteString(fmt.Sprintf("文档: %s (%s)\n", knowledgeTitle, knowledgeID))
} else {
builder.WriteString(fmt.Sprintf("文档 ID: %s\n", knowledgeID))
}
builder.WriteString(fmt.Sprintf("总分块数: %d\n", total))
builder.WriteString(fmt.Sprintf("本次拉取: %d 条(offset=%d)\n\n", fetched, chunkLimit))
if fetched == 0 {
builder.WriteString("未找到任何分块,请确认文档是否已完成解析。\n")
if total > 0 {
builder.WriteString("文档存在但当前页数据为空,请检查分页参数。\n")
}
return builder.String()
}
builder.WriteString("=== 分块内容预览 ===\n\n")
for idx, c := range chunks {
builder.WriteString(fmt.Sprintf("Chunk #%d (Index %d)\n", idx+1, c.ChunkIndex+1))
builder.WriteString(fmt.Sprintf(" chunk_id: %s\n", c.ID))
builder.WriteString(fmt.Sprintf(" 类型: %s\n", c.ChunkType))
builder.WriteString(fmt.Sprintf(" 内容: %s\n\n", summarizeContent(c.Content)))
}
if int64(fetched) < total {
builder.WriteString("提示:文档仍有更多分块,可调整 offset 或多次调用以获取全部内容。\n")
}
return builder.String()
}
func summarizeContent(content string) string {
cleaned := strings.TrimSpace(content)
if cleaned == "" {
return "(空内容)"
}
return strings.TrimSpace(string(cleaned))
}