Add paired song curriculum distillation
This commit is contained in:
parent
7ce1018920
commit
3c07852968
5 changed files with 506 additions and 68 deletions
|
|
@ -37,6 +37,9 @@ try
|
|||
case "dump":
|
||||
await DumpAsync(options);
|
||||
return 0;
|
||||
case "distill":
|
||||
await DistillAsync(options);
|
||||
return 0;
|
||||
case "search":
|
||||
await SearchAsync(options);
|
||||
return 0;
|
||||
|
|
@ -320,6 +323,39 @@ static async Task DumpAsync(Dictionary<string, string> options)
|
|||
Console.WriteLine(output);
|
||||
}
|
||||
|
||||
static async Task DistillAsync(Dictionary<string, string> options)
|
||||
{
|
||||
var store = Required(options, "store");
|
||||
var outputStore = Required(options, "output-store");
|
||||
var output = Required(options, "output");
|
||||
var maxResults = int.TryParse(Value(options, "max-results", "40"), NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedMax)
|
||||
? Math.Clamp(parsedMax, 1, 500)
|
||||
: 40;
|
||||
var minCosine = FloatValue(options, "min-cosine", .35f);
|
||||
var results = await IpaTrialResultCultCacheStore.ReadResultsAsync(store);
|
||||
var evidence = await IpaTrialResultCultCacheStore.ReadSongChallengeEvidenceAsync(store);
|
||||
var selected = DistillResults(results, maxResults, minCosine).ToArray();
|
||||
var selectedReferenceIds = selected.Select(result => result.ReferenceId).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
var selectedEvidence = evidence
|
||||
.Where(document => selectedReferenceIds.Contains(document.ChallengeId))
|
||||
.ToArray();
|
||||
var distillation = SongTrialDistillation(store, results, selected, minCosine);
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(outputStore))!);
|
||||
if (File.Exists(outputStore))
|
||||
{
|
||||
File.Delete(outputStore);
|
||||
}
|
||||
|
||||
await IpaTrialResultCultCacheStore.UpsertResultsAsync(outputStore, selected);
|
||||
await IpaTrialResultCultCacheStore.UpsertSongChallengeEvidenceAsync(outputStore, selectedEvidence);
|
||||
await IpaTrialResultCultCacheStore.UpsertSongTrialDistillationsAsync(outputStore, [distillation]);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(output))!);
|
||||
await File.WriteAllTextAsync(output, DistillationReport(store, outputStore, results, selected, evidence, selectedEvidence, distillation, minCosine), Encoding.UTF8);
|
||||
Console.WriteLine(outputStore);
|
||||
Console.WriteLine(output);
|
||||
}
|
||||
|
||||
static async Task SearchAsync(Dictionary<string, string> options)
|
||||
{
|
||||
var store = Required(options, "store");
|
||||
|
|
@ -1173,6 +1209,43 @@ static IEnumerable<EvidenceChunk> BuildEvidenceChunks(IReadOnlyList<IpaTrialResu
|
|||
.GetResult()
|
||||
.GroupBy(document => document.ChallengeId, StringComparer.OrdinalIgnoreCase)
|
||||
.ToDictionary(group => group.Key, group => group.ToArray(), StringComparer.OrdinalIgnoreCase);
|
||||
var distillations = IpaTrialResultCultCacheStore.ReadSongTrialDistillationsAsync(store)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
|
||||
foreach (var document in distillations)
|
||||
{
|
||||
var linkedTrialIds = document.KeptTrialIds.Length == 0
|
||||
? [document.DistillationId]
|
||||
: document.KeptTrialIds;
|
||||
foreach (var linkedTrialId in linkedTrialIds)
|
||||
{
|
||||
yield return new EvidenceChunk(
|
||||
$"song-trial-distillation:{StoreKey(store)}:{document.DistillationId}:{linkedTrialId}",
|
||||
linkedTrialId,
|
||||
"distillation",
|
||||
"song-trial-distillation",
|
||||
"distillation",
|
||||
$"""
|
||||
song trial distillation
|
||||
id {document.DistillationId}
|
||||
linked_trial {linkedTrialId}
|
||||
source_store {document.SourceStore}
|
||||
input_trials {document.InputTrialCount}
|
||||
kept_trials {document.KeptTrialCount}
|
||||
summary {document.Summary}
|
||||
reusable_scene_roles
|
||||
{string.Join(Environment.NewLine, document.ReusableSceneRoles)}
|
||||
transfer_rules
|
||||
{string.Join(Environment.NewLine, document.TransferRules)}
|
||||
failure_patterns
|
||||
{string.Join(Environment.NewLine, document.FailurePatterns)}
|
||||
aggregate_metrics
|
||||
{string.Join(Environment.NewLine, document.AggregateMetrics.Select(metric => $"{metric.Name} {metric.Value.ToString("0.######", CultureInfo.InvariantCulture)} weight {metric.Weight.ToString("0.######", CultureInfo.InvariantCulture)}"))}
|
||||
""",
|
||||
store);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var result in results)
|
||||
{
|
||||
|
|
@ -1533,6 +1606,256 @@ static string StoreReport(string store, IReadOnlyList<IpaTrialResult> results)
|
|||
return builder.ToString();
|
||||
}
|
||||
|
||||
static IReadOnlyList<IpaTrialResult> DistillResults(
|
||||
IReadOnlyList<IpaTrialResult> results,
|
||||
int maxResults,
|
||||
float minCosine)
|
||||
{
|
||||
var keep = new Dictionary<string, IpaTrialResult>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var result in results
|
||||
.Where(result => result.Verdict.Equals("promising", StringComparison.OrdinalIgnoreCase) ||
|
||||
result.Verdict.Equals("pressure", StringComparison.OrdinalIgnoreCase) ||
|
||||
MetricValue(result, "log_mel_cosine") >= minCosine)
|
||||
.OrderByDescending(ScoreSort))
|
||||
{
|
||||
keep.TryAdd(result.TrialId, result);
|
||||
}
|
||||
|
||||
foreach (var result in results
|
||||
.GroupBy(result => result.ReferenceId, StringComparer.OrdinalIgnoreCase)
|
||||
.Select(group => group.OrderByDescending(ScoreSort).First()))
|
||||
{
|
||||
keep.TryAdd(result.TrialId, result);
|
||||
}
|
||||
|
||||
foreach (var result in results
|
||||
.GroupBy(result => result.CandidateId, StringComparer.OrdinalIgnoreCase)
|
||||
.Select(group => group.OrderByDescending(ScoreSort).First()))
|
||||
{
|
||||
keep.TryAdd(result.TrialId, result);
|
||||
}
|
||||
|
||||
return keep.Values
|
||||
.OrderByDescending(ScoreSort)
|
||||
.ThenByDescending(result => MetricValue(result, "audio_score"))
|
||||
.ThenBy(result => result.TrialId, StringComparer.Ordinal)
|
||||
.Take(maxResults)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
static string DistillationReport(
|
||||
string sourceStore,
|
||||
string outputStore,
|
||||
IReadOnlyList<IpaTrialResult> allResults,
|
||||
IReadOnlyList<IpaTrialResult> selected,
|
||||
IReadOnlyList<SongChallengeEvidenceDocument> allEvidence,
|
||||
IReadOnlyList<SongChallengeEvidenceDocument> selectedEvidence,
|
||||
SongTrialDistillationDocument distillation,
|
||||
float minCosine)
|
||||
{
|
||||
var selectedIds = selected.Select(result => result.TrialId).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
var dropped = allResults.Where(result => !selectedIds.Contains(result.TrialId)).ToArray();
|
||||
var builder = new StringBuilder();
|
||||
builder.AppendLine("# Song Trial Distillation");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine($"source_store: `{sourceStore}`");
|
||||
builder.AppendLine($"output_store: `{outputStore}`");
|
||||
builder.AppendLine($"min_cosine: `{minCosine.ToString("0.######", CultureInfo.InvariantCulture)}`");
|
||||
builder.AppendLine($"input_trials: `{allResults.Count}`");
|
||||
builder.AppendLine($"kept_trials: `{selected.Count}`");
|
||||
builder.AppendLine($"dropped_trials: `{dropped.Length}`");
|
||||
builder.AppendLine($"input_evidence_docs: `{allEvidence.Count}`");
|
||||
builder.AppendLine($"kept_evidence_docs: `{selectedEvidence.Count}`");
|
||||
builder.AppendLine($"distillation_document: `{distillation.DistillationId}`");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("## Distilled Signal");
|
||||
builder.AppendLine(distillation.Summary);
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("### Reusable Scene Roles");
|
||||
foreach (var role in distillation.ReusableSceneRoles)
|
||||
{
|
||||
builder.Append("- ");
|
||||
builder.AppendLine(role);
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("### Transfer Rules");
|
||||
foreach (var rule in distillation.TransferRules)
|
||||
{
|
||||
builder.Append("- ");
|
||||
builder.AppendLine(rule);
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("### Failure Patterns");
|
||||
foreach (var pattern in distillation.FailurePatterns)
|
||||
{
|
||||
builder.Append("- ");
|
||||
builder.AppendLine(pattern);
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("## Kept Trials");
|
||||
foreach (var result in selected)
|
||||
{
|
||||
builder.Append("- ");
|
||||
builder.Append(result.TrialId);
|
||||
builder.Append(" / ");
|
||||
builder.Append(result.CandidateId);
|
||||
builder.Append(" / ");
|
||||
builder.Append(result.Verdict);
|
||||
builder.Append(" / cosine=");
|
||||
builder.Append(Metric(result, "log_mel_cosine"));
|
||||
builder.Append(" / score=");
|
||||
builder.Append(Metric(result, "audio_score"));
|
||||
builder.Append(" / articulation=");
|
||||
builder.Append(Metric(result, "articulation_score"));
|
||||
builder.Append(" / reference=");
|
||||
builder.AppendLine(result.ReferenceId);
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("## Candidate Aggregates");
|
||||
foreach (var group in selected.GroupBy(result => result.CandidateId, StringComparer.OrdinalIgnoreCase)
|
||||
.OrderByDescending(group => group.Average(result => MetricValue(result, "log_mel_cosine"))))
|
||||
{
|
||||
builder.Append("- ");
|
||||
builder.Append(group.Key);
|
||||
builder.Append(": trials=");
|
||||
builder.Append(group.Count());
|
||||
builder.Append(", mean_cosine=");
|
||||
builder.Append(group.Average(result => MetricValue(result, "log_mel_cosine")).ToString("0.######", CultureInfo.InvariantCulture));
|
||||
builder.Append(", min_cosine=");
|
||||
builder.Append(group.Min(result => MetricValue(result, "log_mel_cosine")).ToString("0.######", CultureInfo.InvariantCulture));
|
||||
builder.Append(", max_cosine=");
|
||||
builder.Append(group.Max(result => MetricValue(result, "log_mel_cosine")).ToString("0.######", CultureInfo.InvariantCulture));
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("## Dropped Noise");
|
||||
foreach (var result in dropped.OrderBy(ScoreSort).Take(30))
|
||||
{
|
||||
builder.Append("- ");
|
||||
builder.Append(result.TrialId);
|
||||
builder.Append(" / ");
|
||||
builder.Append(result.CandidateId);
|
||||
builder.Append(" / ");
|
||||
builder.Append(result.Verdict);
|
||||
builder.Append(" / cosine=");
|
||||
builder.Append(Metric(result, "log_mel_cosine"));
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
static SongTrialDistillationDocument SongTrialDistillation(
|
||||
string sourceStore,
|
||||
IReadOnlyList<IpaTrialResult> allResults,
|
||||
IReadOnlyList<IpaTrialResult> selected,
|
||||
float minCosine)
|
||||
{
|
||||
var droppedIds = allResults
|
||||
.Where(result => !selected.Any(kept => kept.TrialId.Equals(result.TrialId, StringComparison.OrdinalIgnoreCase)))
|
||||
.Select(result => result.TrialId)
|
||||
.ToArray();
|
||||
var top = selected.OrderByDescending(ScoreSort).Take(5).ToArray();
|
||||
var bestCosine = selected.Select(result => MetricValue(result, "log_mel_cosine")).DefaultIfEmpty(0).Max();
|
||||
var meanCosine = selected.Select(result => MetricValue(result, "log_mel_cosine")).DefaultIfEmpty(0).Average();
|
||||
var meanScore = selected.Select(result => MetricValue(result, "audio_score")).DefaultIfEmpty(0).Average();
|
||||
var text = string.Join(' ', selected.Select(result =>
|
||||
$"{result.CandidateId} {result.Hypothesis} {result.EvaluationSummary} {string.Join(' ', result.KnownLies)}"));
|
||||
var roles = SceneRolesFrom(text).ToArray();
|
||||
var failures = FailurePatternsFrom(allResults, selected).ToArray();
|
||||
var transferRules = TransferRulesFrom(selected, roles).ToArray();
|
||||
var summary = selected.Count == 0
|
||||
? $"No high-signal trials met min cosine {minCosine:0.###}; keep only best-per-reference scaffolding."
|
||||
: $"Kept {selected.Count} of {allResults.Count} trials. Best cosine {bestCosine:0.######}; kept mean cosine {meanCosine:0.######}; best candidates: {string.Join(", ", top.Select(result => result.CandidateId).Distinct(StringComparer.OrdinalIgnoreCase).Take(5))}.";
|
||||
return new SongTrialDistillationDocument(
|
||||
$"song-distill-{DateTimeOffset.UtcNow:yyyyMMddTHHmmssfff}-{StableUuid(sourceStore)[..8]}",
|
||||
sourceStore,
|
||||
DateTimeOffset.UtcNow.ToString("O", CultureInfo.InvariantCulture),
|
||||
allResults.Count,
|
||||
selected.Count,
|
||||
summary,
|
||||
roles,
|
||||
transferRules,
|
||||
failures,
|
||||
selected.Select(result => result.TrialId).ToArray(),
|
||||
droppedIds,
|
||||
[
|
||||
new SpeechScoreMetric("distilled_best_log_mel_cosine", bestCosine, 0),
|
||||
new SpeechScoreMetric("distilled_mean_log_mel_cosine", meanCosine, 0),
|
||||
new SpeechScoreMetric("distilled_mean_audio_score", meanScore, 0),
|
||||
new SpeechScoreMetric("distilled_kept_ratio", allResults.Count == 0 ? 0 : selected.Count / (float)allResults.Count, 0)
|
||||
]);
|
||||
}
|
||||
|
||||
static IEnumerable<string> SceneRolesFrom(string text)
|
||||
{
|
||||
var lower = text.ToLowerInvariant();
|
||||
if (lower.Contains("formant", StringComparison.Ordinal) || lower.Contains("vowel", StringComparison.Ordinal))
|
||||
{
|
||||
yield return "bright formant/vowel lead: owns pitched synthetic body and vocal-ish spectral peaks.";
|
||||
}
|
||||
if (lower.Contains("dust", StringComparison.Ordinal) || lower.Contains("hat", StringComparison.Ordinal))
|
||||
{
|
||||
yield return "clocked dust/hat texture: owns short gated high-band transients through `texture role=dust`, not static white hiss.";
|
||||
}
|
||||
if (lower.Contains("codec", StringComparison.Ordinal) || lower.Contains("bit", StringComparison.Ordinal))
|
||||
{
|
||||
yield return "codec/bit bed: owns narrow mid-high grit through shaped `texture role=codec` with slow motion.";
|
||||
}
|
||||
if (lower.Contains("bass", StringComparison.Ordinal) || lower.Contains("sub", StringComparison.Ordinal))
|
||||
{
|
||||
yield return "rubber/sub bass: owns low rhythmic support and should stay register-bounded.";
|
||||
}
|
||||
if (lower.Contains("air", StringComparison.Ordinal) || lower.Contains("room", StringComparison.Ordinal) || lower.Contains("bed", StringComparison.Ordinal))
|
||||
{
|
||||
yield return "air/room bed: owns low-level recording color through shaped moving texture, not broad full-duration noise.";
|
||||
}
|
||||
}
|
||||
|
||||
static IEnumerable<string> TransferRulesFrom(IReadOnlyList<IpaTrialResult> selected, IReadOnlyList<string> roles)
|
||||
{
|
||||
if (roles.Count == 0)
|
||||
{
|
||||
yield return "Prefer target analysis artifacts over memorized song names; choose roles from tempo, register, band deltas, and envelope autocorr.";
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return "Start zero-shot production by mapping target analysis to reusable scene roles, then instantiate one owner per role.";
|
||||
}
|
||||
|
||||
if (selected.Any(result => MetricValue(result, "rms_ratio") is > 1.35f or < .7f))
|
||||
{
|
||||
yield return "Normalize loudness after role selection; high cosine can still hide bad RMS balance.";
|
||||
}
|
||||
|
||||
yield return "Use `texture` for background/recording noise and reserve raw `wave=noise` for short gated transients with narrow filters.";
|
||||
yield return "Treat repeated pressure verdicts as reusable direction, not acceptance; keep the patch family but mutate timing/register/noise shaping.";
|
||||
}
|
||||
|
||||
static IEnumerable<string> FailurePatternsFrom(IReadOnlyList<IpaTrialResult> allResults, IReadOnlyList<IpaTrialResult> selected)
|
||||
{
|
||||
var weakCount = allResults.Count(result => result.Verdict.Equals("weak", StringComparison.OrdinalIgnoreCase));
|
||||
if (weakCount > 0)
|
||||
{
|
||||
yield return $"{weakCount} weak trials are dropped from retrieval pressure unless they were best for their reference.";
|
||||
}
|
||||
|
||||
if (allResults.Any(result => MetricValue(result, "log_mel_cosine") < 0))
|
||||
{
|
||||
yield return "Negative cosine candidates are curriculum noise unless preserved as contrast; they should not steer zero-shot production.";
|
||||
}
|
||||
|
||||
if (selected.Any(result => result.KnownLies.Any(lie => lie.Contains("noise", StringComparison.OrdinalIgnoreCase))))
|
||||
{
|
||||
yield return "Scene/noise helper voices are allowed, but only shaped role owners should transfer.";
|
||||
}
|
||||
}
|
||||
|
||||
static float ScoreSort(IpaTrialResult result) =>
|
||||
result.Metrics.FirstOrDefault(metric => metric.Name == "log_mel_cosine")?.Value ?? float.NegativeInfinity;
|
||||
|
||||
|
|
@ -2550,6 +2873,7 @@ static void PrintHelp()
|
|||
song-prepare --source <audio-file> --artifact-root <dir> [--duration-seconds 10] [--seed n] [--output challenge.json]
|
||||
song-score --patch-root <dir> --challenge <challenge.json> --artifact-root <dir> [--batch-id round-001] [--store <trial-results.cc>] [--hypothesizer id]
|
||||
dump --store <trial-results.cc> --output <report.md>
|
||||
distill --store <trial-results.cc> --output-store <distilled.cc> --output <report.md> [--min-cosine .35] [--max-results 40]
|
||||
index --store <trial-results.cc> [--output <report.md>] [--force true]
|
||||
search --store <trial-results.cc> --query <text> --output <report.md> [--limit 12] [--no-vector true] [--require-vector true] [--skip-index true]
|
||||
show --store <trial-results.cc> --trial-id <id-or-candidate> --output <detail.md>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
param(
|
||||
[int]$Passes = 1,
|
||||
[int]$AgentsPerPass = 5,
|
||||
[int]$SongsPerAgent = 2,
|
||||
[string]$Source = "D:\Music\Reinier\Heyoka\Gate Code\Heyoka - Alien Gibberish.mp3",
|
||||
[string]$SourceFolder = "D:\Music\Reinier\Heyoka",
|
||||
[float]$DurationSeconds = 30,
|
||||
|
|
@ -175,6 +176,7 @@ Set-Content -LiteralPath (Join-Path $loopRoot "loop-index.md") -Value @"
|
|||
- codex: $($codex.Source)
|
||||
- passes: $Passes
|
||||
- agents_per_pass: $AgentsPerPass
|
||||
- songs_per_agent: $SongsPerAgent
|
||||
- seed: $seedValue
|
||||
- duration_seconds: $DurationSeconds
|
||||
- new_segment_per_pass: $NewSegmentPerPass
|
||||
|
|
@ -184,6 +186,7 @@ Set-Content -LiteralPath (Join-Path $loopRoot "loop-index.md") -Value @"
|
|||
|
||||
"@
|
||||
|
||||
$latestDistilledStore = ""
|
||||
for ($pass = 1; $pass -le $Passes; $pass++) {
|
||||
$passId = "pass-{0:000}" -f $pass
|
||||
$passDir = Join-Path $loopRoot $passId
|
||||
|
|
@ -192,7 +195,8 @@ for ($pass = 1; $pass -le $Passes; $pass++) {
|
|||
New-Item -ItemType Directory -Force -Path $passDir, $patchRoot | Out-Null
|
||||
|
||||
$preEvidence = Join-Path $passDir "semantic-search-before.md"
|
||||
if (Test-Path -LiteralPath $storePath) {
|
||||
$evidenceStore = if ($latestDistilledStore.Length -gt 0 -and (Test-Path -LiteralPath $latestDistilledStore)) { $latestDistilledStore } else { $storePath }
|
||||
if (Test-Path -LiteralPath $evidenceStore) {
|
||||
Invoke-LoggedProcess `
|
||||
-FilePath "dotnet" `
|
||||
-ArgumentList @(
|
||||
|
|
@ -202,7 +206,7 @@ for ($pass = 1; $pass -le $Passes; $pass++) {
|
|||
"--",
|
||||
"search",
|
||||
"--store",
|
||||
$storePath,
|
||||
$evidenceStore,
|
||||
"--query",
|
||||
"song snippet alien gibberish rhythm tempo timbre spectral envelope modulation scene voices weak promising",
|
||||
"--limit",
|
||||
|
|
@ -212,7 +216,7 @@ for ($pass = 1; $pass -le $Passes; $pass++) {
|
|||
"--skip-index",
|
||||
"true"
|
||||
) `
|
||||
-LogPath (Join-Path $logRoot "$passId-search-before.log")
|
||||
-LogPath (Join-Path $logRoot "$passId-search-before.log")
|
||||
}
|
||||
else {
|
||||
Set-Content -LiteralPath $preEvidence -Value "# No prior song-snippet evidence yet"
|
||||
|
|
@ -223,60 +227,87 @@ for ($pass = 1; $pass -le $Passes; $pass++) {
|
|||
$agentId = "agent-{0:00}" -f $agent
|
||||
$agentDir = Join-Path $passDir $agentId
|
||||
$agentPatchDir = Join-Path $patchRoot $agentId
|
||||
$agentChallengeRoot = Join-Path $agentDir "challenge"
|
||||
$agentChallengePath = Join-Path $agentChallengeRoot "challenge.json"
|
||||
New-Item -ItemType Directory -Force -Path $agentDir, $agentPatchDir, $agentChallengeRoot | Out-Null
|
||||
$sourceIndex = if ($RandomSourcePerAgent) {
|
||||
(($phaseSeed + ($agent * 1543)) % $songSources.Count)
|
||||
New-Item -ItemType Directory -Force -Path $agentDir, $agentPatchDir | Out-Null
|
||||
$challengePaths = @()
|
||||
$challengeReports = @()
|
||||
$targetPatchDirs = @()
|
||||
$agentSources = @()
|
||||
$agentSeeds = @()
|
||||
for ($song = 1; $song -le $SongsPerAgent; $song++) {
|
||||
$songId = "target-{0:00}" -f $song
|
||||
$agentChallengeRoot = Join-Path $agentDir $songId
|
||||
$agentChallengePath = Join-Path $agentChallengeRoot "challenge.json"
|
||||
$targetPatchDir = Join-Path $agentPatchDir $songId
|
||||
New-Item -ItemType Directory -Force -Path $agentChallengeRoot, $targetPatchDir | Out-Null
|
||||
$sourceIndex = if ($RandomSourcePerAgent) {
|
||||
(($phaseSeed + ($agent * 1543) + ($song * 7919)) % $songSources.Count)
|
||||
}
|
||||
else {
|
||||
(($phaseSeed - $seedValue) / 7919 + $song - 1) % $songSources.Count
|
||||
}
|
||||
$agentSource = $songSources[[int]$sourceIndex]
|
||||
$agentSeed = $phaseSeed + ($agent * 104729) + ($song * 8191)
|
||||
Invoke-LoggedProcess `
|
||||
-FilePath "dotnet" `
|
||||
-ArgumentList @(
|
||||
"run",
|
||||
"--project",
|
||||
$workerProject,
|
||||
"--",
|
||||
"song-prepare",
|
||||
"--source",
|
||||
$agentSource,
|
||||
"--artifact-root",
|
||||
$agentChallengeRoot,
|
||||
"--duration-seconds",
|
||||
([string]$DurationSeconds),
|
||||
"--seed",
|
||||
([string]$agentSeed),
|
||||
"--challenge-id",
|
||||
"song-snippet-$loopId-$passId-$agentId-$songId",
|
||||
"--output",
|
||||
$agentChallengePath
|
||||
) `
|
||||
-LogPath (Join-Path $logRoot "$passId-$agentId-$songId-challenge-prepare.log")
|
||||
$challengePaths += $agentChallengePath
|
||||
$challengeReports += (Join-Path $agentChallengeRoot "challenge.md")
|
||||
$targetPatchDirs += $targetPatchDir
|
||||
$agentSources += $agentSource
|
||||
$agentSeeds += $agentSeed
|
||||
}
|
||||
else {
|
||||
(($phaseSeed - $seedValue) / 7919) % $songSources.Count
|
||||
}
|
||||
$agentSource = $songSources[[int]$sourceIndex]
|
||||
$agentSeed = $phaseSeed + ($agent * 104729)
|
||||
Invoke-LoggedProcess `
|
||||
-FilePath "dotnet" `
|
||||
-ArgumentList @(
|
||||
"run",
|
||||
"--project",
|
||||
$workerProject,
|
||||
"--",
|
||||
"song-prepare",
|
||||
"--source",
|
||||
$agentSource,
|
||||
"--artifact-root",
|
||||
$agentChallengeRoot,
|
||||
"--duration-seconds",
|
||||
([string]$DurationSeconds),
|
||||
"--seed",
|
||||
([string]$agentSeed),
|
||||
"--challenge-id",
|
||||
"song-snippet-$loopId-$passId-$agentId",
|
||||
"--output",
|
||||
$agentChallengePath
|
||||
) `
|
||||
-LogPath (Join-Path $logRoot "$passId-$agentId-challenge-prepare.log")
|
||||
$challengeMarkdown = Join-Path $agentChallengeRoot "challenge.md"
|
||||
$challengeList = ($challengePaths | ForEach-Object { "- frozen challenge JSON: $_" }) -join "`n"
|
||||
$challengeReportList = ($challengeReports | ForEach-Object { "- human-readable challenge report: $_" }) -join "`n"
|
||||
$targetPatchList = ($targetPatchDirs | ForEach-Object { "- target patch output directory: $_" }) -join "`n"
|
||||
$agentSourceList = ($agentSources | ForEach-Object { "- assigned source: $_" }) -join "`n"
|
||||
$agentSeedList = ($agentSeeds | ForEach-Object { "- target seed: $_" }) -join "`n"
|
||||
$outputPath = Join-Path $agentDir "hypothesis-agent.md"
|
||||
$promptPath = Join-Path $agentDir "hypothesis-agent.prompt.md"
|
||||
$logPath = Join-Path $logRoot "$passId-$agentId-hypothesis-agent.log"
|
||||
$prompt = @"
|
||||
You are an AquaSynth song-snippet parity worker.
|
||||
|
||||
The dataset is on trial. Your patches, hypotheses, failures, and evaluator scores become curriculum evidence in CultCache and the vector database. Write useful sample data and reusable reasoning, not just a one-off trick that happens to flatter one metric.
|
||||
|
||||
Challenge:
|
||||
- frozen challenge JSON: $agentChallengePath
|
||||
- human-readable challenge report: $challengeMarkdown
|
||||
$challengeList
|
||||
$challengeReportList
|
||||
- shared prior evidence: $preEvidence
|
||||
- patch output directory: $agentPatchDir
|
||||
$targetPatchList
|
||||
- candidate filename prefix: $agentId
|
||||
- phase id: $passId
|
||||
- phase seed: $phaseSeed
|
||||
- assigned source: $agentSource
|
||||
- agent seed: $agentSeed
|
||||
$agentSourceList
|
||||
$agentSeedList
|
||||
- required duration: $DurationSeconds seconds
|
||||
|
||||
Goal:
|
||||
Write exactly one parseable AquaSynth `.aqua` patch that tries to reproduce the frozen $DurationSeconds second reference clip. This is scene-audio parity, not IPA articulation. You may use ordinary voices, FM, AM/tremolo, filters, formants, noise layers, acoustic vocal/syrinx primitives, curves, and helper voices.
|
||||
Write exactly one parseable AquaSynth `.aqua` patch for each frozen $DurationSeconds second reference clip assigned to you. This is scene-audio parity, not IPA articulation. You may use ordinary voices, FM, AM/tremolo, filters, formants, noise layers, acoustic vocal/syrinx primitives, curves, and helper voices.
|
||||
|
||||
Generalization:
|
||||
- You own two target patches, but your report must compare both targets and state what reusable scene/instrument knowledge transfers between them.
|
||||
- The two patches may share instrument roles, texture roles, control idioms, and DSL conventions, but each patch should fit its own target's tempo/register/spectral evidence.
|
||||
- Future agents will receive distilled evidence from this trial when asked to zero-shot an audio production request. Make your `hypotheses.md` useful retrieval context: name what transferred, what did not, and what evidence would change your next patch.
|
||||
|
||||
Noise and texture:
|
||||
- Do not model background or recording color as a full-duration raw `voice wave=noise` bed. That produces static white-noise wash and will be treated as broken subtractive synthesis, not scene modeling.
|
||||
|
|
@ -316,12 +347,12 @@ Instrument and DSL convention invention:
|
|||
- The `.aqua` candidate must use today's implemented syntax only: `pattern`, `scale`, `param`, `curve`, and ordinary patch/voice syntax are legal. Proposed future sugar is evidence for the next DSL cut, not magic accepted by the parser.
|
||||
|
||||
Evidence contract:
|
||||
- Read $challengeMarkdown and $preEvidence.
|
||||
- Write $agentDir/hypotheses.md with: reference features, cited analysis artifacts, tempo/rhythm plan, register/scale plan, scene voices, synthesis owners, invented instrument roles, expected metric movement, known risks.
|
||||
- Write exactly one `.aqua` file under $agentPatchDir named `<agent-id>__<family>__<hypothesis>.aqua`.
|
||||
- Read every assigned challenge report and $preEvidence.
|
||||
- Write $agentDir/hypotheses.md with: per-target reference features, shared cross-target invariants, cited analysis artifacts, tempo/rhythm plan, register/scale plan, scene voices, synthesis owners, invented instrument roles, expected metric movement on both targets, known risks.
|
||||
- Write exactly one `.aqua` file under each target patch output directory named `<agent-id>__<family>__<hypothesis>.aqua`.
|
||||
- Include at least three scene voices or layers in the patch, such as primary alien-voice/body, rhythmic transient/noise, and background/recording color.
|
||||
- Background/recording-color layers must use `texture` or an equivalently gated and band-shaped explicit voice; a static full-duration white-noise bed is failed evidence.
|
||||
- Make the patch duration/gates cover the $DurationSeconds second target.
|
||||
- Make each patch duration/gates cover its matching $DurationSeconds second target.
|
||||
|
||||
Return a short final summary naming the patch and report.
|
||||
"@
|
||||
|
|
@ -337,35 +368,39 @@ Return a short final summary naming the patch and report.
|
|||
}
|
||||
|
||||
Wait-CodexJobs -Jobs $jobs
|
||||
Assert-SongCandidates -PatchDirectory $patchRoot -ExpectedCount $AgentsPerPass
|
||||
Assert-SongCandidates -PatchDirectory $patchRoot -ExpectedCount ($AgentsPerPass * $SongsPerAgent)
|
||||
|
||||
for ($agent = 1; $agent -le $AgentsPerPass; $agent++) {
|
||||
$agentId = "agent-{0:00}" -f $agent
|
||||
$agentDir = Join-Path $passDir $agentId
|
||||
Invoke-LoggedProcess `
|
||||
-FilePath "dotnet" `
|
||||
-ArgumentList @(
|
||||
"run",
|
||||
"--project",
|
||||
$workerProject,
|
||||
"--",
|
||||
"song-score",
|
||||
"--patch-root",
|
||||
(Join-Path $patchRoot $agentId),
|
||||
"--challenge",
|
||||
(Join-Path $agentDir "challenge/challenge.json"),
|
||||
"--artifact-root",
|
||||
$agentDir,
|
||||
"--batch-id",
|
||||
"$passId-$agentId",
|
||||
"--store",
|
||||
$storePath,
|
||||
"--hypothesizer",
|
||||
"external-codex-song-swarm-$passId-$agentId"
|
||||
) `
|
||||
-LogPath (Join-Path $logRoot "$passId-$agentId-score-candidate.log")
|
||||
for ($song = 1; $song -le $SongsPerAgent; $song++) {
|
||||
$songId = "target-{0:00}" -f $song
|
||||
Invoke-LoggedProcess `
|
||||
-FilePath "dotnet" `
|
||||
-ArgumentList @(
|
||||
"run",
|
||||
"--project",
|
||||
$workerProject,
|
||||
"--",
|
||||
"song-score",
|
||||
"--patch-root",
|
||||
(Join-Path (Join-Path $patchRoot $agentId) $songId),
|
||||
"--challenge",
|
||||
(Join-Path $agentDir "$songId/challenge.json"),
|
||||
"--artifact-root",
|
||||
(Join-Path $agentDir $songId),
|
||||
"--batch-id",
|
||||
"$passId-$agentId-$songId",
|
||||
"--store",
|
||||
$storePath,
|
||||
"--hypothesizer",
|
||||
"external-codex-song-swarm-$passId-$agentId-$songId"
|
||||
) `
|
||||
-LogPath (Join-Path $logRoot "$passId-$agentId-$songId-score-candidate.log")
|
||||
}
|
||||
}
|
||||
|
||||
$distilledStore = Join-Path $passDir "song-trial-results.distilled.cc"
|
||||
Invoke-LoggedProcess `
|
||||
-FilePath "dotnet" `
|
||||
-ArgumentList @(
|
||||
|
|
@ -403,6 +438,28 @@ Return a short final summary naming the patch and report.
|
|||
"true"
|
||||
) `
|
||||
-LogPath (Join-Path $logRoot "$passId-search-after.log")
|
||||
|
||||
Invoke-LoggedProcess `
|
||||
-FilePath "dotnet" `
|
||||
-ArgumentList @(
|
||||
"run",
|
||||
"--project",
|
||||
$workerProject,
|
||||
"--",
|
||||
"distill",
|
||||
"--store",
|
||||
$storePath,
|
||||
"--output-store",
|
||||
$distilledStore,
|
||||
"--output",
|
||||
(Join-Path $passDir "distillation-report.md"),
|
||||
"--min-cosine",
|
||||
"0.35",
|
||||
"--max-results",
|
||||
"40"
|
||||
) `
|
||||
-LogPath (Join-Path $logRoot "$passId-distill.log")
|
||||
$latestDistilledStore = $distilledStore
|
||||
}
|
||||
|
||||
Write-Host "Song snippet trial swarm complete: $loopRoot"
|
||||
|
|
|
|||
Loading…
Reference in a new issue