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

93 lines
3.3 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 System.Runtime.CompilerServices;
using BuildFlow.Shared.Domain.Common;
using IM.Domain.Entities.Conversation;
using IM.Domain.Entities.ConversationParticipant;
using IM.Domain.Entities.Group;
using IM.Domain.Entities.GroupMember;
using IM.Domain.Enums;
using IM.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace BuildFlow.IM.Tests;
/// <summary>
/// IM 模块测试数据库工厂:每个用例独立 InMemory 库,并通过 SavingChanges 事件自动补齐
/// IAuditable / IHasOperatorIP 审计字段(InMemory 无 AuditInterceptor,须手动补值,否则非空审计字段为 null)。
/// </summary>
internal static class IMTestDb
{
public static ImDbContext Create([CallerMemberName] string testName = "")
{
var services = new ServiceCollection();
services.AddDbContext<ImDbContext>(options =>
options.UseInMemoryDatabase($"im-{testName}-{Guid.NewGuid()}"));
var db = services.BuildServiceProvider().GetRequiredService<ImDbContext>();
db.SavingChanges += FillAuditFields;
return db;
}
private static void FillAuditFields(object? sender, EventArgs args)
{
if (sender is not ImDbContext db) return;
foreach (var entry in db.ChangeTracker.Entries())
{
if (entry.Entity is IAuditable auditable)
{
auditable.CreatedBy ??= "test";
auditable.UpdatedBy ??= "test";
if (auditable.CreatedAt == default) auditable.CreatedAt = DateTime.UtcNow;
if (auditable.UpdatedAt == default) auditable.UpdatedAt = DateTime.UtcNow;
}
if (entry.Entity is IHasOperatorIP operatorEntity)
operatorEntity.OperatorIP ??= "127.0.0.1";
}
}
/// <summary>
/// 构造一个群聊会话:群主 + 指成员 + 同步会话参与者。审计字段由 SavingChanges 事件补齐。
/// 返回 (会话Id, 群组Id)。
/// </summary>
public static async Task<(Guid ConversationId, Guid GroupId)> SeedGroupAsync(
ImDbContext db, string name, Guid ownerId, params Guid[] memberIds)
{
var conversation = new ConversationEntity { Type = ConversationTypeEnum.Group };
db.Conversations.Add(conversation);
await db.SaveChangesAsync();
var group = new GroupEntity
{
ConversationId = conversation.Id,
Name = name,
OwnerId = ownerId
};
db.Groups.Add(group);
await db.SaveChangesAsync();
var allUserIds = new List<Guid> { ownerId };
allUserIds.AddRange(memberIds.Where(id => id != ownerId));
foreach (var userId in allUserIds)
{
db.GroupMembers.Add(new GroupMemberEntity
{
GroupId = group.Id,
UserId = userId,
Role = userId == ownerId ? GroupMemberRoleEnum.Owner : GroupMemberRoleEnum.Member,
JoinedAt = DateTime.UtcNow
});
db.ConversationParticipants.Add(new ConversationParticipantEntity
{
ConversationId = conversation.Id,
UserId = userId,
JoinedAt = DateTime.UtcNow
});
}
await db.SaveChangesAsync();
return (conversation.Id, group.Id);
}
}