103 lines
3.7 KiB
C#
103 lines
3.7 KiB
C#
using CodeBase.Models;
|
||
using CodeBase.Warehouse;
|
||
|
||
namespace CodeBase.Services
|
||
{
|
||
public class CodeService(ChunkService service,
|
||
ChunkWarehouse warehouse,
|
||
VectorizationService vectorizationService,
|
||
LlmService llmService)
|
||
{
|
||
public async Task<List<CodeChunk>> GetCodeChunksAsync(string path, string name)
|
||
{
|
||
if (!Path.Exists(path))
|
||
{
|
||
throw new Exception("Путь не найден");
|
||
}
|
||
|
||
var allChunks = new List<CodeChunk>();
|
||
|
||
// Находим все .cs файлы во всех вложенных папках
|
||
var csFiles = Directory.GetFiles(path, "*.cs", SearchOption.AllDirectories);
|
||
|
||
foreach (var filePath in csFiles)
|
||
{
|
||
var content = File.ReadAllText(filePath);
|
||
|
||
// Парсим каждый файл отдельно, сохраняя его оригинальный путь
|
||
var fileChunks = service.ChunkCSharpFile(filePath, content);
|
||
|
||
allChunks.AddRange(fileChunks);
|
||
}
|
||
|
||
var chunks = await vectorizationService.EnrichChunksWithVectorsAsync(allChunks);
|
||
|
||
warehouse.UpsertDictionary(name, allChunks);
|
||
|
||
return chunks;
|
||
}
|
||
|
||
public async Task<string> GetAnswerAsync(string name, string question)
|
||
{
|
||
var query = await vectorizationService.GetVectorAsync(question);
|
||
|
||
var answerVectors = Search(query, name, 15);
|
||
|
||
var answer = await llmService.AskQuestionAsync(question, answerVectors.Select(s => s.Chunk).ToList());
|
||
|
||
return answer;
|
||
}
|
||
|
||
// Главный метод поиска
|
||
public List<(CodeChunk Chunk, float Score)> Search(
|
||
float[] queryVector,
|
||
string name,
|
||
int topK = 3) // Возвращаем топ-3 результата
|
||
{
|
||
var memoryBase = warehouse.GetChunks(name);
|
||
|
||
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();
|
||
}
|
||
|
||
// Математика косинусного сходства
|
||
private float CalculateCosineSimilarity(float[] vectorA, float[] vectorB)
|
||
{
|
||
if (vectorA.Length != vectorB.Length)
|
||
throw new ArgumentException("Векторы должны быть одинаковой длины (например, 768).");
|
||
|
||
float dotProduct = 0;
|
||
float magnitudeA = 0;
|
||
float magnitudeB = 0;
|
||
|
||
for (int i = 0; i < vectorA.Length; i++)
|
||
{
|
||
dotProduct += vectorA[i] * vectorB[i];
|
||
magnitudeA += vectorA[i] * vectorA[i];
|
||
magnitudeB += vectorB[i] * vectorB[i];
|
||
}
|
||
|
||
// Защита от деления на ноль
|
||
if (magnitudeA == 0 || magnitudeB == 0)
|
||
return 0;
|
||
|
||
return (float)(dotProduct / (Math.Sqrt(magnitudeA) * Math.Sqrt(magnitudeB)));
|
||
}
|
||
}
|
||
}
|