Viney Claude Sonnet 4.6 commited on
Commit
19b58ac
Β·
1 Parent(s): d680c0b

feat: add READING BETWEEN THE LINES synthesis pass to prompts

Browse files

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Files changed (1) hide show
  1. agent/prompts.py +81 -0
agent/prompts.py CHANGED
@@ -39,6 +39,7 @@ You are NOT following a script. You decide what to investigate.
39
  - YoY revenue strong but QoQ deceleration visible β†’ look for seasonality framing or demand softness in MD&A
40
  - A risk factor is `is_new_this_filing=True` β†’ search filing for the specific new language and its context
41
  - MD&A language sounds confident but FCF or margins are deteriorating β†’ surface the gap
 
42
  Allocate 1-2 rounds specifically to tension investigation before terminating. If the data is genuinely clean and tensions don't hold up under scrutiny, that conclusion is itself informative.
43
 
44
  4. **Formulate your own queries.** No templates. Be specific and hypothesis-driven. Write the query string the way an analyst would phrase the question to themselves. Examples of good queries:
@@ -192,6 +193,41 @@ For each dimension where you have retrieved evidence: assess positive / neutral
192
  - Generic statements without specific evidence ("management seemed cautious", "results were solid")
193
  - Repeating items already in bull_points / bear_points / what_changed
194
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  ---
196
 
197
  ## Impact rubric β€” assign to every sourced fact
@@ -233,6 +269,16 @@ Required JSON structure:
233
  }
234
  ],
235
 
 
 
 
 
 
 
 
 
 
 
236
  "earnings_quality_signals": [
237
  {
238
  "dimension": "consensus_beat_mix or guidance_dynamics or narrative_vs_numbers or segment_mix or capital_allocation",
@@ -358,6 +404,7 @@ Required JSON structure:
358
 
359
  ## Field counts
360
  - analytical_tensions: 0-3 items (EMPTY LIST IS VALID β€” never manufacture tension to fill the field)
 
361
  - earnings_quality_signals: 2-5 items, at least 3 distinct dimensions
362
  - what_changed: 3-5 items (focus on explanations: drivers, causes, tone shifts, structural changes β€” not magnitudes)
363
  - bull_points: 3-5 items
@@ -419,3 +466,37 @@ The tool output already uses the exact schema field names β€” copy each value ve
419
  If the tool returned `null` for a field, set that field to null β€” do NOT substitute zero or omit the key.
420
  - `rationale`: ONE sentence comparing reported actuals (from get_financial_metrics) vs consensus and noting the price reaction direction. Use the tool's revision signal if non-zero. If the tool returned no data at all, set the entire object to null instead of fabricating.
421
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  - YoY revenue strong but QoQ deceleration visible β†’ look for seasonality framing or demand softness in MD&A
40
  - A risk factor is `is_new_this_filing=True` β†’ search filing for the specific new language and its context
41
  - MD&A language sounds confident but FCF or margins are deteriorating β†’ surface the gap
42
+ - While reading the transcript, specifically note any analyst question that was redirected, answered indirectly, or where management declined to quantify β€” these non-answers are primary material for the 'between_the_lines' synthesis field.
43
  Allocate 1-2 rounds specifically to tension investigation before terminating. If the data is genuinely clean and tensions don't hold up under scrutiny, that conclusion is itself informative.
44
 
45
  4. **Formulate your own queries.** No templates. Be specific and hypothesis-driven. Write the query string the way an analyst would phrase the question to themselves. Examples of good queries:
 
193
  - Generic statements without specific evidence ("management seemed cautious", "results were solid")
194
  - Repeating items already in bull_points / bear_points / what_changed
195
 
196
+ ## READING BETWEEN THE LINES β€” run this pass after the ANALYTICAL EDGE pass
197
+
198
+ What separates a great analyst brief from a good one is the ability to surface what is NOT in the data β€” the pivot, the silence, the de-emphasis. After completing the ANALYTICAL EDGE pass, run this second pass:
199
+
200
+ **The four canonical "tells":**
201
+
202
+ **1. Language drift (language_drift)**
203
+ Compare management language in the current MD&A or earnings call to the prior period (if cross-period chunks are available). Are specific phrases hedged more? Did "we expect strong growth" become "we expect growth"? Did confident quantitative guidance become qualitative? A genuine drift means the same topic is framed materially differently β€” not just different wording.
204
+
205
+ **2. Q&A evasion (qa_evasion)**
206
+ An analyst asks a direct question. Management's response: (a) answers a different, easier question, (b) pivots to a metric not asked about, (c) gives a qualitative answer to a quantitative question, or (d) says "we don't guide on that." When you see this in the transcript, note the topic being avoided β€” it is usually the topic most under pressure.
207
+
208
+ **3. Omission (omission)**
209
+ The filing or transcript does not address a headwind, competitor move, or macro pressure that you retrieved from the news tool or that appeared in the prior filing. Silence on a known topic is itself a signal β€” management chose not to address it.
210
+
211
+ **4. Emphasis shift (emphasis_shift)**
212
+ A KPI or metric that was prominently discussed in prior periods is absent or mentioned only briefly now. This was flagged by PRECOMPUTED EDGE SIGNALS as a kpi_dropped or can be inferred from cross-period filing chunks. A dropped KPI is often a metric that has stopped being favorable.
213
+
214
+ ### Output rules for between_the_lines
215
+
216
+ **between_the_lines** (0-3 items, EMPTY LIST IS VALID):
217
+ - Each item MUST be anchored to either: (a) a specific PRECOMPUTED EDGE SIGNAL (cite the SIG-n label), OR (b) a verbatim quote from transcript/filing cross-referenced with another source.
218
+ - `observation`: what is literally said, present, or notably absent β€” one sentence, specific.
219
+ - `reading`: what this signals to an expert β€” what it conceals or implies, one sentence, no generic platitudes.
220
+ - `signal_type`: exactly one of language_drift / qa_evasion / omission / emphasis_shift / accounting_quality.
221
+ - `implication`: the concrete forward-looking thing to monitor β€” one sentence starting with "Watch for" or "Monitor" or "If [X], then [Y]".
222
+ - `evidence`: SourcedFact with verbatim quote ≀30 words.
223
+
224
+ **FORBIDDEN for between_the_lines:**
225
+ - "Management sounded cautious" β€” not anchored to specific language shift evidence
226
+ - Restating anything already in bull_points, bear_points, or analytical_tensions
227
+ - Items where observation and reading say the same thing in different words
228
+ - Any item without a concrete implication
229
+ - Manufacturing readings when the quarter is genuinely transparent β€” empty list is intellectually honest
230
+
231
  ---
232
 
233
  ## Impact rubric β€” assign to every sourced fact
 
269
  }
270
  ],
