@polymorphism-tech/morph-spec 7.5.0 → 7.7.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/README.md +2 -2
- package/bin/morph-spec.js +18 -0
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +12 -4
- package/framework/MORPH.md +18 -10
- package/framework/agents.json +13 -5
- package/framework/commands/morph-preflight.md +10 -5
- package/framework/hooks/README.md +8 -10
- package/framework/hooks/claude-code/core/gate-guard.js +36 -16
- package/framework/hooks/claude-code/core/protect-spec-files.js +20 -5
- package/framework/hooks/claude-code/core/tasks-json-guard.js +81 -0
- package/framework/hooks/shared/tasks-validator.js +68 -0
- package/framework/mcp/registry.json +5 -48
- package/framework/rubrics/backend.json +3 -3
- package/framework/rubrics/default.json +3 -3
- package/framework/rubrics/frontend.json +3 -3
- package/framework/rules/infrastructure-standards.md +4 -4
- package/framework/skills/core/create-umbraco-lp/SKILL.md +22 -2
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +84 -5
- package/framework/skills/core/morph-apply/SKILL.md +53 -40
- package/framework/skills/core/morph-brainstorming/SKILL.md +7 -12
- package/framework/skills/core/morph-eval/SKILL.md +10 -8
- package/framework/skills/core/morph-implement/SKILL.md +27 -21
- package/framework/skills/core/morph-plan/SKILL.md +12 -21
- package/framework/skills/core/morph-proposal/SKILL.md +3 -8
- package/framework/skills/core/morph-review/SKILL.md +30 -14
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/standards/STANDARDS.json +80 -8
- package/framework/standards/ai-agents/durable-workflows-hitl.md +191 -0
- package/framework/standards/ai-agents/rag-custom-pgvector.md +246 -243
- package/framework/standards/ai-agents/setup.md +3 -0
- package/framework/standards/ai-agents/vector-data-extensions.md +170 -0
- package/framework/standards/ai-agents/workflows.md +190 -187
- package/framework/standards/infrastructure/docker/{easypanel-deploy.md → coolify-deploy.md} +49 -20
- package/framework/standards/integration/mcp/mcp-tools.md +38 -43
- package/framework/templates/README.md +7 -7
- package/framework/templates/REGISTRY.json +14 -14
- package/framework/templates/context/CONTEXT.md +3 -3
- package/framework/templates/docs/proposal.md +2 -2
- package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +1 -1
- package/framework/templates/infrastructure/docker/coolify.template.json +49 -0
- package/framework/templates/infrastructure/github/README.md +6 -6
- package/framework/templates/infrastructure/github/workflows/deploy-coolify.yml.hbs +33 -0
- package/package.json +1 -1
- package/src/commands/project/doctor.js +36 -1
- package/src/commands/state/approve.js +29 -4
- package/src/commands/state/score.js +88 -0
- package/src/commands/tasks/dag.js +68 -0
- package/src/dashboard/scanner/rubrics-scanner.js +1 -1
- package/src/lib/generators/recap-generator.js +1 -1
- package/src/lib/tasks/dispatch-planner.js +201 -0
- package/src/lib/validators/score-aggregator.js +3 -3
- package/src/scripts/setup-infra.js +42 -0
- package/src/utils/agents-installer.js +142 -10
- package/src/utils/hooks-installer.js +13 -5
- package/framework/rubrics/eval-rubric.json +0 -66
- package/framework/templates/infrastructure/docker/easypanel.template.json +0 -46
- package/framework/templates/infrastructure/github/workflows/deploy-easypanel.yml.hbs +0 -31
|
@@ -1,243 +1,246 @@
|
|
|
1
|
-
# RAG Custom with pgvector — Knowledge retrieval you control
|
|
2
|
-
|
|
3
|
-
> **Scope:** stacks=["dotnet"]
|
|
4
|
-
> **Layer:** 1 (on-keyword)
|
|
5
|
-
> **Keywords:** rag, vector search, pgvector, embeddings, custom rag, retrieval, semantic search, knowledge base, RAG tool
|
|
6
|
-
> **Read by Claude in:** implement (when an agent needs to retrieve knowledge)
|
|
7
|
-
|
|
8
|
-
**Verified against:** MAF 1.0 GA + Microsoft.Extensions.AI. Last-verified: 2026-05-19.
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## Por que RAG custom
|
|
13
|
-
|
|
14
|
-
Hosted RAG (uploading PDFs to a service) hands control of chunking, embedding, and storage to a third party. You cannot tune chunk size for your document type, cannot switch embedding models without re-indexing, and cannot add tenant isolation at the storage layer. Custom RAG gives you all three.
|
|
15
|
-
|
|
16
|
-
- **Chunking:** you choose the strategy per document type (catalog, contract, technical doc). See `data-vector-search-rag-chunking`.
|
|
17
|
-
- **Embedding:** provider-agnostic. The embedding model is chosen via the `embeddings` alias in the project's Model Registry (see `ai-agents-providers-model-registry`). The project wires an `IEmbeddingGenerator<string, Embedding<float>>` for that alias — switching from `text-embedding-3-small` to another model is a one-file config change.
|
|
18
|
-
- **Storage:** pgvector on Neon — a column you own, indexed by HNSW, with RLS for tenant isolation.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
|
55
|
-
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
var
|
|
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
|
-
|
|
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
|
-
|
|
1
|
+
# RAG Custom with pgvector — Knowledge retrieval you control
|
|
2
|
+
|
|
3
|
+
> **Scope:** stacks=["dotnet"]
|
|
4
|
+
> **Layer:** 1 (on-keyword)
|
|
5
|
+
> **Keywords:** rag, vector search, pgvector, embeddings, custom rag, retrieval, semantic search, knowledge base, RAG tool
|
|
6
|
+
> **Read by Claude in:** implement (when an agent needs to retrieve knowledge)
|
|
7
|
+
|
|
8
|
+
**Verified against:** MAF 1.0 GA + Microsoft.Extensions.AI. Last-verified: 2026-05-19.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Por que RAG custom
|
|
13
|
+
|
|
14
|
+
Hosted RAG (uploading PDFs to a service) hands control of chunking, embedding, and storage to a third party. You cannot tune chunk size for your document type, cannot switch embedding models without re-indexing, and cannot add tenant isolation at the storage layer. Custom RAG gives you all three.
|
|
15
|
+
|
|
16
|
+
- **Chunking:** you choose the strategy per document type (catalog, contract, technical doc). See `data-vector-search-rag-chunking`.
|
|
17
|
+
- **Embedding:** provider-agnostic. The embedding model is chosen via the `embeddings` alias in the project's Model Registry (see `ai-agents-providers-model-registry`). The project wires an `IEmbeddingGenerator<string, Embedding<float>>` for that alias — switching from `text-embedding-3-small` to another model is a one-file config change.
|
|
18
|
+
- **Storage:** pgvector on Neon — a column you own, indexed by HNSW, with RLS for tenant isolation.
|
|
19
|
+
|
|
20
|
+
> **This is the default.** `Microsoft.Extensions.VectorData` (see `ai-agents-vector-data-extensions`) is Microsoft's official, provider-agnostic vector-store abstraction, but its Postgres connector is still a prerelease package as of last-verified date. Adopt VectorData only when genuine multi-backend portability is required — do not migrate this working implementation to a preview dependency by default.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Arquitetura
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
[Document ingestion]
|
|
28
|
+
│
|
|
29
|
+
▼
|
|
30
|
+
EmbeddingService (generates float vectors via IEmbeddingGenerator)
|
|
31
|
+
│
|
|
32
|
+
▼
|
|
33
|
+
kb_embeddings table (pgvector column, HNSW index, tenant_id partition)
|
|
34
|
+
│
|
|
35
|
+
▼
|
|
36
|
+
VectorSearchTool ◄── MAF agent (tool registered via AIFunctionFactory.Create)
|
|
37
|
+
│
|
|
38
|
+
▼
|
|
39
|
+
Top-k chunks → agent context (agent decides when to call the tool)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Three pieces:
|
|
43
|
+
|
|
44
|
+
1. **EmbeddingService** — wraps `IEmbeddingGenerator<string, Embedding<float>>` from `Microsoft.Extensions.AI`. Called during indexing (batch) and at query time (single text).
|
|
45
|
+
2. **`kb_embeddings` table** — stores chunk text + `vector(1536)` + `tenant_id` + `metadata jsonb`. HNSW index with `vector_cosine_ops`.
|
|
46
|
+
3. **VectorSearchTool** — a typed MAF tool. A `[Description]`-decorated method the agent can call. Embeds the query, runs cosine-distance SQL, returns top-k chunks as concatenated text.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Chunking por tipo de documento
|
|
51
|
+
|
|
52
|
+
Chunk strategy varies by document type:
|
|
53
|
+
|
|
54
|
+
| Document type | Recommended strategy | Chunk size |
|
|
55
|
+
|---------------|----------------------|------------|
|
|
56
|
+
| Product catalog | Fixed-size | 256–512 tokens |
|
|
57
|
+
| Legal contract | Sentence-aware | 256 tokens |
|
|
58
|
+
| Technical docs | Semantic (split on headers) | Variable |
|
|
59
|
+
| Q&A pairs | Keep whole pair | Whole Q+A |
|
|
60
|
+
| Code files | Function boundary | Whole function |
|
|
61
|
+
|
|
62
|
+
For implementation of each strategy, see `data-vector-search-rag-chunking`. Do not duplicate chunking code here — that standard is the single source of truth for chunk implementations and the `Chunk` metadata record.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## EmbeddingService
|
|
67
|
+
|
|
68
|
+
Reference template: `templates/code/dotnet/ai-agents/EmbeddingService.cs.template`
|
|
69
|
+
|
|
70
|
+
The service wraps `IEmbeddingGenerator<string, Embedding<float>>` (from `Microsoft.Extensions.AI`) and exposes two methods:
|
|
71
|
+
|
|
72
|
+
```csharp
|
|
73
|
+
// Single text → embedding vector (used at query time)
|
|
74
|
+
Task<ReadOnlyMemory<float>> EmbedAsync(string text, CancellationToken ct = default);
|
|
75
|
+
|
|
76
|
+
// Batch of texts → list of vectors (used during document indexing)
|
|
77
|
+
Task<IReadOnlyList<ReadOnlyMemory<float>>> EmbedBatchAsync(
|
|
78
|
+
IReadOnlyList<string> texts, CancellationToken ct = default);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Both call `GenerateAsync(IEnumerable<string>, ...)` on the injected `IEmbeddingGenerator` (verified via context7 / Microsoft Learn). `GenerateAsync` returns `GeneratedEmbeddings<Embedding<float>>`; each element's `.Vector` property is a `ReadOnlyMemory<float>`.
|
|
82
|
+
|
|
83
|
+
**Wiring the `IEmbeddingGenerator`:** the project registers its chosen embedding provider in DI (e.g., via the OpenAI embedding client's `.AsIEmbeddingGenerator()` extension, or the equivalent for another provider). The `embeddings` alias in `model-registry.json` declares the model (`text-embedding-3-small`, dimensions 1536). The `EmbeddingService` is registered as a scoped or singleton service and injected wherever indexing or search happens.
|
|
84
|
+
|
|
85
|
+
```csharp
|
|
86
|
+
// Example DI registration (Program.cs, in the project — not in framework)
|
|
87
|
+
builder.Services.AddSingleton<IEmbeddingGenerator<string, Embedding<float>>>(sp =>
|
|
88
|
+
{
|
|
89
|
+
var cfg = sp.GetRequiredService<IConfiguration>();
|
|
90
|
+
return new OpenAIClient(cfg["OpenAI:ApiKey"]!)
|
|
91
|
+
.GetEmbeddingClient("text-embedding-3-small")
|
|
92
|
+
.AsIEmbeddingGenerator();
|
|
93
|
+
});
|
|
94
|
+
builder.Services.AddScoped<EmbeddingService>();
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## A tabela pgvector
|
|
100
|
+
|
|
101
|
+
Reference template: `templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template`
|
|
102
|
+
|
|
103
|
+
```sql
|
|
104
|
+
CREATE TABLE kb_embeddings (
|
|
105
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
106
|
+
tenant_id uuid NOT NULL,
|
|
107
|
+
source text NOT NULL, -- document identifier / URL
|
|
108
|
+
chunk_index int NOT NULL, -- position within source document
|
|
109
|
+
content text NOT NULL, -- chunk text returned to the agent
|
|
110
|
+
embedding vector(1536) NOT NULL, -- matches embedding model dimensions
|
|
111
|
+
metadata jsonb NOT NULL DEFAULT '{}',
|
|
112
|
+
created_at timestamptz NOT NULL DEFAULT now()
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
-- HNSW index — cosine distance, production-grade recall
|
|
116
|
+
CREATE INDEX ix_kb_embeddings_hnsw
|
|
117
|
+
ON kb_embeddings
|
|
118
|
+
USING hnsw (embedding vector_cosine_ops);
|
|
119
|
+
|
|
120
|
+
-- Index for tenant filter (applied in every query)
|
|
121
|
+
CREATE INDEX ix_kb_embeddings_tenant
|
|
122
|
+
ON kb_embeddings (tenant_id);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
For pgvector extension setup (`CREATE EXTENSION IF NOT EXISTS vector;`, `UseVector()` on `NpgsqlDataSourceBuilder`, HNSW parameter tuning), see `infrastructure-neon-pgvector`.
|
|
126
|
+
|
|
127
|
+
> **Dimension match:** `vector(1536)` matches `text-embedding-3-small` (default 1536 dimensions). If the project switches models, re-create the column with the new dimension. Never mix models on a single column.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## VectorSearchTool (tool MAF)
|
|
132
|
+
|
|
133
|
+
Reference template: `templates/code/dotnet/ai-agents/VectorSearchTool.cs.template`
|
|
134
|
+
|
|
135
|
+
A sealed class with a `[Description]`-decorated method the agent calls when it needs context from the knowledge base.
|
|
136
|
+
|
|
137
|
+
```csharp
|
|
138
|
+
// Registration — in the agent setup code
|
|
139
|
+
var tool = sp.GetRequiredService<VectorSearchTool>();
|
|
140
|
+
var agent = _modelRegistry.GetChatClient("text-default")
|
|
141
|
+
.AsAIAgent(
|
|
142
|
+
instructions: systemPrompt,
|
|
143
|
+
tools: [AIFunctionFactory.Create(tool.SearchAsync)]);
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The tool:
|
|
147
|
+
1. Takes a natural-language `query` string (the only parameter the LLM sees).
|
|
148
|
+
2. Calls `EmbeddingService.EmbedAsync(query)` to get a query vector.
|
|
149
|
+
3. Runs a parameterized SQL `SELECT content FROM kb_embeddings WHERE tenant_id = @tenant ORDER BY embedding <=> @queryVec LIMIT 5`.
|
|
150
|
+
4. Concatenates chunk contents and returns them as a single string — the agent uses this as grounding context.
|
|
151
|
+
|
|
152
|
+
The `tenant_id` filter is **never a tool parameter** (that would allow the LLM to cross tenant boundaries). It is injected via a constructor dependency — e.g., a scoped `ITenantContext` or a `Guid currentTenantId` resolved from the authenticated request. See §Multi-tenant below.
|
|
153
|
+
|
|
154
|
+
Note on raw Npgsql vs EF Core: this template uses `NpgsqlDataSource` (raw SQL) for the cosine-distance query, since pgvector operators (`<=>`) are easier to express and verify in raw SQL. If the project already wires pgvector through EF Core (`UseVector()`, `HasMethod("hnsw")`), the equivalent `db.Database.SqlQuery<>` form per `infrastructure-neon-pgvector` is equally valid — choose whichever is already in use.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Exemplo de uso
|
|
159
|
+
|
|
160
|
+
```csharp
|
|
161
|
+
// In an agent feature handler (the project's code)
|
|
162
|
+
using Microsoft.Agents.AI;
|
|
163
|
+
using Microsoft.Extensions.AI;
|
|
164
|
+
|
|
165
|
+
public sealed class KnowledgeAgentHandler(
|
|
166
|
+
ModelRegistry modelRegistry,
|
|
167
|
+
VectorSearchTool searchTool,
|
|
168
|
+
IPromptRepository prompts)
|
|
169
|
+
{
|
|
170
|
+
public async Task<string> AnswerAsync(string question, CancellationToken ct = default)
|
|
171
|
+
{
|
|
172
|
+
var systemPrompt = await prompts.GetActivePromptAsync("knowledge-agent", ct);
|
|
173
|
+
|
|
174
|
+
var agent = modelRegistry.GetChatClient("text-default")
|
|
175
|
+
.AsAIAgent(
|
|
176
|
+
instructions: systemPrompt,
|
|
177
|
+
tools: [AIFunctionFactory.Create(searchTool.SearchAsync)]);
|
|
178
|
+
|
|
179
|
+
var response = await agent.RunAsync(question, ct);
|
|
180
|
+
return response.Text;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
The agent decides autonomously when to call `VectorSearchTool.SearchAsync`. It receives the top-k chunks as a string and incorporates them into its response. The handler code stays clean — no manual retrieval loop.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Multi-tenant
|
|
190
|
+
|
|
191
|
+
Each tenant's knowledge is partitioned by `tenant_id`:
|
|
192
|
+
|
|
193
|
+
- Every row in `kb_embeddings` has a `tenant_id UUID NOT NULL`.
|
|
194
|
+
- Every query filters on `tenant_id = @tenant` before any cosine-distance ordering.
|
|
195
|
+
- `VectorSearchTool` resolves `tenant_id` from the authenticated request context (injected at construction time) — **never from a tool parameter the LLM supplies**.
|
|
196
|
+
- For database-level enforcement, enable Row-Level Security:
|
|
197
|
+
|
|
198
|
+
```sql
|
|
199
|
+
ALTER TABLE kb_embeddings ENABLE ROW LEVEL SECURITY;
|
|
200
|
+
-- See the project's RLS policy template (templates/code/sql/postgresql-rls-policy.sql)
|
|
201
|
+
-- for the policy pattern that binds app.current_tenant_id to the session.
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
> **Rule:** never query across tenants without the filter. One missing `WHERE tenant_id = @tenant` leaks every tenant's data to the requester.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Anti-patterns
|
|
209
|
+
|
|
210
|
+
| Anti-pattern | Why it's wrong | Right way |
|
|
211
|
+
|--------------|----------------|-----------|
|
|
212
|
+
| Hosted RAG (upload PDFs to a service) | Loses control of chunking, embedding, and storage; vendor lock-in | Custom RAG: own all three layers |
|
|
213
|
+
| Embeddings column without an index | Full table scan — 100 ms becomes 30 s at 100k rows | HNSW index on the embedding column always |
|
|
214
|
+
| One fixed chunk size for all document types | Contracts need dense sentences; catalogs need short snippets; mixed chunks degrade recall | Choose strategy per document type (see `data-vector-search-rag-chunking`) |
|
|
215
|
+
| `tenant_id` as a tool parameter the LLM passes | LLM can be prompted to pass any GUID — cross-tenant data leak | Inject `tenant_id` from auth context at construction; never expose as a model-facing parameter |
|
|
216
|
+
| Embedding with model A, querying with model B | Vectors live in different spaces — cosine distance is meaningless | One embedding model per column; re-index when switching models |
|
|
217
|
+
| `ORDER BY similarity DESC` in SQL | `<=>` returns distance (0 = identical), not similarity | `ORDER BY embedding <=> @queryVec` (ascending distance = most similar first) |
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Checklist (verifiable by morph-eval)
|
|
222
|
+
|
|
223
|
+
- [ ] EmbeddingService (concrete class) registered in DI; it injects IEmbeddingGenerator<string, Embedding<float>> for the project's chosen embedding model.
|
|
224
|
+
- [ ] Embedding model declared in `model-registry.json` under the `embeddings` alias.
|
|
225
|
+
- [ ] `kb_embeddings` table has `embedding vector(1536)` and HNSW index with `vector_cosine_ops`.
|
|
226
|
+
- [ ] `tenant_id` column present and filtered in every query.
|
|
227
|
+
- [ ] `VectorSearchTool.SearchAsync` takes only `query` + `ct` — no tenant param visible to the LLM.
|
|
228
|
+
- [ ] `VectorSearchTool` registered on the agent via `AIFunctionFactory.Create(tool.SearchAsync)`.
|
|
229
|
+
- [ ] No `Microsoft.SemanticKernel` references in the project.
|
|
230
|
+
- [ ] Vector dimensions consistent between embedding model and `vector(N)` column definition.
|
|
231
|
+
- [ ] RLS enabled on `kb_embeddings` (or equivalent app-level guard documented in `decisions.md`).
|
|
232
|
+
- [ ] Re-indexing strategy documented when document content changes (content hash check).
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## References
|
|
237
|
+
|
|
238
|
+
- `ai-agents-setup` — MAF packages, `AsAIAgent`, `AIFunctionFactory.Create`
|
|
239
|
+
- `ai-agents-providers-model-registry` — `embeddings` alias, `IEmbeddingGenerator` wiring
|
|
240
|
+
- `data-vector-search-rag-chunking` — chunk strategies per document type
|
|
241
|
+
- `infrastructure-neon-pgvector` — extension setup, `UseVector()`, HNSW parameter tuning
|
|
242
|
+
- `ai-agents-vector-data-extensions` — the official `Microsoft.Extensions.VectorData` abstraction, and when it's worth adopting instead of this custom implementation
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
*MORPH-SPEC by Polymorphism Tech — ai-agents/rag-custom-pgvector.md v1.0 (2026-05-19)*
|
|
@@ -76,6 +76,8 @@ Está adicionando um agente AI a um projeto .NET?
|
|
|
76
76
|
- `Microsoft.Extensions.AI.OpenAI` 9.* — replaced by `Microsoft.Agents.AI.OpenAI` in GA.
|
|
77
77
|
- Hosted agent services (Foundry Agent Service, etc.) — use direct models.
|
|
78
78
|
|
|
79
|
+
> **Exceção — conectores de Vector Store:** the ban above targets the SK **orchestration** surface (`Kernel`, `KernelFunction`, `ChatHistory`, `Kernel.InvokePromptAsync()`). It does **not** apply to `Microsoft.SemanticKernel.Connectors.*` packages (PgVector, Qdrant, SqlServer, CosmosNoSQL, InMemory) — those are `Microsoft.Extensions.VectorData` backend connectors with zero SK orchestration code, despite the package name. See `ai-agents-vector-data-extensions` before treating one of these as forbidden.
|
|
80
|
+
|
|
79
81
|
---
|
|
80
82
|
|
|
81
83
|
## 2. Quick Start (canonical minimal agent)
|
|
@@ -250,6 +252,7 @@ var instrumented = agent.WithOpenTelemetry();
|
|
|
250
252
|
- `ai-agents-providers-model-registry` — provider-agnostic pattern
|
|
251
253
|
- `ai-agents-structured-output` (Onda 2) — typed responses
|
|
252
254
|
- `ai-agents-middleware-patterns` (Onda 2) — telemetry, retry, anti-hallucination, streaming agent responses
|
|
255
|
+
- `ai-agents-vector-data-extensions` — the `Microsoft.SemanticKernel.Connectors.*` exception to the Forbidden list above
|
|
253
256
|
- Microsoft.Agents.AI docs: https://learn.microsoft.com/agent-framework/
|
|
254
257
|
- Verified packages list (context7 /microsoft/agent-framework): retrieved 2026-05-19
|
|
255
258
|
|