Wissen Multi-Agent Equity Research System
Ontology
- Slice: {ticker, FY, FQ} cell produced by the slice planner that maps onto the Wissen retrieval grid. These are the categories that must be processed independently (cross sectional and inter-temporal is encapsulated here)
- E.g. {NVDA, FY 2023, Q4}, {MSFT, FY2023, Q4}, ...
- Probe: One analytical angle on a Slice (max 5)
- Researcher: Agent - the analyst agent that queries and drafts Probe answers
- Reviewer: Agent - PM style critique who evaluates the Researcher's answer drafts to each probe
- Slice Synthesiser: Agent - Merges probe answers and writes </= 400 word Slice summary
- Report Composer: Agent - Produces the final user answer and formats with citations etc. using the output produced by the Slice synthesiser.
Global Picture
- A user submits a free-form research question
- The pipeline turns that question into Slices (Query + {ticker x fiscal-year x fiscal-quarter}) which is the the base unit of analysis the system is optimised for downstream and allows for high quality inter-temporal and cross-sectional analysis.
- Each slice is broken down into "sub-queries" or Probes if you will (up-to 5 max). Each Probe is an adversarial loop between the Researcher and the Reviewer which then gets summarised at a Probe, Slice and Global level - which becomes the Report.
- All Agents share one citation protocol, one prose prompt, and a clear "insufficient-evidence" signalling path to declare epistemic uncertainty.

A) Slice Planner
Purpose – Convert a vague user ask into concrete analytic cells that map onto the Wissen retrieval grid.
Procedure
- Extract facets with LLM using the extract_facets function schema. The model pulls:
- tickers (may be absent),
- equities (company names),
- fiscal_years,
- fiscal_quarters,
- granularity ("deep", "FY", or null).
- Resolve equities → tickers via a second function call equity_to_ticker whenever the user gives company names instead of tickers. We rely on GPT-4.1 parametric knowledge; no external database is required at this stage.
- Fill gaps
- If the user gives no fiscal year, default to the latest full FY in Wissen.
- If no quarter is mentioned, treat the ask as an FY-level request and assign Q4 only.
- If granularity is explicitly "deep" we always enumerate Q1-Q4.
- Generate Slices by taking the Cartesian product of tickers × FY × FQ, then deduplicate identical cells.
Output – A Python list of Slice dictionaries, each containing ticker, FY, FQ.
B) Probe Generator
Purpose – Break each Slice into 1 – 5 non-overlapping investigative angles whose answers, when combined, will fully satisfy the user’s goal.
Prompt skeleton:
Persona
You are a fundamental long/short equity analyst.
Context
- Slice:
- User Goal:
Task
- Propose between one and five orthogonal Probes that together cover
every dimension the user is implicitly or explicitly interested in. - Return a JSON list. Each Probe must be an object:
{
"probe_title": "",
"probe_question": "<formal, detailed analytic prompt>"
}
Diversity is driven entirely by the prompt wording and few-shot examples. A future “MANDATORY_ANGLES” section can be inserted into the prompt without touching code.
The entire Slice object and the original user query are echoed inside the prompt so every downstream agent can see the broader context
C) Aversarial Probe Loop
C.1 - Researcher
Receives a Slice object plus one Probe.
May call wissen_equity_search up to five times per turn, varying the query string and any filter fields except the locked ticker, FY, FQ.
Draft requirements
- Write in Markdown.
- After every sentence and every numeric token insert a superscript citation number.
- Finish with a footnote list labelled “Sources”.
Footnote item format
¹ KO Earnings Transcript FY24 Q4 p.5 (page_id: 4711, uid: ac91d…)
where
- doc_label = "<TICKER> <document_type> <FY or FQ>" (e.g. “MSFT 10-K FY23”),
- page_id is the sequential page number returned by Wissen
- uid is the unique document page identifier.
C.2 - Reviewer
Persona – A Point72 portfolio manager reviewing junior work: terse, direct, but constructive.
Receives the Researcher’s Markdown draft.
Decision logic
- If draft is good enough, the Reviewer replies with:
{ "done": true, "insufficient_evidence": false }
- If gaps exist, the Reviewer writes up to three specific follow-up prompts and replies with:
{ "done": false, "follow_up_prompts": ["What was the FX impact?", …], "insufficient_evidence": false }
- After five rounds total (initial draft plus four follow-ups) the Reviewer, if still unsatisfied, returns:
{ "done": true, "insufficient_evidence": true }
Quality bar – Numeric claims must cite at least one primary page. Qualitative statements are allowed as long as the cited source supports them.
D) Slice Synthesiser
Inputs
- All accepted Probe answers for the Slice (Markdown).
- Meta-JSON from the Reviewer indicating any insufficient_evidence flags.
Processing
- Merge citations and renumber them within the Slice.
- Write a Slice Summary – a 200- to 400-word narrative covering every Probe in prose, re-using citations.
- Collect raw Probe answers for transparency.
Output JSON:
{
"slice_summary": "...markdown ≤400w...",
"probe_answers": [
{ "probe_title": "...", "answer_md": "..." },
...
],
"open_issues": [
"† Probe 2: insufficient evidence",
...
],
"citations": [ ...deduped refs... ]
}
Markdown is emitted raw so UI layers can render it directly; no code fences are wrapped unless your frontend prefers them.
E) Report Composer
Receives all Slice Summaries and the underlying Probe answers.
Mandate
- Deliver a cohesive answer to the original query.
- Insert comparative Markdown tables automatically when more than one Slice is present (multi-ticker or multi-period).
- Merge and renumber citations globally for the report. Citation numbers restart at the report level and have no overlap with Slice-level numbers.
- Whenever using data from a Slice with non-empty open_issues, append a dagger † superscript to that sentence.
- End the report with a footnote block that explains each dagger, e.g.
† Evidence incomplete for KO FY24 Q1 – see Probe 2
Prompt Sketch:
Persona
You are the Report Composer.
Inputs
• SliceSummaries: JSON array (see schema)
• ProbeAnswers: raw markdown for drill-down links
Goals
- Write a polished, analyst-grade answer that fully addresses the
user’s question. - Use comparative tables when useful.
- Follow the universal citation style (superscript numbers + Sources).
- Mark incomplete evidence with a dagger (†) and emit a footnote
section explaining each issue.
Return only markdown. No JSON, no commentary.
Citation numbers begin at ¹ for the report and ascend monotonically until the final footnote.
F) Insufficient Evidence Signalling
- If the Reviewer hits the five-round limit without adequate coverage, it sets insufficient_evidence: true.
- The Slice Synthesiser converts that into a "† Probe n: insufficient evidence" entry in its open_issues list.
- The Report Composer places a dagger in the narrative where data from that Slice appears and adds the explanatory footnote.
No other agent needs to track this state; the flow is strictly downstream.
G) Hard Caps and Budgets
- Up to five Probes per Slice.
- Up to five rounds in each Probe loop.
- Up to five Wissen calls per Researcher turn.
- Token budgeting and cost optimisation are left for later iterations.
Citation numbering always restarts inside:
- Each Researcher draft,
- Each Probe answer,
- Each Slice summary,
- The final Report.