A polished, traceable suspicious-activity / fraud triage demo built with Next.js and TypeScript.
The project is optimized for live presentation:
- risk scoring stays rule-based and explainable
- guidance retrieval is local-first and file-based
- multi-agent execution is traceable in the API, terminal logs, and UI
- demo mode still works when OpenAI is unavailable
HYBRID_OPENAIcan make selected agents OpenAI-first while preserving local fallback and verification
This app simulates an analyst workstation that can:
- browse synthetic, generated, and imported cases
- normalize alerts into a richer
CaseAlertdomain model - detect suspicious signals
- compute an explainable score and risk tier
- retrieve relevant local guidance passages with citations
- generate an investigator-facing summary narrative
- show a per-agent execution trace for debugging and demos
Browser workstation
-> POST /api/triage
-> orchestrator
-> AlertIntakeAgent
-> SignalDetectionAgent
-> RiskAssessmentAgent
-> GuidanceRetrievalAgent
-> InvestigatorRecommendationAgent
-> NarrativeSummaryAgent
-> structured result + trace
Important directories:
src/app/api/triage/route.ts: API entry pointsrc/lib/orchestration/: multi-agent runtime and trace collectionsrc/lib/agents/: local agent implementationssrc/lib/openai/: OpenAI-backed agent adapterssrc/lib/rag/: local guidance ingestion, chunking, and retrievalsrc/lib/data/: dataset loading, filtering, random selection, and comparisondata/cases/: sample, generated, and imported case datasetsdata/knowledge/: raw and processed guidance documentstests/: lightweight reliability-focused tests
Execution order:
AlertIntakeAgentSignalDetectionAgentRiskAssessmentAgentGuidanceRetrievalAgentInvestigatorRecommendationAgentNarrativeSummaryAgent
Responsibilities:
AlertIntakeAgent: normalizes request payloads into the shared structured alert shapeSignalDetectionAgent: detects suspicious signals and mitigating contextRiskAssessmentAgent: calculates score, tier, and score breakdownGuidanceRetrievalAgent: retrieves local passages and, in hybrid mode, can use OpenAI to explain why they matterInvestigatorRecommendationAgent: turns risk output into an investigator-ready action recommendationNarrativeSummaryAgent: produces the final narrative summary
LOCAL_RULES_ONLY
- every agent runs locally
- scoring is fully deterministic
- the summary uses the local template
HYBRID_OPENAI
AlertIntakeAgentis OpenAI-first with local fallback/verificationSignalDetectionAgentis OpenAI-first with local fallback/verificationGuidanceRetrievalAgentkeeps raw retrieval local, then uses OpenAI to interpret the passagesInvestigatorRecommendationAgentis OpenAI-first with rule-based verificationNarrativeSummaryAgentis OpenAI-first with local fallbackRiskAssessmentAgentremains local and authoritative
The retrieval layer is deliberately local-first and database-free.
- raw guidance lives in
data/knowledge/raw - the app chunks and processes documents into
data/knowledge/processed/guidance-store.json - retrieval is keyword-first with a lightweight similarity fallback
- results always return visible citation metadata
- in hybrid mode, retrieved passages can also receive an OpenAI-generated relevance explanation
This keeps the system easy to debug and safe for classroom or judge-panel demos.
More detail is in RAG_README.md.
The demo uses a scalable local dataset workflow:
data/cases/sample: curated sample casesdata/cases/generated: synthetic cases generated by scriptdata/cases/imported: CSV-imported cases normalized into the internal schema
The internal case model centers on CaseAlert, not on a flat alert row. A loader utility supports:
- browse all cases
- filter by source and expected risk tier
- load a random case
- compare two cases
More detail is in DATASET_README.md.
- Install dependencies:
npm install-
Copy
.env.exampleto.env.local -
Keep demo mode local and reliable:
TRIAGE_MODE=LOCAL_RULES_ONLY
OPENAI_API_KEY=$OPENAI_API_KEY
OPENAI_MODEL=gpt-4.1-mini
OPENAI_AGENT_MODEL=gpt-4.1-mini- Start the app:
npm run devSet:
TRIAGE_MODE=HYBRID_OPENAI
OPENAI_API_KEY=your_openai_key_here
OPENAI_MODEL=gpt-4.1-mini
OPENAI_AGENT_MODEL=gpt-4.1-miniBehavior in this mode:
- OpenAI becomes the primary implementation for intake, signal detection, guidance interpretation, recommendation, and narrative summary
- local logic still verifies or replaces those outputs when needed
RiskAssessmentAgentremains rule-based and final- if OpenAI fails for any selected agent, that step falls back locally and the pipeline still completes
Tradeoffs:
- stronger demo storytelling and more natural agent behavior
- more API cost and dependency on network/API availability
- more trace complexity, which is why provider/model/fallback details are surfaced explicitly
Based on live logs and replay, intermittent fallback appears more likely than a permanent pipeline failure. The evidence is consistent with output variability on one or two schema-constrained steps, especially because the same case can later succeed without code changes.
This does not prove the root cause is only the model. Prompt/schema brittleness or other nondeterministic application factors could also contribute. The safest next step is to compare models under identical settings and add a single targeted retry on schema-validation failure before local fallback.
Recommended checks:
- verify the OpenAI call uses structured outputs such as
response_format: { type: "json_schema", ... strict: true }or the equivalent Responses API path - pin model versions for consistency-sensitive demos, since prompt behavior can vary across model snapshots
- run a small eval matrix such as
50-100replays per case, per model, with the same settings - log the exact validator failure type: parse error, missing key, enum drift, extra field, truncation, or timeout
- add one retry only for schema-validation failures before fallback
There are three trace surfaces:
- Terminal logs
- each agent step logs timing, provider, and metadata during local development
- API response
/api/triagereturns a structuredtraceobject with run ID, mode, provider summary, timing, and per-agent summaries
- UI trace panel
- the workstation shows the step timeline, provider, model, fallback reason, timing, metadata, and expandable summaries
Trace format includes:
runIdcaseIdmodestartedAtcompletedAtdurationMsproviderSummaryevents[]
Each event includes:
agentNamestatusstartedAtcompletedAtdurationMsinputSummaryoutputSummaryinference(provider,model,attempted,verifiedWithRules,fallbackReason,requestId)- optional
details - optional
metadata
npm run dev
npm run test
npm run test:watch
npm run generate:cases
npm run import:cases -- --input=data/cases/imported/example-import.csv --mapping=data/cases/imported/example-mapping.json- The scoring logic is intentionally simplified for explainability.
- Retrieval is local and lexical; it is not embedding-powered yet.
- Imported CSV support depends on a mapping file rather than automatic schema inference.
- OpenAI-primary behavior depends on strict schema validation and safe fallbacks; malformed model output is discarded in favor of local logic.
- This demo is not suitable for real fraud operations without stronger governance, controls, and data integrations.
- add embedding-backed retrieval behind the existing RAG interfaces
- compare multiple models or providers behind the same agent interface
- expand imported dataset tooling with UI-assisted mapping
- add richer trace analytics and case history persistence