AI Research
AI Research Loop
The AI research pipeline runs Claude as an autonomous agent to deep-research every new token. It produces a structured report covering LARP probability, technical novelty, GitHub authenticity, and comparison to prior art. All findings are stored in R2 (full report) and D1 (summary row).
Pipeline architecture
Research is triggered every 6 hours for the top 20 tokens by volume that haven't been researched in the last 24 hours (market cap > $1,000). It can also be triggered manually via the admin endpoint. The research queue worker calls Claude with gathered context, stores the report in R2, and updates the D1 summary row.
// Queue message flow:
// [2h cron, every 6h] → seshat-research-queue → [research worker]
// ↓
// gather context (Bankr + Dexscreener)
// ↓
// Claude single completion
// ↓
// R2: reports/{address}/{timestamp}.json
// D1: research_reports row → re-score token
// Research trigger criteria:
// market_cap_usd > 1000
// AND no research_report in last 24h
// ORDER BY volume_24h_usd DESC LIMIT 20
// Manual trigger: POST /api/admin/trigger-researchWhat gets researched
For each token, the research agent gathers the following inputs before calling Claude:
Token metadata
Symbol, name, website URL from the tokens table (sourced from Bankr API at discovery time).
Market data
Price, 24h volume, market cap, liquidity — from Dexscreener. Token age derived from created_at.
Developer profile
feeRecipient wallet's developer record: on-chain category, onchain_score, background_score, smart follower count, any KOL/VC associations.
Contract safety
Whether the contract is Basescan-verified and whether ownership has been renounced.
Prior art (web search)
Claude is given the token name and description to compare against known existing projects and assess novelty.
Claude prompt design
The research agent uses a structured system prompt that constrains Claude to output valid JSON. The prompt is designed to minimize output tokens. Claude is instructed to not hallucinate — if something is unknown, it should say so explicitly rather than invent details.
const SYSTEM = `You are Seshat, an on-chain intelligence agent specializing in
crypto project authenticity analysis. Evaluate whether this project is
genuine or a LARP (fake project with no real technical substance).
Output ONLY valid JSON matching the ResearchReport schema. No prose.
Be precise and conservative. Missing data is not evidence of fraud,
but it does lower confidence. Do not invent GitHub repos or team members.`;
// Context passed per token:
const prompt = `
Token: ${symbol} / ${name}
Contract: ${address} (Base)
Website: ${websiteUrl ?? "none"}
Market cap: $${mcap?.toLocaleString() ?? "unknown"}
Volume 24h: $${vol24h?.toLocaleString() ?? "unknown"}
Age: ${ageDays} days since launch
Developer (feeRecipient wallet: ${feeRecipientWallet}):
Category: ${devCategory ?? "unknown"}
On-chain score: ${onchainScore ?? "not yet scored"}
Background: ${backgroundScore ?? "not yet scored"}
Smart followers: ${smartFollowers ?? 0}
KOL associations:${kolNames.join(", ") || "none"}
Contract verified: ${contractVerified ? "yes" : "no"}
Ownership renounced: ${ownershipRenounced ? "yes" : "no"}
Evaluate and output JSON.
`;LARP detection
LARP (Live Action Role Play) detection is Claude's primary task. The output is a 0–100 integer representing probability that the project has no real technical substance. Tokens with larp_probability > 75 are penalised heavily in the composite score.
// research_reports schema — key LARP fields: is_larp: INTEGER -- 1 if Claude determined it's a LARP larp_probability: INTEGER -- 0-100 (not 0.0-1.0) confidence: TEXT -- 'low' | 'medium' | 'high' | 'very_high' // How larp_probability affects composite score (in scoring.ts): // larp_probability >= 75 → tech_research_score = max(2, raw_score * 0.1) // larp_probability >= 50 → tech_research_score = max(10, raw_score * 0.4) // larp_probability < 50 → tech_research_score = raw_score (no penalty) // Tokens with larp_probability > 50 are never sent as alerts // regardless of composite score.
Novelty scoring
Novelty measures whether this is a genuinely new idea or a clone of existing projects. A score near 100 means nothing similar exists. Below 30 typically means it's a direct copy of a well-known concept. Claude is given the token description and asked to compare it against known prior art from memory.
// Novelty scale:
// 80–100: Genuinely novel — new mechanism, new use case, no close comparables
// 60–79: Some novelty — variation on existing theme with differentiation
// 40–59: Low novelty — common concept (e.g. generic "AI agent" token)
// 0–39: Clone / direct copy of existing project
// prior_art field: structured list of similar existing projects
prior_art: [{
name: string, // e.g. "BarnBridge"
url: string, // e.g. "https://barnbridge.com"
similarity: number, // 0-100
launch_date: string,
}]Beyond the report — related agentic loops
The same agentic pattern (Claude + web search, structured JSON, fail-soft) powers three further intelligence outputs that surface on token pages and the dashboard:
Project / developer identity
ProjectResearcher resolves who is really behind a token — project name, company, and the person/team — from the creator wallet's X handle and the open web. Stored on the token (identity_research); qualitative, never scored.
Trending narratives
NarrativeScanner clusters scored tokens into narrative groups (AI, DeFi, Meme, …) with a momentum score, exposed via GET /api/narratives and the dashboard Narratives tab.
Swarm sentiment
A multi-persona Claude swarm debates a high-conviction token (composite ≥ 65) into a bullish / bearish / consensus read with key risks, exposed via GET /api/tokens/:address/swarm. A read-only signal — it never feeds the composite score.
Report storage
// R2: full report JSON (one per research run — multiple runs accumulate)
const r2Key = `reports/${tokenAddress}/${Date.now()}.json`;
await env.REPORTS.put(r2Key, JSON.stringify(fullReport), {
httpMetadata: { contentType: "application/json" },
});
// D1: latest summary row (upserted on each run)
-- research_reports table
CREATE TABLE research_reports (
id TEXT PRIMARY KEY,
token_address TEXT NOT NULL,
is_novel_tech INTEGER, -- 1 = Claude assessed genuine novelty
novelty_score INTEGER, -- 0-100
is_larp INTEGER, -- 1 = LARP detected
larp_probability INTEGER, -- 0-100
is_leading INTEGER, -- 1 = ahead of existing solutions
leadership_score INTEGER, -- 0-100
traction_score INTEGER, -- 0-100
notability_score INTEGER, -- 0-100
prior_art TEXT, -- JSON: PriorArt[]
github_signals TEXT, -- JSON: GitHubSignals
traction_signals TEXT, -- JSON: TractionSignals
summary TEXT, -- 2-3 sentence summary
key_findings TEXT, -- JSON: string[]
confidence TEXT, -- 'low' | 'medium' | 'high' | 'very_high'
model_used TEXT, -- e.g. 'claude-sonnet-4-6'
tokens_used INTEGER,
r2_key TEXT, -- pointer to full report in R2
generated_at TEXT DEFAULT (datetime('now'))
);Cost management
Research is only triggered for tokens not already researched in the last 24 hours, and only when market cap is above $1,000. The average report costs approximately $0.003 in Claude API fees. A token that stays in the tracker for 30 days costs roughly $0.09 in research over its lifetime.
~3,000
Avg input tokens
~600
Avg output tokens
~$0.003
Cost per report