Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Redis

สร้าง AI Article Research Assistant ด้วย Mastra และ Upstash

Introduction to AI Research Agents (บทนำ AI Research Agents)

Academic research เคลื่อนไหวเร็ว — papers ใหม่ปรากฏทุกวันบน arXiv และ pre-print servers อื่นๆ การติดตามด้วยมืออาจ overwhelming ใน guide นี้ เราจะสร้าง AI research assistant ที่:

  1. เข้าใจ natural-language question ของนักวิจัย
  2. หา papers ที่ relevant ที่สุดใน vector database ของ arXiv abstracts
  3. Summarize key insights และอธิบายว่าตอบคำถามอย่างไร
  4. ให้ direct PDF link สำหรับ deeper reading

เราจะทำได้ด้วย Mastra open-source TypeScript framework สำหรับสร้าง AI agents และ Upstash สำหรับ serverless Redis และ Vector storage นี่คือ live demo ของ article agent ที่โฟกัส AI research มัน deployed บน Vercel ให้คุณลอง

สร้าง AI Article Research Assistant ด้วย Mastra และ Upstash

What is Mastra? (Mastra คืออะไร)

Mastra เป็น batteries-included framework ที่ทำให้สร้าง production-grade AI agents ง่าย

  • Agents & Workflows — Compose agents, tools และ multi-step workflows
  • Retrieval-Augmented Generation (RAG) - Built-in memory และ vector stores
  • Multi-LLM - ทำงานกับ OpenAI, Claude และอื่นๆ

เราจะสร้าง agent ที่ใช้ Upstash Redis สำหรับ memory มันจะมี tool หา research articles ที่ relevant ซึ่งเราจะ embedded ไว้ใน Upstash Vector database ก่อนหน้านี้

สำหรับ deeper dive เช็ค Mastra documentation

Tech Stack for the Project (Tech Stack สำหรับโปรเจค)

  • Mastra Framework สร้าง AI agents และ tools
  • Upstash Redis ให้ conversation memory กับ agent
  • Upstash Vector เก็บ embeddings ของ research article abstracts
  • Next.js & Vercel สร้างและ deploy web application

เราจะใช้ Upstash Ratelimit จำกัด requests สำหรับ demo application ด้วย

Implementation Walkthrough (การเดินทาง Implementation)

การสร้าง application นี้เกี่ยวข้องกับการสร้าง 2 main components: Mastra server และ web application แม้จะอยู่ใน project เดียวกันได้ แตcleaner ถ้าแยกกัน มาเริ่มกับ Mastra server

Creating a Mastra Project (การสร้าง Mastra Project)

เพื่อสร้าง Mastra project ใหม่ รันคำสั่งนี้ใน terminal

npm create mastra@latest

มันจะถาม几个คำถาม สำหรับโปรเจคนี้ default settings ใช้ได้

Creating Agents and Tools (การสร้าง Agents และ Tools)

ขั้นแรกในการ config agent คือ define name, purpose และ tools สำคัญถึงการเลือก language model ที่ perform ดีสำหรับ tasks ที่กำหนด ในโปรเจคนี้ เราจะมี 1 agent และ 1 tool

export const articleAgent = new Agent({ name: "articleAgent", instructions: instruction, model: openai('gpt-4o'), tools: { articleQueryTool }, memory: memory });

Config agent ง่ายแค่นี้ เรา define articleAgent ด้วย instruction (เป็น system prompt), tools 专用, model และอีก component สำคัญ: memory

Agent's Memory (Memory ของ Agent)

Mastra ให้ agents ทั้ง chat history และ semantic recall capabilities ด้วยการ maintain memory ใน storage agent ให้คำตอบ personalized และ accurate มากขึ้น มาดู memory config ของ agent

export const memory = new Memory({ storage: myUpstashStore, options: { lastMessages: 10, semanticRecall: false, threads: { generateTitle: true } } });

เพื่อ enable chat history เราใช้ Upstash Redis เป็น storage option เรา initialize เป็น UpstashStore object ซึ่ง extends MastraStorage ให้ทำงาน seamless กับ Mastra agent

export const myUpstashStore = new UpstashStore({ url: process.env.UPSTASH_REDIS_REST_URL!, token: process.env.UPSTASH_REDIS_REST_TOKEN!, });

เราเคยพูดถึงการเพิ่ม semantic recall feature ให้ agent ซึ่งอนุญาตให้พิจารณา previous messages ที่ related กับ current context สำหรับนั้น agent ต้องการ vector database และ embedder เพื่อ process messages เนื่องจาก public demo ของเราไม่ใช่ personal use และไม่ต้อง remember messages across threads ต่างๆ เราจะไม่ใช้ feature นี้ แต implement ได้ดังนี้

