Compare commits

...

2 Commits

5 changed files with 130 additions and 23 deletions

View File

@@ -10,6 +10,7 @@
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.6" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="5.6.0" />
<PackageReference Include="OpenAI" Version="2.12.0" />
<PackageReference Include="Qdrant.Client" Version="1.18.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.2.3" />
</ItemGroup>

View File

@@ -1,9 +1,12 @@
using CodeBase.Services;
using CodeBase.Warehouse;
using Qdrant.Client;
using Qdrant.Client.Grpc;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddSingleton(new QdrantClient("localhost", 6334));
builder.Services.AddSingleton<ChunkWarehouse>();
builder.Services.AddTransient<VectorizationService>();
@@ -11,6 +14,7 @@ builder.Services.AddTransient<LlmService>();
builder.Services.AddScoped<LlmPromptBuilder>();
builder.Services.AddTransient<ChunkService>();
builder.Services.AddTransient<CodeService>();
builder.Services.AddTransient<QdrantRepository>();
builder.Services.AddControllers();
builder.Services.AddSwaggerGen();
@@ -30,4 +34,27 @@ app.UseAuthorization();
app.MapControllers();
using (var scope = app.Services.CreateScope())
{
var qdrantClient = scope.ServiceProvider.GetRequiredService<QdrantClient>();
// Получаем список существующих коллекций
var collections = await qdrantClient.ListCollectionsAsync();
if (!collections.Contains("CodeBaseCollection"))
{
Console.WriteLine("Создаем новую коллекцию в Qdrant...");
await qdrantClient.CreateCollectionAsync(
collectionName: "CodeBaseCollection",
vectorsConfig: new VectorParams
{
Size = 768, // Размер вектора GraphCodeBERT
Distance = Distance.Cosine // Сразу задаем алгоритм поиска (косинусное сходство)
}
);
}
}
app.Run();

View File

@@ -0,0 +1,89 @@
using CodeBase.Models;
using Qdrant.Client;
using Qdrant.Client.Grpc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
public class QdrantRepository
{
private readonly QdrantClient _client;
private const string CollectionName = "CodeBaseCollection";
public QdrantRepository(QdrantClient client)
{
_client = client;
}
// ==========================================
// 1. СОХРАНЕНИЕ ВЕКТОРОВ С ПРИВЯЗКОЙ К ПРОЕКТУ
// ==========================================
public async Task SaveChunksAsync(string projectName, List<CodeChunk> chunks)
{
var points = new List<PointStruct>();
foreach (var chunk in chunks)
{
if (chunk.Vector == null) continue;
// Формируем точку для БД
var point = new PointStruct
{
Id = Guid.NewGuid(), // Уникальный ID чанка
Vectors = chunk.Vector, // Тот самый массив float[]
// Вот здесь мы навешиваем все ярлыки!
Payload =
{
["ProjectName"] = projectName,
["FilePath"] = chunk.FilePath,
["MethodName"] = chunk.MethodName,
["Content"] = chunk.Content // Сам текст кода тоже храним прямо в БД
}
};
points.Add(point);
}
// Отправляем пачку векторов в базу
await _client.UpsertAsync(CollectionName, points);
}
// ==========================================
// 2. ПОИСК ТОЛЬКО В РАМКАХ ОДНОГО ПРОЕКТА
// ==========================================
public async Task<List<CodeChunk>> SearchAsync(string projectName, float[] queryVector, int topK = 15)
{
// Создаем фильтр: искать только там, где ProjectName совпадает с запрошенным
var projectFilter = new Filter
{
Must = {
new Condition
{
Field = new FieldCondition
{
Key = "ProjectName",
Match = new Match { Keyword = projectName }
}
}
}
};
// База делает всю тяжелую математику за нас!
var searchResults = await _client.SearchAsync(
collectionName: CollectionName,
vector: queryVector,
limit: (ulong)topK,
filter: projectFilter, // Передаем наш фильтр
payloadSelector: true // Просим БД вернуть Payload вместе с результатами
);
// Превращаем ответы базы обратно в наши C#-объекты CodeChunk
return searchResults.Select(hit => new CodeChunk
{
FilePath = hit.Payload["FilePath"].StringValue,
MethodName = hit.Payload["MethodName"].StringValue,
Content = hit.Payload["Content"].StringValue
}).ToList();
}
}

View File

@@ -4,7 +4,7 @@ using CodeBase.Warehouse;
namespace CodeBase.Services
{
public class CodeService(ChunkService service,
ChunkWarehouse warehouse,
QdrantRepository repository,
VectorizationService vectorizationService,
LlmService llmService)
{
@@ -32,7 +32,7 @@ namespace CodeBase.Services
var chunks = await vectorizationService.EnrichChunksWithVectorsAsync(allChunks);
warehouse.UpsertDictionary(name, allChunks);
await repository.SaveChunksAsync(name, chunks);
return chunks;
}
@@ -41,38 +41,22 @@ namespace CodeBase.Services
{
var query = await vectorizationService.GetVectorAsync(question);
var answerVectors = Search(query, name, 15);
var answerVectors = await SearchAsync(query, name, 15);
var answer = await llmService.AskQuestionAsync(question, answerVectors.Select(s => s.Chunk).ToList());
var answer = await llmService.AskQuestionAsync(question, answerVectors);
return answer;
}
// Главный метод поиска
public List<(CodeChunk Chunk, float Score)> Search(
public async Task<List<CodeChunk>> SearchAsync(
float[] queryVector,
string name,
int topK = 3) // Возвращаем топ-3 результата
{
var memoryBase = warehouse.GetChunks(name);
var results = await repository.SearchAsync(name, queryVector, topK);
var results = new List<(CodeChunk, float)>();
foreach (var chunk in memoryBase)
{
if (chunk.Vector == null || chunk.Vector.Length == 0)
continue;
// Считаем близость вектора вопроса к вектору кода
float similarity = CalculateCosineSimilarity(queryVector, chunk.Vector);
results.Add((chunk, similarity));
}
// Сортируем по убыванию сходства (чем ближе к 1, тем лучше)
return results
.OrderByDescending(x => x.Item2)
.Take(topK)
.ToList();
return results;
}
// Математика косинусного сходства

6
PythonProject/.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>