From thirty days of votes to a one-paragraph synthesis.
The verdict is not a single AI prompt; it is a sequence of separate computations, each validated before the next runs. Solo Free runs the numerical summary in Python only; paid Solo, Couple, and Family tiers add the our AI synthesis tool synthesis paragraph on top of the same numbers.
The trajectory chart.
Each evening vote is recorded as one of five ordered levels · Strong No, Lean No, Unsure, Lean Yes, Strong Yes · mapped to the integer set {-2, -1, 0, +1, +2}. For each participant, the sequence of daily values across the decision period forms the raw conviction series.
Two transformations are applied. First a missing-day fill: if a participant skipped a day, the gap is left as null, never interpolated; trajectory math runs on the present-day mask. Second a smoothing pass: an Exponentially Weighted Moving Average (EWMA) with a half-life of three days, chosen to follow weekly mood swings without erasing them.
The smoothed series is what the trajectory chart on the verdict page renders. The raw series is preserved alongside it; both are exported in the data export.
Overlap of the two streams.
For decisions with two participants, the agreement rate measures the share of days on which the two participants landed on the same side of the Unsure midpoint. A day counts as agreement if both votes are non-null and lie on the same side of zero; an Unsure day counts as a half-agreement.
The agreement rate is published as a percentage. It is not a verdict by itself; a 90% agreement rate on a question both participants leaned No to is a different verdict from 90% on a question they leaned Yes to. The synthesis paragraph reads agreement rate alongside the trajectory rather than in isolation.
A signed gap, not an absolute one.
The divergence index measures, for couples and families, how far apart the participants' smoothed series sit on average, with sign preserved. A positive value means participant A leaned more positive than participant B across the period; a negative value, the opposite. This is the signed average gap rather than the absolute one, so that the verdict can describe who pulled toward yes and who toward no.
For Family Mode (three to four participants) we compute a divergence pair matrix rather than a single index. The pair with the largest absolute divergence is surfaced as the principal disagreement in the verdict.
TF-IDF on the notes, k-means on the embeddings.
The free-text notes attached to each vote are the richest data the product collects. They are processed in two passes. First, TF-IDF is run across the note corpus for the decision, with a small stopword list extended for couples-decision language (we drop "feel," "think," "today" and the like; they appear in nearly every note and carry no information). The top fifty terms by TF-IDF score are the candidate vocabulary.
Second, each note is embedded (a 768-dimensional vector from a small open-weight model run in-house, never via an external API) and clustered with k-means. The number of clusters is chosen by the gap statistic on a 2-to-6 range; in practice the answer is usually three or four. Each cluster is labelled by the highest-TF-IDF term that appears in a majority of its notes. The output is a small set of named themes with the day-indices on which each theme appeared.
Prompt engineering, in plain language.
On paid tiers, the numerical layers above are passed to our AI synthesis tool to produce the one-paragraph synthesis at the top of the verdict. The prompt is engineered around a single load-bearing constraint: the model writes the synthesis using only the themes the Python layer already extracted. It is forbidden to invent themes, to introduce content not present in the notes, or to override the trajectory with a different reading of the data.
SYSTEM:
You are the synthesis writer for a Counsel.day verdict.
You will be given a structured input containing:
- the question
- participant trajectories (smoothed)
- agreement rate, divergence index
- extracted themes with day-indices
Constraints:
- Write ONE paragraph, 90-140 words.
- Use ONLY the themes provided. Do not invent themes.
- Do not introduce content not present in the notes corpus
(the corpus is summarised in the themes list).
- Do not contradict the trajectory: if both participants
ended at Lean Yes, do not write that the decision is "no."
- Do not give advice, do not recommend a course of action,
do not name a clinician or therapist.
- Refer to participants by the names provided, not generic terms.
OUTPUT_SCHEMA:
{
"synthesis": "string, 90-140 words",
"next_conversation_prompt": "string, one sentence"
}
The model is called with structured output enabled, so the response shape is enforced rather than parsed. The next_conversation_prompt field is the concrete question the verdict suggests opening the post-verdict conversation with; it is rendered separately on the page.
Solo Free tier never calls our AI synthesis tool. The free-tier verdict surfaces the trajectory chart, the agreement rate (for couples), the divergence index, and the theme list, with a one-line numerical summary written by a deterministic Python template. The synthesis paragraph is a paid feature; the underlying data is the same.
What can go wrong, and what we do when it does.
We hold a regression test set of synthetic decisions whose expected outputs are known. The set is the basis for a nightly evaluation run that flags any drift in model behaviour. Three failure modes are explicitly tested.
- Model returns prose with no themes. Detected by checking that every theme in the input appears as a noun phrase in the synthesis, or that the synthesis explicitly states which theme it is focusing on. Failure: regenerate once; if it fails again, fall back to the Python-only verdict and flag the decision for human review.
- Model hallucinates themes not in the notes. Detected by checking that no noun phrase in the synthesis is outside the extracted theme vocabulary plus a small allow-list of bridging words. Failure: regenerate once; on second failure, fall back as above.
- Model produces a synthesis that contradicts the trajectory. Detected by a separate classifier that reads the synthesis and predicts the implied verdict polarity; if it does not match the trajectory's end-state, the synthesis is rejected. Failure: regenerate; on second failure, fall back.
Every failure mode resolves to either a regenerated synthesis or a fall-back to the numerical-only verdict. The verdict never publishes a synthesis the validators rejected.
The validators are unit-tested against the synthetic decision set; the model itself is held to that bar in production by the same validators running in the verdict-worker job before the verdict is written to the database.