This commit is contained in:
2026-08-22 00:41:17 +02:00
parent d06a93f759
commit 9250528442
18 changed files with 48210 additions and 53 deletions
+53 -1
View File
@@ -259,16 +259,34 @@ namespace AiV2.Tests
// 无 JSON → Warning
var noJson = AIAnalysisValidation.ValidateMachineReadableClaims("纯文本回复");
Program.Assert(noJson.Issues.Any(i => i.Kind == AIValidationIssueKind.MissingMachineReadableClaims), "无声明产生 Warning");
Program.Assert(noJson.RequiresRevision, "无声明现在会触发一次修订");
// 高置信无证据 → MissingEvidence
var highNoEvidence = AIAnalysisValidation.ValidateMachineReadableClaims(
"[机器可读声明]\n```json\n{\"unitClaims\":[{\"unitId\":1,\"claim\":\"AlliedMCV\",\"evidenceLevel\":\"confirmed\",\"evidence\":[]}]}\n```");
Program.Assert(highNoEvidence.Issues.Any(i => i.Kind == AIValidationIssueKind.MissingEvidence), "高置信无证据");
Program.Assert(highNoEvidence.RequiresRevision, "高置信无证据会触发修订");
// 低置信无备选/待确认 → MissingAlternative
var lowNoAlt = AIAnalysisValidation.ValidateMachineReadableClaims(
"[机器可读声明]\n```json\n{\"unitClaims\":[{\"unitId\":1,\"claim\":\"AlliedMCV\",\"evidenceLevel\":\"possible\",\"evidence\":[\"select|1:00|1\"]}]}\n```");
Program.Assert(lowNoAlt.Issues.Any(i => i.Kind == AIValidationIssueKind.MissingAlternative), "低置信无备选");
Program.Assert(lowNoAlt.RequiresRevision, "低置信无备选会触发修订");
// 仅 Info(例如声明数量超限)不触发修订
var infoOnlyJson = "[机器可读声明]\n```json\n{\"eventClaims\":["
+ string.Join(",", Enumerable.Range(1, 6).Select(i =>
$"{{\"claim\":\"事件{i}\",\"evidenceLevel\":\"confirmed\",\"evidence\":[]}}"))
+ "]}\n```";
var infoOnly = AIAnalysisValidation.ValidateMachineReadableClaims(infoOnlyJson);
Program.Assert(!infoOnly.RequiresRevision, "仅 Info 不触发修订");
// 全部 JSON 块都不可解析 → Fatal
var allInvalid = AIAnalysisValidation.ValidateMachineReadableClaims(
"[机器可读声明]\n```json\n{\"bad\": }\n```");
Program.Assert(allInvalid.Issues.Any(i => i.Severity == AIValidationSeverity.Fatal),
"全无效 JSON 产生 Fatal");
Program.Assert(allInvalid.RequiresRevision, "Fatal 触发修订");
}
}
@@ -311,6 +329,14 @@ namespace AiV2.Tests
Program.Assert(!warn.Block && !warn.IsOk, "超过预算 → 警告");
var ok = AiContextBudget.CheckPromptUsage(20_000, provider, new AiModel { ContextLength = 1_000_000 });
Program.Assert(ok.IsOk, "预算内 → 通过");
// 含输出余量的请求检查:输入 170K + 余量 32K 超过 200K 模型的 90% 硬上限
var blockTotal = AiContextBudget.CheckRequestUsage(170_000, provider, new AiModel { ContextLength = 200_000 });
Program.Assert(blockTotal.Block, "输入+输出余量超过硬上限 → 拒绝");
var warnTotal = AiContextBudget.CheckRequestUsage(160_000, provider, new AiModel { ContextLength = 1_000_000 });
Program.Assert(!warnTotal.Block && !warnTotal.IsOk, "输入+输出余量超过预算 → 警告");
var okTotal = AiContextBudget.CheckRequestUsage(20_000, provider, new AiModel { ContextLength = 1_000_000 });
Program.Assert(okTotal.IsOk, "输入+输出余量在预算内 → 通过");
}
}
@@ -347,6 +373,8 @@ namespace AiV2.Tests
Program.Assert(digest.Contains("建造者"), "建造者段");
Program.Assert(digest.Contains("第1段"), "分段元数据");
Program.Assert(digest.Contains("开始建造建筑"), "关键事件采样");
Program.Assert(digest.Contains("# 协议选择"), "摘要包含协议选择");
Program.Assert(digest.Contains("# 所有权证据(节选)"), "摘要包含所有权证据");
}
}
@@ -528,6 +556,23 @@ namespace AiV2.Tests
&& index.UnitIdFirstObservedTime.ContainsKey(389)
&& index.UnitIdFirstObservedTime.ContainsKey(587)
&& index.UnitIdFirstObservedTime.ContainsKey(322), "首次出现记录");
// 编队按玩家隔离 + 0x1F6/0x22A 选择类命令
var secondTimeline = ImmutableArray.Create(
(TimeSpan.FromSeconds(1), ImmutableArray.Create(
MakeChunk(0x1FA, 4, Int(3), Obj(401)))),
(TimeSpan.FromSeconds(2), ImmutableArray.Create(
MakeChunk(0x1FB, 5, Int(3)))),
(TimeSpan.FromSeconds(3), ImmutableArray.Create(
MakeChunk(0x1F6, 4, Obj(777)),
MakeChunk(0x22A, 4, Obj(888)))));
var secondIndex = ReplayFactIndex.Build(secondTimeline, new Dictionary<uint, string>());
Program.Assert(secondIndex.PlayerStrongOwnershipUnitIds.TryGetValue(4, out var p4Strong)
&& p4Strong.Contains(401), "P4 创建编队 → 强证据");
Program.Assert(!secondIndex.PlayerStrongOwnershipUnitIds.TryGetValue(5, out _)
|| !secondIndex.PlayerStrongOwnershipUnitIds[5].Contains(401), "P5 选择同号编队不应继承 P4 成员");
Program.Assert(secondIndex.PlayerWeakOwnershipUnitIds.TryGetValue(4, out var p4Weak)
&& p4Weak.Contains(777) && p4Weak.Contains(888), "0x1F6/0x22A → 弱所有权");
}
}
@@ -543,6 +588,10 @@ namespace AiV2.Tests
Program.Assert(knowledge.GetEntity("AlliedMCV") is { } mcv
&& mcv.HasTag(KnowledgeTag.Builder)
&& mcv.HasTag(KnowledgeTag.Unpack), "AlliedMCV 标签");
Program.Assert(knowledge.GetEntity("AlliedAntiInfantryVehicle_Ground") is { } aliasEntity
&& aliasEntity.AssetName == "AlliedAntiInfantryVehicle", "别名可解析到同一实体");
Program.Assert(knowledge.GetEntity("AlliedAntiInfantryVehicle") is { } acv
&& acv.ProducedBy.Contains("AlliedWarFactory"), "alsoProducedBy 合并进 ProducedBy");
// 不存在的 mod → null(无结构化数据时不剥离 flat 文本)
Program.Assert(StructuredKnowledge.GetForMod("corona") is null, "corona 无结构化文件 → null");
@@ -601,9 +650,12 @@ namespace AiV2.Tests
Program.Assert(overview.Contains("[分段概述]"), "总览轮要求 [分段概述] 块");
Program.Assert(overview.Contains("不要修改"), "总览轮不修改边界");
var segment = AIAnalyze.BuildSegmentUserPromptV2(0, 1, slice, 500, "开局");
var segment = AIAnalyze.BuildSegmentUserPromptV2(
0, 1, slice, 500, "开局", "前期平稳发育", new[] { "1:20~1:45" });
Program.Assert(segment.Contains("第1/1段"), "段指令编号");
Program.Assert(segment.Contains("开局"), "段标题");
Program.Assert(segment.Contains("前期平稳发育"), "段概述");
Program.Assert(segment.Contains("1:20~1:45"), "总览回查提示");
Program.Assert(segment.Contains("[回查]"), "回查说明");
Program.Assert(segment.Contains("[小结]"), "小结要求");
Program.Assert(segment.Contains("[机器可读声明]"), "机器可读声明要求");