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)}?`;
|
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) {
|
function collectObservationNodes(graph) {
|
||||||
return (graph?.nodes || []).filter(
|
return (graph?.nodes || []).filter(
|
||||||
(node) => node.kind === "observation" && node.status === "supported",
|
(node) => node.kind === "observation" && node.status === "supported",
|
||||||
@@ -1726,6 +1769,34 @@ export function formulateQuestion({ node, graph, context = {} }) {
|
|||||||
const rejectedQuestionFamilies = rejectedQuestionFamiliesForPattern(
|
const rejectedQuestionFamilies = rejectedQuestionFamiliesForPattern(
|
||||||
reasoningPatternSelection.pattern,
|
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);
|
const foundationalDirectQuestion = buildFoundationalDirectQuestion(node);
|
||||||
if (
|
if (
|
||||||
foundationalDirectQuestion &&
|
foundationalDirectQuestion &&
|
||||||
@@ -1794,7 +1865,7 @@ export function formulateQuestion({ node, graph, context = {} }) {
|
|||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
question = sanitizeQuestionText(
|
question = sanitizeQuestionText(
|
||||||
investigationStrategy &&
|
(investigationStrategy &&
|
||||||
isClaimLikeUnknown(
|
isClaimLikeUnknown(
|
||||||
node,
|
node,
|
||||||
normaliseText(
|
normaliseText(
|
||||||
@@ -1807,7 +1878,8 @@ export function formulateQuestion({ node, graph, context = {} }) {
|
|||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(" "),
|
.join(" "),
|
||||||
),
|
),
|
||||||
)
|
)) ||
|
||||||
|
reasoningPatternSelection.pattern === "diagnosis"
|
||||||
? buildEvidenceFallbackQuestion(fallbackMeaning)
|
? buildEvidenceFallbackQuestion(fallbackMeaning)
|
||||||
: buildNeutralClarificationQuestion(fallbackMeaning),
|
: buildNeutralClarificationQuestion(fallbackMeaning),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -280,12 +280,12 @@ describe("formulateQuestion", () => {
|
|||||||
expect(result.pattern).toBe("comparison");
|
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({
|
const unknown = makeNode({
|
||||||
id: "n-constraint",
|
id: "n-constraint",
|
||||||
label: "Budget constraint",
|
label: "Whether avoiding additional risk is a hard constraint",
|
||||||
description:
|
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",
|
kind: "unknown",
|
||||||
status: "unknown",
|
status: "unknown",
|
||||||
confidence: "medium",
|
confidence: "medium",
|
||||||
@@ -296,8 +296,33 @@ describe("formulateQuestion", () => {
|
|||||||
graph: makeGraphFor(unknown),
|
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).toContain("What evidence");
|
||||||
|
expect(result.question).not.toContain("What would clarify");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("the same unknown can produce different questions when paired with different strategies", () => {
|
it("the same unknown can produce different questions when paired with different strategies", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user