@polymorphism-tech/morph-spec 4.16.0 → 4.18.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 +36 -0
- package/README.md +2 -2
- package/bin/morph-spec.js +3 -0
- package/bin/task-manager.js +999 -962
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/agents/ai-agents/vector-search-expert.md +247 -233
- package/framework/agents/backend/ms-agent-expert.md +1 -1
- package/framework/agents/backend/neon-expert.md +379 -0
- package/framework/agents/frontend/gsap-animator.md +131 -0
- package/framework/agents/integrations/neon-auth.md +151 -0
- package/framework/agents.json +1183 -1085
- package/framework/commands/morph-preflight.md +200 -192
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +1 -1
- package/framework/rules/gsap-standards.md +13 -0
- package/framework/rules/neon-auth-standards.md +13 -0
- package/framework/rules/neon-standards.md +26 -0
- package/framework/skills/level-0-meta/mcp-registry.json +1 -1
- package/framework/skills/level-0-meta/morph-agent-teams/SKILL.md +472 -472
- package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +1 -1
- package/framework/skills/level-0-meta/morph-init/SKILL.md +12 -12
- package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +5 -5
- package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +5 -5
- package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +7 -7
- package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +24 -24
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +12 -12
- package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +9 -9
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +143 -143
- package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +1 -1
- package/framework/standards/STANDARDS.json +197 -116
- package/framework/standards/ai-agents/production.md +1 -0
- package/framework/standards/ai-agents/setup.md +3 -2
- package/framework/standards/ai-agents/workflows.md +1 -0
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +4 -4
- package/framework/standards/backend/database/postgresql/database.md +63 -83
- package/framework/standards/backend/dotnet/scalar-ui.md +172 -172
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +274 -274
- package/framework/standards/backend/integrations/asaas/asaas-api.md +2 -2
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +364 -0
- package/framework/standards/frontend/design-system/animations.md +9 -0
- package/framework/standards/frontend/gsap/gsap-core.md +463 -0
- package/framework/standards/frontend/gsap/gsap-plugins.md +294 -0
- package/framework/standards/frontend/gsap/gsap-scrolltrigger.md +318 -0
- package/framework/standards/frontend/nextjs/motion-patterns.md +1 -0
- package/framework/standards/frontend/nextjs/nextjs-patterns.md +47 -37
- package/framework/standards/infrastructure/docker/easypanel-deploy.md +4 -8
- package/framework/standards/infrastructure/neon/neon-pgrag.md +239 -0
- package/framework/standards/infrastructure/neon/neon-pgvector.md +236 -0
- package/framework/standards/infrastructure/neon/neon-semantic-kernel.md +310 -0
- package/framework/standards/infrastructure/neon/neon-setup.md +186 -0
- package/framework/standards/integration/mcp/mcp-tools.md +32 -34
- package/framework/templates/README.md +20 -20
- package/framework/templates/REGISTRY.json +54 -101
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- package/framework/templates/code/dotnet/backend/repository.cs +148 -148
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +2 -2
- package/framework/templates/code/dotnet/database/vector-entity.cs +93 -0
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +1 -1
- package/framework/templates/code/sql/{supabase-migration.sql → postgresql-migration.sql} +12 -21
- package/framework/templates/code/sql/{supabase-migration.template.sql → postgresql-migration.template.sql} +13 -18
- package/framework/templates/code/sql/{rls-policy.sql → postgresql-rls-policy.sql} +6 -9
- package/framework/templates/code/typescript/contracts.ts +167 -168
- package/framework/templates/context/CONTEXT.md +3 -3
- package/framework/templates/docs/proposal.md +6 -6
- package/framework/templates/docs/ui-components.md +1 -1
- package/framework/templates/feature/decisions.md +122 -121
- package/framework/templates/feature/recap.md +145 -145
- package/framework/templates/feature/tasks.md +199 -199
- package/framework/templates/frontend/nextjs/gsap-scroll-section.tsx.hbs +44 -0
- package/framework/templates/infrastructure/docker/docker-compose.template.yml +50 -67
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/docker/easypanel.template.json +46 -54
- package/framework/templates/infrastructure/github/README.md +56 -56
- package/framework/templates/integrations/neon-auth-config.cs +133 -0
- package/framework/templates/integrations/semantic-kernel-neon.cs +125 -0
- package/framework/workflows/README.md +1 -1
- package/framework/workflows/docs/full-morph.md +69 -69
- package/package.json +1 -1
- package/src/commands/agents/dispatch-agents.js +19 -15
- package/src/commands/state/advance-phase.js +5 -0
- package/src/commands/tasks/task.js +3 -0
- package/src/core/state/state-manager.js +45 -1
- package/src/core/templates/template-registry.js +1 -66
- package/src/lib/detectors/index.js +4 -4
- package/src/lib/generators/architecture-scanner.js +639 -640
- package/src/lib/installers/mcp-installer.js +1 -1
- package/src/lib/tasks/task-classifier.js +15 -1
- package/src/lib/tasks/test-runner.js +5 -1
- package/src/lib/validators/content/content-validator.js +9 -10
- package/src/lib/validators/validation-runner.js +7 -5
- package/framework/agents/backend/supabase-expert.md +0 -256
- package/framework/agents/integrations/clerk-auth.md +0 -109
- package/framework/rules/clerk-standards.md +0 -13
- package/framework/rules/supabase-standards.md +0 -29
- package/framework/standards/backend/integrations/clerk/clerk-auth.md +0 -290
- package/framework/standards/infrastructure/supabase/mcp-setup.md +0 -142
- package/framework/standards/infrastructure/supabase/supabase-auth.md +0 -176
- package/framework/standards/infrastructure/supabase/supabase-custom-jwt.md +0 -206
- package/framework/standards/infrastructure/supabase/supabase-pgvector.md +0 -169
- package/framework/standards/infrastructure/supabase/supabase-rls.md +0 -184
- package/framework/standards/infrastructure/supabase/supabase-storage.md +0 -153
- package/framework/templates/integrations/clerk-config.cs +0 -217
package/claude-plugin.json
CHANGED
package/docs/CHEATSHEET.md
CHANGED
package/docs/QUICKSTART.md
CHANGED
|
@@ -1,233 +1,247 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: vector-search-expert
|
|
3
|
-
description: Vector Search & RAG specialist for embedding pipelines, pgvector indexes, semantic search, hybrid search, and retrieval-augmented generation. Use when implementing embedding generation, vector similarity search, RAG pipelines, or hybrid full-text + vector search.
|
|
4
|
-
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
|
|
5
|
-
color: yellow
|
|
6
|
-
effort: medium
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Vector Search & RAG Specialist
|
|
10
|
-
|
|
11
|
-
Expert in embedding pipelines, pgvector, semantic search, and retrieval-augmented generation.
|
|
12
|
-
|
|
13
|
-
> **Ref:** `framework/standards/backend/database/vector-search-rag.md` — Chunking, embedding, retrieval patterns
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## Embedding Pipeline
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
Documents → Chunking → Embedding → pgvector Storage → Retrieval → LLM
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
### Chunking Strategies
|
|
24
|
-
|
|
25
|
-
| Strategy | Method | Best For | Chunk Size |
|
|
26
|
-
|----------|--------|----------|------------|
|
|
27
|
-
| **Paragraph** | Split by `\n\n` | General text | 200-500 tokens |
|
|
28
|
-
| **Markdown Headers** | Split by `#`, `##` | Documentation | Variable |
|
|
29
|
-
| **Token-based** | Fixed token windows | Uniform chunks | 256-512 tokens |
|
|
30
|
-
| **Semantic** | Cosine similarity threshold | Highest quality | Variable |
|
|
31
|
-
|
|
32
|
-
**Always overlap:** 10-20% overlap between chunks prevents context loss at boundaries.
|
|
33
|
-
|
|
34
|
-
```csharp
|
|
35
|
-
// Simple paragraph chunker
|
|
36
|
-
public static IEnumerable<string> ChunkByParagraph(string text, int maxTokens = 500)
|
|
37
|
-
{
|
|
38
|
-
var paragraphs = text.Split("\n\n", StringSplitOptions.RemoveEmptyEntries);
|
|
39
|
-
var current = new StringBuilder();
|
|
40
|
-
var currentTokens = 0;
|
|
41
|
-
|
|
42
|
-
foreach (var para in paragraphs)
|
|
43
|
-
{
|
|
44
|
-
var paraTokens = EstimateTokens(para);
|
|
45
|
-
if (currentTokens + paraTokens > maxTokens && current.Length > 0)
|
|
46
|
-
{
|
|
47
|
-
yield return current.ToString().Trim();
|
|
48
|
-
current.Clear();
|
|
49
|
-
currentTokens = 0;
|
|
50
|
-
}
|
|
51
|
-
current.AppendLine(para);
|
|
52
|
-
currentTokens += paraTokens;
|
|
53
|
-
}
|
|
54
|
-
if (current.Length > 0) yield return current.ToString().Trim();
|
|
55
|
-
}
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
---
|
|
59
|
-
|
|
60
|
-
## Embedding Generation
|
|
61
|
-
|
|
62
|
-
```csharp
|
|
63
|
-
// Using Microsoft.Extensions.AI
|
|
64
|
-
public class EmbeddingService(IEmbeddingGenerator<string, Embedding<float>> generator)
|
|
65
|
-
{
|
|
66
|
-
public async Task<float[]> GenerateAsync(string text, CancellationToken ct = default)
|
|
67
|
-
{
|
|
68
|
-
var result = await generator.GenerateAsync([text], cancellationToken: ct);
|
|
69
|
-
return result[0].Vector.ToArray();
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
public async Task<IReadOnlyList<float[]>> GenerateBatchAsync(
|
|
73
|
-
IEnumerable<string> texts, CancellationToken ct = default)
|
|
74
|
-
{
|
|
75
|
-
var result = await generator.GenerateAsync(texts.ToList(), cancellationToken: ct);
|
|
76
|
-
return result.Select(e => e.Vector.ToArray()).ToList();
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Registration
|
|
81
|
-
builder.Services.AddEmbeddingGenerator(b => b
|
|
82
|
-
.UseOpenTelemetry()
|
|
83
|
-
.UseLogging()
|
|
84
|
-
.Use(new OpenAIEmbeddingGenerator(
|
|
85
|
-
new OpenAIClient(apiKey),
|
|
86
|
-
"text-embedding-3-small"))); // 1536 dimensions
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
---
|
|
90
|
-
|
|
91
|
-
## pgvector Storage & Search
|
|
92
|
-
|
|
93
|
-
### Table schema
|
|
94
|
-
|
|
95
|
-
```sql
|
|
96
|
-
CREATE TABLE public.embeddings (
|
|
97
|
-
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
|
98
|
-
source_id UUID NOT NULL,
|
|
99
|
-
source_type TEXT NOT NULL, -- 'document', 'faq', 'article'
|
|
100
|
-
chunk_index INT NOT NULL,
|
|
101
|
-
content TEXT NOT NULL,
|
|
102
|
-
embedding vector(1536) NOT NULL,
|
|
103
|
-
metadata JSONB DEFAULT '{}'::jsonb,
|
|
104
|
-
created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
|
|
105
|
-
UNIQUE(source_id, chunk_index)
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
-- HNSW index (fast, recommended for < 1M vectors)
|
|
109
|
-
CREATE INDEX idx_embeddings_hnsw ON public.embeddings
|
|
110
|
-
USING hnsw (embedding vector_cosine_ops)
|
|
111
|
-
WITH (m = 16, ef_construction = 64);
|
|
112
|
-
|
|
113
|
-
-- IVFFlat index (better for > 1M vectors)
|
|
114
|
-
-- CREATE INDEX idx_embeddings_ivf ON public.embeddings
|
|
115
|
-
-- USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### Similarity search
|
|
119
|
-
|
|
120
|
-
```sql
|
|
121
|
-
-- Cosine similarity (1 = identical, 0 = unrelated)
|
|
122
|
-
SELECT id, content, metadata,
|
|
123
|
-
1 - (embedding <=> $1::vector) AS similarity
|
|
124
|
-
FROM public.embeddings
|
|
125
|
-
WHERE 1 - (embedding <=> $1::vector) > 0.78
|
|
126
|
-
ORDER BY embedding <=> $1::vector
|
|
127
|
-
LIMIT 5;
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### Hybrid search (vector + full-text)
|
|
131
|
-
|
|
132
|
-
```sql
|
|
133
|
-
-- Combine vector similarity with PostgreSQL full-text search
|
|
134
|
-
CREATE OR REPLACE FUNCTION hybrid_search(
|
|
135
|
-
query_text TEXT,
|
|
136
|
-
query_embedding vector(1536),
|
|
137
|
-
match_count INT DEFAULT 5,
|
|
138
|
-
vector_weight FLOAT DEFAULT 0.7,
|
|
139
|
-
text_weight FLOAT DEFAULT 0.3
|
|
140
|
-
)
|
|
141
|
-
RETURNS TABLE (id UUID, content TEXT, score FLOAT)
|
|
142
|
-
LANGUAGE plpgsql AS $$
|
|
143
|
-
BEGIN
|
|
144
|
-
RETURN QUERY
|
|
145
|
-
WITH vector_results AS (
|
|
146
|
-
SELECT e.id, e.content,
|
|
147
|
-
1 - (e.embedding <=> query_embedding) AS vscore
|
|
148
|
-
FROM public.embeddings e
|
|
149
|
-
ORDER BY e.embedding <=> query_embedding
|
|
150
|
-
LIMIT match_count * 3
|
|
151
|
-
),
|
|
152
|
-
text_results AS (
|
|
153
|
-
SELECT e.id, e.content,
|
|
154
|
-
ts_rank(to_tsvector('english', e.content), plainto_tsquery('english', query_text)) AS tscore
|
|
155
|
-
FROM public.embeddings e
|
|
156
|
-
WHERE to_tsvector('english', e.content) @@ plainto_tsquery('english', query_text)
|
|
157
|
-
LIMIT match_count * 3
|
|
158
|
-
)
|
|
159
|
-
SELECT COALESCE(v.id, t.id),
|
|
160
|
-
COALESCE(v.content, t.content),
|
|
161
|
-
(COALESCE(v.vscore, 0) * vector_weight + COALESCE(t.tscore, 0) * text_weight) AS score
|
|
162
|
-
FROM vector_results v
|
|
163
|
-
FULL OUTER JOIN text_results t ON v.id = t.id
|
|
164
|
-
ORDER BY score DESC
|
|
165
|
-
LIMIT match_count;
|
|
166
|
-
END;
|
|
167
|
-
$$;
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
---
|
|
171
|
-
|
|
172
|
-
## RAG Tool for Agent Framework
|
|
173
|
-
|
|
174
|
-
```csharp
|
|
175
|
-
public class KnowledgeBaseTool(
|
|
176
|
-
EmbeddingService embeddings,
|
|
177
|
-
|
|
178
|
-
{
|
|
179
|
-
[Description("Search the knowledge base for relevant information")]
|
|
180
|
-
public async Task<string> SearchAsync(
|
|
181
|
-
[Description("Natural language search query")] string query,
|
|
182
|
-
[Description("Maximum results to return")] int topK = 5,
|
|
183
|
-
CancellationToken ct = default)
|
|
184
|
-
{
|
|
185
|
-
var embedding = await embeddings.GenerateAsync(query, ct);
|
|
186
|
-
|
|
187
|
-
var
|
|
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
|
-
|
|
1
|
+
---
|
|
2
|
+
name: vector-search-expert
|
|
3
|
+
description: Vector Search & RAG specialist for embedding pipelines, pgvector indexes, semantic search, hybrid search, and retrieval-augmented generation. Use when implementing embedding generation, vector similarity search, RAG pipelines, or hybrid full-text + vector search.
|
|
4
|
+
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
|
|
5
|
+
color: yellow
|
|
6
|
+
effort: medium
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Vector Search & RAG Specialist
|
|
10
|
+
|
|
11
|
+
Expert in embedding pipelines, pgvector, semantic search, and retrieval-augmented generation.
|
|
12
|
+
|
|
13
|
+
> **Ref:** `framework/standards/backend/database/vector-search-rag.md` — Chunking, embedding, retrieval patterns
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Embedding Pipeline
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
Documents → Chunking → Embedding → pgvector Storage → Retrieval → LLM
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Chunking Strategies
|
|
24
|
+
|
|
25
|
+
| Strategy | Method | Best For | Chunk Size |
|
|
26
|
+
|----------|--------|----------|------------|
|
|
27
|
+
| **Paragraph** | Split by `\n\n` | General text | 200-500 tokens |
|
|
28
|
+
| **Markdown Headers** | Split by `#`, `##` | Documentation | Variable |
|
|
29
|
+
| **Token-based** | Fixed token windows | Uniform chunks | 256-512 tokens |
|
|
30
|
+
| **Semantic** | Cosine similarity threshold | Highest quality | Variable |
|
|
31
|
+
|
|
32
|
+
**Always overlap:** 10-20% overlap between chunks prevents context loss at boundaries.
|
|
33
|
+
|
|
34
|
+
```csharp
|
|
35
|
+
// Simple paragraph chunker
|
|
36
|
+
public static IEnumerable<string> ChunkByParagraph(string text, int maxTokens = 500)
|
|
37
|
+
{
|
|
38
|
+
var paragraphs = text.Split("\n\n", StringSplitOptions.RemoveEmptyEntries);
|
|
39
|
+
var current = new StringBuilder();
|
|
40
|
+
var currentTokens = 0;
|
|
41
|
+
|
|
42
|
+
foreach (var para in paragraphs)
|
|
43
|
+
{
|
|
44
|
+
var paraTokens = EstimateTokens(para);
|
|
45
|
+
if (currentTokens + paraTokens > maxTokens && current.Length > 0)
|
|
46
|
+
{
|
|
47
|
+
yield return current.ToString().Trim();
|
|
48
|
+
current.Clear();
|
|
49
|
+
currentTokens = 0;
|
|
50
|
+
}
|
|
51
|
+
current.AppendLine(para);
|
|
52
|
+
currentTokens += paraTokens;
|
|
53
|
+
}
|
|
54
|
+
if (current.Length > 0) yield return current.ToString().Trim();
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Embedding Generation
|
|
61
|
+
|
|
62
|
+
```csharp
|
|
63
|
+
// Using Microsoft.Extensions.AI
|
|
64
|
+
public class EmbeddingService(IEmbeddingGenerator<string, Embedding<float>> generator)
|
|
65
|
+
{
|
|
66
|
+
public async Task<float[]> GenerateAsync(string text, CancellationToken ct = default)
|
|
67
|
+
{
|
|
68
|
+
var result = await generator.GenerateAsync([text], cancellationToken: ct);
|
|
69
|
+
return result[0].Vector.ToArray();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public async Task<IReadOnlyList<float[]>> GenerateBatchAsync(
|
|
73
|
+
IEnumerable<string> texts, CancellationToken ct = default)
|
|
74
|
+
{
|
|
75
|
+
var result = await generator.GenerateAsync(texts.ToList(), cancellationToken: ct);
|
|
76
|
+
return result.Select(e => e.Vector.ToArray()).ToList();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Registration
|
|
81
|
+
builder.Services.AddEmbeddingGenerator(b => b
|
|
82
|
+
.UseOpenTelemetry()
|
|
83
|
+
.UseLogging()
|
|
84
|
+
.Use(new OpenAIEmbeddingGenerator(
|
|
85
|
+
new OpenAIClient(apiKey),
|
|
86
|
+
"text-embedding-3-small"))); // 1536 dimensions
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## pgvector Storage & Search
|
|
92
|
+
|
|
93
|
+
### Table schema
|
|
94
|
+
|
|
95
|
+
```sql
|
|
96
|
+
CREATE TABLE public.embeddings (
|
|
97
|
+
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
|
98
|
+
source_id UUID NOT NULL,
|
|
99
|
+
source_type TEXT NOT NULL, -- 'document', 'faq', 'article'
|
|
100
|
+
chunk_index INT NOT NULL,
|
|
101
|
+
content TEXT NOT NULL,
|
|
102
|
+
embedding vector(1536) NOT NULL,
|
|
103
|
+
metadata JSONB DEFAULT '{}'::jsonb,
|
|
104
|
+
created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
|
|
105
|
+
UNIQUE(source_id, chunk_index)
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
-- HNSW index (fast, recommended for < 1M vectors)
|
|
109
|
+
CREATE INDEX idx_embeddings_hnsw ON public.embeddings
|
|
110
|
+
USING hnsw (embedding vector_cosine_ops)
|
|
111
|
+
WITH (m = 16, ef_construction = 64);
|
|
112
|
+
|
|
113
|
+
-- IVFFlat index (better for > 1M vectors)
|
|
114
|
+
-- CREATE INDEX idx_embeddings_ivf ON public.embeddings
|
|
115
|
+
-- USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Similarity search
|
|
119
|
+
|
|
120
|
+
```sql
|
|
121
|
+
-- Cosine similarity (1 = identical, 0 = unrelated)
|
|
122
|
+
SELECT id, content, metadata,
|
|
123
|
+
1 - (embedding <=> $1::vector) AS similarity
|
|
124
|
+
FROM public.embeddings
|
|
125
|
+
WHERE 1 - (embedding <=> $1::vector) > 0.78
|
|
126
|
+
ORDER BY embedding <=> $1::vector
|
|
127
|
+
LIMIT 5;
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Hybrid search (vector + full-text)
|
|
131
|
+
|
|
132
|
+
```sql
|
|
133
|
+
-- Combine vector similarity with PostgreSQL full-text search
|
|
134
|
+
CREATE OR REPLACE FUNCTION hybrid_search(
|
|
135
|
+
query_text TEXT,
|
|
136
|
+
query_embedding vector(1536),
|
|
137
|
+
match_count INT DEFAULT 5,
|
|
138
|
+
vector_weight FLOAT DEFAULT 0.7,
|
|
139
|
+
text_weight FLOAT DEFAULT 0.3
|
|
140
|
+
)
|
|
141
|
+
RETURNS TABLE (id UUID, content TEXT, score FLOAT)
|
|
142
|
+
LANGUAGE plpgsql AS $$
|
|
143
|
+
BEGIN
|
|
144
|
+
RETURN QUERY
|
|
145
|
+
WITH vector_results AS (
|
|
146
|
+
SELECT e.id, e.content,
|
|
147
|
+
1 - (e.embedding <=> query_embedding) AS vscore
|
|
148
|
+
FROM public.embeddings e
|
|
149
|
+
ORDER BY e.embedding <=> query_embedding
|
|
150
|
+
LIMIT match_count * 3
|
|
151
|
+
),
|
|
152
|
+
text_results AS (
|
|
153
|
+
SELECT e.id, e.content,
|
|
154
|
+
ts_rank(to_tsvector('english', e.content), plainto_tsquery('english', query_text)) AS tscore
|
|
155
|
+
FROM public.embeddings e
|
|
156
|
+
WHERE to_tsvector('english', e.content) @@ plainto_tsquery('english', query_text)
|
|
157
|
+
LIMIT match_count * 3
|
|
158
|
+
)
|
|
159
|
+
SELECT COALESCE(v.id, t.id),
|
|
160
|
+
COALESCE(v.content, t.content),
|
|
161
|
+
(COALESCE(v.vscore, 0) * vector_weight + COALESCE(t.tscore, 0) * text_weight) AS score
|
|
162
|
+
FROM vector_results v
|
|
163
|
+
FULL OUTER JOIN text_results t ON v.id = t.id
|
|
164
|
+
ORDER BY score DESC
|
|
165
|
+
LIMIT match_count;
|
|
166
|
+
END;
|
|
167
|
+
$$;
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## RAG Tool for Agent Framework
|
|
173
|
+
|
|
174
|
+
```csharp
|
|
175
|
+
public class KnowledgeBaseTool(
|
|
176
|
+
EmbeddingService embeddings,
|
|
177
|
+
NpgsqlDataSource dataSource)
|
|
178
|
+
{
|
|
179
|
+
[Description("Search the knowledge base for relevant information")]
|
|
180
|
+
public async Task<string> SearchAsync(
|
|
181
|
+
[Description("Natural language search query")] string query,
|
|
182
|
+
[Description("Maximum results to return")] int topK = 5,
|
|
183
|
+
CancellationToken ct = default)
|
|
184
|
+
{
|
|
185
|
+
var embedding = await embeddings.GenerateAsync(query, ct);
|
|
186
|
+
|
|
187
|
+
await using var conn = await dataSource.OpenConnectionAsync(ct);
|
|
188
|
+
await using var cmd = new NpgsqlCommand("""
|
|
189
|
+
SELECT id, content, metadata,
|
|
190
|
+
1 - (embedding <=> @embedding::vector) AS similarity
|
|
191
|
+
FROM public.embeddings
|
|
192
|
+
WHERE 1 - (embedding <=> @embedding::vector) > @threshold
|
|
193
|
+
ORDER BY embedding <=> @embedding::vector
|
|
194
|
+
LIMIT @topK
|
|
195
|
+
""", conn);
|
|
196
|
+
|
|
197
|
+
cmd.Parameters.AddWithValue("embedding", embedding);
|
|
198
|
+
cmd.Parameters.AddWithValue("threshold", 0.78);
|
|
199
|
+
cmd.Parameters.AddWithValue("topK", topK);
|
|
200
|
+
|
|
201
|
+
var results = new List<(double Similarity, string Content)>();
|
|
202
|
+
await using var reader = await cmd.ExecuteReaderAsync(ct);
|
|
203
|
+
while (await reader.ReadAsync(ct))
|
|
204
|
+
{
|
|
205
|
+
results.Add((reader.GetDouble(3), reader.GetString(1)));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return string.Join("\n\n---\n\n",
|
|
209
|
+
results.Select(r => $"[Score: {r.Similarity:F2}]\n{r.Content}"));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Index Selection Guide
|
|
217
|
+
|
|
218
|
+
| Vectors | Recommended | Why |
|
|
219
|
+
|---------|-------------|-----|
|
|
220
|
+
| < 10K | No index (sequential scan) | Index overhead not worth it |
|
|
221
|
+
| 10K - 1M | HNSW (m=16, ef=64) | Fast, good recall |
|
|
222
|
+
| 1M+ | IVFFlat (lists=sqrt(n)) | Lower memory, acceptable recall |
|
|
223
|
+
|
|
224
|
+
### Tuning HNSW
|
|
225
|
+
|
|
226
|
+
- `m` (connections per node): higher = better recall, more memory. Default 16.
|
|
227
|
+
- `ef_construction` (build-time search depth): higher = better index, slower build. Default 64.
|
|
228
|
+
- `ef_search` (query-time): `SET hnsw.ef_search = 100;` for better recall at query time.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Checklist
|
|
233
|
+
|
|
234
|
+
- [ ] Chunking strategy selected and tested with real data
|
|
235
|
+
- [ ] Chunk overlap configured (10-20%)
|
|
236
|
+
- [ ] Embedding model chosen (text-embedding-3-small for cost, text-embedding-3-large for quality)
|
|
237
|
+
- [ ] pgvector extension enabled
|
|
238
|
+
- [ ] HNSW or IVFFlat index created based on vector count
|
|
239
|
+
- [ ] Similarity threshold tuned (start at 0.78, adjust by domain)
|
|
240
|
+
- [ ] Hybrid search implemented if full-text relevance matters
|
|
241
|
+
- [ ] Batch embedding for initial ingestion (not one-by-one)
|
|
242
|
+
- [ ] RLS policies on embeddings table
|
|
243
|
+
- [ ] Source tracking (source_id + source_type) for updates/deletes
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
*MORPH-SPEC by Polymorphism Tech*
|
|
@@ -35,7 +35,7 @@ When working on AI agent tasks:
|
|
|
35
35
|
1. Consult `setup.md` for agent configuration, tools, and DI registration
|
|
36
36
|
2. Follow `workflows.md` for orchestration (sequential, parallel, conditional)
|
|
37
37
|
3. Apply `production.md` for middleware, observability, and production patterns
|
|
38
|
-
4.
|
|
38
|
+
4. Prefer Microsoft.Agents.AI for agent workflows (preview). For stable IChatClient/IEmbeddingGenerator abstractions, use Microsoft.Extensions.AI as fallback until MAF reaches GA.
|
|
39
39
|
|
|
40
40
|
## When to Use
|
|
41
41
|
|