BID · Console
Baseline · Intelligence · Decision
src/agents/intelligence/insight-synthesis/prompt.ts 3,868 bytes · typescript
/**
 * Insight Synthesis — system prompt builder.
 *
 * Std 5 — cost-appropriate prompts: callers pass an optional
 * PromptScope listing the standards their step engages; when given,
 * the prompt is trimmed.
 */

import { STANDARDS_SUMMARY, type PromptScope } from '../../../standards.js';
import {
  AGENT_NAME,
  AGENT_VERSION,
  insightSynthesisContract,
  insightSynthesisMatrix,
} from './matrix.js';

interface OpPrinciple { readonly n: number; readonly text: string; }

const OPERATING_PRINCIPLES: readonly OpPrinciple[] = [
  { n: 1, text: 'produce insights from existing comparisons / metrics ONLY. Do not introduce new data, run new computations, or fabricate examples.' },
  { n: 4, text: '(highest-risk for this agent): every claim cites at least one supportingEvidence entry that references a real comparisonId, metricKey, or methodology_id from the upstream payload. If a claim cannot be cited, set isInference=true and explain — or drop it via unsupportedClaimsRemoved.' },
  { n: 5, text: 'query find_methodologies(type="insight_framework") for any narrative framework you intend to apply. If none match the JobRequest\'s business question, you may apply a generic peer-positioning / trend / outlier framework with frameworkUsed naming what you did and isInference=true for any claim that goes beyond direct readouts.' },
  { n: 7, text: 'confidence per insight reflects how directly the data supports it — direct readouts ≥ 0.8, well-supported inference 0.5-0.7, weak inference < 0.5 (consider dropping).' },
  { n: 8, text: 'flag narrative ambiguity (a claim that could be read two contradictory ways) explicitly.' },
  { n: 0, text: 'Intelligence-forbidden: no partner-facing recommendations (Pillar 3\'s job), no fabricated narrative, no fresh data fetches.' },
];

function renderUniversal(engaged?: readonly number[]): string {
  const set = engaged && engaged.length > 0 ? new Set(engaged) : null;
  return STANDARDS_SUMMARY
    .filter(s => !set || set.has(s.n))
    .map(s => `${s.n}. ${s.name} — ${s.gist}`)
    .join('\n');
}

function renderMatrix(): string {
  return Object.entries(insightSynthesisMatrix)
    .map(([key, value]) => `${key.split('_')[0]}. ${value}`)
    .join('\n');
}

function renderRunbook(): string {
  return insightSynthesisContract.runbook
    .map(s => `  ${s.n}. ${s.name} — ${s.description}`)
    .join('\n');
}

function renderPrinciples(engaged?: readonly number[]): string {
  const set = engaged && engaged.length > 0 ? new Set(engaged) : null;
  return OPERATING_PRINCIPLES
    .filter(p => p.n === 0 || !set || set.has(p.n))
    .map(p => (p.n === 0 ? `- ${p.text}` : `- Std ${p.n}: ${p.text}`))
    .join('\n');
}

export function buildSystemPrompt(scope?: PromptScope): string {
  const engaged = scope?.engagedStandards;
  const isScoped = !!(engaged && engaged.length > 0);
  const omitMatrix = scope?.omitMatrix ?? isScoped;
  const omitRunbook = scope?.omitRunbook ?? isScoped;
  const lines: string[] = [
    `You are the ${AGENT_NAME} agent (v${AGENT_VERSION}) — the fourth agent of the BID Intelligence pillar.`,
  ];
  if (isScoped) {
    lines.push(
      `All 12 universal operational standards govern your behavior. ` +
        (scope?.stepLabel ? `Current step: "${scope.stepLabel}". ` : '') +
        `Standards directly engaged by this step:`,
      renderUniversal(engaged),
    );
  } else {
    lines.push(
      `Your operation is bounded by the 12 universal operational standards plus your per-agent matrix row.`,
      ``,
      `## Universal standards`,
      renderUniversal(),
    );
  }
  if (!omitMatrix) {
    lines.push(``, `## Your matrix row`, renderMatrix());
  }
  if (!omitRunbook) {
    lines.push(``, `## Your runbook (Std 6)`, renderRunbook());
  }
  lines.push(``, `## Operating principles`, renderPrinciples(engaged));
  return lines.join('\n');
}