@polymorphism-tech/morph-spec 8.33.0 → 8.35.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 +22 -2
- package/README.md +2 -2
- package/bin/morph-spec.js +43 -5
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +5 -4
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +2 -2
- package/framework/MORPH.md +20 -22
- package/framework/agents.json +198 -44
- package/framework/ai-pin.json +22 -0
- package/framework/commands/morph-media.md +45 -0
- package/framework/docs/CLI.md +238 -10
- package/framework/evals/proof-rubrics/README.md +76 -0
- package/framework/evals/proof-rubrics/eval-de-modelo.md +64 -0
- package/framework/evals/proof-rubrics/fiacao.md +54 -0
- package/framework/evals/proof-rubrics/guard-cego.md +52 -0
- package/framework/evals/proof-rubrics/media-claims.md +109 -0
- package/framework/evals/proof-rubrics/mutation-proof.md +77 -0
- package/framework/evals/proof-rubrics/observabilidade-genai.md +119 -0
- package/framework/evals/proof-rubrics/verde-nao-e-coberto.md +48 -0
- package/framework/evals/rubrics/architecture.md +30 -0
- package/framework/evals/rubrics/code-quality.md +26 -0
- package/framework/evals/rubrics/test-coverage.md +20 -0
- package/framework/hooks/README.md +11 -1
- package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +5 -8
- package/framework/hooks/claude-code/core/block-raw-worktree-add.js +79 -0
- package/framework/hooks/claude-code/core/chain.js +27 -7
- package/framework/hooks/claude-code/core/protect-spec-files.js +41 -4
- package/framework/hooks/claude-code/core/worktree-context.js +83 -14
- package/framework/hooks/claude-code/core/worktree-create.js +206 -0
- package/framework/hooks/claude-code/core/worktree-env-inject.js +87 -0
- package/framework/hooks/claude-code/core/worktree-remove.js +81 -0
- package/framework/hooks/shared/command-tokens.js +23 -0
- package/framework/hooks/shared/e2e-check.js +0 -0
- package/framework/hooks/shared/git-paths.js +25 -0
- package/framework/hooks/shared/hook-response.js +17 -0
- package/framework/hooks/shared/morph-cli.js +134 -0
- package/framework/rules/ai-agents-standards.md +14 -2
- package/framework/rules/infrastructure-standards.md +3 -1
- package/framework/schemas/ai-pin.schema.json +97 -0
- package/framework/schemas/config.schema.json +107 -0
- package/framework/schemas/feature.schema.json +23 -4
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +28 -7
- package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
- package/framework/skills/core/morph-eval/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +2 -2
- package/framework/skills/core/morph-implement/SKILL.md +20 -2
- package/framework/skills/core/morph-media/SKILL.md +215 -0
- package/framework/skills/core/morph-plan/SKILL.md +38 -4
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +166 -9
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +1 -1
- package/framework/standards/PROVENANCE.json +81 -3
- package/framework/standards/STANDARDS.json +1504 -830
- package/framework/standards/ai-agents/agent-archetypes.md +160 -0
- package/framework/standards/ai-agents/agent-session.md +256 -160
- package/framework/standards/ai-agents/agent-spec.md +196 -0
- package/framework/standards/ai-agents/batching.md +217 -193
- package/framework/standards/ai-agents/context-providers.md +261 -188
- package/framework/standards/ai-agents/conversational-agent-with-phases.md +239 -0
- package/framework/standards/ai-agents/cost-and-budget.md +256 -0
- package/framework/standards/ai-agents/durable-workflows-hitl.md +98 -5
- package/framework/standards/ai-agents/evals-with-cache.md +289 -0
- package/framework/standards/ai-agents/guardrails.md +222 -0
- package/framework/standards/ai-agents/llm-runtime-defaults.md +226 -0
- package/framework/standards/ai-agents/mcp-server.md +351 -250
- package/framework/standards/ai-agents/mcp-tools.md +250 -197
- package/framework/standards/ai-agents/media-pipeline.md +352 -0
- package/framework/standards/ai-agents/media-video.md +267 -0
- package/framework/standards/ai-agents/middleware-patterns.md +241 -207
- package/framework/standards/ai-agents/modalities-image-gen.md +422 -173
- package/framework/standards/ai-agents/modalities-speech-to-text.md +177 -171
- package/framework/standards/ai-agents/modalities-text-to-speech.md +189 -181
- package/framework/standards/ai-agents/modalities-vision.md +205 -201
- package/framework/standards/ai-agents/multi-agent-patterns.md +211 -155
- package/framework/standards/ai-agents/observability-patterns.md +227 -17
- package/framework/standards/ai-agents/production.md +260 -231
- package/framework/standards/ai-agents/prompt-sources.md +389 -0
- package/framework/standards/ai-agents/providers/model-registry.md +293 -158
- package/framework/standards/ai-agents/rag-custom-pgvector.md +69 -4
- package/framework/standards/ai-agents/service-tier-flex.md +143 -135
- package/framework/standards/ai-agents/setup.md +166 -50
- package/framework/standards/ai-agents/structured-output.md +280 -249
- package/framework/standards/ai-agents/sweet-spot.md +129 -109
- package/framework/standards/ai-agents/testing-ai.md +236 -0
- package/framework/standards/ai-agents/vector-data-extensions.md +50 -20
- package/framework/standards/ai-agents/workflows.md +27 -7
- package/framework/standards/frontend/design-system/ai-image-generation.md +12 -0
- package/framework/standards/frontend/design-system/ai-video-generation.md +13 -0
- package/framework/standards/infrastructure/docker/local-compose-isolation.md +90 -0
- package/framework/templates/REGISTRY.json +14 -1
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -95
- package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -58
- package/framework/templates/dotnet/ai-kit/Directory.Build.props +25 -0
- package/framework/templates/dotnet/ai-kit/Directory.Packages.props +26 -0
- package/framework/templates/dotnet/ai-kit/Morph.AiKit.sln +56 -0
- package/framework/templates/dotnet/ai-kit/NuGet.config +12 -0
- package/framework/templates/dotnet/ai-kit/README.md +128 -0
- package/framework/templates/dotnet/ai-kit/global.json +9 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentApi.cs +65 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs +289 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactoryOptions.cs +91 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs +91 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpecOptions.cs +181 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/AiKitAssemblyMarker.cs +14 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs +174 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/IConversationStore.cs +54 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/InMemoryConversationStore.cs +75 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/CountingChatClient.cs +71 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalAssertion.cs +246 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheMissException.cs +57 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheOptions.cs +78 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheStamp.cs +225 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs +463 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalMode.cs +32 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalQualityEvaluators.cs +49 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalScenario.cs +214 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalSummary.cs +168 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/OfflineChatClient.cs +142 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/ShortPathResponseCache.cs +349 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaGenerator.cs +42 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaProvider.cs +77 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaArtifact.cs +28 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaDisclosure.cs +107 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaGenerator.cs +253 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaIdempotencyKey.cs +103 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJob.cs +259 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobId.cs +21 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobState.cs +47 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaPricing.cs +102 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaProvenance.cs +44 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaRequest.cs +192 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaResult.cs +94 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaReview.cs +99 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaSeams.cs +97 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaStore.cs +133 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MorphMedia.cs +93 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/GoogleImageProvider.cs +112 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs +288 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs +158 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/IUsageCostSink.cs +29 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs +208 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostRecord.cs +95 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Morph.AiKit.csproj +38 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs +119 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs +82 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs +33 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs +267 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs +474 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/model-registry.v2.json +87 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs +169 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +49 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentFactoryTests.cs +495 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentSpecTests.cs +260 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ConversationStoreTests.cs +405 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/DeltaIngestTests.cs +379 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalAssertionTests.cs +197 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs +139 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs +157 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs +178 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFixtureTests.cs +153 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalRefreshTests.cs +194 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalWorkspace.cs +86 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/KitEvals.cs +213 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/OfflineChatClientTests.cs +95 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs +225 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClientTests.cs +215 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeClock.cs +33 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeMediaProvider.cs +89 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeProviderClientFactory.cs +55 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeUsageCostSink.cs +60 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/InMemoryMediaStore.cs +131 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAccessibilityTests.cs +133 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAliasIsNotAnAgentTests.cs +127 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +216 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaGeneratorTests.cs +264 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaIdempotencyKeyTests.cs +148 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaJobStateTests.cs +184 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaReviewTests.cs +89 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaStoreTests.cs +98 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ModelRegistryTests.cs +451 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +76 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/SkeletonSmokeTests.cs +33 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/TelemetryTests.cs +261 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs +374 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/.morph-eval-cache.json +12 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/orcamento/acme/a-resposta-estruturada.json +24 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/a-saudacao.json +23 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/b-escalonamento.json +24 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/globex/a-saudacao.json +23 -0
- package/framework/templates/infrastructure/docker/docker-compose.template.yml +24 -14
- package/package.json +3 -3
- package/src/commands/e2e/e2e.js +91 -40
- package/src/commands/project/doctor.js +143 -6
- package/src/commands/project/env.js +95 -0
- package/src/commands/project/update.js +469 -153
- package/src/commands/project/worktree.js +274 -53
- package/src/commands/state/advance.js +24 -2
- package/src/commands/state/approve.js +33 -1
- package/src/commands/state/create.js +72 -11
- package/src/commands/state/delete.js +8 -0
- package/src/commands/state/finish.js +360 -53
- package/src/commands/state/gate-check.js +190 -6
- package/src/commands/state/score.js +36 -1
- package/src/commands/validation/verify.js +134 -25
- package/src/core/state/state-manager.js +9 -1
- package/src/dashboard/app/pages/HookInspector.jsx +430 -418
- package/src/dashboard/scanner/hooks-scanner.js +5 -1
- package/src/lib/ai/ai-checks.js +182 -0
- package/src/lib/ai/ai-project-scan.js +347 -0
- package/src/lib/ai/code-audit.js +181 -0
- package/src/lib/ai/comment-stripper.js +184 -0
- package/src/lib/ai/pin-drift.js +138 -0
- package/src/lib/ai/registry-audit.js +285 -0
- package/src/lib/e2e/compose-env.js +55 -14
- package/src/lib/e2e/stack-teardown.js +115 -0
- package/src/lib/evaluator-report.js +269 -15
- package/src/lib/generators/architecture-scanner.js +2 -1
- package/src/lib/git/default-branch.js +92 -4
- package/src/lib/graph/graphifyignore.js +2 -1
- package/src/lib/installers/effects.js +193 -0
- package/src/lib/installers/templates-reconcile.js +3 -3
- package/src/lib/risk-detector.js +284 -0
- package/src/lib/standards/verified-against.js +276 -0
- package/src/lib/tasks/eval-node.js +485 -0
- package/src/lib/tasks/linux-runner.js +297 -0
- package/src/lib/tasks/test-nodes.js +486 -0
- package/src/lib/tasks/test-runner.js +431 -37
- package/src/lib/validators/verify-runner.js +590 -28
- package/src/lib/worktree/dirs.js +32 -0
- package/src/lib/worktree/docker-audit.js +287 -0
- package/src/lib/worktree/env-allocator.js +69 -11
- package/src/lib/worktree/worktree-audit.js +70 -3
- package/src/scripts/setup-infra.js +24 -11
- package/src/utils/agents-installer.js +18 -8
- package/src/utils/claude-md-injector.js +10 -5
- package/src/utils/file-copier.js +28 -16
- package/src/utils/hooks-installer.js +75 -29
- package/src/utils/skills-installer.js +194 -187
- package/src/utils/version-checker.js +68 -3
- package/framework/standards/ai-agents/prompts-in-db.md +0 -214
|
@@ -1,109 +1,129 @@
|
|
|
1
|
-
# Sweet Spot — When to use what in MAF
|
|
2
|
-
|
|
3
|
-
> **Scope:** stacks=["dotnet"]
|
|
4
|
-
> **Layer:** 0 (always-load via persona maf-expert)
|
|
5
|
-
> **Keywords:** sweet spot, when to use agent, agent vs workflow, when not to use workflow, MAF decision, simple agent
|
|
6
|
-
> **Read by Claude in:** plan (decide arquitetura da feature antes de quebrar em tasks) + implement (validar que a arquitetura ainda é a mais simples possível)
|
|
7
|
-
|
|
8
|
-
**Verified against:**
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## The 4-feature rule
|
|
13
|
-
|
|
14
|
-
```
|
|
15
|
-
95% of production AI agents need ONLY:
|
|
16
|
-
|
|
17
|
-
1. Chat (the LLM call)
|
|
18
|
-
2. Structured Output (typed records, not free text)
|
|
19
|
-
3. Tools (typed functions the agent can call)
|
|
20
|
-
4. RAG Custom (pgvector tool, not hosted RAG)
|
|
21
|
-
|
|
22
|
-
If your feature needs ONLY these 4 → use a single agent. Don't reach for workflows.
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
Apply this rule before deciding multi-agent or workflow. Most of the time the answer is "single agent".
|
|
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
|
-
|
|
1
|
+
# Sweet Spot — When to use what in MAF
|
|
2
|
+
|
|
3
|
+
> **Scope:** stacks=["dotnet"]
|
|
4
|
+
> **Layer:** 0 (always-load via persona maf-expert)
|
|
5
|
+
> **Keywords:** sweet spot, when to use agent, agent vs workflow, when not to use workflow, MAF decision, simple agent, tool budget, quantas tools
|
|
6
|
+
> **Read by Claude in:** plan (decide arquitetura da feature antes de quebrar em tasks) + implement (validar que a arquitetura ainda é a mais simples possível)
|
|
7
|
+
|
|
8
|
+
**Verified against:** Microsoft.Agents.AI 1.20.0 + Microsoft.Agents.AI.Workflows 1.20.0 (ai-pin 2026-09-08 para o primeiro; o segundo está **fora do pin**, versão apurada no nuget.org em 2026-09-08). **Verificação documental**, sem cláusula `provado por` — este standard é decisão de arquitetura, não API. A escada de complexidade foi reconferida e **continua correta**; o que entrou de novo é o custo de definição de tool por turno e o status do Handoff. Last-verified: 2026-09-08.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## The 4-feature rule
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
95% of production AI agents need ONLY:
|
|
16
|
+
|
|
17
|
+
1. Chat (the LLM call)
|
|
18
|
+
2. Structured Output (typed records, not free text)
|
|
19
|
+
3. Tools (typed functions the agent can call)
|
|
20
|
+
4. RAG Custom (pgvector tool, not hosted RAG)
|
|
21
|
+
|
|
22
|
+
If your feature needs ONLY these 4 → use a single agent. Don't reach for workflows.
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Apply this rule before deciding multi-agent or workflow. Most of the time the answer is "single agent".
|
|
26
|
+
|
|
27
|
+
### O quinto sinal: quantas tools o agente carrega
|
|
28
|
+
|
|
29
|
+
A regra das 4 features diz **o que** o agente faz. Falta **quanto** ele carrega — e esse é o custo que ninguém vê no código.
|
|
30
|
+
|
|
31
|
+
**Toda tool registrada entra na definição enviada ao modelo em CADA turno**: nome, descrição e o JSON Schema completo dos parâmetros. Um agente com 40 tools paga esses 40 schemas de entrada em todo turno, inclusive nos turnos em que nenhuma tool é chamada — e paga de novo em cada volta do laço de tool calling.
|
|
32
|
+
|
|
33
|
+
**Referência prática: ≤ 12 tools por agente.** Acima disso, dois efeitos se somam, e o segundo é pior que o primeiro:
|
|
34
|
+
|
|
35
|
+
1. **Custo por turno cresce** com o tamanho da definição.
|
|
36
|
+
2. **A escolha piora.** Quanto mais tools parecidas, mais o modelo erra qual chamar — e um erro de escolha custa uma volta inteira.
|
|
37
|
+
|
|
38
|
+
**Quando estourar o teto, na ordem:** (a) **poda** — remova o que não é usado naquele modo; (b) **plano por fase** — uma allow-list por estado do funil, montada em C# (`ai-agents-context-providers` §Ordem de escolha); (c) **só então** agent-as-tool, com o auxiliar carregando o próprio subconjunto (`ai-agents-multi-agent-patterns`). Servidor MCP com dezenas de tools é o caso mais comum de estouro — ver `ai-agents-mcp-tools` §Custo de definição de tool por turno.
|
|
39
|
+
|
|
40
|
+
### Status do Handoff (2026-09-08)
|
|
41
|
+
|
|
42
|
+
O Handoff era marcado **experimental** no 1.0 e **graduou**: `Microsoft.Agents.AI.Workflows.HandoffWorkflowBuilder` **não carrega** `[Experimental]` na assembly 1.20.0 (medido). Isso muda o degrau da escada: handoff deixou de ser "experimental, evite" e passou a ser "estável, mas ainda depois de agent-as-tool" — a ordem de preferência **não** mudou, o motivo mudou. O critério continua o mesmo: agent-as-tool primeiro, handoff quando há N especialistas e a decisão de roteamento não cabe numa tool.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Decision tree (Claude reads this in plan phase)
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
Step 1 — Does the task even need an LLM?
|
|
50
|
+
├─ "Can a deterministic function solve this?" → YES → write the function, no agent
|
|
51
|
+
└─ NO → continue
|
|
52
|
+
|
|
53
|
+
Step 2 — Single agent vs multi-agent?
|
|
54
|
+
├─ One topic + one decision → single agent (DEFAULT)
|
|
55
|
+
├─ Multiple specialized topics with clear handoff → multi-agent
|
|
56
|
+
│ └─ Prefer Agent-as-Tool pattern (one agent uses another as a tool)
|
|
57
|
+
│ └─ See ai-agents-multi-agent-patterns
|
|
58
|
+
└─ "I'm not sure" → start with single agent, escalate if needed
|
|
59
|
+
|
|
60
|
+
Step 3 — Need a Workflow?
|
|
61
|
+
├─ The feature has fan-in/out (many inputs aggregated) → maybe workflow
|
|
62
|
+
│ └─ Confirm: can you write it as a foreach + LINQ aggregate?
|
|
63
|
+
│ └─ YES → write the foreach, NO workflow
|
|
64
|
+
│ └─ NO → workflow may help
|
|
65
|
+
├─ Long-running with human-in-the-loop checkpoints → maybe workflow
|
|
66
|
+
│ └─ See ai-agents-workflows (Onda 2)
|
|
67
|
+
└─ Otherwise → NO workflow
|
|
68
|
+
|
|
69
|
+
Step 4 — Which provider?
|
|
70
|
+
├─ ALWAYS direct models — never a hosted agent service
|
|
71
|
+
├─ Provider choice (OpenAI / Google / Anthropic / Ollama) lives in the
|
|
72
|
+
│ project's Model Registry, not hardcoded → see ai-agents-providers-model-registry
|
|
73
|
+
└─ Hosted services (Foundry Agent Service, Vertex AI hosted) → NOT used
|
|
74
|
+
|
|
75
|
+
Step 5 — RAG approach?
|
|
76
|
+
├─ Need control over chunking/embedding/storage? → RAG custom (pgvector)
|
|
77
|
+
│ └─ See ai-agents-rag-custom-pgvector (Onda 2)
|
|
78
|
+
└─ Don't reach for hosted RAG — loses control
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Anti-patterns (what Claude should NOT do)
|
|
84
|
+
|
|
85
|
+
| Anti-pattern | Why it's wrong | What to do instead |
|
|
86
|
+
|--------------|----------------|---------------------|
|
|
87
|
+
| Reach for Workflow when a function does the job | "Tudo que Workflows fazem, código C# normal faz com menos complexidade" — pesquisa | Write the function. Wrap in a tool if an agent needs to call it. |
|
|
88
|
+
| Use a hosted agent service | Hosted is "always 6-9 months behind" on models + removes control | Direct models always (OpenAI / Google / Anthropic / Ollama) |
|
|
89
|
+
| Upload PDFs to a hosted RAG | Loses control of chunking + embedding + storage | Custom RAG with pgvector. See ai-agents-rag-custom-pgvector |
|
|
90
|
+
| Add Memory/Session because "agents need state" | If not building a chatbot, don't add chat state | Skip. Add when a real chat UI exists. |
|
|
91
|
+
| Use Semantic Kernel for new features | SK is superseded by MAF | Use Microsoft.Agents.AI. See ai-agents-setup |
|
|
92
|
+
| Return free text and parse it downstream | LLM output is non-deterministic | Structured Output. See ai-agents-structured-output (Onda 2) |
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## When the 4-feature rule does NOT apply
|
|
97
|
+
|
|
98
|
+
You may need to step outside the sweet spot when:
|
|
99
|
+
- The feature genuinely fans-out (e.g., monthly report aggregating N items) → workflow with fan-in
|
|
100
|
+
- The feature needs vision/audio/image-gen → modality-specific standards (see ai-agents-modalities-* — Onda 3)
|
|
101
|
+
|
|
102
|
+
**Even in these cases:** the core of the agent still follows the 4-feature rule. Only the orchestration layer changes.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Checklist (verifiable by morph-eval)
|
|
107
|
+
|
|
108
|
+
- [ ] Was each agent designed as single-agent BEFORE reaching for multi-agent? (Check `decisions.md`.)
|
|
109
|
+
- [ ] If a Workflow is used, is fan-in/out or HITL checkpointing genuinely present? Or could `foreach + LINQ` replace it?
|
|
110
|
+
- [ ] Are the 4 features (Chat, Structured Output, Tools, RAG Custom) all present where applicable?
|
|
111
|
+
- [ ] Are hosted agent services avoided?
|
|
112
|
+
- [ ] Nenhum agente carrega mais de ~12 tools sem uma decisão escrita em `decisions.md` sobre a poda
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## References
|
|
117
|
+
|
|
118
|
+
- `ai-agents-setup` — packages and DI
|
|
119
|
+
- `ai-agents-providers-model-registry` — provider-agnostic config
|
|
120
|
+
- `ai-agents-structured-output` (Onda 2)
|
|
121
|
+
- `ai-agents-multi-agent-patterns` (Onda 2)
|
|
122
|
+
- `ai-agents-workflows` (Onda 2, reframed)
|
|
123
|
+
- `ai-agents-rag-custom-pgvector` (Onda 2)
|
|
124
|
+
- `ai-agents-mcp-tools` — o caso mais comum de estouro do teto de tools
|
|
125
|
+
- `ai-agents-context-providers` — plano por fase com allow-list de tools
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
*MORPH-SPEC by Polymorphism Tech — ai-agents/sweet-spot.md v1.1 (2026-09-08)*
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# Testing AI — como se testa um agente sem rede, sem chave e sem variância
|
|
2
|
+
|
|
3
|
+
> **Scope:** stacks=["dotnet"]
|
|
4
|
+
> **Layer:** 1 (on-keyword)
|
|
5
|
+
> **Keywords:** testar agente, fake IChatClient, ScriptedChatClient, harness de turno, eval de agente, teste sem rede, golden de prompt, compatibilidade binaria
|
|
6
|
+
> **Read by Claude in:** implement (ao escrever o teste de qualquer código que fala com um modelo)
|
|
7
|
+
|
|
8
|
+
**Verified against:** Microsoft.Extensions.AI 10.9.0 + Microsoft.Agents.AI 1.20.0 (ai-pin 2026-09-08); provado por `templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs` e `.../src/Morph.AiKit/Providers/IProviderClientFactory.cs` — o fake roteirizado e a costura que dispensa rede são o que o kit compila e exercita. Harness de turno completo e evals com cache de resposta são **verificação documental** — o ai-kit não os traz. Last-verified: 2026-09-08.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Fronteira com o testing genérico
|
|
13
|
+
|
|
14
|
+
`backend-dotnet-testing` continua dono do que é de teste em .NET: xUnit, `FakeTimeProvider`,
|
|
15
|
+
isolamento entre casos, nomes de teste, organização de fixtures. **Nada disso se repete aqui.**
|
|
16
|
+
|
|
17
|
+
Este standard cobre **só o que é de agente** — e o que é de agente cabe em uma frase: o único ponto
|
|
18
|
+
em que o seu código toca um provider é um `IChatClient`. Todo o resto deste arquivo é consequência
|
|
19
|
+
disso.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Fake de IChatClient
|
|
24
|
+
|
|
25
|
+
**Roteirizado e burro.** Uma fila de respostas escritas à mão, servidas na ordem:
|
|
26
|
+
|
|
27
|
+
- resposta de texto;
|
|
28
|
+
- resposta com chamada de função (`FunctionCallContent`);
|
|
29
|
+
- resposta de texto com uso preenchido (para exercitar a conta de custo).
|
|
30
|
+
|
|
31
|
+
A fila guarda **respostas**, não exceções: no exemplar do kit, os três métodos de enfileiramento
|
|
32
|
+
recebem `ChatResponse` (direto, por texto, ou por texto + uso), e o único `throw` do fake é o de
|
|
33
|
+
**fila esgotada**. Para exercitar o caminho de falha do provider, injete um cliente que lança em vez
|
|
34
|
+
de enfileirar uma exceção — são costuras diferentes, e confundi-las faz o teste de falha passar pelo
|
|
35
|
+
motivo errado.
|
|
36
|
+
|
|
37
|
+
E do outro lado, **captura**: as mensagens recebidas, as opções recebidas e a contagem de chamadas —
|
|
38
|
+
uma entrada por chamada, na ordem. É o que permite assertar *o que foi ao provedor*, e não apenas
|
|
39
|
+
*o que voltou*.
|
|
40
|
+
|
|
41
|
+
**A regra que define o padrão: o fake não pode ser inteligente.** Ele não escolhe resposta por
|
|
42
|
+
conteúdo, não simula um modelo, não "responde razoavelmente". E, esgotada a fila, **ele lança**, com
|
|
43
|
+
a contagem exata de chamadas. Devolver uma resposta vazia em silêncio faria um teste passar por
|
|
44
|
+
engano — que é a única coisa pior do que um teste falhando.
|
|
45
|
+
|
|
46
|
+
O argumento mais forte a favor desta forma não é teórico: **duas implementações independentes, em
|
|
47
|
+
dois produtos que não se conhecem, chegaram à mesma coisa** (um `FakeChatClient` e um
|
|
48
|
+
`ScriptedChatClient`, com a mesma fila e a mesma captura). O ai-kit compila a terceira.
|
|
49
|
+
|
|
50
|
+
### A costura, e por que `sealed` não é desculpa para abri-la
|
|
51
|
+
|
|
52
|
+
O fake só serve se houver **um** ponto de injeção. No kit, esse ponto é uma interface de uma
|
|
53
|
+
operação — dado um alias resolvido, devolva o `IChatClient`. O registry permanece `sealed` e continua
|
|
54
|
+
sem saber construir cliente de provider nenhum.
|
|
55
|
+
|
|
56
|
+
> **O que o kit traz, e o que ele não traz.** A única implementação dessa interface no kit é a de
|
|
57
|
+
> **teste** (`FakeProviderClientFactory`) — medido. O adaptador de produção, aquele que faz o
|
|
58
|
+
> `switch` por provider e constrói o cliente de verdade, é **seu**: o kit prova que a costura
|
|
59
|
+
> funciona, não que o outro lado dela existe. É a mesma distinção de níveis do resto deste standard.
|
|
60
|
+
|
|
61
|
+
Precedente de campo com a **variante**: quando o registry é `sealed` e não se quer uma interface
|
|
62
|
+
pública, a costura foi um **construtor `internal`** recebendo uma função `alias -> IChatClient`,
|
|
63
|
+
exposta ao projeto de teste. As duas formas resolvem o mesmo problema; ambas preservam `sealed`, que
|
|
64
|
+
é a política da casa. **Costura de teste não é justificativa para tornar uma classe herdável.**
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Harness de turno
|
|
69
|
+
|
|
70
|
+
**Rode o loop real por cima do fake.** O valor não está em testar o fake; está em fazer o cliente de
|
|
71
|
+
invocação de funções **real** chamar as tools **reais**, com os clientes externos mockados.
|
|
72
|
+
|
|
73
|
+
É assim que se prova, sem rede e sem chave de API:
|
|
74
|
+
|
|
75
|
+
- que a tool escolhida foi a certa para aquele estado;
|
|
76
|
+
- que os argumentos montados batem;
|
|
77
|
+
- que o guard barrou o que devia barrar;
|
|
78
|
+
- que o teto de tool calls encerrou o turno onde devia;
|
|
79
|
+
- que o custo somou o que devia somar.
|
|
80
|
+
|
|
81
|
+
Nenhuma dessas cinco coisas é observável testando o compositor isolado — todas dependem do loop.
|
|
82
|
+
|
|
83
|
+
**Nota de ownership de recurso:** quem cria o cliente e quem o descarta precisa ser decidido uma vez.
|
|
84
|
+
Se a fábrica guardasse e reaproveitasse clientes por fora, existiriam duas caches da mesma coisa — e
|
|
85
|
+
duas caches da mesma coisa é como um cliente vazado sobrevive a um teste e polui o seguinte.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Eval e unit
|
|
90
|
+
|
|
91
|
+
**"Eval" aqui não significa "modelo real".**
|
|
92
|
+
|
|
93
|
+
O padrão medido, e é o que dá mais retorno por hora escrita, é o **eval determinístico**: exercitar o
|
|
94
|
+
caminho **completo e real** — processador de turno, compositor, runner com middleware, tools,
|
|
95
|
+
persistência — com o comportamento do modelo **roteirizado**. Ganha-se cobertura de decisão sem
|
|
96
|
+
variância, e o teste falha por um motivo legível em vez de "o modelo respondeu diferente hoje".
|
|
97
|
+
|
|
98
|
+
Para o que **precisa** do modelo de verdade, o passo seguinte é uma biblioteca de avaliação com
|
|
99
|
+
**cache de resposta**, em **categoria própria** de teste — nunca misturada à suíte determinística. E
|
|
100
|
+
a regra que fecha: **gate duro só vale sobre propriedade determinística.** Reprovar um build porque
|
|
101
|
+
uma nota subjetiva caiu de 8,1 para 7,9 é transformar ruído em bloqueio.
|
|
102
|
+
|
|
103
|
+
**Zero chamada real ao provider dentro do `dotnet test`.** É uma propriedade **verificável
|
|
104
|
+
mecanicamente**, e o jeito de verificá-la é uma varredura sobre `tests/` procurando chave de
|
|
105
|
+
provider, leitura de variável de ambiente e cliente HTTP: ela precisa voltar **vazia**.
|
|
106
|
+
|
|
107
|
+
> **Quem faz essa varredura hoje: ninguém, automaticamente.** Medido em 2026-09-08 sobre o ai-kit
|
|
108
|
+
> (`grep -rniE "OPENAI_API_KEY|GetEnvironmentVariable|HttpClient|EnumerateFiles|Directory.Get"` em
|
|
109
|
+
> `templates/dotnet/ai-kit/**`): **zero ocorrências** — o que prova que o kit **não chama** o
|
|
110
|
+
> provider, e simultaneamente que **não existe teste que cobre essa ausência**. O kit descreve a
|
|
111
|
+
> propriedade em prosa (`FakeChatClient.cs`), não a guarda. Adotar esta regra significa **escrever a
|
|
112
|
+
> varredura no seu projeto**; herdá-la do kit é herdar uma intenção.
|
|
113
|
+
|
|
114
|
+
A medição de campo que sustenta a regra é do **acervo**, não do kit: `grep -c OPENAI_API_KEY` em
|
|
115
|
+
`tests/` = 0, sem nenhum teste marcado como pulado nem categoria de exclusão — ou seja, não há
|
|
116
|
+
"teste real desligado", há ausência de teste real.
|
|
117
|
+
|
|
118
|
+
**E o standard registra o preço disso**, porque ele é real: o harness com modelo de verdade daquele
|
|
119
|
+
projeto (onze cenários) **nunca foi rodado por completo ao vivo**. O que fica fora do `dotnet test`
|
|
120
|
+
tende a não rodar. Se um caminho precisa do modelo real, ele precisa de um dono e de um gatilho — ou
|
|
121
|
+
vai apodrecer.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Captura do que foi ao provedor
|
|
126
|
+
|
|
127
|
+
O que se quer saber depois de um bug é: **o que exatamente foi enviado?**
|
|
128
|
+
|
|
129
|
+
Os dois artefatos que existem no campo, e nenhum deles se chama "captura da chamada crua":
|
|
130
|
+
|
|
131
|
+
1. **As opções recebidas pelo fake** — em teste, é a prova direta de que a lista de tools ofertadas
|
|
132
|
+
saiu da lista real, de que o modo de tool era o esperado, e de que o esforço de raciocínio foi o
|
|
133
|
+
do alias.
|
|
134
|
+
2. **O hash do texto que foi ao provedor** — em produção.
|
|
135
|
+
|
|
136
|
+
**A regra: prompt cru só em desenvolvimento e no harness, com opt-in explícito e sem dado pessoal;
|
|
137
|
+
em produção, hash.** Um prompt cru persistido em produção é um vazamento esperando indexação.
|
|
138
|
+
|
|
139
|
+
Duas asserções que o campo escreveu e que valem copiar: que a lista de tools ofertadas **saiu da
|
|
140
|
+
lista real** (e não de uma constante paralela), e que **turno cancelado não grava** — porque um
|
|
141
|
+
registro de algo que não aconteceu é pior que ausência de registro.
|
|
142
|
+
|
|
143
|
+
A metade de produção deste assunto — como o hash vira span, e a invariante que o torna confiável —
|
|
144
|
+
é de `ai-agents-observability-patterns`.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Golden com parcimônia
|
|
149
|
+
|
|
150
|
+
Golden test congela uma saída em arquivo e compara. É excelente onde o **contrato é a forma**, e é
|
|
151
|
+
peso morto onde o conteúdo muda por motivo legítimo.
|
|
152
|
+
|
|
153
|
+
**Onde compensa:** o render de uma persona a partir de blocos, o contrato de saída estruturada, o
|
|
154
|
+
formato de um artefato publicado.
|
|
155
|
+
|
|
156
|
+
**Onde não compensa, com contra-exemplo real:** goldens de dois blobs de prompt foram **removidos de
|
|
157
|
+
propósito** de um projeto, com a justificativa medida — *"13,6 KB congelados para provar duas linhas
|
|
158
|
+
de concatenação"*. Cada edição de prompt exigia regravar 13,6 KB, o diff era ilegível, e a revisão
|
|
159
|
+
virava um "aceitar tudo". Um golden que ninguém lê no diff não é teste: é atrito.
|
|
160
|
+
|
|
161
|
+
O critério: **golden prova forma, não conteúdo.** Se a asserção real é "estas duas linhas
|
|
162
|
+
concatenam", escreva as duas linhas.
|
|
163
|
+
|
|
164
|
+
### Teste de compatibilidade binária
|
|
165
|
+
|
|
166
|
+
Técnica, não filosofia: comparar em runtime a versão do assembly do SDK carregado com a que a ponte
|
|
167
|
+
referencia, e conferir a **assinatura** do membro que já quebrou uma vez. É barato, roda sem rede, e
|
|
168
|
+
pega a classe de defeito que custou uma chamada paga sem resposta. Ponte com
|
|
169
|
+
`ai-agents-llm-runtime-defaults`, seção de pin de SDK.
|
|
170
|
+
|
|
171
|
+
### Prompt versionado é texto testável
|
|
172
|
+
|
|
173
|
+
Quando o prompt vive fora do código, ele continua sendo texto — e texto se testa. Ler a fonte
|
|
174
|
+
versionada e assertar:
|
|
175
|
+
|
|
176
|
+
- **presença** de uma regra que precisa estar lá;
|
|
177
|
+
- **vocabulário** das chaves que o consumidor espera;
|
|
178
|
+
- **ausência** de uma instrução revogada — que é a asserção que ninguém escreve e a que mais pega
|
|
179
|
+
regressão.
|
|
180
|
+
|
|
181
|
+
Precedente de campo: treze arquivos e cerca de 2.500 linhas fazendo exatamente isso. Ponte com
|
|
182
|
+
`ai-agents-prompt-sources`, seção de reconciliação.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Anti-patterns
|
|
187
|
+
|
|
188
|
+
| Anti-pattern | Por quê é problema | Solução |
|
|
189
|
+
|---|---|---|
|
|
190
|
+
| Fake "inteligente" que escolhe resposta por conteúdo | Você passa a testar o fake | Fila roteirizada, servida na ordem |
|
|
191
|
+
| Fake que devolve vazio quando a fila acaba | O teste passa por engano | Lance, com a contagem de chamadas |
|
|
192
|
+
| Testar só o compositor, sem o loop real | Tool escolhida, guard e teto ficam sem cobertura | Harness com o cliente de invocação real |
|
|
193
|
+
| Chamada real ao provider dentro do `dotnet test` | Custo, flake e dependência de rede na suíte | Zero; e verifique com varredura |
|
|
194
|
+
| Harness com modelo real sem dono nem gatilho | Onze cenários que nunca rodaram por completo | Dono, gatilho e categoria própria |
|
|
195
|
+
| Gate duro sobre nota subjetiva de eval | Ruído virando bloqueio de build | Gate só sobre propriedade determinística |
|
|
196
|
+
| Prompt cru persistido em produção | Vazamento esperando indexação | Hash em produção; cru só em dev/harness com opt-in |
|
|
197
|
+
| Gravar telemetria de turno cancelado | Registro de algo que não aconteceu | Não grave |
|
|
198
|
+
| Golden de blob de prompt | 13,6 KB congelados para provar duas linhas | Asserte as duas linhas |
|
|
199
|
+
| Abrir a classe (`sealed` removido) para poder testar | Costura de teste virando decisão de arquitetura | Interface de uma operação, ou construtor `internal` |
|
|
200
|
+
| Fábrica que guarda e reaproveita clientes por fora | Duas caches da mesma coisa; cliente vazado entre testes | Ownership decidido num lugar |
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Checklist (verifiable by morph-eval)
|
|
205
|
+
|
|
206
|
+
- [ ] Existe um fake de `IChatClient` roteirizado, com captura de mensagens, opções e contagem
|
|
207
|
+
- [ ] O fake **lança** quando a fila acaba
|
|
208
|
+
- [ ] Há ao menos um teste que roda o loop **real** de invocação de funções por cima do fake
|
|
209
|
+
- [ ] Existe **no seu projeto** um teste que varre `tests/` por chave de provider, leitura de ambiente
|
|
210
|
+
e cliente HTTP, e ele volta vazio (o ai-kit **não** traz essa varredura — medido; herdá-la do
|
|
211
|
+
kit é herdar uma intenção)
|
|
212
|
+
- [ ] Nenhum teste real de modelo está apenas "pulado" — ou tem dono e gatilho, ou não existe
|
|
213
|
+
- [ ] Nenhum gate duro sobre nota subjetiva de eval
|
|
214
|
+
- [ ] Existe asserção de que a lista de tools ofertadas saiu da lista real
|
|
215
|
+
- [ ] Turno cancelado não grava telemetria
|
|
216
|
+
- [ ] Nenhum golden de blob de prompt; goldens existentes provam **forma**
|
|
217
|
+
- [ ] Existe teste de compatibilidade binária do par SDK × ponte
|
|
218
|
+
- [ ] O prompt versionado tem teste de texto, incluindo **ausência** de instrução revogada
|
|
219
|
+
- [ ] Nenhuma classe deixou de ser `sealed` por causa de teste
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## References
|
|
224
|
+
|
|
225
|
+
- `backend-dotnet-testing` — dono do testing .NET genérico (xUnit, `FakeTimeProvider`, isolamento).
|
|
226
|
+
Nada dele se repete aqui
|
|
227
|
+
- `ai-agents-llm-runtime-defaults` — o pin de SDK que o teste de compatibilidade binária protege
|
|
228
|
+
- `ai-agents-prompt-sources` — a fonte versionada que o teste de prompt como texto lê
|
|
229
|
+
- `ai-agents-observability-patterns` — a metade de produção da captura (hash, span, invariante)
|
|
230
|
+
- `ai-agents-conversational-agent-with-phases` — compositor, allow-list e teto: o que o harness exercita
|
|
231
|
+
- `ai-agents-cost-and-budget` — a conta que o harness confere com uso roteirizado
|
|
232
|
+
- `ai-agents-guardrails` — os guards que só o loop real prova
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
*MORPH-SPEC by Polymorphism Tech — ai-agents/testing-ai.md v1.0 (2026-09-08)*
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
> **Keywords:** microsoft extensions vectordata, vectorstorecollection, vector store abstraction, data ingestion, ingestion pipeline, semantic kernel connectors exception, pgvector connector, vectorstorewriter
|
|
6
6
|
> **Read by Claude in:** plan (when choosing a RAG storage backend) + implement (when deciding whether to adopt VectorData over custom pgvector)
|
|
7
7
|
|
|
8
|
-
**Verified against:** Microsoft.Extensions.VectorData.Abstractions
|
|
8
|
+
**Verified against:** Microsoft.Extensions.VectorData.Abstractions **10.9.0** + CommunityToolkit.VectorData.PgVector **1.0.1** + Microsoft.Extensions.DataIngestion **10.9.0-preview.1.26411.16** — nenhum deles está no `framework/ai-pin.json` (que cobre os 5 pacotes de agente), portanto **todas as versões acima foram apuradas na API do nuget.org em 2026-09-08** e são citadas com essa data, não como pin. **Verificação documental**, sem cláusula `provado por`: o ai-kit **não referencia nem exercita** VectorData — o pacote não está no `Morph.AiKit.csproj`, cujos cinco `PackageReference` são exatamente os do pin (medido 2026-09-08). Aqui nem a régua fraca do restore existe. Last-verified: 2026-09-08.
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -33,7 +33,23 @@ A single `VectorStoreCollection<TKey, TRecord>` shape works identically whether
|
|
|
33
33
|
|
|
34
34
|
## A pegadinha de nomenclatura (crítico — leia antes de tudo)
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
> **Atualização de 2026-09-08 — a pegadinha mudou de forma.** Os conectores concretos **saíram** dos nomes `Microsoft.SemanticKernel.Connectors.*` e passaram a `CommunityToolkit.VectorData.*` (repositório `github.com/CommunityToolkit/AI`). Medido na API do nuget.org em 2026-09-08:
|
|
37
|
+
>
|
|
38
|
+
> | Pacote | Última versão | Publicada | Estável? |
|
|
39
|
+
> |---|---|---|---|
|
|
40
|
+
> | `Microsoft.SemanticKernel.Connectors.PgVector` | `1.74.0-preview` | 2026-03-20 | **Nunca teve versão estável**; parou aí |
|
|
41
|
+
> | `CommunityToolkit.VectorData.PgVector` | **`1.0.1`** | **2026-08-31** | **Sim** |
|
|
42
|
+
> | `CommunityToolkit.VectorData.InMemory` | `1.0.1` | 2026-08-31 | Sim |
|
|
43
|
+
>
|
|
44
|
+
> Os dois carregam o **mesmo título e a mesma descrição** ("Postgres (with pgvector extension) provider for Microsoft.Extensions.VectorData"), trocando apenas o autor — *"by Semantic Kernel"* → *"by the .NET Community Toolkit"*. É a mesma peça, com outro dono e outro nome.
|
|
45
|
+
>
|
|
46
|
+
> **Use `CommunityToolkit.VectorData.PgVector` 1.0.1.** O pacote com nome de Semantic Kernel é caminho morto: última versão em março de 2026, e preview.
|
|
47
|
+
>
|
|
48
|
+
> *Fronteira do que foi medido:* a substituição acima é medida (existência, versões, datas, título e descrição, apurados na API do nuget.org em 2026-09-08). Que a página do NuGet **exiba um aviso de rename** não foi confirmado nem pelo `.nuspec` da 1.74.0-preview nem pelos metadados de deprecação da registration API — a afirmação aqui é a substituição, não o aviso.
|
|
49
|
+
|
|
50
|
+
A pegadinha original — abaixo — continua valendo para **projeto legado** que ainda referencia o nome antigo, e para quem tropeça no nome ao ler código existente.
|
|
51
|
+
|
|
52
|
+
The concrete vector-store **connectors** — the packages that actually implement `VectorStoreCollection` against a real backend — **used to** ship under `Microsoft.SemanticKernel.Connectors.*` package names (`Microsoft.SemanticKernel.Connectors.PgVector`, `.Qdrant`, `.SqlServer`, `.CosmosNoSQL`, `.InMemory`, etc.), even though `Microsoft.Extensions.VectorData.Abstractions` itself has been transferred out of the Semantic Kernel repo into `dotnet/extensions` and is fully independent of SK orchestration.
|
|
37
53
|
|
|
38
54
|
Microsoft's own docs are explicit about this:
|
|
39
55
|
|
|
@@ -45,28 +61,36 @@ Microsoft's own docs are explicit about this:
|
|
|
45
61
|
|
|
46
62
|
## Maturidade atual (verificado 2026-07-06)
|
|
47
63
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
|
51
|
-
|
|
52
|
-
| `Microsoft.
|
|
53
|
-
| `Microsoft.
|
|
54
|
-
| `
|
|
64
|
+
> Tabela **reapurada em 2026-09-08** na API do nuget.org. Cada linha traz a versão exata e a data — nenhum destes pacotes está no `framework/ai-pin.json`, então a data de apuração é o que dá validade ao número.
|
|
65
|
+
|
|
66
|
+
| Package | Versão (apurada 2026-09-08) | Status |
|
|
67
|
+
|---------|------------------------------|--------|
|
|
68
|
+
| `Microsoft.Extensions.VectorData.Abstractions` | **10.9.0** (publicada 2026-08-11) | **GA** — é a camada de abstração |
|
|
69
|
+
| `Microsoft.Extensions.AI` / `Microsoft.Extensions.AI.Abstractions` | **10.9.0** (no ai-pin) | **GA** |
|
|
70
|
+
| `CommunityToolkit.VectorData.PgVector` (Postgres/pgvector connector) | **1.0.1** (publicada 2026-08-31) | **Estável** — é o conector recomendado hoje |
|
|
71
|
+
| `CommunityToolkit.VectorData.InMemory` / demais conectores | **1.0.1** (2026-08-31) | Estável; confira cada um antes de adicionar |
|
|
72
|
+
| `Microsoft.SemanticKernel.Connectors.PgVector` | `1.74.0-preview` (2026-03-20) | **Caminho morto** — nunca teve versão estável e não avança desde março/2026 |
|
|
73
|
+
| `Microsoft.Extensions.DataIngestion` | **`10.9.0-preview.1.26411.16`** | **Preview** — sem versão estável |
|
|
55
74
|
|
|
56
|
-
|
|
75
|
+
> **Cuidado com o número que o conector declara.** `CommunityToolkit.VectorData.PgVector` 1.0.1 depende de `Microsoft.Extensions.VectorData.Abstractions` **10.8.2** — esse é o **piso da dependência**, não a versão corrente da abstração, que é 10.9.0. Citar 10.8.2 como "a versão do VectorData" é ler o `.nuspec` do conector como se fosse o do pacote.
|
|
76
|
+
|
|
77
|
+
Preview packages are subject to breaking API changes before GA (see the rename `PostgresVectorStoreRecordCollection` → `PostgresCollection` as precedent).
|
|
57
78
|
|
|
58
79
|
---
|
|
59
80
|
|
|
60
81
|
## Decision matrix — custom pgvector vs VectorData
|
|
61
82
|
|
|
83
|
+
**O critério, em uma frase:** a abstração se paga quando **a troca de store é plausível**; o SQL cru se paga quando **já funciona e é testado**. Nada aqui manda migrar o que está de pé.
|
|
84
|
+
|
|
62
85
|
| Situation | Use |
|
|
63
86
|
|-----------|-----|
|
|
64
|
-
| Default production RAG on Neon/Postgres (this framework's default stack) | **`ai-agents-rag-custom-pgvector`** —
|
|
65
|
-
|
|
|
66
|
-
|
|
|
67
|
-
|
|
|
87
|
+
| Default production RAG on Neon/Postgres (this framework's default stack), **already implemented and tested** | **`ai-agents-rag-custom-pgvector`** — framework-owned raw SQL, zero external connector, already the checklisted default. **Não se migra o que funciona** (item D4 da elevação-9) |
|
|
88
|
+
| **Projeto novo** onde a escolha do store ainda não foi cravada, e trocá-lo depois deve custar pouco | `Microsoft.Extensions.VectorData.Abstractions` 10.9.0 + `CommunityToolkit.VectorData.PgVector` 1.0.1 — hoje **ambos estáveis**, o que remove o argumento "é preview" que existia em julho/2026 |
|
|
89
|
+
| Project needs genuine portability across >1 vector backend (a client contract requires swapping Postgres for Qdrant or Azure AI Search later) | `Microsoft.Extensions.VectorData` + o conector correspondente — document the choice in `decisions.md` |
|
|
90
|
+
| Prototyping, tests, or demos where an in-memory store is enough | `CommunityToolkit.VectorData.InMemory` 1.0.1 |
|
|
91
|
+
| Ingestão em pipeline (reader → chunker → writer) hoje | Só com `Microsoft.Extensions.DataIngestion`, que **continua preview**: registre a aceitação e um lembrete de revisitar no GA |
|
|
68
92
|
|
|
69
|
-
Do not migrate the existing `ai-agents-rag-custom-pgvector` implementation to
|
|
93
|
+
Do not migrate the existing `ai-agents-rag-custom-pgvector` implementation to VectorData as a default — that would rewrite a working, tested, framework-owned implementation for no functional gain. **O que mudou desde a verificação anterior:** o argumento *"o conector é preview"* **caiu** (o `CommunityToolkit` é estável desde 2026-08-31); o argumento *"não se reescreve o que funciona"* **permanece**, e é ele que sustenta o default hoje.
|
|
70
94
|
|
|
71
95
|
---
|
|
72
96
|
|
|
@@ -138,9 +162,11 @@ This maps conceptually onto the existing custom pattern: `IngestionDocumentReade
|
|
|
138
162
|
|
|
139
163
|
| Anti-pattern | Why it's wrong | Right way |
|
|
140
164
|
|--------------|----------------|-----------|
|
|
141
|
-
| Refusing to add
|
|
142
|
-
|
|
|
143
|
-
|
|
|
165
|
+
| Refusing to add a VectorData connector because of the "Forbidden: Microsoft.SemanticKernel.*" rule | The rule targets SK orchestration, not VectorData connectors — see the naming-trap section above | Read this standard's exception note before treating any `Microsoft.SemanticKernel.Connectors.*` package as forbidden |
|
|
166
|
+
| **Adding `Microsoft.SemanticKernel.Connectors.PgVector` in a new project** | Caminho morto: `1.74.0-preview` de 2026-03-20, sem versão estável | `CommunityToolkit.VectorData.PgVector` 1.0.1 |
|
|
167
|
+
| **Citar `Microsoft.Extensions.VectorData.Abstractions` 10.8.2 como "a versão atual"** | 10.8.2 é o **piso da dependência** declarado pelo conector; a corrente é 10.9.0 | Ler a versão no pacote da abstração, não no `.nuspec` de quem depende dela |
|
|
168
|
+
| Migrating the working custom pgvector implementation to VectorData "because it's official" | Reescreve uma implementação testada sem ganho funcional. Note que o motivo mudou: já **não** é mais "porque é preview" | Keep `ai-agents-rag-custom-pgvector` as the default; adopt VectorData per the decision matrix |
|
|
169
|
+
| Treating `Microsoft.Extensions.DataIngestion` as production-ready | Continua preview em 2026-09-08 (`10.9.0-preview.1.26411.16`), sem versão estável | Track it for future adoption; do not wire it into a shipped feature today |
|
|
144
170
|
| Importing `Microsoft.SemanticKernel` (the core orchestration package) to "just get the vector store types" | Pulls in the full SK orchestration surface this framework has deliberately moved away from | Import only `Microsoft.Extensions.VectorData.Abstractions` + the specific `*.Connectors.*` package needed |
|
|
145
171
|
|
|
146
172
|
---
|
|
@@ -150,7 +176,9 @@ This maps conceptually onto the existing custom pattern: `IngestionDocumentReade
|
|
|
150
176
|
- [ ] If `Microsoft.SemanticKernel.Connectors.*` appears in a `.csproj`, it is used only for `VectorStoreCollection`/`VectorStore` types — no `Kernel`, `KernelFunction`, or `ChatHistory` usage anywhere in the project.
|
|
151
177
|
- [ ] No plain `Microsoft.SemanticKernel` (core) package reference.
|
|
152
178
|
- [ ] If VectorData was chosen over custom pgvector, the reason is recorded in `decisions.md` per the decision matrix.
|
|
153
|
-
- [ ]
|
|
179
|
+
- [ ] No new `Microsoft.SemanticKernel.Connectors.*` reference — new work uses `CommunityToolkit.VectorData.*`.
|
|
180
|
+
- [ ] If a preview package (`Microsoft.Extensions.DataIngestion`, or any `*-preview` connector) was adopted, `decisions.md` records the preview-acceptance and a revisit-at-GA note.
|
|
181
|
+
- [ ] Every VectorData/ingestion `PackageReference` carries an exact version — these packages are **not** in `framework/ai-pin.json`, so the project's own `decisions.md` (or a comment) states when the version was checked.
|
|
154
182
|
- [ ] Embedding model/dimensions consistent between the `VectorStoreVector(N)` attribute and the chosen embedding model.
|
|
155
183
|
|
|
156
184
|
---
|
|
@@ -164,7 +192,9 @@ This maps conceptually onto the existing custom pattern: `IngestionDocumentReade
|
|
|
164
192
|
- Data ingestion - .NET | Microsoft Learn: https://learn.microsoft.com/en-us/dotnet/ai/conceptual/data-ingestion
|
|
165
193
|
- AI and Vector Data Extensions GA announcement: https://devblogs.microsoft.com/dotnet/ai-vector-data-dotnet-extensions-ga/
|
|
166
194
|
- Introducing Data Ingestion Building Blocks (Preview): https://devblogs.microsoft.com/dotnet/introducing-data-ingestion-building-blocks-preview/
|
|
195
|
+
- Vector stores overview: https://learn.microsoft.com/en-us/dotnet/ai/vector-stores/overview
|
|
196
|
+
- `CommunityToolkit.VectorData.*` — repositório https://github.com/CommunityToolkit/AI (apurado 2026-09-08)
|
|
167
197
|
|
|
168
198
|
---
|
|
169
199
|
|
|
170
|
-
*MORPH-SPEC by Polymorphism Tech — ai-agents/vector-data-extensions.md
|
|
200
|
+
*MORPH-SPEC by Polymorphism Tech — ai-agents/vector-data-extensions.md v2.0 (2026-09-08)*
|