Добавлена интеграция с Qdrant для поиска по векторам
This commit is contained in:
@@ -4,6 +4,7 @@ using LazyBear.MCP.Services.Jira;
|
||||
using LazyBear.MCP.Services.Kubernetes;
|
||||
using LazyBear.MCP.Services.Logging;
|
||||
using LazyBear.MCP.Services.Mcp;
|
||||
using LazyBear.MCP.Services.Qdrant;
|
||||
using LazyBear.MCP.Services.ToolRegistry;
|
||||
using LazyBear.MCP.TUI;
|
||||
using LazyBear.MCP.TUI.Components;
|
||||
@@ -27,12 +28,14 @@ var host = Host.CreateDefaultBuilder(args)
|
||||
services.AddSingleton<JiraClientProvider>();
|
||||
services.AddSingleton<ConfluenceClientProvider>();
|
||||
services.AddSingleton<GitLabClientProvider>();
|
||||
services.AddSingleton<QdrantClientProvider>();
|
||||
|
||||
// Модули инструментов (добавь новый IToolModule — он появится в TUI)
|
||||
services.AddSingleton<IToolModule, JiraToolModule>();
|
||||
services.AddSingleton<IToolModule, KubernetesToolModule>();
|
||||
services.AddSingleton<IToolModule, ConfluenceToolModule>();
|
||||
services.AddSingleton<IToolModule, GitLabToolModule>();
|
||||
services.AddSingleton<IToolModule, QdrantToolModule>();
|
||||
|
||||
// HTTP MCP endpoint запускаем в фоне, чтобы TUI оставался владельцем консоли
|
||||
services.AddHostedService<McpWebHostedService>();
|
||||
|
||||
29
LazyBear.MCP/Services/Qdrant/QdrantClientProvider.cs
Normal file
29
LazyBear.MCP/Services/Qdrant/QdrantClientProvider.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using RestSharp;
|
||||
|
||||
namespace LazyBear.MCP.Services.Qdrant;
|
||||
|
||||
public sealed class QdrantClientProvider
|
||||
{
|
||||
public RestClient? Client { get; }
|
||||
|
||||
public string? InitializationError { get; }
|
||||
|
||||
public QdrantClientProvider(IConfiguration configuration)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = configuration["Qdrant:Url"];
|
||||
if (string.IsNullOrWhiteSpace(url))
|
||||
{
|
||||
throw new InvalidOperationException("Qdrant:Url не настроен в конфигурации.");
|
||||
}
|
||||
|
||||
Client = new RestClient(url);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
InitializationError = $"{ex.GetType().Name}: {ex.Message}";
|
||||
}
|
||||
}
|
||||
}
|
||||
333
LazyBear.MCP/Services/Qdrant/QdrantKnowledgeTools.cs
Normal file
333
LazyBear.MCP/Services/Qdrant/QdrantKnowledgeTools.cs
Normal file
@@ -0,0 +1,333 @@
|
||||
using System.ComponentModel;
|
||||
using System.Text.Json;
|
||||
using LazyBear.MCP.Services.ToolRegistry;
|
||||
using ModelContextProtocol.Server;
|
||||
using RestSharp;
|
||||
|
||||
namespace LazyBear.MCP.Services.Qdrant;
|
||||
|
||||
[McpServerToolType]
|
||||
public sealed class QdrantKnowledgeTools(
|
||||
QdrantClientProvider provider,
|
||||
IConfiguration configuration,
|
||||
ToolRegistryService registry)
|
||||
{
|
||||
private readonly RestClient? _client = provider.Client;
|
||||
private readonly string? _clientInitializationError = provider.InitializationError;
|
||||
private readonly string _apiKey = configuration["Qdrant:ApiKey"] ?? string.Empty;
|
||||
private readonly string _defaultCollection = configuration["Qdrant:DefaultCollection"] ?? "knowledge";
|
||||
|
||||
private const string ModuleName = "Qdrant";
|
||||
|
||||
private bool TryCheckEnabled(string toolName, out string error)
|
||||
{
|
||||
if (!registry.IsToolEnabled(ModuleName, toolName))
|
||||
{
|
||||
error = $"Инструмент '{toolName}' модуля Qdrant отключён в TUI.";
|
||||
return false;
|
||||
}
|
||||
|
||||
error = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
[McpServerTool, Description("Получить список коллекций Qdrant")]
|
||||
public async Task<string> ListCollections(CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!TryCheckEnabled("ListCollections", out var enabledError)) return enabledError;
|
||||
if (!TryGetClient(out var client, out var error)) return error;
|
||||
|
||||
try
|
||||
{
|
||||
var request = CreateRequest("/collections");
|
||||
var response = await client.ExecuteAsync(request, cancellationToken);
|
||||
|
||||
if (!response.IsSuccessful || string.IsNullOrWhiteSpace(response.Content))
|
||||
{
|
||||
return FormatResponseError("list_collections", response);
|
||||
}
|
||||
|
||||
using var document = JsonDocument.Parse(response.Content);
|
||||
if (!document.RootElement.TryGetProperty("result", out var resultElement) ||
|
||||
!resultElement.TryGetProperty("collections", out var collectionsElement) ||
|
||||
collectionsElement.GetArrayLength() == 0)
|
||||
{
|
||||
return "Коллекции Qdrant не найдены.";
|
||||
}
|
||||
|
||||
var names = collectionsElement
|
||||
.EnumerateArray()
|
||||
.Select(item => GetNestedString(item, "name") ?? "unknown")
|
||||
.ToArray();
|
||||
|
||||
return $"Коллекции Qdrant ({names.Length} шт.): {string.Join(", ", names)}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return FormatException("list_collections", ex);
|
||||
}
|
||||
}
|
||||
|
||||
[McpServerTool, Description("Создать коллекцию Qdrant для базы знаний")]
|
||||
public async Task<string> CreateCollection(
|
||||
[Description("Имя коллекции. Если пусто — используется Qdrant:DefaultCollection")] string? collection = null,
|
||||
[Description("Размер вектора") ] int vectorSize = 1536,
|
||||
[Description("Метрика расстояния: Cosine, Euclid, Dot или Manhattan")] string distance = "Cosine",
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!TryCheckEnabled("CreateCollection", out var enabledError)) return enabledError;
|
||||
if (!TryGetClient(out var client, out var error)) return error;
|
||||
|
||||
var resolvedCollection = ResolveCollection(collection);
|
||||
if (vectorSize <= 0)
|
||||
return "vectorSize должен быть больше 0.";
|
||||
|
||||
if (distance is not ("Cosine" or "Euclid" or "Dot" or "Manhattan"))
|
||||
return $"distance должен быть одним из: Cosine, Euclid, Dot, Manhattan. Получено: '{distance}'.";
|
||||
|
||||
try
|
||||
{
|
||||
var request = CreateRequest($"/collections/{resolvedCollection}", Method.Put);
|
||||
request.AddJsonBody(new
|
||||
{
|
||||
vectors = new
|
||||
{
|
||||
size = vectorSize,
|
||||
distance
|
||||
}
|
||||
});
|
||||
|
||||
var response = await client.ExecuteAsync(request, cancellationToken);
|
||||
if (!response.IsSuccessful)
|
||||
{
|
||||
return FormatResponseError("create_collection", response, resolvedCollection);
|
||||
}
|
||||
|
||||
return $"Коллекция Qdrant '{resolvedCollection}' создана (vectorSize={vectorSize}, distance={distance}).";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return FormatException("create_collection", ex, resolvedCollection);
|
||||
}
|
||||
}
|
||||
|
||||
[McpServerTool, Description("Добавить или обновить документ в базе знаний Qdrant")]
|
||||
public async Task<string> UpsertKnowledgeDocument(
|
||||
[Description("ID документа/точки") ] string id,
|
||||
[Description("Вектор embedding") ] float[] vector,
|
||||
[Description("Текст/контент документа") ] string content,
|
||||
[Description("Имя коллекции. Если пусто — используется Qdrant:DefaultCollection")] string? collection = null,
|
||||
[Description("Дополнительные метаданные JSON-объектом") ] Dictionary<string, object?>? metadata = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!TryCheckEnabled("UpsertKnowledgeDocument", out var enabledError)) return enabledError;
|
||||
if (!TryGetClient(out var client, out var error)) return error;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(id)) return "id документа не задан.";
|
||||
if (!Guid.TryParse(id, out _) && !ulong.TryParse(id, out _))
|
||||
return $"id должен быть UUID (например, 550e8400-e29b-41d4-a716-446655440000) или uint64. Получено: '{id}'.";
|
||||
if (vector is null || vector.Length == 0) return "vector не задан.";
|
||||
|
||||
var resolvedCollection = ResolveCollection(collection);
|
||||
|
||||
try
|
||||
{
|
||||
var payload = new Dictionary<string, object?>
|
||||
{
|
||||
["content"] = content,
|
||||
["updatedAt"] = DateTimeOffset.UtcNow
|
||||
};
|
||||
|
||||
if (metadata is not null)
|
||||
{
|
||||
foreach (var (key, value) in metadata)
|
||||
{
|
||||
payload[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
var request = CreateRequest($"/collections/{resolvedCollection}/points", Method.Put);
|
||||
request.AddJsonBody(new
|
||||
{
|
||||
points = new object[]
|
||||
{
|
||||
new
|
||||
{
|
||||
id,
|
||||
vector,
|
||||
payload
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var response = await client.ExecuteAsync(request, cancellationToken);
|
||||
if (!response.IsSuccessful)
|
||||
{
|
||||
return FormatResponseError("upsert_knowledge_document", response, resolvedCollection);
|
||||
}
|
||||
|
||||
return $"Документ '{id}' сохранён в коллекции '{resolvedCollection}'.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return FormatException("upsert_knowledge_document", ex, resolvedCollection);
|
||||
}
|
||||
}
|
||||
|
||||
[McpServerTool, Description("Векторный поиск по базе знаний Qdrant")]
|
||||
public async Task<string> SearchKnowledge(
|
||||
[Description("Вектор запроса") ] float[] queryVector,
|
||||
[Description("Имя коллекции. Если пусто — используется Qdrant:DefaultCollection")] string? collection = null,
|
||||
[Description("Количество результатов") ] int limit = 5,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!TryCheckEnabled("SearchKnowledge", out var enabledError)) return enabledError;
|
||||
if (!TryGetClient(out var client, out var error)) return error;
|
||||
|
||||
if (queryVector is null || queryVector.Length == 0) return "queryVector не задан.";
|
||||
|
||||
var resolvedCollection = ResolveCollection(collection);
|
||||
var resolvedLimit = Math.Max(1, limit);
|
||||
|
||||
try
|
||||
{
|
||||
var request = CreateRequest($"/collections/{resolvedCollection}/points/query", Method.Post);
|
||||
request.AddJsonBody(new
|
||||
{
|
||||
query = queryVector,
|
||||
limit = resolvedLimit,
|
||||
with_payload = true
|
||||
});
|
||||
|
||||
var response = await client.ExecuteAsync(request, cancellationToken);
|
||||
if (!response.IsSuccessful || string.IsNullOrWhiteSpace(response.Content))
|
||||
{
|
||||
return FormatResponseError("search_knowledge", response, resolvedCollection);
|
||||
}
|
||||
|
||||
using var document = JsonDocument.Parse(response.Content);
|
||||
if (!document.RootElement.TryGetProperty("result", out var resultsElement) ||
|
||||
resultsElement.GetArrayLength() == 0)
|
||||
{
|
||||
return "Поиск по базе знаний не дал результатов.";
|
||||
}
|
||||
|
||||
var lines = new List<string>();
|
||||
foreach (var item in resultsElement.EnumerateArray())
|
||||
{
|
||||
var pointId = item.TryGetProperty("id", out var idElement) ? idElement.ToString() : "-";
|
||||
var score = item.TryGetProperty("score", out var scoreElement)
|
||||
? scoreElement.GetDouble().ToString("0.####")
|
||||
: "-";
|
||||
var content = (GetNestedString(item, "payload", "content") ?? "(без контента)")
|
||||
.ReplaceLineEndings(" ");
|
||||
lines.Add($"id={pointId}; score={score}; content={content}");
|
||||
}
|
||||
|
||||
return $"Результаты поиска Qdrant ({lines.Count}):\n{string.Join('\n', lines)}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return FormatException("search_knowledge", ex, resolvedCollection);
|
||||
}
|
||||
}
|
||||
|
||||
[McpServerTool, Description("Удалить документ из базы знаний Qdrant по ID")]
|
||||
public async Task<string> DeleteKnowledgeDocument(
|
||||
[Description("ID документа/точки") ] string id,
|
||||
[Description("Имя коллекции. Если пусто — используется Qdrant:DefaultCollection")] string? collection = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!TryCheckEnabled("DeleteKnowledgeDocument", out var enabledError)) return enabledError;
|
||||
if (!TryGetClient(out var client, out var error)) return error;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(id)) return "id документа не задан.";
|
||||
if (!Guid.TryParse(id, out _) && !ulong.TryParse(id, out _))
|
||||
return $"id должен быть UUID (например, 550e8400-e29b-41d4-a716-446655440000) или uint64. Получено: '{id}'.";
|
||||
|
||||
var resolvedCollection = ResolveCollection(collection);
|
||||
|
||||
try
|
||||
{
|
||||
var request = CreateRequest($"/collections/{resolvedCollection}/points/delete", Method.Post);
|
||||
request.AddJsonBody(new
|
||||
{
|
||||
points = new[] { id }
|
||||
});
|
||||
|
||||
var response = await client.ExecuteAsync(request, cancellationToken);
|
||||
if (!response.IsSuccessful)
|
||||
{
|
||||
return FormatResponseError("delete_knowledge_document", response, resolvedCollection);
|
||||
}
|
||||
|
||||
return $"Документ '{id}' удалён из коллекции '{resolvedCollection}'.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return FormatException("delete_knowledge_document", ex, resolvedCollection);
|
||||
}
|
||||
}
|
||||
|
||||
private string ResolveCollection(string? collection)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(collection) ? _defaultCollection : collection;
|
||||
}
|
||||
|
||||
private RestRequest CreateRequest(string resource, Method method = Method.Get)
|
||||
{
|
||||
var request = new RestRequest(resource, method);
|
||||
request.AddHeader("Accept", "application/json");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(_apiKey))
|
||||
{
|
||||
request.AddHeader("api-key", _apiKey);
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
private bool TryGetClient(out RestClient client, out string error)
|
||||
{
|
||||
if (_client is null)
|
||||
{
|
||||
client = null!;
|
||||
var details = string.IsNullOrWhiteSpace(_clientInitializationError)
|
||||
? string.Empty
|
||||
: $" Детали: {_clientInitializationError}";
|
||||
error = "Qdrant клиент не инициализирован. Проверьте Qdrant:Url." + details;
|
||||
return false;
|
||||
}
|
||||
|
||||
client = _client;
|
||||
error = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string? GetNestedString(JsonElement element, params string[] path)
|
||||
{
|
||||
var current = element;
|
||||
foreach (var segment in path)
|
||||
{
|
||||
if (!current.TryGetProperty(segment, out current))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return current.ValueKind == JsonValueKind.String ? current.GetString() : current.ToString();
|
||||
}
|
||||
|
||||
private static string FormatResponseError(string toolName, RestResponse response, string? resource = null)
|
||||
{
|
||||
var resourcePart = string.IsNullOrWhiteSpace(resource) ? string.Empty : $", resource='{resource}'";
|
||||
var body = string.IsNullOrWhiteSpace(response.Content) ? "-" : response.Content;
|
||||
return $"Ошибка Qdrant в tool '{toolName}'{resourcePart}: status={(int)response.StatusCode}, details={body}";
|
||||
}
|
||||
|
||||
private static string FormatException(string toolName, Exception exception, string? resource = null)
|
||||
{
|
||||
var resourcePart = string.IsNullOrWhiteSpace(resource) ? string.Empty : $", resource='{resource}'";
|
||||
return $"Ошибка Qdrant в tool '{toolName}'{resourcePart}: {exception.GetType().Name}: {exception.Message}";
|
||||
}
|
||||
}
|
||||
18
LazyBear.MCP/Services/Qdrant/QdrantToolModule.cs
Normal file
18
LazyBear.MCP/Services/Qdrant/QdrantToolModule.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using LazyBear.MCP.Services.ToolRegistry;
|
||||
|
||||
namespace LazyBear.MCP.Services.Qdrant;
|
||||
|
||||
public sealed class QdrantToolModule : IToolModule
|
||||
{
|
||||
public string ModuleName => "Qdrant";
|
||||
public string Description => "Qdrant: база знаний (коллекции, документы, векторный поиск)";
|
||||
|
||||
public IReadOnlyList<string> ToolNames =>
|
||||
[
|
||||
"ListCollections",
|
||||
"CreateCollection",
|
||||
"UpsertKnowledgeDocument",
|
||||
"SearchKnowledge",
|
||||
"DeleteKnowledgeDocument"
|
||||
];
|
||||
}
|
||||
@@ -19,6 +19,11 @@
|
||||
"Token": "",
|
||||
"Project": ""
|
||||
},
|
||||
"Qdrant": {
|
||||
"Url": "",
|
||||
"ApiKey": "",
|
||||
"DefaultCollection": "knowledge"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
1. ✅ Создать Memory Bank структуру
|
||||
2. 🔄 Продолжить документирование архитектуры и паттернов
|
||||
3. ⏳ Обновлять Memory Bank после значимых изменений
|
||||
4. ⏳ Добавить Qdrant в Memory Bank
|
||||
|
||||
## 🔍 Важные решения
|
||||
|
||||
@@ -61,6 +62,11 @@
|
||||
"Token": "",
|
||||
"Username": "",
|
||||
"SpaceKey": ""
|
||||
},
|
||||
"Qdrant": {
|
||||
"Url": "", // URL Qdrant сервера
|
||||
"ApiKey": "", // Опционально для авторизации
|
||||
"DefaultCollection": "knowledge" // Default коллекция для векторного поиска
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -125,6 +125,22 @@ TUI показывает:
|
||||
- Статус подов
|
||||
- Последние events
|
||||
- Кнопки действий
|
||||
- Векторные индексы Qdrant (если используется)
|
||||
```
|
||||
|
||||
### Сценарий 4: Поиск знаний через Qdrant
|
||||
|
||||
```
|
||||
User (via AI): "Как настроить деплой nginx?"
|
||||
↓
|
||||
AI вызывает: qdrantKnowledgeTools/search({
|
||||
query: "nginx деплой настройка",
|
||||
vector_embedding: [0.1, 0.2, ...]
|
||||
})
|
||||
↓
|
||||
Qdrant возвращает релевантные документы
|
||||
↓
|
||||
AI предоставляет из Confluence/документации
|
||||
```
|
||||
|
||||
## Метрики успеха
|
||||
@@ -133,6 +149,7 @@ TUI показывает:
|
||||
|---------|------|
|
||||
| Время на задачу (Jira) | -50% после интеграции |
|
||||
| Время на задачу (K8s) | -70% после интеграции |
|
||||
| Время на поиск знаний | -60% с Qdrant |
|
||||
| Довольство пользователей | >4.5/5 |
|
||||
| Количество инцидентов | Снизить на -30% |
|
||||
|
||||
|
||||
@@ -50,6 +50,15 @@
|
||||
- `create_branch` — создать ветку
|
||||
- `delete_branch` — удалить ветку
|
||||
|
||||
### Qdrant Integration (Vector DB)
|
||||
|
||||
- ✅ CRUD коллекции (создание, удаление)
|
||||
- ✅ Добавление/обновление документов (upsert)
|
||||
- ✅ Векторный поиск по коллекции
|
||||
- ✅ Удаление документов по ID
|
||||
- ✅ Поддержка метрик (Cosine, Euclid, Dot)
|
||||
- ✅ Поддержка API ключа (опционально)
|
||||
|
||||
### MCP Server
|
||||
|
||||
- ✅ HTTP Transport MCP 1.2.0
|
||||
@@ -168,7 +177,7 @@
|
||||
|
||||
**Состояние**: Development
|
||||
|
||||
**Последний commit**: `e96bab114ea1a58f3ea7bd5ab40d4645d456cd8f`
|
||||
**Последний commit**: `b5fe2623b3d14333a7138c22456862bff3781b82`
|
||||
|
||||
**Что работает**: Все основные функциональности готовы
|
||||
|
||||
|
||||
@@ -18,16 +18,17 @@
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Services Layer (IToolModule) │
|
||||
│ ┌──────────┐ ┌───────────────┐ ┌────────────────────┐ │
|
||||
│ │JiraTools │ │ConfluenceTools│ │KubernetesTools │ │
|
||||
│ └──────────┘ └───────────────┘ └────────────────────┘ │
|
||||
│ ┌──────────┐ ┌───────────────┐ ┌────────────────────┐ ┌─────┐│
|
||||
│ │JiraTools │ │ConfluenceTools│ │KubernetesTools │ │Qdrant││
|
||||
│ └──────────┘ └───────────────┘ └────────────────────┘ └─────┘│
|
||||
│ └────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ External API Layer │
|
||||
│ ┌──────────┐ ┌───────────────┐ ┌────────────────────┐ │
|
||||
│ │ Jira API │ │Confluence API │ │ K8s API │ │
|
||||
│ └──────────┘ └───────────────┘ └────────────────────┘ │
|
||||
│ ┌──────────┐ ┌───────────────┐ ┌────────────────────┐ ┌────┐│
|
||||
│ │ Jira API │ │Confluence API │ │ K8s API │ │Qdr│ │
|
||||
│ └──────────┘ └───────────────┘ └────────────────────┘ └───┘ │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
@@ -289,4 +290,33 @@ Console.WriteLine
|
||||
|
||||
---
|
||||
|
||||
### Qdrant Client Provider Pattern
|
||||
|
||||
**File**: `LazyBear.MCP/Services/Qdrant/QdrantClientProvider.cs`
|
||||
|
||||
```csharp
|
||||
public class QdrantClientProvider
|
||||
{
|
||||
private readonly IConfiguration _config;
|
||||
|
||||
public QdrantClient GetClient()
|
||||
{
|
||||
// Конфиг из appsettings.json
|
||||
var url = _config["Qdrant:Url"];
|
||||
var apiKey = _config["Qdrant:ApiKey"] ?? string.Empty;
|
||||
|
||||
return new QdrantClient(url, apiKey);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Fallback порядок**:
|
||||
1. Explicit URL из конфига
|
||||
2. Environment variable QDRANT_URL
|
||||
3. Localhost default
|
||||
|
||||
**Ответственность**: Создание клиентов Qdrant с поддержкой API ключа (опционально)
|
||||
|
||||
---
|
||||
|
||||
*Файл описывает систему архитектуры, ключевые компоненты и потоки данных. Обновлять при введении новых архитектурных решений.*
|
||||
@@ -13,6 +13,7 @@
|
||||
| **Model Context Protocol** | 1.2.0 | MCP стандарт |
|
||||
| **Kubernetes Client** | 13+ | .NET SDK для K8s |
|
||||
| **RazorConsole** | Latest | TUI framework |
|
||||
| **Qdrant.Client** | Latest | Векторный поиск,知识库 |
|
||||
|
||||
### Файлы конфигурации
|
||||
|
||||
@@ -101,6 +102,10 @@ LazyBear.MCP/
|
||||
│ │ └── JiraClientProvider.cs
|
||||
│ ├── Confluence/
|
||||
│ │ └── ConfluencePagesTools.cs
|
||||
│ ├── Qdrant/
|
||||
│ │ ├── QdrantClientProvider.cs
|
||||
│ │ ├── QdrantKnowledgeTools.cs
|
||||
│ │ └── QdrantToolModule.cs
|
||||
│ └── Kubernetes/
|
||||
│ ├── K8sConfigTools.cs
|
||||
│ ├── K8sDeploymentTools.cs
|
||||
@@ -215,6 +220,13 @@ echo '{"jsonrpc":"2.0","id":1,"method":"k8sPodsTools/getPodStatus","params":{"na
|
||||
|
||||
- `Jira:Url` обязателен, иначе инициализация провайдера может упасть
|
||||
- `Kubernetes:KubeconfigPath` может быть пустым — используется fallback
|
||||
- `Qdrant:Url` обязателен для векторного поиска, если используется
|
||||
- `Qdrant:ApiKey` опционален, но рекомендуется для безопасного доступа
|
||||
|
||||
### Qdrant Gotchas
|
||||
|
||||
- Косинусная метрика — default для векторного поиска
|
||||
- Размер вектора должен быть фиксированным при создании коллекции
|
||||
|
||||
### RazorConsole Gotchas
|
||||
|
||||
|
||||
Reference in New Issue
Block a user