141 lines
9.8 KiB
JavaScript
141 lines
9.8 KiB
JavaScript
import { formulateQuestion } from "@/lib/graph/question-formulator.js";
|
|
|
|
const FOCUSED_ANSWER_SCHEMA_FIELDS = [
|
|
"targetNodeId",
|
|
"observations",
|
|
"uncertainties",
|
|
"assumptions",
|
|
"relationships",
|
|
"possibleFollowUpQuestions",
|
|
];
|
|
|
|
const FORBIDDEN_GRAPH_MUTATION_FIELDS = [
|
|
"addedNodes",
|
|
"updatedNodes",
|
|
"removedNodes",
|
|
"addedEdges",
|
|
"removedEdges",
|
|
"resolvedNodeIds",
|
|
"activeUnknownNodeId",
|
|
"selectedQuestion",
|
|
];
|
|
|
|
/**
|
|
* Formulate a question for the explicitly user-selected unresolved node.
|
|
*
|
|
* Input: { situationGraph, targetNodeId }
|
|
* Validates that target exists, is an unknown, and is not resolved.
|
|
* Calls formulateQuestion with only factual explicit-user-targeting context.
|
|
* Does NOT invoke selectActiveUnknownCandidate, determineGraphBackedQuestion,
|
|
* global ranking, or global recommendation.
|
|
* Does NOT mutate activeUnknownNodeId, selectedQuestion, SituationGraph, or resolvedNodeIds.
|
|
*/
|
|
export function formulateQuestionForTarget({ situationGraph, targetNodeId }) {
|
|
const nodesById = new Map(situationGraph?.nodes?.map((n) => [n.id, n]) || []);
|
|
const targetNode = nodesById.get(targetNodeId);
|
|
|
|
if (!targetNode) {
|
|
return { success: false, error: `Target node ${targetNodeId} not found in graph.` };
|
|
}
|
|
|
|
if (targetNode.kind !== "unknown") {
|
|
return { success: false, error: `Target node ${targetNodeId} is not an unknown (kind=${targetNode.kind}).` };
|
|
}
|
|
|
|
if (targetNode.status === "resolved") {
|
|
return { success: false, error: `Target node ${targetNodeId} is already resolved.` };
|
|
}
|
|
|
|
// Build explicit user-targeting context — only factual fields from the targeting request.
|
|
// No activeUnknownNodeId override, no selectedQuestion mutation, no global selection state.
|
|
const context = {
|
|
resolvedValues: [],
|
|
suppressDecisionSufficiencyConfirmation: false,
|
|
_explicitTargetNodeId: targetNodeId,
|
|
_explicitTargetLabel: targetNode.label,
|
|
_explicitTargetDescription: targetNode.description,
|
|
};
|
|
|
|
const result = formulateQuestion({ node: targetNode, graph: situationGraph, context });
|
|
|
|
return {
|
|
success: true,
|
|
targetNodeId,
|
|
question: result.question,
|
|
strategy: result.strategy ?? null,
|
|
reasoningPattern: result.reasoningPattern,
|
|
reasoningPatternReason: result.reasoningPatternReason,
|
|
reason: result.reason,
|
|
questionFamily: result.questionFamily,
|
|
selectedQuestionTemplate: result.selectedQuestionTemplate,
|
|
allowedQuestionFamilies: result.allowedQuestionFamilies,
|
|
rejectedQuestionFamilies: result.rejectedQuestionFamilies,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Build a focused answer deconstruction prompt for one explicitly user-chosen investigation.
|
|
*/
|
|
export function buildFocusedDeconstructPrompt({ targetLabel, targetDescription, centralStatement, question, answer }) {
|
|
return `You are performing focused answer deconstruction for one explicitly user-chosen investigation.
|
|
|
|
Return exactly one JSON object. Return JSON only.
|
|
|
|
This is NOT a graph update task.
|
|
Do NOT output graph mutations.
|
|
Do NOT output selection, ranking, ownership, recommendation, confidence, or next-best-question semantics.
|
|
Do NOT include any of these fields: addedNodes, updatedNodes, removedNodes, addedEdges, removedEdges, resolvedNodeIds, activeUnknownNodeId, selectedQuestion.
|
|
|
|
Required top-level fields:
|
|
- targetNodeId
|
|
- observations
|
|
- uncertainties
|
|
- assumptions
|
|
- relationships
|
|
- possibleFollowUpQuestions
|
|
|
|
Field rules (semantic contract):
|
|
- targetNodeId must be included as a string identifying this investigation node
|
|
- observations: only meaning directly supported by what the user's answer states. Do not strengthen implications into observations.
|
|
- uncertainties: must be a JSON array containing exactly one string — the single nearest unresolved relationship exposed by this answer. This is the specific gap between what the answer established and what remains unknown right here. Do NOT widen the frontier: identify only the one thing that must be understood before you can know what to ask after it. Do NOT include everything else that might matter, future constraints, broader capability questions, other branches of the investigation, or possible remedies. The uncertainty must be narrow enough that one user answer could materially clarify it. Use ordinary language that a capable person with no specialist vocabulary can understand immediately. If the uncertainty needs abstract phrases, management jargon, specialist terminology, or several concepts joined together to express it, break the reasoning down again before returning it. Simple wording of an over-composed idea is still a failure: first ask "what is the smallest thing we actually do not know yet?" then express that one thing simply.
|
|
- assumptions: what unstated proposition does the user's answer itself rely upon for it to make sense? Include only when such a proposition is genuinely attributable to the user's reasoning. The boundary is narrow: attribute only propositions that the user's answer would cease to make sense if they were false. Do NOT import plausible interpretations from the wider investigation context, scenario framing, domain relevance, strategic implications, or model-generated analysis into this field — those belong in uncertainties, relationships (where permitted), or possibleFollowUpQuestions. Do NOT connect a factual statement the user makes to a broader capability or constraint concept unless the user explicitly links them. Example: answering "I only have bank account access" to a question about delegation constraints does NOT assume that "delegation feasibility is contingent upon banking access" — it only states a fact about access, and connecting that fact to delegation feasibility is your own scenario-level inference, not a user-held assumption. If the user's answer does not contain or rely upon an identifiable assumption, return assumptions: []. Do NOT require verbatim copying from the user's answer; paraphrasing is allowed only when the reasoning genuinely relies on it.
|
|
- relationships: must connect two distinct propositions that the user's answer itself links. Do not create a relationship by merely restating, reformatting, or relabelling an observation. Co-mentioned facts do not themselves create a relationship. Tentative, speculative, or conditional language must not be promoted into an established relationship. If the answer does not directly establish a relationship, return relationships: [].
|
|
- possibleFollowUpQuestions: must be a JSON array containing exactly one string — your single best follow-up question. Example shape: ["one question"]. This question must directly investigate the single uncertainty returned in uncertainties (uncertainties[0] → possibleFollowUpQuestions[0]): one unresolved proposition mapped to one question designed to clarify it. The question must not introduce a second unresolved issue, must not broaden beyond the uncertainty it is meant to resolve, and must not contain more than one investigative step. Do not provide alternatives, a roadmap, or questions that belong after this one has been answered. A later question must be generated only after the current question has been answered and deconstructed. Do not ask about consequences, expansion, requirements, interventions, or other branches until the immediate unresolved relationship has been clarified. Those may become later questions after new evidence is obtained. Ask only what the Engine has earned the right to ask now. Each epistemic step waits its turn — do not combine steps that should happen in sequence across multiple turns: one question that investigates one thing only, never a bundle of future reasoning joined together. Before formulating, check whether the question tests a proposition against the current epistemic state: if an explanation, deficit, dependency, cause, intervention, recommendation, or solution has not been established by prior evidence, phrase the question so it tests whether that proposition is true rather than assuming it — verify the unresolved fact before seeking remedy. Prefer questions that identify what remains unknown, distinguish competing explanations, test whether a suspected factor actually matters, clarify scope, or identify what evidence would change the investigation. Do not jump to implementation details unless the answer has already established that intervention as the relevant next issue. Use ordinary language that a capable person with no specialist vocabulary can understand immediately. If the question needs abstract phrases, management jargon, specialist terminology, or several concepts joined together to express it, break the reasoning down again before returning it. Simple wording of an over-composed idea is still a failure: first ask "what is the smallest thing we actually do not know yet?" then express that one thing simply.
|
|
- cross-field ownership: preserve who or what owns each proposition. When a statement expresses the user's comfort, willingness, threshold, belief, uncertainty, preference, or judgement, keep it attached to that stance — do not elevate it into an objective requirement, capability fact, or situational constraint.
|
|
|
|
Focused case context:
|
|
- target label: ${targetLabel}
|
|
- target description: ${targetDescription}
|
|
- central case statement: ${centralStatement}
|
|
|
|
Question:
|
|
${question}
|
|
|
|
Answer:
|
|
${answer}`;
|
|
}
|
|
|
|
/**
|
|
* Validate that a focused-answer deconstruction result contains only the expected fields.
|
|
* Returns an array of errors (empty = valid).
|
|
*/
|
|
export function validateFocusedDeconstructSchema(result) {
|
|
const errors = [];
|
|
|
|
for (const field of FOCUSED_ANSWER_SCHEMA_FIELDS) {
|
|
if (!(field in result)) {
|
|
errors.push(`Missing required field: ${field}`);
|
|
}
|
|
}
|
|
|
|
for (const field of FORBIDDEN_GRAPH_MUTATION_FIELDS) {
|
|
if (field in result) {
|
|
errors.push(`Forbidden graph-mutation field present: ${field}`);
|
|
}
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
export { FOCUSED_ANSWER_SCHEMA_FIELDS, FORBIDDEN_GRAPH_MUTATION_FIELDS };
|