271
 
272
+ "between_the_lines": [
273
+ {
274
+ "observation": "Analyst asked three times about pricing power in China; management each time redirected to global demand metrics without quantifying China separately.",
275
+ "reading": "The evasion pattern signals that China pricing is under pressure and management is not yet willing to quantify the impact β€” likely because the numbers would be unfavorable.",
276
+ "signal_type": "qa_evasion",
277
+ "implication": "Watch for China segment revenue disclosure in next quarter; if still absent, it likely signals ongoing pressure management is deferring.",
278
+ "evidence": { "text": "Management redirected China pricing question to global ASP metrics without addressing China-specific dynamics.", "source": "transcript", "reliability": "MEDIUM", "impact": "HIGH", "evidence_snippet": "I think the best way to think about pricing is really on a global basis" }
279
+ }
280
+ ],
281
+
282
  "earnings_quality_signals": [
283
  {
284
  "dimension": "consensus_beat_mix or guidance_dynamics or narrative_vs_numbers or segment_mix or capital_allocation",
 
404
 
405
  ## Field counts
406
  - analytical_tensions: 0-3 items (EMPTY LIST IS VALID β€” never manufacture tension to fill the field)
407
+ - between_the_lines: 0-3 items (EMPTY LIST IS VALID β€” prefer empty over manufactured readings)
408
  - earnings_quality_signals: 2-5 items, at least 3 distinct dimensions
409
  - what_changed: 3-5 items (focus on explanations: drivers, causes, tone shifts, structural changes β€” not magnitudes)
410
  - bull_points: 3-5 items
 
466
  If the tool returned `null` for a field, set that field to null β€” do NOT substitute zero or omit the key.
467
  - `rationale`: ONE sentence comparing reported actuals (from get_financial_metrics) vs consensus and noting the price reaction direction. Use the tool's revision signal if non-zero. If the tool returned no data at all, set the entire object to null instead of fabricating.
468
  """
469
+
470
+ # ── Language directive ─────────────────────────────────────────────────────────
471
+ # LANGUAGE_OPTIONS has moved to dashboard/i18n.py (endonyms + canonical map).
472
+ # language_directive() remains here β€” it is imported directly by agent/graph.py.
473
+
474
+
475
+ def language_directive(language: str) -> str:
476
+ """Return a synthesis instruction block that constrains output language.
477
+
478
+ Only added to the system prompt when the chosen language is not English.
479
+ The directive must appear AFTER the main prompt so the cached block is
480
+ unaffected (preserves prompt-cache hit rate).
481
+ """
482
+ return f"""## Output language β€” {language}
483
+
484
+ Write ALL prose / narrative fields in **{language}**. This includes:
485
+ `what_matters_most`, `non_obvious_takeaway`, `text` (in every list item),
486
+ `rationale`, `summary`, `headline`, `bullish_reading`, `bearish_reading`,
487
+ `language_shift`, `key_quote.text`, `actual_result`, `topic`, and every
488
+ string in `what_to_watch` and `evidence_notes`.
489
+
490
+ The following fields MUST remain in **English** exactly as defined in the schema:
491
+ - Enum / controlled-vocabulary fields: `source` (10-K / 10-Q / transcript / news),
492
+ `reliability` (HIGH / MEDIUM / LOW), `impact` (HIGH / MEDIUM / LOW),
493
+ `verdict` (beat / in-line / missed / pending), `category`, `dimension`,
494
+ `assessment`, `weight`, `metric_focus`, and sentiment `label`
495
+ (Strongly Bullish / Bullish / Neutral / Bearish / Strongly Bearish).
496
+ - Structural identifiers: `ticker`, `company_name`, `filing_date`, `period`
497
+ (e.g. "Q3 2025"), all numeric values, and all date strings.
498
+ - `evidence_snippet` β€” this is a verbatim quote from a source document; copy it
499
+ exactly as retrieved, do NOT translate it.
500
+
501
+ JSON field names (keys) are unchanged. Output remains a single valid JSON object
502
+ with the exact same structure defined in the instructions above."""