M4: the assembler — assemble → model → parse (Increment 2)
SharedKernel contracts (so Assembler stays decoupled): IAgentRunContextProvider (agent + task) and ISkillCatalog (skill prompts by key). Implemented by OrgBoard (AgentRunContextProvider) and Skills (SkillCatalog). Assembler: - PromptAssembler builds house-style + identity + the agent's skill bodies + the task, and derives the primary action + risk from the agent's first skill. RAG/working-memory join at M6. - AgentRunExecutor (real): resolve context + skills → assemble → resolve BYOK config (with fallback) → call IModelClient → parse into action + risk → capture all on the AgentRun. Verified: build green; ArchitectureTests 8/8; IntegrationTests 29/29 — incl. the M4 acceptance: assigning a Spec task to Aria (PO, gated, stub BYOK) yields a Completed run with the assembled prompt (skill body + task title), action "write-spec", risk "Draft", and model output. Nothing executes — the gate is M5. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TeamUp.Modules.Skills.Domain;
|
||||
using TeamUp.Modules.Skills.Persistence;
|
||||
using TeamUp.SharedKernel.Ai;
|
||||
|
||||
namespace TeamUp.Modules.Skills.Catalog;
|
||||
|
||||
/// <summary>Resolves skill prompts by key (latest version) for the assembler.</summary>
|
||||
internal sealed class SkillCatalog(SkillsDbContext db) : ISkillCatalog
|
||||
{
|
||||
public async Task<IReadOnlyList<SkillPrompt>> GetByKeysAsync(
|
||||
IReadOnlyCollection<string> keys,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (keys.Count == 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var wanted = keys.ToHashSet();
|
||||
var skills = await db.Skills.Where(s => wanted.Contains(s.SkillKey)).ToListAsync(cancellationToken);
|
||||
|
||||
return skills
|
||||
.GroupBy(s => s.SkillKey)
|
||||
.Select(group => group.OrderByDescending(s => s.Version, StringComparer.Ordinal).First())
|
||||
.Select(s =>
|
||||
{
|
||||
var primary = s.Actions.Count > 0 ? s.Actions[0] : null;
|
||||
return new SkillPrompt(
|
||||
s.SkillKey,
|
||||
s.Name,
|
||||
s.Body,
|
||||
primary?.Name ?? "respond",
|
||||
(primary?.Risk ?? ActionRisk.Draft).ToString(),
|
||||
s.Roles);
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,12 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using TeamUp.Modules.Skills.Catalog;
|
||||
using TeamUp.Modules.Skills.Endpoints;
|
||||
using TeamUp.Modules.Skills.Indexing;
|
||||
using TeamUp.Modules.Skills.Persistence;
|
||||
using TeamUp.Modules.Skills.Sync;
|
||||
using TeamUp.SharedKernel.Ai;
|
||||
using TeamUp.SharedKernel.Modularity;
|
||||
using TeamUp.SharedKernel.Persistence;
|
||||
|
||||
@@ -27,6 +29,7 @@ public sealed class SkillsModule : IModule
|
||||
services.AddSingleton<ISkillEmbedder, HashingSkillEmbedder>();
|
||||
services.AddScoped<SkillIndexer>();
|
||||
services.AddScoped<SkillSyncService>();
|
||||
services.AddScoped<ISkillCatalog, SkillCatalog>();
|
||||
services.TryAddSingleton(TimeProvider.System);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user