reasoning: preserve evidence versus clarification distinction
This commit is contained in:
@@ -123,6 +123,49 @@ function buildEvidenceFallbackQuestion(meaning) {
|
||||
return `What evidence would confirm or rule out ${stripTrailingPunctuation(meaning)}?`;
|
||||
}
|
||||
|
||||
function extractConstraintClarificationSubject(node) {
|
||||
const label = stripTrailingPunctuation(node?.label || "");
|
||||
const description = String(node?.description || "");
|
||||
const combined = `${label} ${description}`;
|
||||
|
||||
const labelMatch = label.match(
|
||||
/^Whether\s+(.+)\s+is\s+a\s+hard constraint$/i,
|
||||
);
|
||||
if (labelMatch?.[1]) {
|
||||
return labelMatch[1].trim();
|
||||
}
|
||||
|
||||
const descriptionMatch = combined.match(
|
||||
/whether\s+(.+?)\s+is\s+a\s+hard constraint\s+or\s+a\s+preference(?:\/|-|\s)trade(?:\/|-|\s)?off/i,
|
||||
);
|
||||
if (descriptionMatch?.[1]) {
|
||||
return descriptionMatch[1].trim();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function buildUserMeaningClarificationQuestion(node) {
|
||||
const subject = extractConstraintClarificationSubject(node);
|
||||
|
||||
if (subject) {
|
||||
return `Is ${subject} a hard constraint or a preference/trade-off?`;
|
||||
}
|
||||
|
||||
return buildNeutralClarificationQuestion(extractMeaning(node));
|
||||
}
|
||||
|
||||
function isUserOwnedMeaningBoundaryUnknown(node) {
|
||||
const text = normaliseText(`${node?.label || ""} ${node?.description || ""}`);
|
||||
return (
|
||||
text.includes("hard constraint") &&
|
||||
(text.includes("preference trade off") ||
|
||||
text.includes("preference/trade-off") ||
|
||||
text.includes("preference or trade off") ||
|
||||
text.includes("preference or trade-off"))
|
||||
);
|
||||
}
|
||||
|
||||
function collectObservationNodes(graph) {
|
||||
return (graph?.nodes || []).filter(
|
||||
(node) => node.kind === "observation" && node.status === "supported",
|
||||
@@ -1726,6 +1769,34 @@ export function formulateQuestion({ node, graph, context = {} }) {
|
||||
const rejectedQuestionFamilies = rejectedQuestionFamiliesForPattern(
|
||||
reasoningPatternSelection.pattern,
|
||||
);
|
||||
|
||||
if (isUserOwnedMeaningBoundaryUnknown(node)) {
|
||||
const question = sanitizeQuestionText(
|
||||
buildUserMeaningClarificationQuestion(node),
|
||||
);
|
||||
const questionComplexity = assessQuestionComplexity({
|
||||
question,
|
||||
selectedUnknown: node,
|
||||
graph,
|
||||
});
|
||||
|
||||
return {
|
||||
question,
|
||||
reason:
|
||||
"Formulated as a user-clarification question because this unresolved distinction depends on the user's own meaning rather than external evidence.",
|
||||
strategy: null,
|
||||
investigationStrategy: null,
|
||||
reasoningPattern: reasoningPatternSelection.pattern,
|
||||
reasoningPatternReason: reasoningPatternSelection.reason,
|
||||
questionFamily: "prioritisation",
|
||||
allowedQuestionFamilies,
|
||||
rejectedQuestionFamilies,
|
||||
selectedQuestionTemplate: "user_meaning_clarification",
|
||||
questionComplexity,
|
||||
plainLanguageNormalisations: [],
|
||||
};
|
||||
}
|
||||
|
||||
const foundationalDirectQuestion = buildFoundationalDirectQuestion(node);
|
||||
if (
|
||||
foundationalDirectQuestion &&
|
||||
@@ -1794,7 +1865,7 @@ export function formulateQuestion({ node, graph, context = {} }) {
|
||||
)
|
||||
) {
|
||||
question = sanitizeQuestionText(
|
||||
investigationStrategy &&
|
||||
(investigationStrategy &&
|
||||
isClaimLikeUnknown(
|
||||
node,
|
||||
normaliseText(
|
||||
@@ -1807,7 +1878,8 @@ export function formulateQuestion({ node, graph, context = {} }) {
|
||||
.filter(Boolean)
|
||||
.join(" "),
|
||||
),
|
||||
)
|
||||
)) ||
|
||||
reasoningPatternSelection.pattern === "diagnosis"
|
||||
? buildEvidenceFallbackQuestion(fallbackMeaning)
|
||||
: buildNeutralClarificationQuestion(fallbackMeaning),
|
||||
);
|
||||
|
||||
@@ -280,12 +280,12 @@ describe("formulateQuestion", () => {
|
||||
expect(result.pattern).toBe("comparison");
|
||||
});
|
||||
|
||||
it("constraint unknown uses evidence-gathering within the fixed strategy set", () => {
|
||||
it("user-owned constraint ambiguity produces a clarification question rather than an evidence request", () => {
|
||||
const unknown = makeNode({
|
||||
id: "n-constraint",
|
||||
label: "Budget constraint",
|
||||
label: "Whether avoiding additional risk is a hard constraint",
|
||||
description:
|
||||
"Need the main budget constraint because it limits the available options.",
|
||||
"Need to know whether avoiding additional risk is a hard constraint or a preference/trade-off.",
|
||||
kind: "unknown",
|
||||
status: "unknown",
|
||||
confidence: "medium",
|
||||
@@ -296,8 +296,33 @@ describe("formulateQuestion", () => {
|
||||
graph: makeGraphFor(unknown),
|
||||
});
|
||||
|
||||
expect(result.strategy).toBe("evidence_gathering");
|
||||
expect(result.strategy).toBeNull();
|
||||
expect(result.question).toBe(
|
||||
"Is avoiding additional risk a hard constraint or a preference/trade-off?",
|
||||
);
|
||||
});
|
||||
|
||||
it("evidence-resolvable competing-cause unknown stays on an evidence route rather than neutral clarification", () => {
|
||||
const unknown = makeNode({
|
||||
id: "n-delivery-cause",
|
||||
label: "Possible causes of the delivery delay",
|
||||
description:
|
||||
"Need to determine whether staff capacity or supplier lead times are responsible for the delivery delay.",
|
||||
kind: "unknown",
|
||||
status: "unknown",
|
||||
confidence: "medium",
|
||||
});
|
||||
|
||||
const result = formulateQuestion({
|
||||
node: unknown,
|
||||
graph: makeGraphFor(unknown, {
|
||||
centralStatement: "Delivery is delayed and the cause is still unknown.",
|
||||
}),
|
||||
});
|
||||
|
||||
expect(result.reasoningPattern).toBe("diagnosis");
|
||||
expect(result.question).toContain("What evidence");
|
||||
expect(result.question).not.toContain("What would clarify");
|
||||
});
|
||||
|
||||
it("the same unknown can produce different questions when paired with different strategies", () => {
|
||||
|
||||
Reference in New Issue
Block a user