synthesis.ts
2,736 bytes
| 1 | import type { AnonymizedPeer } from '../anonymize'; |
|---|---|
| 2 | import type { LlmMessage } from '../llm-client'; |
| 3 | import { jsonInstruction, ROUNDTABLE_PREAMBLE } from './index'; |
| 4 | |
| 5 | const SYNTHESIS_SHAPE = `{ |
| 6 | "finalAnswer": "the single best answer to the question, synthesized from the council", |
| 7 | "dissent": [ |
| 8 | { |
| 9 | "topic": "short label for an unresolved disagreement", |
| 10 | "positions": [ |
| 11 | { "label": "A", "position": "what response A maintains" }, |
| 12 | { "label": "B", "position": "what response B maintains" } |
| 13 | ] |
| 14 | } |
| 15 | ] |
| 16 | }`; |
| 17 | |
| 18 | /** |
| 19 | * Chairman synthesis (final stage). |
| 20 | * |
| 21 | * The chairman receives the anonymized final answers, the outstanding |
| 22 | * disagreements from the convergence check, and produces the authoritative |
| 23 | * answer plus an explicit dissent report. Answers are anonymized to the chairman |
| 24 | * as well - this is deliberate self-preference mitigation: even a chairman drawn |
| 25 | * from the same provider family as a council member cannot favor "its own" |
| 26 | * answer if it cannot identify it. Labels are mapped back to real models only |
| 27 | * after the chairman has committed its judgment. |
| 28 | */ |
| 29 | export function buildSynthesisPrompt( |
| 30 | question: string, |
| 31 | finalAnswers: AnonymizedPeer[], |
| 32 | remainingDisagreements: Array<{ topic: string; summary: string }>, |
| 33 | ): LlmMessage[] { |
| 34 | const answersBlock = finalAnswers |
| 35 | .map((a) => `--- Response ${a.label} ---\n${a.content}`) |
| 36 | .join('\n\n'); |
| 37 | |
| 38 | const disagreementBlock = |
| 39 | remainingDisagreements.length === 0 |
| 40 | ? 'The council largely converged; no major disagreements were flagged.' |
| 41 | : remainingDisagreements.map((d) => `- ${d.topic}: ${d.summary}`).join('\n'); |
| 42 | |
| 43 | return [ |
| 44 | { |
| 45 | role: 'system', |
| 46 | content: |
| 47 | 'You are the chairman of an expert roundtable. Several anonymized experts have ' + |
| 48 | 'answered a question and revised their answers after mutual critique. Your job ' + |
| 49 | 'is to deliver the single best final answer and an honest dissent report.\n\n' + |
| 50 | `${ROUNDTABLE_PREAMBLE}\n\n` + |
| 51 | 'Synthesize - do not merely pick one answer or average them. Take the strongest, ' + |
| 52 | 'best-supported reasoning from across the council. Where the council genuinely ' + |
| 53 | 'disagrees and the evidence does not settle it, do NOT paper over it: record it ' + |
| 54 | "in the dissent report with each side's position. The responses are anonymized; " + |
| 55 | 'judge only on merit.\n\n' + |
| 56 | jsonInstruction(SYNTHESIS_SHAPE), |
| 57 | }, |
| 58 | { |
| 59 | role: 'user', |
| 60 | content: |
| 61 | `Question:\n${question}\n\n` + |
| 62 | `Final answers from the council:\n\n${answersBlock}\n\n` + |
| 63 | `Points still in dispute after deliberation:\n${disagreementBlock}\n\n` + |
| 64 | 'Return your JSON synthesis now.', |
| 65 | }, |
| 66 | ]; |
| 67 | } |
| 68 | |