- 添加HNSW向量索引提升检索性能 - 实现Hybrid检索模式支持向量和关键词双路召回 - 采用RRF算法融合多路召回结果 - 支持ParentChild分块模式的上下文回填 - 优化向量写入采用批量SQL操作提升性能 - 添加RAG PostgreSQL连接字符串配置 - 更新部署脚本增加PGVector集成测试 - 修正表名引用从document_chunks到b_document_chunks - 重构RagRetrievalService实现完整的检索流程 - 增加重排策略支持关键词和LLM重排 - 修改服务器部署地址配置
94 lines
3.6 KiB
C#
94 lines
3.6 KiB
C#
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<IMediator> 模拟 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;
|
||
}
|
||
}
|