@polymorphism-tech/morph-spec 1.0.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +1381 -0
- package/LICENSE +72 -0
- package/README.md +89 -6
- package/bin/detect-agents.js +225 -0
- package/bin/morph-spec.js +120 -0
- package/bin/render-template.js +302 -0
- package/bin/semantic-detect-agents.js +246 -0
- package/bin/validate-agents-skills.js +239 -0
- package/bin/validate-agents.js +69 -0
- package/bin/validate-phase.js +263 -0
- package/content/.azure/README.md +293 -0
- package/content/.azure/docs/azure-devops-setup.md +454 -0
- package/content/.azure/docs/branch-strategy.md +398 -0
- package/content/.azure/docs/local-development.md +515 -0
- package/content/.azure/pipelines/pipeline-variables.yml +34 -0
- package/content/.azure/pipelines/prod-pipeline.yml +319 -0
- package/content/.azure/pipelines/staging-pipeline.yml +234 -0
- package/content/.azure/pipelines/templates/build-dotnet.yml +75 -0
- package/content/.azure/pipelines/templates/deploy-app-service.yml +94 -0
- package/content/.azure/pipelines/templates/deploy-container-app.yml +120 -0
- package/content/.azure/pipelines/templates/infra-deploy.yml +90 -0
- package/content/.claude/commands/morph-apply.md +118 -26
- package/content/.claude/commands/morph-archive.md +9 -9
- package/content/.claude/commands/morph-clarify.md +184 -0
- package/content/.claude/commands/morph-design.md +275 -0
- package/content/.claude/commands/morph-proposal.md +56 -15
- package/content/.claude/commands/morph-setup.md +100 -0
- package/content/.claude/commands/morph-status.md +47 -32
- package/content/.claude/commands/morph-tasks.md +319 -0
- package/content/.claude/commands/morph-uiux.md +211 -0
- package/content/.claude/skills/specialists/ai-system-architect.md +604 -0
- package/content/.claude/skills/specialists/ms-agent-expert.md +143 -89
- package/content/.claude/skills/specialists/ui-ux-designer.md +744 -9
- package/content/.claude/skills/stacks/dotnet-blazor.md +244 -8
- package/content/.claude/skills/stacks/dotnet-nextjs.md +2 -2
- package/content/.morph/.morphversion +5 -0
- package/content/.morph/config/agents.json +101 -8
- package/content/.morph/config/azure-pricing.json +70 -0
- package/content/.morph/config/azure-pricing.schema.json +50 -0
- package/content/.morph/config/config.template.json +15 -3
- package/content/.morph/docs/STORY-DRIVEN-DEVELOPMENT.md +392 -0
- package/content/.morph/hooks/README.md +239 -0
- package/content/.morph/hooks/pre-commit-agents.sh +24 -0
- package/content/.morph/hooks/pre-commit-all.sh +48 -0
- package/content/.morph/hooks/pre-commit-costs.sh +91 -0
- package/content/.morph/hooks/pre-commit-specs.sh +49 -0
- package/content/.morph/hooks/pre-commit-tests.sh +60 -0
- package/content/.morph/project.md +5 -4
- package/content/.morph/schemas/agent.schema.json +296 -0
- package/content/.morph/standards/agent-framework-setup.md +453 -0
- package/content/.morph/standards/architecture.md +142 -7
- package/content/.morph/standards/azure.md +218 -23
- package/content/.morph/standards/coding.md +47 -12
- package/content/.morph/standards/dotnet10-migration.md +494 -0
- package/content/.morph/standards/fluent-ui-setup.md +590 -0
- package/content/.morph/standards/migration-guide.md +514 -0
- package/content/.morph/standards/passkeys-auth.md +423 -0
- package/content/.morph/standards/vector-search-rag.md +536 -0
- package/content/.morph/state.json +18 -0
- package/content/.morph/templates/FluentDesignTheme.cs +149 -0
- package/content/.morph/templates/MudTheme.cs +281 -0
- package/content/.morph/templates/contracts.cs +55 -55
- package/content/.morph/templates/decisions.md +4 -4
- package/content/.morph/templates/design-system.css +226 -0
- package/content/.morph/templates/infra/.dockerignore.example +89 -0
- package/content/.morph/templates/infra/Dockerfile.example +82 -0
- package/content/.morph/templates/infra/README.md +286 -0
- package/content/.morph/templates/infra/app-service.bicep +164 -0
- package/content/.morph/templates/infra/deploy.ps1 +229 -0
- package/content/.morph/templates/infra/deploy.sh +208 -0
- package/content/.morph/templates/infra/main.bicep +41 -7
- package/content/.morph/templates/infra/parameters.dev.json +6 -0
- package/content/.morph/templates/infra/parameters.prod.json +6 -0
- package/content/.morph/templates/infra/parameters.staging.json +29 -0
- package/content/.morph/templates/proposal.md +3 -3
- package/content/.morph/templates/recap.md +3 -3
- package/content/.morph/templates/spec.md +9 -8
- package/content/.morph/templates/sprint-status.yaml +68 -0
- package/content/.morph/templates/state.template.json +222 -0
- package/content/.morph/templates/story.md +143 -0
- package/content/.morph/templates/tasks.md +1 -1
- package/content/.morph/templates/ui-components.md +276 -0
- package/content/.morph/templates/ui-design-system.md +286 -0
- package/content/.morph/templates/ui-flows.md +336 -0
- package/content/.morph/templates/ui-mockups.md +133 -0
- package/content/.morph/test-infra/example.bicep +59 -0
- package/content/CLAUDE.md +124 -0
- package/content/README.md +79 -0
- package/detectors/config-detector.js +223 -0
- package/detectors/conversation-analyzer.js +163 -0
- package/detectors/index.js +84 -0
- package/detectors/standards-generator.js +275 -0
- package/detectors/structure-detector.js +221 -0
- package/docs/README.md +149 -0
- package/docs/api/cost-calculator.js.html +513 -0
- package/docs/api/design-system-generator.js.html +382 -0
- package/docs/api/fonts/Montserrat/Montserrat-Bold.eot +0 -0
- package/docs/api/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
- package/docs/api/fonts/Montserrat/Montserrat-Bold.woff +0 -0
- package/docs/api/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
- package/docs/api/fonts/Montserrat/Montserrat-Regular.eot +0 -0
- package/docs/api/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
- package/docs/api/fonts/Montserrat/Montserrat-Regular.woff +0 -0
- package/docs/api/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +978 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1049 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
- package/docs/api/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
- package/docs/api/global.html +5263 -0
- package/docs/api/index.html +96 -0
- package/docs/api/scripts/collapse.js +39 -0
- package/docs/api/scripts/commonNav.js +28 -0
- package/docs/api/scripts/linenumber.js +25 -0
- package/docs/api/scripts/nav.js +12 -0
- package/docs/api/scripts/polyfill.js +4 -0
- package/docs/api/scripts/prettify/Apache-License-2.0.txt +202 -0
- package/docs/api/scripts/prettify/lang-css.js +2 -0
- package/docs/api/scripts/prettify/prettify.js +28 -0
- package/docs/api/scripts/search.js +99 -0
- package/docs/api/state-manager.js.html +423 -0
- package/docs/api/styles/jsdoc.css +776 -0
- package/docs/api/styles/prettify.css +80 -0
- package/docs/examples.md +328 -0
- package/docs/getting-started.md +302 -0
- package/docs/installation.md +361 -0
- package/docs/templates.md +418 -0
- package/docs/validation-checklist.md +266 -0
- package/package.json +39 -12
- package/src/commands/cost.js +181 -0
- package/src/commands/create-story.js +283 -0
- package/src/commands/detect.js +104 -0
- package/src/commands/doctor.js +67 -0
- package/src/commands/generate.js +149 -0
- package/src/commands/init.js +69 -45
- package/src/commands/shard-spec.js +224 -0
- package/src/commands/sprint-status.js +250 -0
- package/src/commands/state.js +333 -0
- package/src/commands/sync.js +167 -0
- package/src/commands/update-pricing.js +206 -0
- package/src/commands/update.js +88 -13
- package/src/lib/complexity-analyzer.js +292 -0
- package/src/lib/cost-calculator.js +429 -0
- package/src/lib/design-system-generator.js +298 -0
- package/src/lib/state-manager.js +340 -0
- package/src/utils/file-copier.js +59 -0
- package/src/utils/version-checker.js +175 -0
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
# Vector Search + RAG - EF Core 10 (.NET 10)
|
|
2
|
+
|
|
3
|
+
> **Novidade .NET 10:** EF Core 10 suporta vector search nativo no Azure SQL e SQL Server para workloads de AI.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🎯 O Que É Vector Search?
|
|
8
|
+
|
|
9
|
+
**Vector search** permite buscar dados por **similaridade semântica** ao invés de correspondência exata de texto.
|
|
10
|
+
|
|
11
|
+
### Conceito
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
Texto → Embedding (vetor de números) → Busca por similaridade
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Exemplo:**
|
|
18
|
+
- Query: "Como resetar senha?"
|
|
19
|
+
- Documento 1: "Tutorial de recuperação de senha" ← **Match semântico!**
|
|
20
|
+
- Documento 2: "Alterar credenciais de acesso" ← **Match semântico!**
|
|
21
|
+
- Documento 3: "Configurar email" ← Não match
|
|
22
|
+
|
|
23
|
+
### Casos de Uso
|
|
24
|
+
|
|
25
|
+
| Caso de Uso | Descrição |
|
|
26
|
+
|-------------|-----------|
|
|
27
|
+
| **RAG (Retrieval-Augmented Generation)** | Buscar documentos relevantes para enviar ao LLM |
|
|
28
|
+
| **Busca semântica** | Encontrar conteúdo similar sem keywords exatas |
|
|
29
|
+
| **Recomendações** | Sugerir produtos/artigos similares |
|
|
30
|
+
| **Deduplicação** | Identificar conteúdo duplicado |
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 📦 Setup
|
|
35
|
+
|
|
36
|
+
### 1. Packages Necessários
|
|
37
|
+
|
|
38
|
+
```xml
|
|
39
|
+
<!-- .csproj -->
|
|
40
|
+
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.0" />
|
|
41
|
+
<PackageReference Include="Microsoft.Extensions.AI.Embeddings" Version="1.0.0" />
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 2. Azure SQL / SQL Server Requerido
|
|
45
|
+
|
|
46
|
+
**Requisito:** Azure SQL Database ou SQL Server 2022+
|
|
47
|
+
|
|
48
|
+
**Nota:** Vector search não funciona em LocalDB.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 🗄️ Modelo de Dados com Vectors
|
|
53
|
+
|
|
54
|
+
### Entidade com Embedding
|
|
55
|
+
|
|
56
|
+
```csharp
|
|
57
|
+
using Microsoft.EntityFrameworkCore;
|
|
58
|
+
using System.Numerics.Tensors;
|
|
59
|
+
|
|
60
|
+
public class Document
|
|
61
|
+
{
|
|
62
|
+
public int Id { get; set; }
|
|
63
|
+
public string Title { get; set; } = null!;
|
|
64
|
+
public string Content { get; set; } = null!;
|
|
65
|
+
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
66
|
+
|
|
67
|
+
// Vector embedding (1536 dimensões para text-embedding-3-small)
|
|
68
|
+
public ReadOnlyMemory<float> Embedding { get; set; }
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### DbContext Configuração
|
|
73
|
+
|
|
74
|
+
```csharp
|
|
75
|
+
public class AppDbContext : DbContext
|
|
76
|
+
{
|
|
77
|
+
public DbSet<Document> Documents { get; set; }
|
|
78
|
+
|
|
79
|
+
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
80
|
+
{
|
|
81
|
+
modelBuilder.Entity<Document>(entity =>
|
|
82
|
+
{
|
|
83
|
+
entity.HasKey(d => d.Id);
|
|
84
|
+
|
|
85
|
+
// Configurar coluna de vector
|
|
86
|
+
entity.Property(d => d.Embedding)
|
|
87
|
+
.HasColumnType("vector(1536)") // 1536 = dimensões do embedding
|
|
88
|
+
.IsRequired();
|
|
89
|
+
|
|
90
|
+
// Índice de vector para performance
|
|
91
|
+
entity.HasIndex(d => d.Embedding)
|
|
92
|
+
.HasMethod("ivfflat") // ou "hnsw"
|
|
93
|
+
.HasOptions("lists = 100");
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Migration
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
dotnet ef migrations add AddVectorSearch
|
|
103
|
+
dotnet ef database update
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**SQL Gerado:**
|
|
107
|
+
```sql
|
|
108
|
+
ALTER TABLE Documents ADD Embedding vector(1536) NOT NULL;
|
|
109
|
+
CREATE INDEX IX_Documents_Embedding ON Documents USING ivfflat (Embedding) WITH (lists = 100);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 🔢 Gerando Embeddings
|
|
115
|
+
|
|
116
|
+
### Serviço de Embedding
|
|
117
|
+
|
|
118
|
+
```csharp
|
|
119
|
+
using Microsoft.Extensions.AI;
|
|
120
|
+
|
|
121
|
+
public interface IEmbeddingService
|
|
122
|
+
{
|
|
123
|
+
Task<ReadOnlyMemory<float>> GenerateEmbeddingAsync(string text, CancellationToken ct = default);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public class EmbeddingService : IEmbeddingService
|
|
127
|
+
{
|
|
128
|
+
private readonly IEmbeddingGenerator<string, Embedding<float>> _embeddingGenerator;
|
|
129
|
+
|
|
130
|
+
public EmbeddingService(IEmbeddingGenerator<string, Embedding<float>> embeddingGenerator)
|
|
131
|
+
{
|
|
132
|
+
_embeddingGenerator = embeddingGenerator;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
public async Task<ReadOnlyMemory<float>> GenerateEmbeddingAsync(
|
|
136
|
+
string text,
|
|
137
|
+
CancellationToken ct = default)
|
|
138
|
+
{
|
|
139
|
+
var embeddings = await _embeddingGenerator.GenerateAsync([text], cancellationToken: ct);
|
|
140
|
+
return embeddings[0].Vector;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Configuração no Program.cs
|
|
146
|
+
|
|
147
|
+
```csharp
|
|
148
|
+
using Microsoft.Extensions.AI;
|
|
149
|
+
|
|
150
|
+
builder.Services.AddSingleton<IEmbeddingGenerator<string, Embedding<float>>>(sp =>
|
|
151
|
+
{
|
|
152
|
+
var config = sp.GetRequiredService<IConfiguration>();
|
|
153
|
+
|
|
154
|
+
return new EmbeddingGenerator(
|
|
155
|
+
model: "text-embedding-3-small", // 1536 dimensões
|
|
156
|
+
credential: new ApiKeyCredential(config["AzureOpenAI:ApiKey"]!),
|
|
157
|
+
endpoint: new Uri(config["AzureOpenAI:Endpoint"]!)
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
builder.Services.AddScoped<IEmbeddingService, EmbeddingService>();
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## 🔍 Vector Search com EF Core 10
|
|
167
|
+
|
|
168
|
+
### Query de Similaridade
|
|
169
|
+
|
|
170
|
+
```csharp
|
|
171
|
+
using Microsoft.EntityFrameworkCore;
|
|
172
|
+
|
|
173
|
+
public class DocumentSearchService
|
|
174
|
+
{
|
|
175
|
+
private readonly AppDbContext _context;
|
|
176
|
+
private readonly IEmbeddingService _embeddingService;
|
|
177
|
+
|
|
178
|
+
public DocumentSearchService(
|
|
179
|
+
AppDbContext context,
|
|
180
|
+
IEmbeddingService embeddingService)
|
|
181
|
+
{
|
|
182
|
+
_context = context;
|
|
183
|
+
_embeddingService = embeddingService;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
public async Task<List<DocumentSearchResult>> SearchAsync(
|
|
187
|
+
string query,
|
|
188
|
+
int limit = 5,
|
|
189
|
+
CancellationToken ct = default)
|
|
190
|
+
{
|
|
191
|
+
// 1. Gerar embedding da query
|
|
192
|
+
var queryEmbedding = await _embeddingService.GenerateEmbeddingAsync(query, ct);
|
|
193
|
+
|
|
194
|
+
// 2. Buscar documentos similares
|
|
195
|
+
var results = await _context.Documents
|
|
196
|
+
.Select(d => new DocumentSearchResult
|
|
197
|
+
{
|
|
198
|
+
Document = d,
|
|
199
|
+
// Calcular distância (menor = mais similar)
|
|
200
|
+
Distance = EF.Functions.VectorDistance(d.Embedding, queryEmbedding, DistanceFunction.Cosine)
|
|
201
|
+
})
|
|
202
|
+
.OrderBy(r => r.Distance)
|
|
203
|
+
.Take(limit)
|
|
204
|
+
.ToListAsync(ct);
|
|
205
|
+
|
|
206
|
+
return results;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
public class DocumentSearchResult
|
|
211
|
+
{
|
|
212
|
+
public Document Document { get; set; } = null!;
|
|
213
|
+
public double Distance { get; set; }
|
|
214
|
+
public double Similarity => 1 - Distance; // Converter distância em similaridade
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Funções de Distância
|
|
219
|
+
|
|
220
|
+
| Função | Descrição | Quando Usar |
|
|
221
|
+
|--------|-----------|-------------|
|
|
222
|
+
| `Cosine` | Distância cosseno (0-2) | **Recomendado** para texto |
|
|
223
|
+
| `Euclidean` | Distância euclidiana | Dados numéricos |
|
|
224
|
+
| `DotProduct` | Produto escalar | Vetores normalizados |
|
|
225
|
+
|
|
226
|
+
```csharp
|
|
227
|
+
EF.Functions.VectorDistance(vector1, vector2, DistanceFunction.Cosine)
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## 🤖 RAG Pattern Completo
|
|
233
|
+
|
|
234
|
+
### Implementação RAG com Agent Framework
|
|
235
|
+
|
|
236
|
+
```csharp
|
|
237
|
+
using Microsoft.Agents.AI;
|
|
238
|
+
|
|
239
|
+
public interface IDocumentAssistantAgent
|
|
240
|
+
{
|
|
241
|
+
Task<string> AskQuestionAsync(string question, CancellationToken ct = default);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
public class DocumentAssistantAgent : IDocumentAssistantAgent
|
|
245
|
+
{
|
|
246
|
+
private readonly IChatClient _chatClient;
|
|
247
|
+
private readonly DocumentSearchService _searchService;
|
|
248
|
+
private readonly ILogger<DocumentAssistantAgent> _logger;
|
|
249
|
+
|
|
250
|
+
public DocumentAssistantAgent(
|
|
251
|
+
IChatClient chatClient,
|
|
252
|
+
DocumentSearchService searchService,
|
|
253
|
+
ILogger<DocumentAssistantAgent> logger)
|
|
254
|
+
{
|
|
255
|
+
_chatClient = chatClient;
|
|
256
|
+
_searchService = searchService;
|
|
257
|
+
_logger = logger;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
public async Task<string> AskQuestionAsync(string question, CancellationToken ct = default)
|
|
261
|
+
{
|
|
262
|
+
// 1. Retrieval: Buscar documentos relevantes
|
|
263
|
+
var relevantDocs = await _searchService.SearchAsync(question, limit: 3, ct);
|
|
264
|
+
|
|
265
|
+
_logger.LogInformation(
|
|
266
|
+
"Encontrados {Count} documentos relevantes para: {Question}",
|
|
267
|
+
relevantDocs.Count,
|
|
268
|
+
question
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
// 2. Construir contexto
|
|
272
|
+
var context = string.Join("\n\n", relevantDocs.Select(r =>
|
|
273
|
+
$"[Documento {r.Document.Id} - Similaridade: {r.Similarity:P0}]\n" +
|
|
274
|
+
$"Título: {r.Document.Title}\n" +
|
|
275
|
+
$"Conteúdo: {r.Document.Content}"
|
|
276
|
+
));
|
|
277
|
+
|
|
278
|
+
// 3. Augmentation: Criar agente com contexto
|
|
279
|
+
var agent = _chatClient.CreateAgent(
|
|
280
|
+
instructions: """
|
|
281
|
+
Você é um assistente que responde perguntas baseado em documentos fornecidos.
|
|
282
|
+
|
|
283
|
+
Regras:
|
|
284
|
+
1. Use APENAS informações dos documentos fornecidos
|
|
285
|
+
2. Se a informação não estiver nos documentos, diga "Não encontrei informação sobre isso"
|
|
286
|
+
3. Cite o número do documento ao responder
|
|
287
|
+
4. Seja conciso e objetivo
|
|
288
|
+
|
|
289
|
+
Documentos disponíveis:
|
|
290
|
+
""" + context,
|
|
291
|
+
name: "DocumentAssistant"
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
// 4. Generation: Gerar resposta
|
|
295
|
+
var response = await agent.RunAsync(question, cancellationToken: ct);
|
|
296
|
+
|
|
297
|
+
return response.Content;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Uso no Blazor
|
|
303
|
+
|
|
304
|
+
```razor
|
|
305
|
+
@page "/ask"
|
|
306
|
+
@inject IDocumentAssistantAgent Assistant
|
|
307
|
+
|
|
308
|
+
<h3>Assistente de Documentos</h3>
|
|
309
|
+
|
|
310
|
+
<EditForm Model="_input" OnValidSubmit="AskQuestion">
|
|
311
|
+
<InputText @bind-Value="_input.Question" placeholder="Faça uma pergunta..." />
|
|
312
|
+
<button type="submit" disabled="@_isLoading">Perguntar</button>
|
|
313
|
+
</EditForm>
|
|
314
|
+
|
|
315
|
+
@if (_isLoading)
|
|
316
|
+
{
|
|
317
|
+
<p>Buscando resposta...</p>
|
|
318
|
+
}
|
|
319
|
+
else if (!string.IsNullOrEmpty(_answer))
|
|
320
|
+
{
|
|
321
|
+
<div class="answer">
|
|
322
|
+
<strong>Resposta:</strong>
|
|
323
|
+
<p>@_answer</p>
|
|
324
|
+
</div>
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
@code {
|
|
328
|
+
private QuestionInput _input = new();
|
|
329
|
+
private string _answer = "";
|
|
330
|
+
private bool _isLoading;
|
|
331
|
+
|
|
332
|
+
private async Task AskQuestion()
|
|
333
|
+
{
|
|
334
|
+
_isLoading = true;
|
|
335
|
+
_answer = "";
|
|
336
|
+
|
|
337
|
+
try
|
|
338
|
+
{
|
|
339
|
+
_answer = await Assistant.AskQuestionAsync(_input.Question);
|
|
340
|
+
}
|
|
341
|
+
finally
|
|
342
|
+
{
|
|
343
|
+
_isLoading = false;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
public class QuestionInput
|
|
348
|
+
{
|
|
349
|
+
public string Question { get; set; } = "";
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## 📥 Indexação de Documentos
|
|
357
|
+
|
|
358
|
+
### Serviço de Indexação
|
|
359
|
+
|
|
360
|
+
```csharp
|
|
361
|
+
public class DocumentIndexingService
|
|
362
|
+
{
|
|
363
|
+
private readonly AppDbContext _context;
|
|
364
|
+
private readonly IEmbeddingService _embeddingService;
|
|
365
|
+
|
|
366
|
+
public async Task IndexDocumentAsync(
|
|
367
|
+
string title,
|
|
368
|
+
string content,
|
|
369
|
+
CancellationToken ct = default)
|
|
370
|
+
{
|
|
371
|
+
// 1. Gerar embedding do conteúdo
|
|
372
|
+
var embedding = await _embeddingService.GenerateEmbeddingAsync(
|
|
373
|
+
$"{title}\n{content}", // Combinar título e conteúdo
|
|
374
|
+
ct
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
// 2. Criar documento
|
|
378
|
+
var document = new Document
|
|
379
|
+
{
|
|
380
|
+
Title = title,
|
|
381
|
+
Content = content,
|
|
382
|
+
Embedding = embedding,
|
|
383
|
+
CreatedAt = DateTime.UtcNow
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
// 3. Salvar no banco
|
|
387
|
+
_context.Documents.Add(document);
|
|
388
|
+
await _context.SaveChangesAsync(ct);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
public async Task BulkIndexAsync(
|
|
392
|
+
List<(string Title, string Content)> documents,
|
|
393
|
+
CancellationToken ct = default)
|
|
394
|
+
{
|
|
395
|
+
foreach (var (title, content) in documents)
|
|
396
|
+
{
|
|
397
|
+
await IndexDocumentAsync(title, content, ct);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### Job de Indexação com Hangfire
|
|
404
|
+
|
|
405
|
+
```csharp
|
|
406
|
+
public class DocumentIndexingJob
|
|
407
|
+
{
|
|
408
|
+
private readonly DocumentIndexingService _indexingService;
|
|
409
|
+
private readonly IDocumentProvider _documentProvider;
|
|
410
|
+
|
|
411
|
+
public async Task IndexAllDocumentsAsync()
|
|
412
|
+
{
|
|
413
|
+
// Buscar documentos de fonte externa (API, arquivos, etc.)
|
|
414
|
+
var documents = await _documentProvider.GetAllDocumentsAsync();
|
|
415
|
+
|
|
416
|
+
await _indexingService.BulkIndexAsync(documents);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Program.cs - Agendar job diário
|
|
421
|
+
RecurringJob.AddOrUpdate<DocumentIndexingJob>(
|
|
422
|
+
"index-documents",
|
|
423
|
+
job => job.IndexAllDocumentsAsync(),
|
|
424
|
+
Cron.Daily
|
|
425
|
+
);
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
## 📊 Índices de Performance
|
|
431
|
+
|
|
432
|
+
### Tipos de Índices
|
|
433
|
+
|
|
434
|
+
| Tipo | Descrição | Performance | Precisão |
|
|
435
|
+
|------|-----------|-------------|----------|
|
|
436
|
+
| **IVFFlat** | Inverted File + Flat compression | Boa | Alta |
|
|
437
|
+
| **HNSW** | Hierarchical Navigable Small World | Excelente | Alta |
|
|
438
|
+
|
|
439
|
+
### Configuração IVFFlat
|
|
440
|
+
|
|
441
|
+
```csharp
|
|
442
|
+
entity.HasIndex(d => d.Embedding)
|
|
443
|
+
.HasMethod("ivfflat")
|
|
444
|
+
.HasOptions("lists = 100"); // Ajustar conforme dataset
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
**Recomendação de `lists`:**
|
|
448
|
+
- Pequeno dataset (<10k docs): `lists = 50`
|
|
449
|
+
- Médio dataset (10k-100k): `lists = 100`
|
|
450
|
+
- Grande dataset (>100k): `lists = 500+`
|
|
451
|
+
|
|
452
|
+
### Configuração HNSW
|
|
453
|
+
|
|
454
|
+
```csharp
|
|
455
|
+
entity.HasIndex(d => d.Embedding)
|
|
456
|
+
.HasMethod("hnsw")
|
|
457
|
+
.HasOptions("m = 16, ef_construction = 64");
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
**Parâmetros:**
|
|
461
|
+
- `m`: Número de conexões (padrão: 16)
|
|
462
|
+
- `ef_construction`: Qualidade do índice (padrão: 64)
|
|
463
|
+
|
|
464
|
+
---
|
|
465
|
+
|
|
466
|
+
## 💰 Custos
|
|
467
|
+
|
|
468
|
+
### Embedding Generation
|
|
469
|
+
|
|
470
|
+
| Modelo | Dimensões | Custo |
|
|
471
|
+
|--------|-----------|-------|
|
|
472
|
+
| text-embedding-3-small | 1536 | $0.02 / 1M tokens |
|
|
473
|
+
| text-embedding-3-large | 3072 | $0.13 / 1M tokens |
|
|
474
|
+
|
|
475
|
+
**Recomendação:** Use `text-embedding-3-small` (melhor custo-benefício).
|
|
476
|
+
|
|
477
|
+
### Storage
|
|
478
|
+
|
|
479
|
+
| Dimensões | Tamanho por Documento | 10k Docs | 100k Docs |
|
|
480
|
+
|-----------|-----------------------|----------|-----------|
|
|
481
|
+
| 1536 | ~6 KB | ~60 MB | ~600 MB |
|
|
482
|
+
| 3072 | ~12 KB | ~120 MB | ~1.2 GB |
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
## ✅ Checklist de Implementação
|
|
487
|
+
|
|
488
|
+
- [ ] EF Core 10 instalado
|
|
489
|
+
- [ ] Azure SQL ou SQL Server 2022+
|
|
490
|
+
- [ ] Entidade com propriedade `ReadOnlyMemory<float>`
|
|
491
|
+
- [ ] Índice de vector criado (`ivfflat` ou `hnsw`)
|
|
492
|
+
- [ ] `IEmbeddingService` configurado
|
|
493
|
+
- [ ] Serviço de search implementado
|
|
494
|
+
- [ ] RAG pattern com Agent Framework
|
|
495
|
+
- [ ] Job de indexação configurado
|
|
496
|
+
- [ ] Testes de similaridade funcionando
|
|
497
|
+
|
|
498
|
+
---
|
|
499
|
+
|
|
500
|
+
## 🐛 Troubleshooting
|
|
501
|
+
|
|
502
|
+
### Erro: "vector type not supported"
|
|
503
|
+
|
|
504
|
+
**Causa:** SQL Server não suporta vectors ou versão antiga.
|
|
505
|
+
|
|
506
|
+
**Solução:** Use Azure SQL Database ou SQL Server 2022+.
|
|
507
|
+
|
|
508
|
+
### Performance lenta em queries
|
|
509
|
+
|
|
510
|
+
**Causa:** Índice não criado ou mal configurado.
|
|
511
|
+
|
|
512
|
+
**Solução:**
|
|
513
|
+
- Verifique se índice foi criado: `SELECT * FROM sys.indexes WHERE name LIKE '%Embedding%'`
|
|
514
|
+
- Ajuste parâmetros `lists` (IVFFlat) ou `m` (HNSW)
|
|
515
|
+
|
|
516
|
+
### Embeddings com dimensões erradas
|
|
517
|
+
|
|
518
|
+
**Causa:** Modelo de embedding diferente do esperado.
|
|
519
|
+
|
|
520
|
+
**Solução:**
|
|
521
|
+
- `text-embedding-3-small` → 1536 dimensões
|
|
522
|
+
- `text-embedding-3-large` → 3072 dimensões
|
|
523
|
+
- Atualize `vector(N)` na migration
|
|
524
|
+
|
|
525
|
+
---
|
|
526
|
+
|
|
527
|
+
## 📚 Referências
|
|
528
|
+
|
|
529
|
+
- [EF Core 10 - Vector Search](https://learn.microsoft.com/ef/core/what-is-new/ef-core-10.0/whatsnew)
|
|
530
|
+
- [Azure SQL Vector Search](https://learn.microsoft.com/azure/azure-sql/database/ai-artificial-intelligence-vector-search)
|
|
531
|
+
- [OpenAI Embeddings](https://platform.openai.com/docs/guides/embeddings)
|
|
532
|
+
- [RAG Architecture](https://learn.microsoft.com/azure/architecture/ai-ml/guide/rag/rag-solution-design-and-evaluation-guide)
|
|
533
|
+
|
|
534
|
+
---
|
|
535
|
+
|
|
536
|
+
*MORPH-SPEC by Polymorphism Tech*
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./templates/state.template.json",
|
|
3
|
+
"version": "2.1.1",
|
|
4
|
+
"project": {
|
|
5
|
+
"name": "{PROJECT_NAME}",
|
|
6
|
+
"type": "blazor-server",
|
|
7
|
+
"createdAt": null,
|
|
8
|
+
"updatedAt": null
|
|
9
|
+
},
|
|
10
|
+
"features": {},
|
|
11
|
+
"metadata": {
|
|
12
|
+
"totalFeatures": 0,
|
|
13
|
+
"completedFeatures": 0,
|
|
14
|
+
"totalCostEstimated": 0,
|
|
15
|
+
"totalTimeSpent": 0,
|
|
16
|
+
"lastUpdated": null
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
using Microsoft.FluentUI.AspNetCore.Components;
|
|
2
|
+
|
|
3
|
+
namespace {Namespace}.Themes;
|
|
4
|
+
|
|
5
|
+
/// <summary>
|
|
6
|
+
/// Fluent UI Design Theme customizado para {Project Name}.
|
|
7
|
+
/// Gerado automaticamente baseado em ui-design-system.md
|
|
8
|
+
/// </summary>
|
|
9
|
+
/// <remarks>
|
|
10
|
+
/// Template MORPH-SPEC v2.1.1 by Polymorphism Tech
|
|
11
|
+
/// </remarks>
|
|
12
|
+
public static class FluentDesignTheme
|
|
13
|
+
{
|
|
14
|
+
/// <summary>
|
|
15
|
+
/// Paleta de cores do tema
|
|
16
|
+
/// </summary>
|
|
17
|
+
public static DesignThemePalette GetPalette() => new()
|
|
18
|
+
{
|
|
19
|
+
// Cores Principais
|
|
20
|
+
Primary = "#{hex da cor primária}", // Ex: #3b82f6
|
|
21
|
+
Secondary = "#{hex da cor secundária}", // Ex: #6b7280
|
|
22
|
+
|
|
23
|
+
// Cores de Estado
|
|
24
|
+
Success = "#{hex}", // Ex: #10b981
|
|
25
|
+
Error = "#{hex}", // Ex: #ef4444
|
|
26
|
+
Warning = "#{hex}", // Ex: #f59e0b
|
|
27
|
+
Info = "#{hex}", // Ex: #06b6d4
|
|
28
|
+
|
|
29
|
+
// Neutros
|
|
30
|
+
NeutralLight = "#{hex do gray-50}", // Ex: #f9fafb
|
|
31
|
+
NeutralLighter = "#{hex do gray-100}", // Ex: #f3f4f6
|
|
32
|
+
NeutralDark = "#{hex do gray-800}", // Ex: #1f2937
|
|
33
|
+
NeutralDarker = "#{hex do gray-900}", // Ex: #111827
|
|
34
|
+
|
|
35
|
+
// Background e Foreground
|
|
36
|
+
Background = "#ffffff",
|
|
37
|
+
Foreground = "#{hex do gray-900}", // Texto principal
|
|
38
|
+
|
|
39
|
+
// Accent (geralmente = Primary)
|
|
40
|
+
Accent = "#{hex da cor primária}",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/// <summary>
|
|
44
|
+
/// Configuração de tipografia
|
|
45
|
+
/// </summary>
|
|
46
|
+
public static DesignThemeTypography GetTypography() => new()
|
|
47
|
+
{
|
|
48
|
+
FontFamily = "'{Font Name}', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
49
|
+
|
|
50
|
+
// Tamanhos
|
|
51
|
+
FontSizeBase = "1rem", // 16px
|
|
52
|
+
FontSizeSmall = "0.875rem", // 14px
|
|
53
|
+
FontSizeLarge = "1.125rem", // 18px
|
|
54
|
+
|
|
55
|
+
// Weights
|
|
56
|
+
FontWeightRegular = 400,
|
|
57
|
+
FontWeightSemibold = 600,
|
|
58
|
+
FontWeightBold = 700,
|
|
59
|
+
|
|
60
|
+
// Line Heights
|
|
61
|
+
LineHeightBase = 1.5,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/// <summary>
|
|
65
|
+
/// Configuração de espaçamento e layout
|
|
66
|
+
/// </summary>
|
|
67
|
+
public static DesignThemeLayout GetLayout() => new()
|
|
68
|
+
{
|
|
69
|
+
// Espaçamento (baseado em múltiplos de {base}px - ex: 4px)
|
|
70
|
+
SpacingBase = "0.25rem", // 4px
|
|
71
|
+
SpacingSmall = "0.5rem", // 8px
|
|
72
|
+
SpacingMedium = "1rem", // 16px
|
|
73
|
+
SpacingLarge = "1.5rem", // 24px
|
|
74
|
+
|
|
75
|
+
// Border Radius
|
|
76
|
+
BorderRadiusSmall = "0.125rem", // 2px
|
|
77
|
+
BorderRadiusMedium = "0.375rem", // 6px
|
|
78
|
+
BorderRadiusLarge = "0.5rem", // 8px
|
|
79
|
+
|
|
80
|
+
// Shadows (depende da biblioteca - ajustar conforme API)
|
|
81
|
+
Elevation1 = "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
|
|
82
|
+
Elevation2 = "0 4px 6px -1px rgba(0, 0, 0, 0.1)",
|
|
83
|
+
Elevation3 = "0 10px 15px -3px rgba(0, 0, 0, 0.1)",
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/// <summary>
|
|
87
|
+
/// Aplica o tema customizado ao FluentUI
|
|
88
|
+
/// </summary>
|
|
89
|
+
/// <param name="services">Service collection</param>
|
|
90
|
+
public static void AddFluentUIWithCustomTheme(this IServiceCollection services)
|
|
91
|
+
{
|
|
92
|
+
services.AddFluentUIComponents(options =>
|
|
93
|
+
{
|
|
94
|
+
// Aplicar paleta customizada
|
|
95
|
+
options.DesignThemePalette = GetPalette();
|
|
96
|
+
|
|
97
|
+
// Outras configurações do Fluent UI
|
|
98
|
+
// ...
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/// <summary>
|
|
104
|
+
/// Classe de configuração de tipografia
|
|
105
|
+
/// (Ajustar conforme API do Fluent UI Blazor v4.x)
|
|
106
|
+
/// </summary>
|
|
107
|
+
public class DesignThemeTypography
|
|
108
|
+
{
|
|
109
|
+
public string FontFamily { get; set; } = string.Empty;
|
|
110
|
+
public string FontSizeBase { get; set; } = string.Empty;
|
|
111
|
+
public string FontSizeSmall { get; set; } = string.Empty;
|
|
112
|
+
public string FontSizeLarge { get; set; } = string.Empty;
|
|
113
|
+
public int FontWeightRegular { get; set; }
|
|
114
|
+
public int FontWeightSemibold { get; set; }
|
|
115
|
+
public int FontWeightBold { get; set; }
|
|
116
|
+
public double LineHeightBase { get; set; }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/// <summary>
|
|
120
|
+
/// Classe de configuração de layout
|
|
121
|
+
/// (Ajustar conforme API do Fluent UI Blazor v4.x)
|
|
122
|
+
/// </summary>
|
|
123
|
+
public class DesignThemeLayout
|
|
124
|
+
{
|
|
125
|
+
public string SpacingBase { get; set; } = string.Empty;
|
|
126
|
+
public string SpacingSmall { get; set; } = string.Empty;
|
|
127
|
+
public string SpacingMedium { get; set; } = string.Empty;
|
|
128
|
+
public string SpacingLarge { get; set; } = string.Empty;
|
|
129
|
+
|
|
130
|
+
public string BorderRadiusSmall { get; set; } = string.Empty;
|
|
131
|
+
public string BorderRadiusMedium { get; set; } = string.Empty;
|
|
132
|
+
public string BorderRadiusLarge { get; set; } = string.Empty;
|
|
133
|
+
|
|
134
|
+
public string Elevation1 { get; set; } = string.Empty;
|
|
135
|
+
public string Elevation2 { get; set; } = string.Empty;
|
|
136
|
+
public string Elevation3 { get; set; } = string.Empty;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/// <summary>
|
|
140
|
+
/// EXEMPLO DE USO:
|
|
141
|
+
///
|
|
142
|
+
/// // Program.cs
|
|
143
|
+
/// builder.Services.AddFluentUIWithCustomTheme();
|
|
144
|
+
///
|
|
145
|
+
/// // App.razor ou MainLayout.razor
|
|
146
|
+
/// <FluentDesignTheme Mode="DesignThemeMode.Light" />
|
|
147
|
+
///
|
|
148
|
+
/// // Componentes podem acessar cores via CSS variables ou propriedades do tema
|
|
149
|
+
/// </summary>
|