Add one explicit action-order rule in Additional Guidance for when rule #6 applies to explicitly unresolved uncertainty: 1. First check whether an existing unresolved node already represents the same uncertainty. 2. If so, update/refine that existing structure rather than creating a duplicate. 3. If no such node exists, add a new unknown that directly represents the unresolved uncertainty. 4. Do not use an edge alone to represent a previously unrepresented uncertainty. 14 focused prompt tests verify: existing-first ordering, reuse path, fallback-to-add, related-node-insufficient, edge-only-prohibited, possibleInference separation, resolution path preserved, duplicate contract preserved, scope uncertainty-only, fidelity/traceability preserved, noop validator untouched, no semantic classifier added.
147 lines
8.4 KiB
JavaScript
147 lines
8.4 KiB
JavaScript
import {
|
|
ConfidenceLevel,
|
|
SituationKind,
|
|
SituationRelationship,
|
|
SituationStatus,
|
|
} from "./schema.js";
|
|
|
|
const DEFAULT_PROMPT_VERSION = "v0.4";
|
|
|
|
function formatEnumValues(values) {
|
|
return Object.values(values).join(" | ");
|
|
}
|
|
|
|
function formatGraph(graph) {
|
|
return JSON.stringify(graph, null, 2);
|
|
}
|
|
|
|
function formatExampleAnswerBlock() {
|
|
return [
|
|
"Example answer the model must be able to handle without hard-coding output:",
|
|
'"The complaint rate fell from 2.0 complaints per 100 units to 1.9 complaints per 100 units."',
|
|
"This may justify resolving a rate-related unknown or updating a metric node, but only if the current graph and answer support that proposal.",
|
|
].join("\n");
|
|
}
|
|
|
|
export function buildGraphUpdatePrompt({
|
|
situationGraph,
|
|
previousQuestion,
|
|
answer,
|
|
promptVersion = DEFAULT_PROMPT_VERSION,
|
|
}) {
|
|
const nodeKinds = formatEnumValues(SituationKind);
|
|
const nodeStatuses = formatEnumValues(SituationStatus);
|
|
const edgeRelationships = formatEnumValues(SituationRelationship);
|
|
const confidenceLevels = formatEnumValues(ConfidenceLevel);
|
|
|
|
return `You are proposing a graph update for Confidence Engine ${promptVersion}.
|
|
|
|
Return exactly one JSON object matching the GraphUpdate contract.
|
|
Return JSON only. Do not include markdown, explanation, or any text before or after the JSON object.
|
|
|
|
## Current Situation Graph
|
|
${formatGraph(situationGraph)}
|
|
|
|
## Previous Selected Question
|
|
${previousQuestion}
|
|
|
|
## User Answer
|
|
${answer}
|
|
|
|
## Allowed Node Kinds
|
|
${nodeKinds}
|
|
|
|
## Allowed Node Statuses
|
|
${nodeStatuses}
|
|
|
|
## Allowed Edge Relationships
|
|
${edgeRelationships}
|
|
|
|
## Allowed Confidence Values
|
|
${confidenceLevels}
|
|
|
|
## Required JSON Field Names
|
|
The JSON object must contain exactly these top-level fields:
|
|
- addedNodes
|
|
- updatedNodes
|
|
- addedEdges
|
|
- removedEdgeIds
|
|
- resolvedUnknownNodeIds
|
|
- affectedNodeIds
|
|
- selectedQuestion
|
|
- answerMeaning
|
|
|
|
## Required Shapes
|
|
- addedNodes: array of nodes using these exact keys:
|
|
id, label, description, kind, status, confidence, value, unit, evidenceIds, dependsOn, affects, parentId, childIds
|
|
- updatedNodes: array of node updates using these exact keys:
|
|
nodeId, previousStatus, newStatus, previousValue, newValue, reason
|
|
- addedEdges: array of edges using these exact keys:
|
|
id, fromNodeId, toNodeId, relationship, confidence, description
|
|
- removedEdgeIds: array of strings
|
|
- resolvedUnknownNodeIds: array of strings
|
|
- affectedNodeIds: array of strings
|
|
- selectedQuestion: either null or an object using these exact keys:
|
|
nodeId, question, reason
|
|
- answerMeaning: either null or an object using these exact keys:
|
|
userSupportedMeaning, possibleInference, supportCategory, resolutionGuidance
|
|
|
|
## Proposal Rules
|
|
1. Propose changes only. Never return a replacement graph.
|
|
2. Preserve unrelated nodes and edges by omitting them from the proposal.
|
|
3. Reference existing node IDs when updating an existing concept.
|
|
4. Use addedNodes only for genuinely new concepts.
|
|
5. Resolve the answered unknown first when the answer supports it.
|
|
6. If answerMeaning.userSupportedMeaning contains consequential information or unresolved uncertainty that is not already represented in the graph, you MUST express its effect through structural mutation. This may be an update/refinement of existing structure, resolution of an existing unknown, a genuinely new unknown, or a justified relationship. answerMeaning alone is not sufficient for a successful proposal.
|
|
7. Add new unknown nodes only when the answer introduces a new decision, claim, object, measure, dependency, or unresolved term directly relevant to the case.
|
|
8. Add at most 3 new unknown nodes.
|
|
9. Every new unknown must be directly traceable to the user's answer and its description must state why that uncertainty matters.
|
|
9a. In the description of every new unknown, explicitly include a short why-it-matters clause using wording such as because, so that, needed to decide, or matters because.
|
|
10. Do not add broad generic discovery questions.
|
|
11. Do not add duplicate unknowns.
|
|
12. Do not expand unrelated branches.
|
|
13. Propagate only through explicit dependencies or relationships already present in the graph, except for the minimal new edges needed to connect validated new unknowns to the relevant answer-derived decision or context node.
|
|
13a. For every new unknown node, include at least one added edge that connects it to an existing updated/resolved node or to a newly added non-unknown node introduced from the answer.
|
|
14. Do not invent evidence.
|
|
15. Do not create unsupported causal edges.
|
|
16. If consequential unresolved unknowns exist, selectedQuestion may identify one valid candidate unknown, but the engine will deterministically choose final priority after validation.
|
|
17. selectedQuestion.nodeId must reference an unresolved unknown node that exists either already in the graph or in addedNodes.
|
|
18. selectedQuestion.question must be one narrow non-compound question about that one unknown.
|
|
19. Do not prioritise downstream implementation, pricing, optimisation, or speculative branches ahead of prerequisite definitions, actors, success criteria, constraints, measures, or terminology.
|
|
20. Return selectedQuestion as null only when no consequential unresolved unknown remains.
|
|
21. Use empty arrays when there are no changes in a category.
|
|
22. Never return null array entries.
|
|
23. Never use unknown enum values.
|
|
24. Do not change existing IDs.
|
|
25. Do not replace the whole graph, and do not restate unchanged graph content inside the proposal.
|
|
26. answerMeaning.userSupportedMeaning must state only what the user's answer directly supports.
|
|
27. Put any stronger interpretation in answerMeaning.possibleInference, not in userSupportedMeaning.
|
|
28. supportCategory and resolutionGuidance are optional descriptive hints only; if you are unsure of the exact wording, leave them null rather than inventing rigid category labels.
|
|
29. If the answer is conditional or qualified, preserve that qualification explicitly in userSupportedMeaning.
|
|
30. If the answer says the user is unsure or does not resolve the distinction, state that uncertainty directly in userSupportedMeaning.
|
|
31. If the answer explicitly states a hard constraint, state that directly in userSupportedMeaning.
|
|
|
|
## Additional Guidance
|
|
- If the answer only clarifies an existing unknown, prefer updatedNodes and resolvedUnknownNodeIds over creating duplicate nodes.
|
|
- When rule #6 applies to explicitly unresolved uncertainty: first check whether an existing unresolved node already represents the same uncertainty; if so, update/refine that existing structure rather than adding a duplicate; if no such node exists, add a new unknown that directly represents the unresolved uncertainty; do not use an edge alone to represent a previously unrepresented uncertainty.
|
|
- When an answer resolves an existing unknown, include that existing node ID in resolvedUnknownNodeIds and update that node rather than creating only a parallel observation.
|
|
- If the answer creates a more specific decision situation, add the smallest set of new nodes and edges needed to represent that situation and only its most consequential unknowns.
|
|
- If you add a new unknown, do not leave it floating: connect it with an added edge to the relevant decision/context node created or updated from the answer.
|
|
- If you add a new unknown, its description must do two jobs in one sentence: what is unknown, and why resolving it matters for the case.
|
|
- Treat selectedQuestion as a candidate only; the engine will apply deterministic information-value scoring after validation.
|
|
- If rule #6 does not apply (the answer contains no user-supported meaning that requires graph progress) and there is no other justification for change, return empty arrays for every category.
|
|
- If rule #6 applies but you choose an update/refinement of existing structure, resolve an existing unknown, or add justified new structure, your structural proposal plus answerMeaning together represent the complete response — answerMeaning preserves semantic fidelity while structural mutation handles graph progress; neither replaces the other.
|
|
- If you add a new unknown with addedNodes, connect it with at least one addedEdge to an existing updated/resolved node or to a newly added non-unknown node from the answer.
|
|
|
|
## Example Constraint Reminder
|
|
${formatExampleAnswerBlock()}
|
|
|
|
## Output Contract Reminder
|
|
Return one JSON object only, with exact field names and exact enum values.
|
|
Never include a full graph.
|
|
Never include any field other than the contract fields above.
|
|
`;
|
|
}
|
|
|
|
export const buildUpdatePrompt = buildGraphUpdatePrompt;
|