export const myUpstashVector = new UpstashVector({ url: process.env.UPSTASH_VECTOR_REST_URL!, token: process.env.UPSTASH_VECTOR_REST_TOKEN!, }); export const memory = new Memory({ storage: myUpstashStore, vector: myUpstashVector, embedder: openai.embedding("text-embedding-3-small"), options: { lastMessages: 10, semanticRecall: { topK: 3, messageRange: 2, scope: 'resource' }, threads: { generateTitle: true } } });

ใน semantic recall config topK ระบุจำนวน similar messages ที่ retrieve messageRange กำหนด surrounding context มากน้อยแค่ไหนกับแต่ละ match และตั้ง scope เป็น 'resource' ทำให้ agent search across all threads ที่ associated กับ user ชื่อ 'resource' Cross-thread memory นี้ powerful และมีให้กับ Upstash

Tools (เครื่องมือ)

การสร้าง tool ใกล้เคียงการสร้าง agent เราให้ name, description, input/output schemas และ function ที่ execute ตอน agent ต้องการความสามารถของ tool

export const articleQueryTool = createTool({ id: 'get-relevant-article', description: 'Get relevant article information', inputSchema: z.object({ question: z.string().describe('the question about the field'), }), outputSchema: z.object({ bestOption: z.object({ abstract: z.string().describe('the abstract of the article'), title: z.string().describe('the title of the article'), pdfUrl: z.string().describe('the PDF URL of the article') }) }), execute: async ({ context }) => { return await querySimilar(context.question); }, });

เราใช้ Zod validate input/output schemas ช่วย maintain consistent responses และ minimize potential errors จาก LLM เรายัง define function สำหรับ tool ใช้ Tool ของเราจะ query large collection ของ research articles ซึ่ง periodically updated via arXiv API และ embedded ใน Upstash Vector database

const querySimilar = async (query: string) => { const { embedding } = await embed({ value: query, model: openai.embedding("text-embedding-3-small"), }); const results = await myMastraUpstashVector.query({ indexName: "arxiv", queryVector: embedding, topK: 3, }); if (results && results.length > 0) { const bestMatch = results[0]; const metadata = bestMatch.metadata as ArxivPaper; return { bestOption: { abstract: metadata.abstract, title: metadata.title, pdfUrl: metadata.pdfUrl } }; } throw new Error("No relevant information found"); }

เราสามารถทำ simple operations บน vector database ผ่าน UpstashVector instance ซึ่ง extends MastraVector ข้างต้น เรา query สำหรับ similar article abstracts ที่ embedded ไว้ก่อนหน้า และคืนค่า best result ให้ tool จดว่าเราใช้ embedding model เดียวกันสำหรับ query และ articles เราจะอธิบาย article embedding 詳細 ต่อไป

The Mastra Instance (Mastra Instance)

export const mastra = new Mastra({ storage: myMastraUpstashStore, agents: { articleAgent }, deployer: new VercelDeployer() });

เราแค่ระบุ agents ที่ใช้ และ Mastra object พร้อม เรายังให้ storage เพื่อ persist data beyond in-memory storage คุณเลือก deployment configs ที่มีอยู่ได้ เราจะ deploy ด้วย Vercel

Dengan default options จาก create-mastra-app เรามี file structure ที่ต้องการแล้ว:

. └── mastra ├── agents │ └── index.ts ├── tools │ └── index.ts └── index.ts 

เหลือแค่ environment variables ก่อน deploy

OPENAI_API_KEY= UPSTASH_VECTOR_REST_URL= UPSTASH_VECTOR_REST_TOKEN= UPSTASH_REDIS_REST_URL= UPSTASH_REDIS_REST_TOKEN=

วางใน .env.local สำหรับ local dev และเพิ่มใน deployment environment

ตอนนี้พร้อม build และ deploy Mastra server

npm run build && vercel --prod

เช็ค Vercel documentation ว่า deploy ยังไง

ระหว่าง develop เราใช้ Mastra Playground ดู server output รัน:

npm run dev

นี่ให้ link ไป web interface ที่คุณ chat กับ agent, รัน tools explicitly และ explore capabilities ของ server

ตอนนี้ถึงเวลาพูดถึงส่วนอื่นของ application

Next.js Server

เมื่อ Mastra server setup แล้ว เราต้องจัดการ 3 อย่าง: UI, communication กับ Mastra server และ article service ที่คุยกับ arXiv API และ embed abstracts เข้า Upstash Vector Mastra มี client SDK expose server functionalities ผ่านมัน คุณ access agents, tools, memory และมากกว่า Usage straightforward แตเราจะ share 例子 สำหรับ details เช็ค documentation ที่นี่ ใน Next.js project คุณแค่ install และใช้ client SDK

