For the last year, we operated on a lie.
The lie was that with enough data and enough GPU time, you could build a sovereign Large Language Model that understood the Indian market. We chased this dragon down the rabbit hole. We scraped 10 million pages of SEBI filings. We meticulously deserialized multi-page PDF tables with vision models. We even wrote a custom loss function to penalize the model for getting numbers wrong.
We built a 70-billion parameter beast that could quote the exact "Related Party Transaction" value from a 2019 annual report. We thought we had won.
We were dead wrong.
Our model was a brilliant historian. But the market doesn't pay for history. The moment a new RBI circular dropped, our model suffered from catastrophic forgetting. When a company quietly filed an addendum on the MCA portal, our "truth" became a lie. We had built a static photograph of a market that is a chaotic, flowing river.
So we burned it all down.
"We pivot from building a better model to engineering a better mind. This is the technical deep dive on how we rebuilt Heightss as a Cyclic Agentic Graph."
The Core Problem: Financial Entropy and the Limits of Memory
To build our original model, we had to conquer the "Data Labyrinth" of Indian finance. This journey revealed the fundamental flaws in the static LLM approach.
The Unstructured Nightmare
We started with PyPDF2. It failed on multi-column layouts. We moved to Camelot. It failed on borderless tables. We finally landed on fine-tuned LayoutLMv3 vision models to treat pages as images, detecting tables, hierarchies, and merged cells. We even developed a proprietary Markdown Serialization protocol, injecting tokens like <HEADER level=1> and <TOTAL parent_id=3> to preserve the 2D structure of a balance sheet in a 1D sequence. The engineering was heroic. The result was a perfect snapshot of the past.
The "Redeemable Preference Share" Trap
Here's a classic "gotcha." Under old IGAAP accounting, Redeemable Preference Shares were Equity. Under new Ind AS, they're Debt. A model trained on a mix of data without a temporal reasoning layer will calculate a company's leverage completely wrong, reporting a massive (and illusory) spike in debt post-2016. No amount of fine-tuning can fix this without dynamic logic.
The New Stack: A Digital Investment Committee
We stopped thinking about models and started thinking about roles. An investment committee has a junior analyst, a senior analyst, a librarian, and a skeptical partner. Our graph replicates this.
- The Reasoning Engine: Claude 4.5 Sonnet. We chose Claude 4.5 for its monstrous context window and its state-of-the-art instruction following. It functions as our "Senior Analyst," capable of holding the entire logical structure of a 400-page filing in its "mind" to trace complex relationships.
- The Grounding Oracle: Perplexity Sonar Pro. A standard RAG pipeline is a "dumb" librarian. Perplexity Sonar Pro is a "synthesizing librarian." It doesn't just return 10 blue links; it reads them, understands them, and provides a direct, synthesized answer from the latest market data.
- The Central Nervous System: LangGraph. This is the quantum leap. Instead of a linear chain, LangGraph allows us to build a stateful, cyclic graph. We implement a "Cognitive Cycle" for financial analysis. The state of the graph acts as the agent's short-term memory.
Cognitive Architecture: Tracing a Due Diligence Query
Let's trace a real-world query from start to finish: "Reliance Industries' Q3 FY25 debt seems low. Is this genuine, or are they hiding leverage in off-balance-sheet items or subsidiary debt?"
Step 4: The "Oh Sh*t" Moment (The Critic Node - Claude 4.5)
Sonar Pro returns a synthesized answer: "On December 2nd, 2025, RIL filed a notice with the BSE stating they have settled the GST dispute for a one-time payment of ₹15,000 Crores, to be paid in Q4." This information is not in the Q3 report. The graph now calls a "Critic" instance of Claude. The prompt is designed for conflict:
"DRAFT ANALYSIS: [Original analysis showing low risk]. NEW EVIDENCE: [Sonar Pro's output about the ₹15,000 Crore settlement]. CRITIQUE: The draft is now factually incorrect. Re-evaluate the debt analysis considering this new cash outflow. Is the 'low debt' narrative still valid?"
Code Philosophy: The State Machine
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
from langgraph.graph.message import add_messages
# The Agent's "Short-Term Memory"
class ForensicState(TypedDict):
messages: Annotated[list, add_messages]
# Define Nodes: The "specialists" in the investment committee
def analyst_node(state: ForensicState):
# Call Claude 4.5 to perform analysis
response = analyst_model.invoke(state['messages'])
return {"messages": [response]}
# The Conditional Router: The manager deciding who to talk to next
def router(state: ForensicState) -> str:
if state['messages'][-1].tool_calls:
return "researcher" # If the analyst needs facts, go to the researcher
else:
return "end" # If the analyst is confident, finish
The Real Alpha is the Agent Itself
We stopped chasing the perfect model because the market is an imperfect, moving target. The only way to win is with a system that is as dynamic, skeptical, and adaptive as the best human analyst.
The future of financial AI isn't a better chatbot. It's an autonomous agent that can run its own OODA loop (Observe, Orient, Decide, Act)—an agent that can watch your portfolio, detect an anomaly in a filing you haven't even read, and flag it for you before the market even notices.
We are no longer in the business of building language models. We are in the business of building digital minds.

