@polymorphism-tech/morph-spec 7.0.0 → 7.1.1
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/README.md +368 -368
- package/claude-plugin.json +14 -14
- package/docs/CHEATSHEET.md +199 -199
- package/docs/QUICKSTART.md +200 -200
- package/framework/CLAUDE.md +1 -1
- package/framework/MORPH.md +4 -1
- package/framework/agents.json +132 -127
- package/framework/rubrics/backend.json +41 -36
- package/framework/rules/ai-agents-standards.md +45 -15
- package/framework/rules/neon-standards.md +25 -26
- package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +3 -1
- package/framework/skills/core/morph-stack-scan/SKILL.md +3 -3
- package/framework/standards/STANDARDS.json +1729 -1133
- package/framework/standards/ai-agents/agent-session.md +160 -0
- package/framework/standards/ai-agents/batching.md +193 -0
- package/framework/standards/ai-agents/context-providers.md +188 -0
- package/framework/standards/ai-agents/mcp-server.md +250 -0
- package/framework/standards/ai-agents/mcp-tools.md +197 -0
- package/framework/standards/ai-agents/middleware-patterns.md +207 -0
- package/framework/standards/ai-agents/modalities-image-gen.md +173 -0
- package/framework/standards/ai-agents/modalities-speech-to-text.md +171 -0
- package/framework/standards/ai-agents/modalities-text-to-speech.md +181 -0
- package/framework/standards/ai-agents/modalities-vision.md +201 -0
- package/framework/standards/ai-agents/multi-agent-patterns.md +155 -0
- package/framework/standards/ai-agents/production.md +114 -258
- package/framework/standards/ai-agents/prompts-in-db.md +214 -0
- package/framework/standards/ai-agents/providers/model-registry.md +158 -0
- package/framework/standards/ai-agents/rag-custom-pgvector.md +243 -0
- package/framework/standards/ai-agents/service-tier-flex.md +135 -0
- package/framework/standards/ai-agents/setup.md +142 -316
- package/framework/standards/ai-agents/structured-output.md +249 -0
- package/framework/standards/ai-agents/sweet-spot.md +109 -0
- package/framework/standards/ai-agents/workflows.md +114 -284
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +439 -429
- package/framework/standards/backend/api/validation.md +92 -88
- package/framework/standards/backend/database/postgresql/database.md +336 -332
- package/framework/standards/backend/database/vector-search-rag.md +599 -541
- package/framework/standards/backend/dotnet/fluent-validation-vsa.md +4 -0
- package/framework/standards/backend/dotnet/result-pattern.md +4 -0
- package/framework/standards/backend/dotnet/scalar-ui.md +176 -172
- package/framework/standards/backend/dotnet/scrutor.md +4 -0
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +310 -274
- package/framework/standards/backend/integrations/asaas/asaas-api.md +260 -216
- package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +354 -350
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +30 -19
- package/framework/standards/backend/integrations/resend/resend-email.md +400 -385
- package/framework/standards/data/nosql/cache/redis.md +106 -97
- package/framework/standards/data/nosql/cosmos-db.md +127 -118
- package/framework/standards/data/vector-search/rag-chunking.md +113 -104
- package/framework/standards/frontend/design-system/ai-image-generation.md +4 -0
- package/framework/standards/frontend/design-system/animations.md +164 -160
- package/framework/standards/frontend/design-system/naming.md +68 -64
- package/framework/standards/frontend/gsap/gsap-core.md +14 -8
- package/framework/standards/frontend/gsap/gsap-plugins.md +26 -21
- package/framework/standards/frontend/gsap/gsap-scrolltrigger.md +9 -7
- package/framework/standards/frontend/nextjs/app-router.md +127 -123
- package/framework/standards/frontend/nextjs/components.md +136 -132
- package/framework/standards/frontend/nextjs/data-fetching.md +130 -126
- package/framework/standards/frontend/nextjs/forms.md +134 -128
- package/framework/standards/frontend/nextjs/motion-patterns.md +328 -322
- package/framework/standards/frontend/nextjs/naming-conventions.md +71 -67
- package/framework/standards/frontend/nextjs/nextjs-patterns.md +229 -225
- package/framework/standards/frontend/nextjs/project-structure.md +106 -102
- package/framework/standards/frontend/nextjs/state-management.md +76 -72
- package/framework/standards/frontend/nextjs/testing.md +115 -111
- package/framework/standards/infrastructure/docker/easypanel-deploy.md +196 -192
- package/framework/standards/infrastructure/neon/neon-pgrag.md +6 -2
- package/framework/standards/infrastructure/neon/neon-pgvector.md +4 -0
- package/framework/standards/infrastructure/neon/neon-setup.md +5 -1
- package/framework/standards/integration/mcp/mcp-tools.md +471 -462
- package/framework/templates/CLAUDE.md.core.template +1 -1
- package/framework/templates/REGISTRY.json +1745 -1239
- package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -0
- package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -0
- package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -0
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -0
- package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -0
- package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -0
- package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -0
- package/framework/templates/code/dotnet/backend/repository.cs +3 -10
- package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -0
- package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -0
- package/package.json +89 -83
- package/src/core/templates/template-registry.js +289 -256
- package/src/lib/generators/context-generator.js +516 -518
- package/src/lib/generators/scaffold-generator.js +23 -26
- package/framework/standards/infrastructure/neon/neon-semantic-kernel.md +0 -310
- package/framework/templates/integrations/semantic-kernel-neon.cs +0 -125
|
@@ -1,541 +1,599 @@
|
|
|
1
|
-
# Vector Search + RAG
|
|
2
|
-
|
|
3
|
-
> **Scope:** blazor-azure
|
|
4
|
-
> **Layer:** 2 (on keyword)
|
|
5
|
-
> **Keywords:**
|
|
6
|
-
> **Load When:** vector search or rag keywords detected
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## 🎯 O Que É Vector Search?
|
|
13
|
-
|
|
14
|
-
**Vector search** permite buscar dados por **similaridade semântica** ao invés de correspondência exata de texto.
|
|
15
|
-
|
|
16
|
-
### Conceito
|
|
17
|
-
|
|
18
|
-
```
|
|
19
|
-
Texto → Embedding (vetor de números) → Busca por similaridade
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
**Exemplo:**
|
|
23
|
-
- Query: "Como resetar senha?"
|
|
24
|
-
- Documento 1: "Tutorial de recuperação de senha" ← **Match semântico!**
|
|
25
|
-
- Documento 2: "Alterar credenciais de acesso" ← **Match semântico!**
|
|
26
|
-
- Documento 3: "Configurar email" ← Não match
|
|
27
|
-
|
|
28
|
-
### Casos de Uso
|
|
29
|
-
|
|
30
|
-
| Caso de Uso | Descrição |
|
|
31
|
-
|-------------|-----------|
|
|
32
|
-
| **RAG (Retrieval-Augmented Generation)** | Buscar documentos relevantes para enviar ao LLM |
|
|
33
|
-
| **Busca semântica** | Encontrar conteúdo similar sem keywords exatas |
|
|
34
|
-
| **Recomendações** | Sugerir produtos/artigos similares |
|
|
35
|
-
| **Deduplicação** | Identificar conteúdo duplicado |
|
|
36
|
-
|
|
37
|
-
---
|
|
38
|
-
|
|
39
|
-
## 📦 Setup
|
|
40
|
-
|
|
41
|
-
### 1. Packages Necessários
|
|
42
|
-
|
|
43
|
-
```xml
|
|
44
|
-
<!-- .csproj -->
|
|
45
|
-
<PackageReference Include="
|
|
46
|
-
<PackageReference Include="
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
public
|
|
70
|
-
public
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
{
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
public
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
{
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
{
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
{
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
{
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
);
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
**
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
1
|
+
# Vector Search + RAG — EF Core 10 + pgvector (.NET 10)
|
|
2
|
+
|
|
3
|
+
> **Scope:** blazor-azure
|
|
4
|
+
> **Layer:** 2 (on keyword)
|
|
5
|
+
> **Keywords:** postgres, pgvector, vector, embedding, search, rag, ai
|
|
6
|
+
> **Load When:** vector search or rag keywords detected
|
|
7
|
+
|
|
8
|
+
EF Core 10 (.NET 10) com PostgreSQL + extensão `pgvector` para vector search em workloads de AI. Para a modelagem da coluna `vector`, índices `ivfflat`/`hnsw` e os operadores de distância do `pgvector`, ver `infrastructure/neon/neon-pgvector.md` — este standard cobre o pipeline RAG end-to-end (embeddings, search, geração) sobre essa base.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 🎯 O Que É Vector Search?
|
|
13
|
+
|
|
14
|
+
**Vector search** permite buscar dados por **similaridade semântica** ao invés de correspondência exata de texto.
|
|
15
|
+
|
|
16
|
+
### Conceito
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Texto → Embedding (vetor de números) → Busca por similaridade
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Exemplo:**
|
|
23
|
+
- Query: "Como resetar senha?"
|
|
24
|
+
- Documento 1: "Tutorial de recuperação de senha" ← **Match semântico!**
|
|
25
|
+
- Documento 2: "Alterar credenciais de acesso" ← **Match semântico!**
|
|
26
|
+
- Documento 3: "Configurar email" ← Não match
|
|
27
|
+
|
|
28
|
+
### Casos de Uso
|
|
29
|
+
|
|
30
|
+
| Caso de Uso | Descrição |
|
|
31
|
+
|-------------|-----------|
|
|
32
|
+
| **RAG (Retrieval-Augmented Generation)** | Buscar documentos relevantes para enviar ao LLM |
|
|
33
|
+
| **Busca semântica** | Encontrar conteúdo similar sem keywords exatas |
|
|
34
|
+
| **Recomendações** | Sugerir produtos/artigos similares |
|
|
35
|
+
| **Deduplicação** | Identificar conteúdo duplicado |
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 📦 Setup
|
|
40
|
+
|
|
41
|
+
### 1. Packages Necessários
|
|
42
|
+
|
|
43
|
+
```xml
|
|
44
|
+
<!-- .csproj -->
|
|
45
|
+
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.0" />
|
|
46
|
+
<PackageReference Include="Pgvector.EntityFrameworkCore" Version="0.2.2" />
|
|
47
|
+
<PackageReference Include="Microsoft.Extensions.AI" Version="9.9.1" />
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 2. PostgreSQL + pgvector Requerido
|
|
51
|
+
|
|
52
|
+
**Requisito:** PostgreSQL com a extensão `pgvector` instalada (`CREATE EXTENSION vector;`).
|
|
53
|
+
Neon, Supabase e Azure Database for PostgreSQL já trazem `pgvector` disponível.
|
|
54
|
+
|
|
55
|
+
**Nota:** habilite a extensão via migration EF Core — `migrationBuilder.Sql("CREATE EXTENSION IF NOT EXISTS vector;")` — antes de criar as colunas de vetor.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 🗄️ Modelo de Dados com Vectors
|
|
60
|
+
|
|
61
|
+
### Entidade com Embedding
|
|
62
|
+
|
|
63
|
+
```csharp
|
|
64
|
+
using Microsoft.EntityFrameworkCore;
|
|
65
|
+
using Pgvector;
|
|
66
|
+
|
|
67
|
+
public class Document
|
|
68
|
+
{
|
|
69
|
+
public int Id { get; set; }
|
|
70
|
+
public string Title { get; set; } = null!;
|
|
71
|
+
public string Content { get; set; } = null!;
|
|
72
|
+
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
73
|
+
|
|
74
|
+
// Vector embedding (1536 dimensões para text-embedding-3-small).
|
|
75
|
+
// O tipo `Vector` vem do pacote Pgvector — EF Core não mapeia `float[]`.
|
|
76
|
+
public Vector Embedding { get; set; } = null!;
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### DbContext Configuração
|
|
81
|
+
|
|
82
|
+
```csharp
|
|
83
|
+
public class AppDbContext : DbContext
|
|
84
|
+
{
|
|
85
|
+
public DbSet<Document> Documents { get; set; }
|
|
86
|
+
|
|
87
|
+
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
88
|
+
{
|
|
89
|
+
// Registra a extensão pgvector — gera `CREATE EXTENSION vector` na migration
|
|
90
|
+
modelBuilder.HasPostgresExtension("vector");
|
|
91
|
+
|
|
92
|
+
modelBuilder.Entity<Document>(entity =>
|
|
93
|
+
{
|
|
94
|
+
entity.HasKey(d => d.Id);
|
|
95
|
+
|
|
96
|
+
// Configurar coluna de vector
|
|
97
|
+
entity.Property(d => d.Embedding)
|
|
98
|
+
.HasColumnType("vector(1536)") // 1536 = dimensões do embedding
|
|
99
|
+
.IsRequired();
|
|
100
|
+
|
|
101
|
+
// Índice de vector para performance — operador de distância cosseno
|
|
102
|
+
entity.HasIndex(d => d.Embedding)
|
|
103
|
+
.HasMethod("hnsw") // ou "ivfflat"
|
|
104
|
+
.HasOperators("vector_cosine_ops")
|
|
105
|
+
.HasStorageParameter("m", 16)
|
|
106
|
+
.HasStorageParameter("ef_construction", 64);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
> O provider Npgsql precisa de `UseVector()` no `NpgsqlDataSourceBuilder` (ou
|
|
113
|
+
> no `UseNpgsql(...)`) para mapear o tipo `vector` — ver a configuração do
|
|
114
|
+
> `Program.cs` abaixo e `infrastructure/neon/neon-pgvector.md`.
|
|
115
|
+
|
|
116
|
+
### Migration
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
dotnet ef migrations add AddVectorSearch
|
|
120
|
+
dotnet ef database update
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**SQL Gerado:**
|
|
124
|
+
```sql
|
|
125
|
+
CREATE EXTENSION IF NOT EXISTS vector;
|
|
126
|
+
ALTER TABLE "Documents" ADD "Embedding" vector(1536) NOT NULL;
|
|
127
|
+
CREATE INDEX "IX_Documents_Embedding" ON "Documents"
|
|
128
|
+
USING hnsw ("Embedding" vector_cosine_ops) WITH (m = 16, ef_construction = 64);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 🔢 Gerando Embeddings
|
|
134
|
+
|
|
135
|
+
### Serviço de Embedding
|
|
136
|
+
|
|
137
|
+
```csharp
|
|
138
|
+
using Microsoft.Extensions.AI;
|
|
139
|
+
using Pgvector;
|
|
140
|
+
|
|
141
|
+
public interface IEmbeddingService
|
|
142
|
+
{
|
|
143
|
+
Task<Vector> GenerateEmbeddingAsync(string text, CancellationToken ct = default);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
public class EmbeddingService : IEmbeddingService
|
|
147
|
+
{
|
|
148
|
+
private readonly IEmbeddingGenerator<string, Embedding<float>> _embeddingGenerator;
|
|
149
|
+
|
|
150
|
+
public EmbeddingService(IEmbeddingGenerator<string, Embedding<float>> embeddingGenerator)
|
|
151
|
+
{
|
|
152
|
+
_embeddingGenerator = embeddingGenerator;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
public async Task<Vector> GenerateEmbeddingAsync(
|
|
156
|
+
string text,
|
|
157
|
+
CancellationToken ct = default)
|
|
158
|
+
{
|
|
159
|
+
// GenerateVectorAsync devolve o ReadOnlyMemory<float> diretamente
|
|
160
|
+
var vector = await _embeddingGenerator.GenerateVectorAsync(text, cancellationToken: ct);
|
|
161
|
+
return new Vector(vector);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Configuração no Program.cs
|
|
167
|
+
|
|
168
|
+
```csharp
|
|
169
|
+
using Azure.AI.OpenAI;
|
|
170
|
+
using Microsoft.Extensions.AI;
|
|
171
|
+
using System.ClientModel;
|
|
172
|
+
|
|
173
|
+
// DbContext com o type handler do pgvector registrado
|
|
174
|
+
builder.Services.AddDbContext<AppDbContext>(options =>
|
|
175
|
+
options.UseNpgsql(
|
|
176
|
+
builder.Configuration.GetConnectionString("Default"),
|
|
177
|
+
npgsql => npgsql.UseVector()));
|
|
178
|
+
|
|
179
|
+
// Embedding generator do Azure OpenAI exposto como IEmbeddingGenerator
|
|
180
|
+
builder.Services.AddSingleton<IEmbeddingGenerator<string, Embedding<float>>>(sp =>
|
|
181
|
+
{
|
|
182
|
+
var config = sp.GetRequiredService<IConfiguration>();
|
|
183
|
+
|
|
184
|
+
var azureClient = new AzureOpenAIClient(
|
|
185
|
+
new Uri(config["AzureOpenAI:Endpoint"]!),
|
|
186
|
+
new ApiKeyCredential(config["AzureOpenAI:ApiKey"]!));
|
|
187
|
+
|
|
188
|
+
return azureClient
|
|
189
|
+
.GetEmbeddingClient("text-embedding-3-small") // 1536 dimensões
|
|
190
|
+
.AsIEmbeddingGenerator();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
builder.Services.AddScoped<IEmbeddingService, EmbeddingService>();
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## 🔍 Vector Search com EF Core 10
|
|
199
|
+
|
|
200
|
+
### Query de Similaridade
|
|
201
|
+
|
|
202
|
+
```csharp
|
|
203
|
+
using Microsoft.EntityFrameworkCore;
|
|
204
|
+
|
|
205
|
+
public class DocumentSearchService
|
|
206
|
+
{
|
|
207
|
+
private readonly AppDbContext _context;
|
|
208
|
+
private readonly IEmbeddingService _embeddingService;
|
|
209
|
+
|
|
210
|
+
public DocumentSearchService(
|
|
211
|
+
AppDbContext context,
|
|
212
|
+
IEmbeddingService embeddingService)
|
|
213
|
+
{
|
|
214
|
+
_context = context;
|
|
215
|
+
_embeddingService = embeddingService;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
public async Task<List<DocumentSearchResult>> SearchAsync(
|
|
219
|
+
string query,
|
|
220
|
+
int limit = 5,
|
|
221
|
+
CancellationToken ct = default)
|
|
222
|
+
{
|
|
223
|
+
// 1. Gerar embedding da query
|
|
224
|
+
var queryEmbedding = await _embeddingService.GenerateEmbeddingAsync(query, ct);
|
|
225
|
+
|
|
226
|
+
// 2. Buscar documentos similares — EF.Functions.CosineDistance traduz para
|
|
227
|
+
// o operador `<=>` do pgvector e usa o índice HNSW vector_cosine_ops
|
|
228
|
+
var results = await _context.Documents
|
|
229
|
+
.Select(d => new DocumentSearchResult
|
|
230
|
+
{
|
|
231
|
+
Document = d,
|
|
232
|
+
// Distância cosseno (menor = mais similar)
|
|
233
|
+
Distance = EF.Functions.CosineDistance(d.Embedding, queryEmbedding)
|
|
234
|
+
})
|
|
235
|
+
.OrderBy(r => r.Distance)
|
|
236
|
+
.Take(limit)
|
|
237
|
+
.ToListAsync(ct);
|
|
238
|
+
|
|
239
|
+
return results;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
public class DocumentSearchResult
|
|
244
|
+
{
|
|
245
|
+
public Document Document { get; set; } = null!;
|
|
246
|
+
public double Distance { get; set; }
|
|
247
|
+
public double Similarity => 1 - Distance; // Converter distância em similaridade
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Funções de Distância
|
|
252
|
+
|
|
253
|
+
O `Pgvector.EntityFrameworkCore` expõe as funções de distância como métodos
|
|
254
|
+
`EF.Functions.*`, cada uma traduzida para o operador correspondente do `pgvector`:
|
|
255
|
+
|
|
256
|
+
| Função (`EF.Functions.*`) | Operador pgvector | Quando Usar |
|
|
257
|
+
|---------------------------|-------------------|-------------|
|
|
258
|
+
| `CosineDistance` | `<=>` | **Recomendado** para texto / embeddings normalizados |
|
|
259
|
+
| `L2Distance` | `<->` | Distância euclidiana — dados numéricos |
|
|
260
|
+
| `MaxInnerProduct` | `<#>` | Produto interno negativo — vetores normalizados |
|
|
261
|
+
|
|
262
|
+
```csharp
|
|
263
|
+
EF.Functions.CosineDistance(d.Embedding, queryEmbedding)
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
> O `HasOperators` do índice precisa casar com a função usada na query —
|
|
267
|
+
> `vector_cosine_ops` para `CosineDistance`, `vector_l2_ops` para `L2Distance`,
|
|
268
|
+
> `vector_ip_ops` para `MaxInnerProduct`. Operador e índice divergentes fazem o
|
|
269
|
+
> Postgres ignorar o índice e cair em scan sequencial.
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## 🤖 RAG Pattern Completo
|
|
274
|
+
|
|
275
|
+
### Implementação RAG com Agent Framework
|
|
276
|
+
|
|
277
|
+
```csharp
|
|
278
|
+
using Microsoft.Extensions.AI;
|
|
279
|
+
|
|
280
|
+
public interface IDocumentAssistantAgent
|
|
281
|
+
{
|
|
282
|
+
Task<string> AskQuestionAsync(string question, CancellationToken ct = default);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
public class DocumentAssistantAgent : IDocumentAssistantAgent
|
|
286
|
+
{
|
|
287
|
+
private readonly IChatClient _chatClient;
|
|
288
|
+
private readonly DocumentSearchService _searchService;
|
|
289
|
+
private readonly ILogger<DocumentAssistantAgent> _logger;
|
|
290
|
+
|
|
291
|
+
public DocumentAssistantAgent(
|
|
292
|
+
IChatClient chatClient,
|
|
293
|
+
DocumentSearchService searchService,
|
|
294
|
+
ILogger<DocumentAssistantAgent> logger)
|
|
295
|
+
{
|
|
296
|
+
_chatClient = chatClient;
|
|
297
|
+
_searchService = searchService;
|
|
298
|
+
_logger = logger;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
public async Task<string> AskQuestionAsync(string question, CancellationToken ct = default)
|
|
302
|
+
{
|
|
303
|
+
// 1. Retrieval: Buscar documentos relevantes
|
|
304
|
+
var relevantDocs = await _searchService.SearchAsync(question, limit: 3, ct);
|
|
305
|
+
|
|
306
|
+
_logger.LogInformation(
|
|
307
|
+
"Encontrados {Count} documentos relevantes para: {Question}",
|
|
308
|
+
relevantDocs.Count,
|
|
309
|
+
question
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
// 2. Construir contexto
|
|
313
|
+
var context = string.Join("\n\n", relevantDocs.Select(r =>
|
|
314
|
+
$"[Documento {r.Document.Id} - Similaridade: {r.Similarity:P0}]\n" +
|
|
315
|
+
$"Título: {r.Document.Title}\n" +
|
|
316
|
+
$"Conteúdo: {r.Document.Content}"
|
|
317
|
+
));
|
|
318
|
+
|
|
319
|
+
// 3. Augmentation: Criar agente com contexto
|
|
320
|
+
// VERIFY: _chatClient.CreateAgent / agent.RunAsync — superfície MAF; alinhar com o MAF Framework Revamp em andamento
|
|
321
|
+
var agent = _chatClient.CreateAgent(
|
|
322
|
+
instructions: """
|
|
323
|
+
Você é um assistente que responde perguntas baseado em documentos fornecidos.
|
|
324
|
+
|
|
325
|
+
Regras:
|
|
326
|
+
1. Use APENAS informações dos documentos fornecidos
|
|
327
|
+
2. Se a informação não estiver nos documentos, diga "Não encontrei informação sobre isso"
|
|
328
|
+
3. Cite o número do documento ao responder
|
|
329
|
+
4. Seja conciso e objetivo
|
|
330
|
+
|
|
331
|
+
Documentos disponíveis:
|
|
332
|
+
""" + context,
|
|
333
|
+
name: "DocumentAssistant"
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
// 4. Generation: Gerar resposta
|
|
337
|
+
var response = await agent.RunAsync(question, cancellationToken: ct);
|
|
338
|
+
|
|
339
|
+
return response.Content;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### Uso no Blazor
|
|
345
|
+
|
|
346
|
+
```razor
|
|
347
|
+
@page "/ask"
|
|
348
|
+
@inject IDocumentAssistantAgent Assistant
|
|
349
|
+
|
|
350
|
+
<h3>Assistente de Documentos</h3>
|
|
351
|
+
|
|
352
|
+
<EditForm Model="_input" OnValidSubmit="AskQuestion">
|
|
353
|
+
<InputText @bind-Value="_input.Question" placeholder="Faça uma pergunta..." />
|
|
354
|
+
<button type="submit" disabled="@_isLoading">Perguntar</button>
|
|
355
|
+
</EditForm>
|
|
356
|
+
|
|
357
|
+
@if (_isLoading)
|
|
358
|
+
{
|
|
359
|
+
<p>Buscando resposta...</p>
|
|
360
|
+
}
|
|
361
|
+
else if (!string.IsNullOrEmpty(_answer))
|
|
362
|
+
{
|
|
363
|
+
<div class="answer">
|
|
364
|
+
<strong>Resposta:</strong>
|
|
365
|
+
<p>@_answer</p>
|
|
366
|
+
</div>
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
@code {
|
|
370
|
+
private QuestionInput _input = new();
|
|
371
|
+
private string _answer = "";
|
|
372
|
+
private bool _isLoading;
|
|
373
|
+
|
|
374
|
+
private async Task AskQuestion()
|
|
375
|
+
{
|
|
376
|
+
_isLoading = true;
|
|
377
|
+
_answer = "";
|
|
378
|
+
|
|
379
|
+
try
|
|
380
|
+
{
|
|
381
|
+
_answer = await Assistant.AskQuestionAsync(_input.Question);
|
|
382
|
+
}
|
|
383
|
+
finally
|
|
384
|
+
{
|
|
385
|
+
_isLoading = false;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
public class QuestionInput
|
|
390
|
+
{
|
|
391
|
+
public string Question { get; set; } = "";
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## 📥 Indexação de Documentos
|
|
399
|
+
|
|
400
|
+
### Serviço de Indexação
|
|
401
|
+
|
|
402
|
+
```csharp
|
|
403
|
+
public class DocumentIndexingService
|
|
404
|
+
{
|
|
405
|
+
private readonly AppDbContext _context;
|
|
406
|
+
private readonly IEmbeddingService _embeddingService;
|
|
407
|
+
|
|
408
|
+
public async Task IndexDocumentAsync(
|
|
409
|
+
string title,
|
|
410
|
+
string content,
|
|
411
|
+
CancellationToken ct = default)
|
|
412
|
+
{
|
|
413
|
+
// 1. Gerar embedding do conteúdo
|
|
414
|
+
var embedding = await _embeddingService.GenerateEmbeddingAsync(
|
|
415
|
+
$"{title}\n{content}", // Combinar título e conteúdo
|
|
416
|
+
ct
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
// 2. Criar documento
|
|
420
|
+
var document = new Document
|
|
421
|
+
{
|
|
422
|
+
Title = title,
|
|
423
|
+
Content = content,
|
|
424
|
+
Embedding = embedding,
|
|
425
|
+
CreatedAt = DateTime.UtcNow
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
// 3. Salvar no banco
|
|
429
|
+
_context.Documents.Add(document);
|
|
430
|
+
await _context.SaveChangesAsync(ct);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
public async Task BulkIndexAsync(
|
|
434
|
+
List<(string Title, string Content)> documents,
|
|
435
|
+
CancellationToken ct = default)
|
|
436
|
+
{
|
|
437
|
+
foreach (var (title, content) in documents)
|
|
438
|
+
{
|
|
439
|
+
await IndexDocumentAsync(title, content, ct);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### Job de Indexação com Hangfire
|
|
446
|
+
|
|
447
|
+
```csharp
|
|
448
|
+
public class DocumentIndexingJob
|
|
449
|
+
{
|
|
450
|
+
private readonly DocumentIndexingService _indexingService;
|
|
451
|
+
private readonly IDocumentProvider _documentProvider;
|
|
452
|
+
|
|
453
|
+
public async Task IndexAllDocumentsAsync()
|
|
454
|
+
{
|
|
455
|
+
// Buscar documentos de fonte externa (API, arquivos, etc.)
|
|
456
|
+
var documents = await _documentProvider.GetAllDocumentsAsync();
|
|
457
|
+
|
|
458
|
+
await _indexingService.BulkIndexAsync(documents);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Program.cs - Agendar job diário
|
|
463
|
+
RecurringJob.AddOrUpdate<DocumentIndexingJob>(
|
|
464
|
+
"index-documents",
|
|
465
|
+
job => job.IndexAllDocumentsAsync(),
|
|
466
|
+
Cron.Daily
|
|
467
|
+
);
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
---
|
|
471
|
+
|
|
472
|
+
## 📊 Índices de Performance
|
|
473
|
+
|
|
474
|
+
### Tipos de Índices
|
|
475
|
+
|
|
476
|
+
| Tipo | Descrição | Performance | Precisão |
|
|
477
|
+
|------|-----------|-------------|----------|
|
|
478
|
+
| **IVFFlat** | Inverted File + Flat compression | Boa | Alta |
|
|
479
|
+
| **HNSW** | Hierarchical Navigable Small World | Excelente | Alta |
|
|
480
|
+
|
|
481
|
+
### Configuração IVFFlat
|
|
482
|
+
|
|
483
|
+
```csharp
|
|
484
|
+
entity.HasIndex(d => d.Embedding)
|
|
485
|
+
.HasMethod("ivfflat")
|
|
486
|
+
.HasOperators("vector_cosine_ops")
|
|
487
|
+
.HasStorageParameter("lists", 100); // Ajustar conforme dataset
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
**Recomendação de `lists`:**
|
|
491
|
+
- Pequeno dataset (<10k docs): `lists = 50`
|
|
492
|
+
- Médio dataset (10k-100k): `lists = 100`
|
|
493
|
+
- Grande dataset (>100k): `lists = 500+`
|
|
494
|
+
|
|
495
|
+
> IVFFlat só fica eficiente depois de o índice ser populado — crie-o **após**
|
|
496
|
+
> carregar os dados, ou reindexe (`REINDEX`) quando o dataset crescer.
|
|
497
|
+
|
|
498
|
+
### Configuração HNSW
|
|
499
|
+
|
|
500
|
+
```csharp
|
|
501
|
+
entity.HasIndex(d => d.Embedding)
|
|
502
|
+
.HasMethod("hnsw")
|
|
503
|
+
.HasOperators("vector_cosine_ops")
|
|
504
|
+
.HasStorageParameter("m", 16)
|
|
505
|
+
.HasStorageParameter("ef_construction", 64);
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
**Parâmetros:**
|
|
509
|
+
- `m`: número de conexões por nó (padrão: 16)
|
|
510
|
+
- `ef_construction`: qualidade do índice na construção (padrão: 64)
|
|
511
|
+
|
|
512
|
+
HNSW é o padrão recomendado: melhor recall e não exige dados pré-carregados.
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
## 💰 Custos
|
|
517
|
+
|
|
518
|
+
### Embedding Generation
|
|
519
|
+
|
|
520
|
+
| Modelo | Dimensões | Custo |
|
|
521
|
+
|--------|-----------|-------|
|
|
522
|
+
| text-embedding-3-small | 1536 | $0.02 / 1M tokens |
|
|
523
|
+
| text-embedding-3-large | 3072 | $0.13 / 1M tokens |
|
|
524
|
+
|
|
525
|
+
**Recomendação:** Use `text-embedding-3-small` (melhor custo-benefício).
|
|
526
|
+
|
|
527
|
+
### Storage
|
|
528
|
+
|
|
529
|
+
| Dimensões | Tamanho por Documento | 10k Docs | 100k Docs |
|
|
530
|
+
|-----------|-----------------------|----------|-----------|
|
|
531
|
+
| 1536 | ~6 KB | ~60 MB | ~600 MB |
|
|
532
|
+
| 3072 | ~12 KB | ~120 MB | ~1.2 GB |
|
|
533
|
+
|
|
534
|
+
---
|
|
535
|
+
|
|
536
|
+
## ✅ Checklist de Implementação
|
|
537
|
+
|
|
538
|
+
- [ ] EF Core 10 + `Npgsql.EntityFrameworkCore.PostgreSQL` + `Pgvector.EntityFrameworkCore` instalados
|
|
539
|
+
- [ ] PostgreSQL com extensão `pgvector` (`HasPostgresExtension("vector")`)
|
|
540
|
+
- [ ] `UseVector()` chamado no `UseNpgsql(...)`
|
|
541
|
+
- [ ] Entidade com propriedade `Pgvector.Vector`
|
|
542
|
+
- [ ] Índice de vector criado (`hnsw` ou `ivfflat`) com `HasOperators("vector_cosine_ops")`
|
|
543
|
+
- [ ] `IEmbeddingService` configurado
|
|
544
|
+
- [ ] Serviço de search implementado com `EF.Functions.CosineDistance`
|
|
545
|
+
- [ ] RAG pattern com Agent Framework
|
|
546
|
+
- [ ] Job de indexação configurado
|
|
547
|
+
- [ ] Testes de similaridade funcionando
|
|
548
|
+
|
|
549
|
+
---
|
|
550
|
+
|
|
551
|
+
## 🐛 Troubleshooting
|
|
552
|
+
|
|
553
|
+
### Erro: "type \"vector\" does not exist"
|
|
554
|
+
|
|
555
|
+
**Causa:** extensão `pgvector` não instalada no banco.
|
|
556
|
+
|
|
557
|
+
**Solução:** rode `CREATE EXTENSION vector;` — ou registre `HasPostgresExtension("vector")`
|
|
558
|
+
no `OnModelCreating` para que a migration EF Core a crie automaticamente.
|
|
559
|
+
|
|
560
|
+
### Erro: "column ... is of type vector but expression is of type ..."
|
|
561
|
+
|
|
562
|
+
**Causa:** `UseVector()` não foi chamado — o Npgsql não mapeia o tipo `vector`.
|
|
563
|
+
|
|
564
|
+
**Solução:** chame `npgsql.UseVector()` no `UseNpgsql(...)` (ou `UseVector()` no
|
|
565
|
+
`NpgsqlDataSourceBuilder`).
|
|
566
|
+
|
|
567
|
+
### Performance lenta em queries
|
|
568
|
+
|
|
569
|
+
**Causa:** índice não criado, ou operador da query difere do `HasOperators` do índice.
|
|
570
|
+
|
|
571
|
+
**Solução:**
|
|
572
|
+
- Verifique se o índice existe: `SELECT * FROM pg_indexes WHERE indexname LIKE '%Embedding%'`
|
|
573
|
+
- Confirme com `EXPLAIN ANALYZE` que a query usa o índice (e não `Seq Scan`)
|
|
574
|
+
- Garanta que o operador casa: `CosineDistance` ↔ `vector_cosine_ops`
|
|
575
|
+
- Ajuste parâmetros `m`/`ef_construction` (HNSW) ou `lists` (IVFFlat)
|
|
576
|
+
|
|
577
|
+
### Embeddings com dimensões erradas
|
|
578
|
+
|
|
579
|
+
**Causa:** Modelo de embedding diferente do esperado.
|
|
580
|
+
|
|
581
|
+
**Solução:**
|
|
582
|
+
- `text-embedding-3-small` → 1536 dimensões
|
|
583
|
+
- `text-embedding-3-large` → 3072 dimensões
|
|
584
|
+
- Atualize `vector(N)` na migration
|
|
585
|
+
|
|
586
|
+
---
|
|
587
|
+
|
|
588
|
+
## 📚 Referências
|
|
589
|
+
|
|
590
|
+
- [pgvector](https://github.com/pgvector/pgvector) — extensão PostgreSQL para vector search
|
|
591
|
+
- [Pgvector.EntityFrameworkCore](https://github.com/pgvector/pgvector-dotnet) — type handler + `EF.Functions` de distância
|
|
592
|
+
- [Npgsql EF Core Provider](https://www.npgsql.org/efcore/) — `UseVector()`, `HasPostgresExtension`
|
|
593
|
+
- [Microsoft.Extensions.AI - Embeddings](https://learn.microsoft.com/dotnet/ai/microsoft-extensions-ai)
|
|
594
|
+
- [OpenAI Embeddings](https://platform.openai.com/docs/guides/embeddings)
|
|
595
|
+
- `infrastructure/neon/neon-pgvector.md` — modelagem da coluna, índices e operadores pgvector
|
|
596
|
+
|
|
597
|
+
---
|
|
598
|
+
|
|
599
|
+
*MORPH-SPEC by Polymorphism Tech*
|