npm install @mastra/client-js@latest

ในโค้ด สร้าง instance ของ MastraClient ใช้ในโปรเจค

import { MastraClient } from "@mastra/client-js"; export const mastra_sdk = new MastraClient({ baseUrl: process.env.NEXT_PUBLIC_MASTRA_API!, retries: 3, });

คุณต้องตั้ง NEXT_PUBLIC_MASTRA_API เป็น address ของ Mastra server ถ้า develop locally จะเป็น localhost address เนื่องจาก port conflict บน 3000 น่าจะเกิด คุณเปลี่ยน config ของ Mastra server ตอนรัน locally ได้ดังนี้:

export const mastra = new Mastra({ storage: myMastraUpstashStore, agents: { articleAgent }, server: { port: 4111, timeout: 10000, } });

ตอนนี้ รัน Mastra server locally ด้วย npm run dev มัน serve ที่ port 4111 เราตั้ง NEXT_PUBLIC_MASTRA_API เป็น https://localhost:4111 ตอนรัน Next.js project locally

มาเห็นว่าใช้ Mastra client SDK ยังไง

export const MASTRA_CONFIG = { resourceId: process.env.NEXT_PUBLIC_RESOURCE_ID || "articleAgent", agentId: "articleAgent", baseUrl: process.env.NEXT_PUBLIC_MASTRA_API || "https://localhost:4111", retries: 3, }; // exported ในอีกไฟล์เพื่อใช้ anywhere ใน codebase // Get agent และ stream response ผ่าน agent object const agent = mastra_sdk.getAgent(MASTRA_CONFIG.agentId); const response = await agent.stream({ messages: [message], resourceId: MASTRA_CONFIG.resourceId, threadId: threadId });

คุณ get tools และ agents และเมื่อได้แล้ว คุณทำเกือบทุกอย่างที่ทำได้กับ actual objects ผ่าน client SDK

เนื่องจากเราจะ publish demo project publicly สำคัญที่จะหลีกเลี่ยง heavy loads บน agent นี่คือที่ Upstash Ratelimit เข้ามาช่วย ก่อนทุก stream request เราจะเช็คว่า user rate-limited หรือไม่ เพื่อ config rate limiter เราต้องการ Upstash Redis เราสามารถใช้ Redis database เดียวกับ Mastra agent

import { Ratelimit } from '@upstash/ratelimit'; import { Redis } from '@upstash/redis'; // Using the same Redis DB across the project export const rateLimit = new Ratelimit({ redis: new Redis({ url: process.env.UPSTASH_REDIS_MEMORY_URL!, token: process.env.UPSTASH_REDIS_MEMORY_TOKEN! }), limiter: Ratelimit.slidingWindow(10, '10s'), prefix: 'upstash-ratelimit', }); // Fetch function นี้ก่อนทุก stream export async function isRateLimited(id: string): Promise<boolean> { const { success } = await rateLimit.limit(id); return !success; }

แบบนี้ เรา ensure endpoints ไม่ under heavy load

ตอนสร้าง chat agents ด้วย Mastra รู้บ้าง features ของ Mastra's thread generation ช่วยได้ จดว่าตอน config memory ให้ agent เราตั้ง generateTitle เป็น true ใน threads object นี่ทำให้ Mastra generate titles อัตโนมัติสำหรับ newly created threads แตที่นี่มี catch: สามารถสร้าง thread explicitly ได้ แต automatic title generation ไม่ trigger ที่ way นั้น Normally วิธีสร้าง new thread คือ:

const thread = await mastraClient.createMemoryThread({ title: "New Conversation", metadata: { category: "support" }, resourceId: "resource-1", agentId: "agent-1", });

อย่างไรก็ตาม นี่เอาความสามารถ generate titles อัตโนมัติของ agent ไป เพราะคุณตั้งมัน manual เปล่า title field ก็ไม่ work ในกรณีนี้ เราดูว่า Playground ทำอะไร จดได้ว่า Playground ที่ Mastra ให้เพื่อ experience capabilities ของ server ระหว่าง development ถ้าเรา inspect network tab ใน browser dev tools เราเห็นว่าตอนสร้าง new thread มันไม่ส่ง API request สร้างมันจริง แทนที่จะรอคุณ submit first message หลังนั้น ส่ง stream request ด้วย newly generated thread ID นี่บอก Mastra ว่าไม่มี thread ID นี้ ควรสร้างใหม่ และถ้า generateTitle เป็น true generate title จาก first message

มาต่อกับ component สุดท้าย: arXiv articles

arXiv Articles (บทความ arXiv)

