Raw data is valuable, but insights are transformative. Synapse has been good enough at capturing daily activities, but the next logical step has always been to synthesize that data into high-level understanding. Today’s task is to implement the new feature on the roadmap: AI-Powered Weekly Reports.
This feature moves beyond simple data retrieval and uses LLM to analyze a week’s worth of activities, generating a concise, structured summary of your time allocation, key accomplishments, and emerging trends.
High-Level Architecture
The design is straightforward, focusing on reliability and efficiency. Storing the structured output from the LLM is a core principle, serving as a cache to prevent expensive re-computation and creating a historical dataset for future analysis.
The data flow is as follows:
- User Request: A
POST
request is sent from the frontend to/api/reports
with a specific date. - API Endpoint: The Next.js route handler checks if a report for that week already exists in the database. If so, it returns the cached version.
- Data Retrieval: If no report exists, the API fetches all relevant
Activity
entries for the calculated week from the PostgreSQL database via Prisma. - Report Agent: The activities are passed to our new
reportAgent
, which bundles the data into a carefully engineered prompt. - LLM Analysis: The agent sends the prompt to our local LLM, requesting a structured JSON response.
- Validation & Storage: The agent validates the LLM’s JSON output against a Zod schema. On success, the structured content is saved to the new
Report
table in the database. - Response: The newly created report is returned to the user.
Key Technical Components
1. Database Schema: Storing the Insights
We introduced a new Report
model in our prisma/schema.prisma
. Storing the output as Json
is crucial for flexibility.
// prisma/schema.prisma
model Report {
id String @id @default(cuid())
type ReportType // WEEKLY or MONTHLY
status ReportStatus // PENDING, COMPLETED, FAILED
startDate DateTime
endDate DateTime
content Json? // The structured JSON from the LLM
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([type, startDate])
}
2. The Report Agent: Enforcing Structure with Zod
Again, the import imperative is to ensure that the data we receive from the LLM adheres to a specific structure. The reportAgent uses Zod to define a strict schema for the expected output.
// src/lib/reportAgent.ts
export const WeeklyReportSchema = z.object({
title: z.string(),
summary: z.string(),
timeAnalysis: z.object({
totalMinutes: z.number(),
workMinutes: z.number(),
lifeMinutes: z.number(),
workLifeRatio: z.string(),
}),
keyActivities: z.array(z.object(/* ... */)),
tagAnalysis: z.array(z.object(/* ... */)),
insightsAndTrends: z.string()
});
The LLM is explicitly instructed to generate a JSON object conforming to this schema, which we then parse and validate before use.
3. Final Thoughts
Once the backend is done, then the frontend features a new /reports page with a two-column layout. Also a dedicated
Working with Gemini has made this process easy and straight forward from feature definition to architectural design and final implementation. I think the future work I will involve in (whatever kind, web, or mobile which I have never touched before), function or feature wise will be super straightforward. More interesting work to be carried and experimented would be model evaluation and fine-tuning to compare different models’ performances.🚀🚀🚀