feat(polish): add question guard, ABE routing, and flow trace
Harden polish so question drafts stay questions, add local density routing with style-specific degrade, expand fun style packs, and add end-to-end FlowTrace logging plus offline guard eval scripts.
This commit is contained in:
@@ -126,6 +126,87 @@ final class PolishRouterTests: XCTestCase {
|
||||
XCTAssertTrue(prompt.contains("本次模式:保守清理"))
|
||||
}
|
||||
|
||||
func testQuestionDraftIsDetectedAcrossStyles() {
|
||||
for id in ["builtin.xhs", "builtin.dating", "builtin.flex", "builtin.corp", "builtin.chat"] {
|
||||
let decision = PolishRouter.decide(
|
||||
text: "你觉得这个包怎么样",
|
||||
styleID: id,
|
||||
intensity: .heavy
|
||||
)
|
||||
XCTAssertTrue(decision.preservesQuestion, id)
|
||||
XCTAssertTrue(decision.reasons.contains("Q:keep_question"), id)
|
||||
}
|
||||
}
|
||||
|
||||
/// DiBa quotes the other party, so the user's reply may answer that question.
|
||||
func testDibaOpponentQuoteDoesNotTriggerQuestionGuard() {
|
||||
let decision = PolishRouter.decide(
|
||||
text: "回他别老说大家都觉得你点名是谁",
|
||||
styleID: "builtin.diba",
|
||||
intensity: .heavy
|
||||
)
|
||||
XCTAssertFalse(decision.preservesQuestion)
|
||||
}
|
||||
|
||||
func testStatementDraftDoesNotTriggerQuestionGuard() {
|
||||
let decision = PolishRouter.decide(
|
||||
text: "这款防晒霜我用了不油夏天可以推荐",
|
||||
styleID: "builtin.xhs",
|
||||
intensity: .heavy
|
||||
)
|
||||
XCTAssertFalse(decision.preservesQuestion)
|
||||
}
|
||||
|
||||
func testPromptBlockAlwaysCarriesNeverAnswerBoundary() {
|
||||
for id in ["builtin.light", "builtin.structured", "builtin.formal",
|
||||
"builtin.chat", "builtin.dating", "builtin.flex",
|
||||
"builtin.corp", "builtin.diba", "builtin.xhs"] {
|
||||
let block = PolishRouter.promptBlock(
|
||||
mode: .full,
|
||||
styleID: id,
|
||||
useChineseGuidance: true
|
||||
)
|
||||
XCTAssertTrue(block.contains("绝对边界:只润色,不作答"), id)
|
||||
}
|
||||
}
|
||||
|
||||
func testPromptBlockAddsQuestionGuardWhenAsking() {
|
||||
let guarded = PolishRouter.promptBlock(
|
||||
mode: .full,
|
||||
styleID: "builtin.dating",
|
||||
useChineseGuidance: true,
|
||||
preservesQuestion: true
|
||||
)
|
||||
XCTAssertTrue(guarded.contains("问句守卫"))
|
||||
XCTAssertTrue(guarded.contains("同一个人提出的同一个问句"))
|
||||
|
||||
let unguarded = PolishRouter.promptBlock(
|
||||
mode: .full,
|
||||
styleID: "builtin.dating",
|
||||
useChineseGuidance: true
|
||||
)
|
||||
XCTAssertFalse(unguarded.contains("问句守卫"))
|
||||
}
|
||||
|
||||
func testComposerCarriesQuestionGuardIntoPrompt() {
|
||||
let style = PolishStylePackCatalog.resolve(
|
||||
id: "builtin.dating",
|
||||
userCatalog: .empty
|
||||
)
|
||||
let prompt = PolishPromptComposer.compose(
|
||||
text: "你觉得这个包怎么样",
|
||||
style: style,
|
||||
context: PolishContext(intensity: .heavy),
|
||||
dictionaryBlock: "",
|
||||
globalContract: "GLOBAL",
|
||||
useChineseGuidance: true,
|
||||
routingMode: .full,
|
||||
preservesQuestion: true
|
||||
)
|
||||
XCTAssertTrue(prompt.contains("问句守卫"))
|
||||
XCTAssertTrue(prompt.contains("绝对边界:只润色,不作答"))
|
||||
}
|
||||
|
||||
func testIsInformationSparseDetectsHollowShorts() {
|
||||
XCTAssertTrue(PolishRouter.isInformationSparse("香香的"))
|
||||
XCTAssertTrue(PolishRouter.isInformationSparse("这个还行吧"))
|
||||
|
||||
@@ -240,6 +240,22 @@ final class PolishStylePackTests: XCTestCase {
|
||||
XCTAssertFalse(xhsHeavy.contains("Style override"))
|
||||
}
|
||||
|
||||
func testXHSStyleForbidsInventedAudience() {
|
||||
let pack = PolishStylePackCatalog.resolve(id: "builtin.xhs", userCatalog: .empty)
|
||||
XCTAssertTrue(pack.prompt.contains("不主动新增受众称呼"))
|
||||
XCTAssertTrue(pack.prompt.contains("禁止凭空新增受众或称呼"))
|
||||
XCTAssertTrue(pack.prompt.contains("禁止立场翻转"))
|
||||
XCTAssertTrue(pack.prompt.contains("原文没有受众"))
|
||||
|
||||
for level in [PolishIntensity.light, .medium, .heavy] {
|
||||
let guideline = level.promptGuideline(styleID: "builtin.xhs")
|
||||
XCTAssertTrue(
|
||||
guideline.lowercased().contains("audience"),
|
||||
"\(level) must forbid inventing an audience"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func testHeavyIntensityStillAllowsStructuredStyle() {
|
||||
let guideline = PolishIntensity.heavy.promptGuideline(styleID: "builtin.structured")
|
||||
|
||||
@@ -260,6 +276,59 @@ final class PolishStylePackTests: XCTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
func testEveryBuiltinHasForbiddenItemsChapter() {
|
||||
for pack in PolishStylePackCatalog.builtins {
|
||||
XCTAssertTrue(
|
||||
pack.prompt.contains("# 禁止事项"),
|
||||
pack.id
|
||||
)
|
||||
XCTAssertTrue(
|
||||
pack.prompt.contains("接话") || pack.prompt.contains("代答") || pack.prompt.contains("不作答"),
|
||||
"\(pack.id) should forbid interlocutor replies"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func testFunForbiddenItemsKeepQuestionDrafts() {
|
||||
let cases: [(String, String)] = [
|
||||
("builtin.dating", "你觉得这个包怎么样"),
|
||||
("builtin.flex", "你觉得这个包怎么样"),
|
||||
("builtin.corp", "你觉得这个方案怎么样"),
|
||||
("builtin.xhs", "你觉得这个包怎么样"),
|
||||
("builtin.chat", "你觉得这个包怎么样"),
|
||||
]
|
||||
for (id, marker) in cases {
|
||||
let pack = PolishStylePackCatalog.resolve(id: id, userCatalog: .empty)
|
||||
XCTAssertTrue(pack.prompt.contains("# 禁止事项"), id)
|
||||
XCTAssertTrue(pack.prompt.contains(marker), id)
|
||||
XCTAssertTrue(pack.prompt.contains("✘→"), id)
|
||||
}
|
||||
}
|
||||
|
||||
func testEveryBuiltinForbidsAnsweringTheTranscript() {
|
||||
for pack in PolishStylePackCatalog.builtins {
|
||||
XCTAssertTrue(
|
||||
pack.prompt.contains("绝对边界"),
|
||||
pack.id
|
||||
)
|
||||
XCTAssertTrue(
|
||||
pack.prompt.contains("不作答"),
|
||||
pack.id
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func testFunStylesKeepQuestionDraftsAsQuestions() {
|
||||
for id in ["builtin.dating", "builtin.flex", "builtin.corp", "builtin.xhs"] {
|
||||
let pack = PolishStylePackCatalog.resolve(id: id, userCatalog: .empty)
|
||||
XCTAssertTrue(
|
||||
pack.prompt.contains("问句")
|
||||
|| pack.prompt.contains("仍然是同一个人提出的同一个问句"),
|
||||
id
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func testStructuredStyleEncodesActiveItemizationHardRules() {
|
||||
let pack = PolishStylePackCatalog.resolve(id: "builtin.structured", userCatalog: .empty)
|
||||
XCTAssertTrue(pack.prompt.contains("自动结构化(偏积极)"))
|
||||
|
||||
Reference in New Issue
Block a user