feat: introduce reasoning pattern selection
This commit is contained in:
@@ -1766,6 +1766,15 @@ export function determineGraphBackedQuestion({ situationGraph }) {
|
||||
reason: formulatedQuestion.reason,
|
||||
strategy: formulatedQuestion.strategy,
|
||||
investigationStrategy: formulatedQuestion.investigationStrategy,
|
||||
reasoningPattern: formulatedQuestion.reasoningPattern,
|
||||
reasoningPatternReason: formulatedQuestion.reasoningPatternReason,
|
||||
questionFamily: formulatedQuestion.questionFamily,
|
||||
allowedQuestionFamilies:
|
||||
formulatedQuestion.allowedQuestionFamilies,
|
||||
rejectedQuestionFamilies:
|
||||
formulatedQuestion.rejectedQuestionFamilies,
|
||||
selectedQuestionTemplate:
|
||||
formulatedQuestion.selectedQuestionTemplate,
|
||||
questionComplexity: formulatedQuestion.questionComplexity,
|
||||
plainLanguageNormalisations:
|
||||
formulatedQuestion.plainLanguageNormalisations,
|
||||
@@ -2469,6 +2478,15 @@ export function applyValidatedProposal({
|
||||
reason: formulatedQuestion?.reason || deterministicSelection.reason,
|
||||
strategy: formulatedQuestion?.strategy,
|
||||
investigationStrategy: formulatedQuestion?.investigationStrategy,
|
||||
reasoningPattern: formulatedQuestion?.reasoningPattern,
|
||||
reasoningPatternReason: formulatedQuestion?.reasoningPatternReason,
|
||||
questionFamily: formulatedQuestion?.questionFamily,
|
||||
allowedQuestionFamilies:
|
||||
formulatedQuestion?.allowedQuestionFamilies,
|
||||
rejectedQuestionFamilies:
|
||||
formulatedQuestion?.rejectedQuestionFamilies,
|
||||
selectedQuestionTemplate:
|
||||
formulatedQuestion?.selectedQuestionTemplate,
|
||||
questionComplexity,
|
||||
plainLanguageNormalisations,
|
||||
}
|
||||
|
||||
@@ -59,6 +59,12 @@ function buildDiagnostics({
|
||||
decompositionReason,
|
||||
selectedContainerUnknown,
|
||||
selectedChildUnknown,
|
||||
reasoningPattern,
|
||||
questionFamily,
|
||||
allowedQuestionFamilies,
|
||||
rejectedQuestionFamilies,
|
||||
selectedQuestionTemplate,
|
||||
reasoningPatternReason,
|
||||
}) {
|
||||
return {
|
||||
promptVersion: analysis?.promptVersion ?? null,
|
||||
@@ -88,6 +94,12 @@ function buildDiagnostics({
|
||||
decompositionReason: decompositionReason ?? null,
|
||||
selectedContainerUnknown: selectedContainerUnknown ?? null,
|
||||
selectedChildUnknown: selectedChildUnknown ?? null,
|
||||
reasoningPattern: reasoningPattern ?? null,
|
||||
questionFamily: questionFamily ?? null,
|
||||
allowedQuestionFamilies: allowedQuestionFamilies ?? [],
|
||||
rejectedQuestionFamilies: rejectedQuestionFamilies ?? [],
|
||||
selectedQuestionTemplate: selectedQuestionTemplate ?? null,
|
||||
reasoningPatternReason: reasoningPatternReason ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -177,6 +189,12 @@ function buildUpdateDiagnostics({
|
||||
selectedUnknownBefore,
|
||||
selectedUnknownAfter,
|
||||
plainLanguageNormalisations,
|
||||
reasoningPattern,
|
||||
questionFamily,
|
||||
allowedQuestionFamilies,
|
||||
rejectedQuestionFamilies,
|
||||
selectedQuestionTemplate,
|
||||
reasoningPatternReason,
|
||||
}) {
|
||||
return {
|
||||
promptVersion: promptVersion ?? "v0.4",
|
||||
@@ -257,6 +275,12 @@ function buildUpdateDiagnostics({
|
||||
selectedUnknownBefore: selectedUnknownBefore ?? null,
|
||||
selectedUnknownAfter: selectedUnknownAfter ?? null,
|
||||
plainLanguageNormalisations: plainLanguageNormalisations ?? [],
|
||||
reasoningPattern: reasoningPattern ?? null,
|
||||
questionFamily: questionFamily ?? null,
|
||||
allowedQuestionFamilies: allowedQuestionFamilies ?? [],
|
||||
rejectedQuestionFamilies: rejectedQuestionFamilies ?? [],
|
||||
selectedQuestionTemplate: selectedQuestionTemplate ?? null,
|
||||
reasoningPatternReason: reasoningPatternReason ?? null,
|
||||
unknownSelectionExplanation: unknownSelectionExplanation ?? null,
|
||||
};
|
||||
}
|
||||
@@ -370,6 +394,21 @@ export async function startCase(body) {
|
||||
initialQuestionResult.selectedContainerUnknown ?? null,
|
||||
selectedChildUnknown:
|
||||
initialQuestionResult.selectedChildUnknown ?? null,
|
||||
reasoningPattern:
|
||||
initialQuestionResult.selectedQuestion?.reasoningPattern ?? null,
|
||||
questionFamily:
|
||||
initialQuestionResult.selectedQuestion?.questionFamily ?? null,
|
||||
allowedQuestionFamilies:
|
||||
initialQuestionResult.selectedQuestion?.allowedQuestionFamilies ?? [],
|
||||
rejectedQuestionFamilies:
|
||||
initialQuestionResult.selectedQuestion?.rejectedQuestionFamilies ??
|
||||
[],
|
||||
selectedQuestionTemplate:
|
||||
initialQuestionResult.selectedQuestion?.selectedQuestionTemplate ??
|
||||
null,
|
||||
reasoningPatternReason:
|
||||
initialQuestionResult.selectedQuestion?.reasoningPatternReason ??
|
||||
null,
|
||||
}),
|
||||
validationErrors: graphReferenceValidation.errors,
|
||||
statusCode: 500,
|
||||
@@ -413,6 +452,19 @@ export async function startCase(body) {
|
||||
selectedContainerUnknown:
|
||||
initialQuestionResult.selectedContainerUnknown ?? null,
|
||||
selectedChildUnknown: initialQuestionResult.selectedChildUnknown ?? null,
|
||||
reasoningPattern:
|
||||
initialQuestionResult.selectedQuestion?.reasoningPattern ?? null,
|
||||
questionFamily:
|
||||
initialQuestionResult.selectedQuestion?.questionFamily ?? null,
|
||||
allowedQuestionFamilies:
|
||||
initialQuestionResult.selectedQuestion?.allowedQuestionFamilies ?? [],
|
||||
rejectedQuestionFamilies:
|
||||
initialQuestionResult.selectedQuestion?.rejectedQuestionFamilies ?? [],
|
||||
selectedQuestionTemplate:
|
||||
initialQuestionResult.selectedQuestion?.selectedQuestionTemplate ??
|
||||
null,
|
||||
reasoningPatternReason:
|
||||
initialQuestionResult.selectedQuestion?.reasoningPatternReason ?? null,
|
||||
}),
|
||||
};
|
||||
}
|
||||
@@ -709,6 +761,18 @@ async function updateCaseWithDependencies(body, dependencies = {}) {
|
||||
selectedUnknownAfter: applicationResult.selectedUnknownAfter,
|
||||
plainLanguageNormalisations:
|
||||
applicationResult.plainLanguageNormalisations,
|
||||
reasoningPattern:
|
||||
applicationResult.selectedQuestion?.reasoningPattern ?? null,
|
||||
questionFamily:
|
||||
applicationResult.selectedQuestion?.questionFamily ?? null,
|
||||
allowedQuestionFamilies:
|
||||
applicationResult.selectedQuestion?.allowedQuestionFamilies ?? [],
|
||||
rejectedQuestionFamilies:
|
||||
applicationResult.selectedQuestion?.rejectedQuestionFamilies ?? [],
|
||||
selectedQuestionTemplate:
|
||||
applicationResult.selectedQuestion?.selectedQuestionTemplate ?? null,
|
||||
reasoningPatternReason:
|
||||
applicationResult.selectedQuestion?.reasoningPatternReason ?? null,
|
||||
unknownSelectionExplanation: buildUnknownSelectionDiagnostics(
|
||||
applicationResult.updatedSituationGraph,
|
||||
applicationResult.updatedSituationGraph.resolvedNodeIds || [],
|
||||
@@ -787,6 +851,12 @@ async function updateCaseWithDependencies(body, dependencies = {}) {
|
||||
selectedUnknownBefore: null,
|
||||
selectedUnknownAfter: null,
|
||||
plainLanguageNormalisations: [],
|
||||
reasoningPattern: null,
|
||||
questionFamily: null,
|
||||
allowedQuestionFamilies: [],
|
||||
rejectedQuestionFamilies: [],
|
||||
selectedQuestionTemplate: null,
|
||||
reasoningPatternReason: null,
|
||||
unknownSelectionExplanation: buildUnknownSelectionDiagnostics(
|
||||
situationGraph,
|
||||
situationGraph.resolvedNodeIds || [],
|
||||
|
||||
@@ -527,6 +527,10 @@ function buildFoundationalDirectQuestion(node) {
|
||||
|
||||
function isRelationshipExplanationUnknown(node, graph) {
|
||||
const text = normaliseText(`${node?.label || ""} ${node?.description || ""}`);
|
||||
if (isDirectlyAnswerableObservationChildText(text)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
collectObservationNodes(graph).length >= 2 &&
|
||||
/\b(explain|explanation|divergence|moved differently|difference between|change or event|what changed|why the observations)/.test(
|
||||
@@ -729,6 +733,433 @@ export function assessUnknownAtomicity({ node, graph }) {
|
||||
};
|
||||
}
|
||||
|
||||
const ALL_REASONING_PATTERNS = [
|
||||
"decision",
|
||||
"explanation",
|
||||
"contradiction",
|
||||
"definition",
|
||||
"diagnosis",
|
||||
"comparison",
|
||||
"prioritisation",
|
||||
];
|
||||
|
||||
const QUESTION_FAMILIES_BY_PATTERN = {
|
||||
decision: [
|
||||
"decision_foundation",
|
||||
"decision_evidence",
|
||||
"decision_threshold",
|
||||
"definition",
|
||||
],
|
||||
explanation: ["explanation", "comparison"],
|
||||
contradiction: ["contradiction", "comparison", "explanation"],
|
||||
definition: ["definition"],
|
||||
diagnosis: ["diagnosis", "comparison"],
|
||||
comparison: ["comparison"],
|
||||
prioritisation: ["prioritisation", "decision_threshold"],
|
||||
};
|
||||
|
||||
const STRATEGIES_BY_PATTERN = {
|
||||
decision: [
|
||||
"decision_threshold",
|
||||
"evidence_gathering",
|
||||
"definition",
|
||||
"baseline_reconstruction",
|
||||
],
|
||||
explanation: ["evidence_gathering", "baseline_reconstruction"],
|
||||
contradiction: [
|
||||
"contradiction_resolution",
|
||||
"baseline_reconstruction",
|
||||
"evidence_gathering",
|
||||
],
|
||||
definition: ["definition"],
|
||||
diagnosis: ["evidence_gathering", "baseline_reconstruction"],
|
||||
comparison: ["baseline_reconstruction", "evidence_gathering"],
|
||||
prioritisation: ["decision_threshold", "evidence_gathering"],
|
||||
};
|
||||
|
||||
function buildParentChain(node, graph) {
|
||||
const nodesById = buildNodeMap(graph);
|
||||
const chain = [];
|
||||
let current = node?.parentId ? nodesById.get(node.parentId) : null;
|
||||
|
||||
while (current) {
|
||||
chain.push(current);
|
||||
current = current.parentId ? nodesById.get(current.parentId) : null;
|
||||
}
|
||||
|
||||
return chain;
|
||||
}
|
||||
|
||||
function hasDecisionContext(node, graph, relatedNodes = []) {
|
||||
const ancestry = buildParentChain(node, graph);
|
||||
const contextText = normaliseText(
|
||||
[
|
||||
graph?.centralStatement,
|
||||
node?.label,
|
||||
node?.description,
|
||||
...relatedNodes.map((relatedNode) => relatedNode.label),
|
||||
...relatedNodes.map((relatedNode) => relatedNode.description),
|
||||
...ancestry.map((ancestor) => ancestor.label),
|
||||
...ancestry.map((ancestor) => ancestor.description),
|
||||
...collectResolvedContextValues(graph),
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" "),
|
||||
);
|
||||
|
||||
return /\b(whether to|build|launch|continue|proceed|invest|commercially justified|commercial justification|commercial value|business case|viability)\b/.test(
|
||||
contextText,
|
||||
);
|
||||
}
|
||||
|
||||
function hasObservationRelationshipTopology(node, graph, relatedNodes = []) {
|
||||
const ancestry = buildParentChain(node, graph);
|
||||
const topologyNodes = [node, ...relatedNodes, ...ancestry].filter(Boolean);
|
||||
|
||||
return (
|
||||
collectObservationNodes(graph).length >= 2 &&
|
||||
topologyNodes.some(
|
||||
(candidate) =>
|
||||
candidate.kind === "observation" || candidate.kind === "relationship",
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function isDefinitionPatternCandidate(node, graph) {
|
||||
const text = normaliseText(`${node?.label || ""} ${node?.description || ""}`);
|
||||
return isDefinitionLikeUnknown(
|
||||
text,
|
||||
`${text} ${graph?.centralStatement || ""}`,
|
||||
);
|
||||
}
|
||||
|
||||
function isContradictionPatternCandidate(node, graph, relatedNodes = []) {
|
||||
const relationshipStatus = graph?.reasoningState?.relationshipStatus ?? null;
|
||||
const text = normaliseText(
|
||||
[
|
||||
node?.label,
|
||||
node?.description,
|
||||
...relatedNodes.map((relatedNode) => relatedNode.label),
|
||||
...relatedNodes.map((relatedNode) => relatedNode.description),
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" "),
|
||||
);
|
||||
|
||||
return (
|
||||
relationshipStatus === "contradictory" ||
|
||||
/\b(contradiction|contradict|conflict|inconsistent|mismatch|opposing)\b/.test(
|
||||
text,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function isComparisonPatternCandidate(node, graph, relatedNodes = []) {
|
||||
if (isRelationshipExplanationUnknown(node, graph)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const text = normaliseText(
|
||||
[
|
||||
node?.label,
|
||||
node?.description,
|
||||
...relatedNodes.map((relatedNode) => relatedNode.label),
|
||||
...relatedNodes.map((relatedNode) => relatedNode.description),
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" "),
|
||||
);
|
||||
|
||||
return (
|
||||
(hasObservationRelationshipTopology(node, graph, relatedNodes) ||
|
||||
collectObservationNodes(graph).length >= 2 ||
|
||||
graph?.reasoningState?.comparabilityStatus === "uncertain") &&
|
||||
/\b(compare|comparison|different timing|measured|measurement|basis|scale|period|alternative|alternatives|better than)\b/.test(
|
||||
text,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function isExplanationPatternCandidate(node, graph, relatedNodes = []) {
|
||||
if (isRelationshipExplanationUnknown(node, graph)) return true;
|
||||
|
||||
const text = normaliseText(
|
||||
[
|
||||
node?.label,
|
||||
node?.description,
|
||||
...relatedNodes.map((relatedNode) => relatedNode.label),
|
||||
...relatedNodes.map((relatedNode) => relatedNode.description),
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" "),
|
||||
);
|
||||
|
||||
return (
|
||||
hasObservationRelationshipTopology(node, graph, relatedNodes) &&
|
||||
/\b(explain|explanation|why .* but|difference between|divergence|what changed|moved differently)\b/.test(
|
||||
text,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function isPrioritisationPatternCandidate(node, graph, relatedNodes = []) {
|
||||
const text = normaliseText(
|
||||
[
|
||||
node?.label,
|
||||
node?.description,
|
||||
graph?.centralStatement,
|
||||
...relatedNodes.map((relatedNode) => relatedNode.label),
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" "),
|
||||
);
|
||||
|
||||
return /\b(prioritise|prioritize|priority|rank|ranking|trade off|tradeoff|which first)\b/.test(
|
||||
text,
|
||||
);
|
||||
}
|
||||
|
||||
export function selectReasoningPattern({ node, graph, context = {} }) {
|
||||
const relatedNodes = collectRelatedNodes(node, graph);
|
||||
const patternContext = {
|
||||
hasDecisionContext: hasDecisionContext(node, graph, relatedNodes),
|
||||
hasObservationRelationshipTopology: hasObservationRelationshipTopology(
|
||||
node,
|
||||
graph,
|
||||
relatedNodes,
|
||||
),
|
||||
};
|
||||
|
||||
if (isDefinitionPatternCandidate(node, graph)) {
|
||||
return {
|
||||
pattern: "definition",
|
||||
reason:
|
||||
"Selected definition because the active unknown is about meaning, scope, or term boundaries.",
|
||||
context: patternContext,
|
||||
};
|
||||
}
|
||||
|
||||
if (isContradictionPatternCandidate(node, graph, relatedNodes)) {
|
||||
return {
|
||||
pattern: "contradiction",
|
||||
reason:
|
||||
"Selected contradiction because the graph indicates opposing claims or incompatible observations.",
|
||||
context: patternContext,
|
||||
};
|
||||
}
|
||||
|
||||
if (isComparisonPatternCandidate(node, graph, relatedNodes)) {
|
||||
return {
|
||||
pattern: "comparison",
|
||||
reason:
|
||||
"Selected comparison because the active unknown is about distinguishing measurements, timing, basis, or alternatives.",
|
||||
context: patternContext,
|
||||
};
|
||||
}
|
||||
|
||||
if (isExplanationPatternCandidate(node, graph, relatedNodes)) {
|
||||
return {
|
||||
pattern: "explanation",
|
||||
reason:
|
||||
"Selected explanation because the active unknown is about accounting for a relationship between observations.",
|
||||
context: patternContext,
|
||||
};
|
||||
}
|
||||
|
||||
if (patternContext.hasDecisionContext) {
|
||||
return {
|
||||
pattern: "decision",
|
||||
reason:
|
||||
"Selected decision because the active unknown sits inside a build, continue, invest, or commercial-justification decision context.",
|
||||
context: patternContext,
|
||||
};
|
||||
}
|
||||
|
||||
if (isPrioritisationPatternCandidate(node, graph, relatedNodes)) {
|
||||
return {
|
||||
pattern: "prioritisation",
|
||||
reason:
|
||||
"Selected prioritisation because the active unknown is about ordering options or trade-offs.",
|
||||
context: patternContext,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
pattern: "diagnosis",
|
||||
reason:
|
||||
"Selected diagnosis as the default because the active unknown needs clarifying evidence or mechanism-level investigation.",
|
||||
context: patternContext,
|
||||
};
|
||||
}
|
||||
|
||||
function allowedQuestionFamiliesForPattern(pattern) {
|
||||
return QUESTION_FAMILIES_BY_PATTERN[pattern] || [pattern];
|
||||
}
|
||||
|
||||
function rejectedQuestionFamiliesForPattern(pattern) {
|
||||
const allowed = new Set(allowedQuestionFamiliesForPattern(pattern));
|
||||
return ALL_REASONING_PATTERNS.flatMap((candidatePattern) =>
|
||||
(
|
||||
QUESTION_FAMILIES_BY_PATTERN[candidatePattern] || [candidatePattern]
|
||||
).filter((family) => !allowed.has(family)),
|
||||
).filter((family, index, list) => list.indexOf(family) === index);
|
||||
}
|
||||
|
||||
function constrainStrategyToReasoningPattern(strategy, reasoningPattern) {
|
||||
if (!strategy) return null;
|
||||
const allowedStrategies = STRATEGIES_BY_PATTERN[reasoningPattern] || [];
|
||||
return allowedStrategies.includes(strategy.key) ? strategy : null;
|
||||
}
|
||||
|
||||
function selectQuestionFamily({
|
||||
node,
|
||||
graph,
|
||||
reasoningPattern,
|
||||
investigationStrategy,
|
||||
}) {
|
||||
const text = normaliseText(`${node?.label || ""} ${node?.description || ""}`);
|
||||
|
||||
if (reasoningPattern === "definition") {
|
||||
return { family: "definition", template: "definition_meaning" };
|
||||
}
|
||||
|
||||
if (reasoningPattern === "contradiction") {
|
||||
if (/\b(period|timing|basis|scale|measure|measured)\b/.test(text)) {
|
||||
return {
|
||||
family: "comparison",
|
||||
template: "comparison_reconcile_measurement",
|
||||
};
|
||||
}
|
||||
return {
|
||||
family: "contradiction",
|
||||
template: "contradiction_resolve_opposition",
|
||||
};
|
||||
}
|
||||
|
||||
if (reasoningPattern === "explanation") {
|
||||
return {
|
||||
family: "explanation",
|
||||
template: "explanation_broad_investigation",
|
||||
};
|
||||
}
|
||||
|
||||
if (reasoningPattern === "comparison") {
|
||||
return /\b(period|timing)\b/.test(text) &&
|
||||
!/\bhow the two observations were measured|measurement\b/.test(text)
|
||||
? { family: "comparison", template: "comparison_timing_basis" }
|
||||
: { family: "comparison", template: "comparison_measurement_basis" };
|
||||
}
|
||||
|
||||
if (reasoningPattern === "decision") {
|
||||
if (
|
||||
/\b(audience|customer|user|buyer|stakeholder|recipient|who experiences)\b/.test(
|
||||
text,
|
||||
)
|
||||
) {
|
||||
return { family: "decision_foundation", template: "decision_audience" };
|
||||
}
|
||||
if (
|
||||
/\b(alternative|alternatives|better than|different from|deal with)\b/.test(
|
||||
text,
|
||||
)
|
||||
) {
|
||||
return {
|
||||
family: "decision_foundation",
|
||||
template: "decision_current_alternatives",
|
||||
};
|
||||
}
|
||||
if (/\b(problem|need|demand)\b/.test(text)) {
|
||||
return {
|
||||
family: "decision_foundation",
|
||||
template: "decision_problem_existence",
|
||||
};
|
||||
}
|
||||
if (investigationStrategy?.key === "decision_threshold") {
|
||||
return {
|
||||
family: "decision_threshold",
|
||||
template: "decision_threshold_outcome",
|
||||
};
|
||||
}
|
||||
return {
|
||||
family: "decision_evidence",
|
||||
template: "decision_evidence_clarification",
|
||||
};
|
||||
}
|
||||
|
||||
if (reasoningPattern === "prioritisation") {
|
||||
return { family: "prioritisation", template: "prioritisation_tradeoff" };
|
||||
}
|
||||
|
||||
return investigationStrategy?.key === "baseline_reconstruction"
|
||||
? { family: "comparison", template: "diagnosis_baseline_comparison" }
|
||||
: { family: "diagnosis", template: "diagnosis_evidence" };
|
||||
}
|
||||
|
||||
function buildQuestionFromFamily({
|
||||
node,
|
||||
graph,
|
||||
reasoningPattern,
|
||||
questionFamily,
|
||||
selectedQuestionTemplate,
|
||||
investigationStrategy,
|
||||
}) {
|
||||
const meaning = extractMeaning(node);
|
||||
|
||||
if (reasoningPattern === "decision") {
|
||||
if (selectedQuestionTemplate === "decision_audience") {
|
||||
return "Who experiences this problem?";
|
||||
}
|
||||
if (selectedQuestionTemplate === "decision_current_alternatives") {
|
||||
return "How do people deal with this today?";
|
||||
}
|
||||
if (selectedQuestionTemplate === "decision_problem_existence") {
|
||||
return "What makes you think this is a real problem?";
|
||||
}
|
||||
if (selectedQuestionTemplate === "decision_threshold_outcome") {
|
||||
return buildQuestionFromStrategy(
|
||||
investigationStrategy || {
|
||||
key: "decision_threshold",
|
||||
meaning,
|
||||
actionPhrase: null,
|
||||
},
|
||||
);
|
||||
}
|
||||
return `What evidence would clarify ${stripTrailingPunctuation(meaning)}?`;
|
||||
}
|
||||
|
||||
if (questionFamily === "definition") {
|
||||
return `What does ${meaning} mean in this situation?`;
|
||||
}
|
||||
|
||||
if (reasoningPattern === "comparison") {
|
||||
if (selectedQuestionTemplate === "comparison_timing_basis") {
|
||||
return `What evidence would clarify whether ${stripTrailingPunctuation(meaning)}?`;
|
||||
}
|
||||
if (selectedQuestionTemplate === "comparison_measurement_basis") {
|
||||
return `What evidence would clarify ${stripTrailingPunctuation(meaning)}?`;
|
||||
}
|
||||
return `What evidence would clarify ${stripTrailingPunctuation(meaning)}?`;
|
||||
}
|
||||
|
||||
if (reasoningPattern === "contradiction") {
|
||||
return investigationStrategy?.key === "contradiction_resolution"
|
||||
? buildQuestionFromStrategy(investigationStrategy)
|
||||
: `What fact would resolve the contradiction about ${stripTrailingPunctuation(meaning)}?`;
|
||||
}
|
||||
|
||||
if (reasoningPattern === "explanation") {
|
||||
return buildBroadInvestigationQuestion(graph);
|
||||
}
|
||||
|
||||
if (reasoningPattern === "prioritisation") {
|
||||
return `Which option should be investigated first, and why?`;
|
||||
}
|
||||
|
||||
return investigationStrategy
|
||||
? buildQuestionFromStrategy(investigationStrategy)
|
||||
: buildNeutralClarificationQuestion(meaning);
|
||||
}
|
||||
|
||||
export function formulateTieResolutionQuestion({ graph }) {
|
||||
const comparability = assessComparability(graph);
|
||||
if (comparability.comparabilityStatus === "uncertain") {
|
||||
@@ -747,6 +1178,14 @@ export function formulateTieResolutionQuestion({ graph }) {
|
||||
relationshipStatus: deferredRelationship.relationshipStatus,
|
||||
relationshipReason: deferredRelationship.reason,
|
||||
relationshipAssessed: deferredRelationship.relationshipAssessed,
|
||||
reasoningPattern: "comparison",
|
||||
reasoningPatternReason:
|
||||
"Tie resolution is using the comparison family because comparability is still unresolved.",
|
||||
allowedQuestionFamilies: allowedQuestionFamiliesForPattern("comparison"),
|
||||
rejectedQuestionFamilies:
|
||||
rejectedQuestionFamiliesForPattern("comparison"),
|
||||
questionFamily: "comparison",
|
||||
selectedQuestionTemplate: "comparison_tie_resolution",
|
||||
questionRequired: true,
|
||||
reasoningStages: deferredRelationship.reasoningStages,
|
||||
};
|
||||
@@ -766,6 +1205,14 @@ export function formulateTieResolutionQuestion({ graph }) {
|
||||
relationshipReason: relationship.reason,
|
||||
relationshipAssessed: relationship.relationshipAssessed,
|
||||
contradictionReasoningAllowed: relationship.contradictionReasoningAllowed,
|
||||
reasoningPattern: "comparison",
|
||||
reasoningPatternReason:
|
||||
"Tie resolution remains in the comparison family because no distinct winning unknown exists.",
|
||||
allowedQuestionFamilies: allowedQuestionFamiliesForPattern("comparison"),
|
||||
rejectedQuestionFamilies:
|
||||
rejectedQuestionFamiliesForPattern("comparison"),
|
||||
questionFamily: "comparison",
|
||||
selectedQuestionTemplate: "comparison_no_question_required",
|
||||
questionRequired: relationship.questionRequired,
|
||||
questionSuppressedReason: relationship.questionSuppressedReason,
|
||||
reasoningStages: relationship.reasoningStages,
|
||||
@@ -786,6 +1233,14 @@ export function formulateTieResolutionQuestion({ graph }) {
|
||||
relationshipReason: relationship.reason,
|
||||
relationshipAssessed: relationship.relationshipAssessed,
|
||||
contradictionReasoningAllowed: relationship.contradictionReasoningAllowed,
|
||||
reasoningPattern: "explanation",
|
||||
reasoningPatternReason:
|
||||
"Tie resolution is using the explanation family because the observations appear related and need a neutral explanation question.",
|
||||
allowedQuestionFamilies: allowedQuestionFamiliesForPattern("explanation"),
|
||||
rejectedQuestionFamilies:
|
||||
rejectedQuestionFamiliesForPattern("explanation"),
|
||||
questionFamily: "explanation",
|
||||
selectedQuestionTemplate: "explanation_broad_investigation",
|
||||
questionRequired: relationship.questionRequired,
|
||||
reasoningStages: relationship.reasoningStages,
|
||||
};
|
||||
@@ -812,6 +1267,14 @@ export function formulateTieResolutionQuestion({ graph }) {
|
||||
relationshipReason: relationship.reason,
|
||||
relationshipAssessed: relationship.relationshipAssessed,
|
||||
contradictionReasoningAllowed: relationship.contradictionReasoningAllowed,
|
||||
reasoningPattern: "contradiction",
|
||||
reasoningPatternReason:
|
||||
"Tie resolution is using the contradiction family because the graph is trying to distinguish incompatible explanations.",
|
||||
allowedQuestionFamilies: allowedQuestionFamiliesForPattern("contradiction"),
|
||||
rejectedQuestionFamilies:
|
||||
rejectedQuestionFamiliesForPattern("contradiction"),
|
||||
questionFamily: "contradiction",
|
||||
selectedQuestionTemplate: "contradiction_distinguishing_change",
|
||||
questionRequired: relationship.questionRequired,
|
||||
reasoningStages: relationship.reasoningStages,
|
||||
};
|
||||
@@ -899,6 +1362,9 @@ function buildInvestigationStrategy({
|
||||
}
|
||||
|
||||
export function selectInvestigationStrategy({ node, graph, context = {} }) {
|
||||
const effectiveReasoningPattern =
|
||||
context.reasoningPattern ||
|
||||
selectReasoningPattern({ node, graph, context }).pattern;
|
||||
const relatedNodes = collectRelatedNodes(node, graph);
|
||||
const meaning = extractMeaning(node);
|
||||
const combinedText = [
|
||||
@@ -949,8 +1415,10 @@ export function selectInvestigationStrategy({ node, graph, context = {} }) {
|
||||
const hasPrimaryBaselineLanguage =
|
||||
/\b(before|previous|baseline|prior|comparable state)\b/.test(nodeText);
|
||||
|
||||
let selectedStrategy = null;
|
||||
|
||||
if (hasBaselineLanguage && hasPrimaryBaselineLanguage) {
|
||||
return buildInvestigationStrategy({
|
||||
selectedStrategy = buildInvestigationStrategy({
|
||||
key: "baseline_reconstruction",
|
||||
reason:
|
||||
"Selected because the unknown explicitly references a missing previous or baseline state.",
|
||||
@@ -991,8 +1459,11 @@ export function selectInvestigationStrategy({ node, graph, context = {} }) {
|
||||
relatedNode.kind === "conclusion",
|
||||
);
|
||||
|
||||
if (hasPrimaryDefinitionLanguage || hasDefinitionLanguage) {
|
||||
return buildInvestigationStrategy({
|
||||
if (
|
||||
!selectedStrategy &&
|
||||
(hasPrimaryDefinitionLanguage || hasDefinitionLanguage)
|
||||
) {
|
||||
selectedStrategy = buildInvestigationStrategy({
|
||||
key: "definition",
|
||||
reason:
|
||||
"Selected because the unknown is primarily about clarifying what a term means in this case.",
|
||||
@@ -1004,8 +1475,8 @@ export function selectInvestigationStrategy({ node, graph, context = {} }) {
|
||||
});
|
||||
}
|
||||
|
||||
if (hasDecisionValueLanguage || hasCriteriaLanguage) {
|
||||
return buildInvestigationStrategy({
|
||||
if (!selectedStrategy && (hasDecisionValueLanguage || hasCriteriaLanguage)) {
|
||||
selectedStrategy = buildInvestigationStrategy({
|
||||
key: "decision_threshold",
|
||||
reason:
|
||||
"Selected because the unknown determines the threshold for making or justifying a decision.",
|
||||
@@ -1017,8 +1488,11 @@ export function selectInvestigationStrategy({ node, graph, context = {} }) {
|
||||
});
|
||||
}
|
||||
|
||||
if (hasPrimaryBaselineLanguage || hasBaselineLanguage) {
|
||||
return buildInvestigationStrategy({
|
||||
if (
|
||||
!selectedStrategy &&
|
||||
(hasPrimaryBaselineLanguage || hasBaselineLanguage)
|
||||
) {
|
||||
selectedStrategy = buildInvestigationStrategy({
|
||||
key: "baseline_reconstruction",
|
||||
reason:
|
||||
"Selected because reconstructing the prior state is the most direct way to resolve the unknown.",
|
||||
@@ -1030,8 +1504,8 @@ export function selectInvestigationStrategy({ node, graph, context = {} }) {
|
||||
});
|
||||
}
|
||||
|
||||
if (hasContradictionLanguage) {
|
||||
return buildInvestigationStrategy({
|
||||
if (!selectedStrategy && hasContradictionLanguage) {
|
||||
selectedStrategy = buildInvestigationStrategy({
|
||||
key: "contradiction_resolution",
|
||||
reason:
|
||||
"Selected because the graph context indicates conflicting claims or inconsistent states that must be reconciled.",
|
||||
@@ -1043,8 +1517,11 @@ export function selectInvestigationStrategy({ node, graph, context = {} }) {
|
||||
});
|
||||
}
|
||||
|
||||
if (hasEvidenceLanguage || hasMeasurementLanguage || hasConstraintLanguage) {
|
||||
return buildInvestigationStrategy({
|
||||
if (
|
||||
!selectedStrategy &&
|
||||
(hasEvidenceLanguage || hasMeasurementLanguage || hasConstraintLanguage)
|
||||
) {
|
||||
selectedStrategy = buildInvestigationStrategy({
|
||||
key: "evidence_gathering",
|
||||
reason:
|
||||
hasConstraintLanguage && hasPrimaryConstraintLanguage
|
||||
@@ -1058,7 +1535,10 @@ export function selectInvestigationStrategy({ node, graph, context = {} }) {
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
return constrainStrategyToReasoningPattern(
|
||||
selectedStrategy,
|
||||
effectiveReasoningPattern,
|
||||
);
|
||||
}
|
||||
|
||||
function buildQuestionFromStrategy(strategy) {
|
||||
@@ -1232,8 +1712,22 @@ export function formulateQuestion({ node, graph, context = {} }) {
|
||||
return formulateTieResolutionQuestion({ graph });
|
||||
}
|
||||
|
||||
const reasoningPatternSelection = selectReasoningPattern({
|
||||
node,
|
||||
graph,
|
||||
context,
|
||||
});
|
||||
const allowedQuestionFamilies = allowedQuestionFamiliesForPattern(
|
||||
reasoningPatternSelection.pattern,
|
||||
);
|
||||
const rejectedQuestionFamilies = rejectedQuestionFamiliesForPattern(
|
||||
reasoningPatternSelection.pattern,
|
||||
);
|
||||
const foundationalDirectQuestion = buildFoundationalDirectQuestion(node);
|
||||
if (foundationalDirectQuestion) {
|
||||
if (
|
||||
foundationalDirectQuestion &&
|
||||
reasoningPatternSelection.pattern === "decision"
|
||||
) {
|
||||
const plainLanguage = applyPlainLanguageNormalisations(
|
||||
sanitizeQuestionText(foundationalDirectQuestion),
|
||||
);
|
||||
@@ -1249,6 +1743,12 @@ export function formulateQuestion({ node, graph, context = {} }) {
|
||||
"Formulated as a direct foundational question because this child unknown should be answered one step at a time.",
|
||||
strategy: null,
|
||||
investigationStrategy: null,
|
||||
reasoningPattern: reasoningPatternSelection.pattern,
|
||||
reasoningPatternReason: reasoningPatternSelection.reason,
|
||||
questionFamily: "decision_foundation",
|
||||
allowedQuestionFamilies,
|
||||
rejectedQuestionFamilies,
|
||||
selectedQuestionTemplate: "decision_foundation_direct_child",
|
||||
questionComplexity,
|
||||
plainLanguageNormalisations: plainLanguage.normalisations,
|
||||
};
|
||||
@@ -1257,14 +1757,27 @@ export function formulateQuestion({ node, graph, context = {} }) {
|
||||
const investigationStrategy = selectInvestigationStrategy({
|
||||
node,
|
||||
graph,
|
||||
context,
|
||||
context: {
|
||||
...context,
|
||||
reasoningPattern: reasoningPatternSelection.pattern,
|
||||
},
|
||||
});
|
||||
|
||||
let question = investigationStrategy
|
||||
? buildQuestionFromStrategy(investigationStrategy)
|
||||
: isRelationshipExplanationUnknown(node, graph)
|
||||
? buildBroadInvestigationQuestion(graph)
|
||||
: buildNeutralClarificationQuestion(extractMeaning(node));
|
||||
const questionFamilySelection = selectQuestionFamily({
|
||||
node,
|
||||
graph,
|
||||
reasoningPattern: reasoningPatternSelection.pattern,
|
||||
investigationStrategy,
|
||||
});
|
||||
|
||||
let question = buildQuestionFromFamily({
|
||||
node,
|
||||
graph,
|
||||
reasoningPattern: reasoningPatternSelection.pattern,
|
||||
questionFamily: questionFamilySelection.family,
|
||||
selectedQuestionTemplate: questionFamilySelection.template,
|
||||
investigationStrategy,
|
||||
});
|
||||
|
||||
question = sanitizeQuestionText(question);
|
||||
const plainLanguage = applyPlainLanguageNormalisations(question);
|
||||
@@ -1310,6 +1823,12 @@ export function formulateQuestion({ node, graph, context = {} }) {
|
||||
: "Formulated as a neutral clarification question because no narrower investigation strategy clearly applied.",
|
||||
strategy: investigationStrategy?.key ?? null,
|
||||
investigationStrategy,
|
||||
reasoningPattern: reasoningPatternSelection.pattern,
|
||||
reasoningPatternReason: reasoningPatternSelection.reason,
|
||||
questionFamily: questionFamilySelection.family,
|
||||
allowedQuestionFamilies,
|
||||
rejectedQuestionFamilies,
|
||||
selectedQuestionTemplate: questionFamilySelection.template,
|
||||
questionComplexity,
|
||||
plainLanguageNormalisations: plainLanguage.normalisations,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user