arXiv เป็น open-access archive สำหรับเกือบ 2.4 ล้าน research articles ในหลาย fields articleQueryTool query Upstash Vector database ซึ่ง fed by articles fetched via arXiv API API ใช้ง่าย หา details ได้ที่นี่

ในโปรเจคเรา เรา fetch และ store articles daily ครั้งแรกที่ server รัน fetch ประมาณ 30,000 articles จาก categories ที่ระบุ หลังจากนั้น fetch new articles ที่ publish วานนี้ เพื่อระบุ article categories และว่า fetch initial large batch หรือไม่ เราตั้ง environment variables ตาม เราควรให้ desired article categories ใช้ arXiv taxonomy คั่นด้วย comma คุณ lookup categories ได้ที่นี่

CATEGORIES=cs.AI RUN_BEGINNING_STACK=false

ถ้าอยาก comprehensive database มากกว่า ใช้ arXiv's bulk data access โดยไม่มี เรา limited 30,000 articles ต่อ API query ซึ่งพอสำหรับเรา

Simple query ไป arXiv ดูประมาณนี้:

const categories = process.env.CATEGORIES?.split(',') || []; // Get desired categories และ split สำหรับ query const searchQuery = categories.length === 1 ? `cat:${categories[0]}` : `(${categories.map(c => `cat:${c}`).join(" OR ")})`; const query = `search_query=${searchQuery}&sortBy=submittedDate&sortOrder=descending`; const url = `https://export.arxiv.org/api/query?${query}`; const response = await axios.get(url); // Make API call ด้วย URL ที่สร้าง

เราทำ calls คล้ายกันเพื่อได้ latest articles ทุกวัน และ fetch initial stack

หลัง fetch articles เรา normalize และ embed พวกมันสำหรับเก็บใน Upstash Vector นี่ต้องเป็น vector database เดียวกับที่ Mastra tool ใช้ "Normalize" หมายถึง parse fetched articles เป็น standard ArxivPaper type ที่เราใช้ทั่ว codebase

export interface ArxivPaper { id: string; title: string; abstract: string; authors: string[]; published: string; pdfUrl: string; category: string; }// Type สำหรับ articles ทั่ว codebase async function storeAbstracts(papers: ArxivPaper[]) { const embeddingModel = openai.embedding("text-embedding-3-small"); // Same model 用来 query ที่ Mastra side const embeddings = await embedArticles(papers, embeddingModel) // Put embeddings into required form พร้อม metadata const vectorsToUpsert = getVectorsToUpsert(embeddings, papers) for (let j = 0; j < vectorsToUpsert.length; j++) { await vectorStore.upsert(vectorsToUpsert[j], { namespace: "arxiv" }); // Upsert embeddings พร้อม metadata ไป Upstash Vector } }

เพื่อให้ database current กับ latest research เรา implement Upstash QStash สำหรับ scheduled task execution กับ deployment บน Vercel เราต้องป้องกัน function timeouts ที่เกิดกับ extended processing intervals เราแก้โดย expose public API endpoint บน server ให้ QStash instance trigger daily database update function reliably

// src/app/api/arxiv_reneval/route.ts import { verifySignatureAppRouter } from "@upstash/qstash/nextjs" import { fetchAndUpsertYesterday} from "@/services/arxiv" async function handler(request: Request) { console.log("Fetching and upserting yesterday's papers...") await fetchAndUpsertYesterday() console.log("Fetching and upserting yesterday's papers completed") return Response.json({ success: true }) } export const POST = verifySignatureAppRouter(handler)

Scheduler สามารถ config ผ่าน Upstash Console ให้ trigger requests ไป endpoint นี้ daily ที่ 6:00 AM UTC

สร้าง AI Article Research Assistant ด้วย Mastra และ Upstash

ด้วย scheduler config นี้ server จะทำ automated database updates ทุกเช้า ให้ continuous data freshness

เราควรให้ credentials สำหรับ QStash instance ทุก required env variables ให้ใน example env file

นี่คือพอสมควร ถ้าอยากลองเล่น code fork repositories และเริ่ม develop ไปที่ Mastra part repo ที่นี่ และอีก repo ที่นี่ หลัง fork:

  • Clone มา local machine
  • Fill environment variables (example .env files provided)
  • ไป root directory ทั้ง 2 projects ใน terminals แยกกัน
  • รันคำสั่ง:
npm install npm run dev

ตอนนี้คุณเห็น application ที่ https://localhost:3000

ด้วย Mastra คุณสร้างสิ่งซับซ้อนมากขึ้นได้ด้วยการใช้ templates อื่นๆ อย่าง RAG, workflows และ networks ดูเหมือนว่า memory และ storage เล่นบทบาทสำคัญในทุก purposes นี่คือที่ Upstash ส่องแสง