Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
135 lines
2.9 KiB
Go
135 lines
2.9 KiB
Go
package chatpipeline
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/Tencent/WeKnora/internal/types"
|
|
)
|
|
|
|
// populateFAQAnswers populates FAQ answers for the search results
|
|
func (p *PluginMerge) populateFAQAnswers(
|
|
ctx context.Context,
|
|
chatManage *types.ChatManage,
|
|
results []*types.SearchResult,
|
|
) []*types.SearchResult {
|
|
if len(results) == 0 || p.chunkRepo == nil {
|
|
return results
|
|
}
|
|
|
|
tenantID, _ := types.TenantIDFromContext(ctx)
|
|
if tenantID == 0 && chatManage != nil {
|
|
tenantID = chatManage.TenantID
|
|
}
|
|
if tenantID == 0 {
|
|
pipelineWarn(ctx, "Merge", "faq_enrich_skip", map[string]interface{}{
|
|
"reason": "missing_tenant",
|
|
})
|
|
return results
|
|
}
|
|
|
|
chunkResultMap := make(map[string][]*types.SearchResult)
|
|
chunkIDSet := make(map[string]struct{})
|
|
for _, r := range results {
|
|
if r == nil || r.ID == "" {
|
|
continue
|
|
}
|
|
if r.ChunkType != string(types.ChunkTypeFAQ) {
|
|
continue
|
|
}
|
|
chunkResultMap[r.ID] = append(chunkResultMap[r.ID], r)
|
|
if _, exists := chunkIDSet[r.ID]; !exists {
|
|
chunkIDSet[r.ID] = struct{}{}
|
|
}
|
|
}
|
|
|
|
if len(chunkIDSet) == 0 {
|
|
return results
|
|
}
|
|
|
|
chunkIDs := make([]string, 0, len(chunkIDSet))
|
|
for id := range chunkIDSet {
|
|
chunkIDs = append(chunkIDs, id)
|
|
}
|
|
|
|
chunks, err := p.chunkRepo.ListChunksByID(ctx, tenantID, chunkIDs)
|
|
if err != nil {
|
|
pipelineWarn(ctx, "Merge", "faq_chunk_fetch_failed", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
return results
|
|
}
|
|
|
|
updated := 0
|
|
for _, chunk := range chunks {
|
|
if chunk == nil {
|
|
continue
|
|
}
|
|
meta, err := chunk.FAQMetadata()
|
|
if err != nil || meta == nil {
|
|
if err != nil {
|
|
pipelineWarn(ctx, "Merge", "faq_metadata_parse_failed", map[string]interface{}{
|
|
"chunk_id": chunk.ID,
|
|
"error": err.Error(),
|
|
})
|
|
}
|
|
continue
|
|
}
|
|
content := buildFAQAnswerContent(meta)
|
|
if content == "" {
|
|
continue
|
|
}
|
|
for _, r := range chunkResultMap[chunk.ID] {
|
|
if r == nil {
|
|
continue
|
|
}
|
|
r.Content = content
|
|
updated++
|
|
}
|
|
}
|
|
|
|
if updated > 0 {
|
|
pipelineInfo(ctx, "Merge", "faq_content_enriched", map[string]interface{}{
|
|
"chunk_cnt": updated,
|
|
})
|
|
}
|
|
return results
|
|
}
|
|
|
|
// buildFAQAnswerContent builds the content of a FAQ answer
|
|
func buildFAQAnswerContent(meta *types.FAQChunkMetadata) string {
|
|
if meta == nil {
|
|
return ""
|
|
}
|
|
|
|
question := strings.TrimSpace(meta.StandardQuestion)
|
|
answers := make([]string, 0, len(meta.Answers))
|
|
for _, ans := range meta.Answers {
|
|
if trimmed := strings.TrimSpace(ans); trimmed != "" {
|
|
answers = append(answers, trimmed)
|
|
}
|
|
}
|
|
|
|
if question == "" && len(answers) == 0 {
|
|
return ""
|
|
}
|
|
|
|
var builder strings.Builder
|
|
if question != "" {
|
|
builder.WriteString("Q: ")
|
|
builder.WriteString(question)
|
|
builder.WriteString("\n")
|
|
}
|
|
|
|
if len(answers) > 0 {
|
|
builder.WriteString("Answer:\n")
|
|
for _, ans := range answers {
|
|
builder.WriteString("- ")
|
|
builder.WriteString(ans)
|
|
builder.WriteString("\n")
|
|
}
|
|
}
|
|
|
|
return strings.TrimSpace(builder.String())
|
|
}
|