xiangning 7479521546 feat(rag): 实现RAG模块的Hybrid检索和向量索引优化
- 添加HNSW向量索引提升检索性能
- 实现Hybrid检索模式支持向量和关键词双路召回
- 采用RRF算法融合多路召回结果
- 支持ParentChild分块模式的上下文回填
- 优化向量写入采用批量SQL操作提升性能
- 添加RAG PostgreSQL连接字符串配置
- 更新部署脚本增加PGVector集成测试
- 修正表名引用从document_chunks到b_document_chunks
- 重构RagRetrievalService实现完整的检索流程
- 增加重排策略支持关键词和LLM重排
- 修改服务器部署地址配置
2026-08-11 10:59:09 +08:00

94 lines
3.6 KiB
C#
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.

using BuildFlow.Application.Integration;
using BuildFlow.Application.RagQA.Commands;
using BuildFlow.Application.RagQA.DTOs;
using BuildFlow.Domain.Entities.KnowledgeBase;
using BuildFlow.Domain.Enums;
using BuildFlow.Infrastructure.Persistence;
using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using MediatR;
using Moq;
using Workflow.Application.Integration.Commands;
using Workflow.Application.Integration.DTOs;
using Xunit;
namespace BuildFlow.Rag.Tests;
/// <summary>
/// AI Bot 问答处理器测试(IM @AI → RAG/Workflow 串联)。
/// 用 Mock&lt;IMediator&gt; 模拟 RAG 查询与工作流触发,覆盖:无知识库、工作流意图触发、RAG 路径返回答案。
/// </summary>
public class AiBotHandlerTests
{
[Fact]
public async Task AnswerAsync_NoKnowledgeBase_ReturnsWarning()
{
await using var db = CreateDb();
var mediator = new Mock<IMediator>();
var handler = new AiBotHandler(mediator.Object, db);
var answer = await handler.AnswerAsync("知识库里有什么", Guid.NewGuid(), Guid.NewGuid(), CancellationToken.None);
answer.Should().Contain("暂无可用知识库");
mediator.Verify(m => m.Send(It.IsAny<RAGQueryCommand>(), It.IsAny<CancellationToken>()), Times.Never);
}
[Fact]
public async Task AnswerAsync_WorkflowIntentDetected_TriggersWorkflow()
{
await using var db = CreateDb();
var instanceId = Guid.NewGuid();
var mediator = new Mock<IMediator>();
mediator.Setup(m => m.Send(It.IsAny<StartWorkflowByNameCommand>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new WorkflowTriggerResultDto(true, instanceId, "报销流程", null));
var handler = new AiBotHandler(mediator.Object, db);
var answer = await handler.AnswerAsync("帮我发起报销流程", Guid.NewGuid(), Guid.NewGuid(), CancellationToken.None);
answer.Should().Contain("已为您发起");
answer.Should().Contain(instanceId.ToString());
mediator.Verify(m => m.Send(
It.Is<StartWorkflowByNameCommand>(c => c.DefinitionKey == "报销"),
It.IsAny<CancellationToken>()), Times.Once);
}
[Fact]
public async Task AnswerAsync_RagPath_ReturnsAnswer()
{
await using var db = CreateDb();
var kbId = Guid.NewGuid();
db.KnowledgeBases.Add(new KnowledgeBaseEntity
{
Id = kbId,
Name = "测试知识库",
Status = KnowledgeBaseStatusEnum.Active,
CreatedBy = "test",
UpdatedBy = "test",
OperatorIP = "127.0.0.1",
});
await db.SaveChangesAsync();
var mediator = new Mock<IMediator>();
mediator.Setup(m => m.Send(It.IsAny<RAGQueryCommand>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new RAGQueryResponse("这是知识库答案", []));
var handler = new AiBotHandler(mediator.Object, db);
var answer = await handler.AnswerAsync("知识库里有什么", Guid.NewGuid(), Guid.NewGuid(), CancellationToken.None);
answer.Should().Be("这是知识库答案");
mediator.Verify(m => m.Send(
It.Is<RAGQueryCommand>(c => c.KnowledgeBaseId == kbId),
It.IsAny<CancellationToken>()), Times.Once);
}
private static RagDbContext CreateDb()
{
var options = new DbContextOptionsBuilder<RagDbContext>()
.UseInMemoryDatabase($"rag-aibot-{Guid.NewGuid():N}")
.Options;
var db = new RagDbContext(options);
db.SavingChanges += (_, _) => RagTestDb.FillAudit(db);
return db;
}
}