feat: add READING BETWEEN THE LINES synthesis pass to prompts
Browse filesCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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."""
|