[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"academy-blogs-en-1-1-all-build-enterprise-rag-internal-ai-tool-golang-qdrant-all--*":3,"academy-blog-translations-wqf1daar37kumo1":106},{"data":4,"page":92,"perPage":92,"totalItems":92,"totalPages":92},[5],{"alt":6,"collectionId":7,"collectionName":8,"content":9,"cover_image":10,"cover_image_path":11,"created":12,"created_by":13,"expand":14,"id":100,"keywords":101,"locale":72,"published_at":102,"scheduled_at":88,"school_blog":96,"short_description":103,"status":94,"title":104,"updated":105,"updated_by":13,"slug":97,"views":99},"Cover image for Golang The Series EP.160 - Building an Enterprise Internal AI Tool (RAG) with Qdrant and Gin by Superdev Academy","sclblg987654321","school_blog_translations","\u003Cp>Welcome back, \u003Cstrong>Superdev Academy\u003C\u002Fstrong>! Welcome to EP.160, our major hands-on workshop for this season. Throughout this series, we’ve covered everything from environment setup, text embeddings, strategic data chunking, to advanced RAG (Retrieval-Augmented Generation) concepts like Hybrid Search and Context Injection.\u003C\u002Fp>\u003Cp>Today, it's time to bring all these pieces together to build a robust \u003Cstrong>Internal AI Tool\u003C\u002Fstrong> for an enterprise using \u003Cstrong>Go (Golang)\u003C\u002Fstrong>. We will create an intelligent Q&amp;A server capable of querying internal knowledge bases, enforcing department-level access control via Hybrid Search, and streaming real-time responses to users using Server-Sent Events (SSE).\u003C\u002Fp>\u003Ch2>Project Structure\u003C\u002Fh2>\u003Cp>To ensure our code remains modular, maintainable, and adheres to clean architecture principles, we will organize our project using a Layered Architecture:\u003C\u002Fp>\u003Cp>Plaintext\u003C\u002Fp>\u003Cpre>\u003Ccode>internal-ai-tool\u002F\r\n├── config\u002F\r\n│   └── qdrant.go      # Manages Qdrant Vector DB connections\r\n├── handlers\u002F\r\n│   └── qa.go          # HTTP Handlers for incoming requests and SSE streaming\r\n├── services\u002F\r\n│   ├── ai.go          # Wrapper for OpenAI API (Embeddings &amp; Chat Stream)\r\n│   └── vector.go      # Handles Hybrid Search queries on Qdrant\r\n├── go.mod\r\n├── go.sum\r\n└── main.go            # Entry point and dependency injection setup\r\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>Let's initialize our Go module and install the required core dependencies:\u003C\u002Fp>\u003Cp>Bash\u003C\u002Fp>\u003Cpre>\u003Ccode>go mod init internal-ai-tool\r\ngo get github.com\u002Fgin-gonic\u002Fgin\r\ngo get github.com\u002Fsashabaranov\u002Fgo-openai\r\ngo get github.com\u002Fqdrant\u002Fgo-client\u002Fqdrant\r\ngo get github.com\u002Fgoogle\u002Fuuid\r\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch2>Configuration &amp; Services Layer\u003C\u002Fh2>\u003Cp>In this layer, we isolate the database connection logic and core service engines following the Separation of Concerns principle.\u003C\u002Fp>\u003Ch3>1. Vector DB Connection Setup (\u003Ccode>config\u002Fqdrant.go\u003C\u002Fcode>)\u003C\u002Fh3>\u003Cp>We will implement an initialization function to establish a connection with the Qdrant Vector Database. This client will be injected into our services later via the \u003Ccode>main.go\u003C\u002Fcode> file.\u003C\u002Fp>\u003Cp>Go\u003C\u002Fp>\u003Cpre>\u003Ccode>package config\r\n\r\nimport (\r\n\t\"log\"\r\n\t\"github.com\u002Fqdrant\u002Fgo-client\u002Fqdrant\"\r\n)\r\n\r\n\u002F\u002F NewQdrantClient establishes and returns a new client connection for Qdrant Vector DB\r\nfunc NewQdrantClient(host string, port int) *qdrant.Client {\r\n\tclient, err := qdrant.NewClient(&amp;qdrant.Config{\r\n\t\tHost: host,\r\n\t\tPort: port,\r\n\t})\r\n\tif err != nil {\r\n\t\tlog.Fatalf(\"❌ Failed to connect to Qdrant: %v\", err)\r\n\t}\r\n\treturn client\r\n}\r\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch3>2. OpenAI Service Integration (\u003Ccode>services\u002Fai.go\u003C\u002Fcode>)\u003C\u002Fh3>\u003Cp>This service handles all communication with the OpenAI API, specifically generating text embeddings for user queries and initiating a Chat Completion Stream using the \u003Ccode>gpt-4o\u003C\u002Fcode> model.\u003C\u002Fp>\u003Cp>Go\u003C\u002Fp>\u003Cpre>\u003Ccode>package services\r\n\r\nimport (\r\n\t\"context\"\r\n\t\"github.com\u002Fsashabaranov\u002Fgo-openai\"\r\n)\r\n\r\ntype AIService struct {\r\n\tclient *openai.Client\r\n}\r\n\r\nfunc NewAIService(apiKey string) *AIService {\r\n\treturn &amp;AIService{client: openai.NewClient(apiKey)}\r\n}\r\n\r\n\u002F\u002F CreateEmbedding converts raw input text into a high-dimensional semantic vector\r\nfunc (s *AIService) CreateEmbedding(ctx context.Context, text string) ([]float32, error) {\r\n\treq := openai.EmbeddingRequest{\r\n\t\tInput: []string{text},\r\n\t\tModel: openai.SmallEmbedding3Small, \u002F\u002F Cost-efficient and highly performant model\r\n\t}\r\n\tresp, err := s.client.CreateEmbeddings(ctx, req)\r\n\tif err != nil {\r\n\t\treturn nil, err\r\n\t}\r\n\treturn resp.Data[0].Embedding, nil\r\n}\r\n\r\n\u002F\u002F GetChatStream sends a completion request to OpenAI and returns a real-time stream\r\nfunc (s *AIService) GetChatStream(ctx context.Context, systemPrompt, userPrompt string) (*openai.ChatCompletionStream, error) {\r\n\treturn s.client.CreateChatCompletionStream(ctx, openai.ChatCompletionRequest{\r\n\t\tModel: openai.GPT4o,\r\n\t\tMessages: []openai.ChatCompletionMessage{\r\n\t\t\t{Role: openai.ChatMessageRoleSystem, Content: systemPrompt},\r\n\t\t\t{Role: openai.ChatMessageRoleUser, Content: userPrompt},\r\n\t\t},\r\n\t})\r\n}\r\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch3>3. Secure Vector Search Service (\u003Ccode>services\u002Fvector.go\u003C\u002Fcode>)\u003C\u002Fh3>\u003Cp>The core of our RAG mechanism lies here. We utilize Qdrant's querying features to perform a Hybrid Search with a strict security filter, ensuring employees can only retrieve active documents matching their specific department.\u003C\u002Fp>\u003Cp>Go\u003C\u002Fp>\u003Cpre>\u003Ccode>package services\r\n\r\nimport (\r\n\t\"context\"\r\n\t\"github.com\u002Fqdrant\u002Fgo-client\u002Fqdrant\"\r\n)\r\n\r\ntype VectorService struct {\r\n\tclient *qdrant.Client\r\n}\r\n\r\nfunc NewVectorService(client *qdrant.Client) *VectorService {\r\n\treturn &amp;VectorService{client: client}\r\n}\r\n\r\n\u002F\u002F SearchRelevantContext queries Qdrant using a vector while enforcing a department-level security filter\r\nfunc (s *VectorService) SearchRelevantContext(ctx context.Context, vector []float32, department string) ([]string, error) {\r\n\tsearchLimit := uint64(3) \u002F\u002F Fetch top 3 most relevant context chunks\r\n\t\r\n\t\u002F\u002F Security Filter: Only match active documents belonging to the user's department\r\n\tsearchFilters := &amp;qdrant.Filter{\r\n\t\tMust: []*qdrant.Condition{\r\n\t\t\tqdrant.NewMatchKeyword(\"department\", department),\r\n\t\t\tqdrant.NewMatchKeyword(\"status\", \"active\"),\r\n\t\t},\r\n\t}\r\n\r\n\tresp, err := s.client.Query(ctx, &amp;qdrant.QueryPoints{\r\n\t\tCollectionName: \"ai_knowledge_base\",\r\n\t\tQuery:          qdrant.NewQuery(vector...),\r\n\t\tFilter:         searchFilters,\r\n\t\tLimit:          &amp;searchLimit,\r\n\t})\r\n\tif err != nil {\r\n\t\treturn nil, err\r\n\t}\r\n\r\n\tvar chunks []string\r\n\tfor _, point := range resp {\r\n\t\tif contentVal, exists := point.Payload[\"content\"]; exists {\r\n\t\t\tchunks = append(chunks, contentVal.GetStringValue())\r\n\t\t}\r\n\t}\r\n\treturn chunks, nil\r\n}\r\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch2>API Endpoint Layer &amp; Server-Sent Events (SSE) Streaming\u003C\u002Fh2>\u003Cp>This layer manages client requests and coordinates data processing between our services. We use the \u003Cstrong>Gin Gonic\u003C\u002Fstrong> framework to create our HTTP API endpoint and stream tokenized responses back to the frontend in real time using \u003Cstrong>Server-Sent Events (SSE)\u003C\u002Fstrong>.\u003C\u002Fp>\u003Ch3>1. Request Struct &amp; Validation\u003C\u002Fh3>\u003Cp>First, we set up our Data Transfer Object (DTO) structure to capture incoming payloads and enforce required parameters.\u003C\u002Fp>\u003Cp>Go\u003C\u002Fp>\u003Cpre>\u003Ccode>package handlers\r\n\r\nimport (\r\n\t\"errors\"\r\n\t\"fmt\"\r\n\t\"io\"\r\n\t\"net\u002Fhttp\"\r\n\t\"strings\"\r\n\r\n\t\"internal-ai-tool\u002Fservices\"\r\n\t\"github.com\u002Fgin-gonic\u002Fgin\"\r\n)\r\n\r\n\u002F\u002F QARequest binds incoming JSON payloads from the frontend client\r\ntype QARequest struct {\r\n\tQuestion   string `json:\"question\" binding:\"required\"`\r\n\tDepartment string `json:\"department\" binding:\"required\"` \u002F\u002F Used for access control filtering\r\n}\r\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch3>2. Query Retrieval &amp; Context Injection\u003C\u002Fh3>\u003Cp>Next, inside our \u003Ccode>HandleQAStream\u003C\u002Fcode> handler, we take the user's question, turn it into an embedding vector, look up authorized document chunks, and inject them cleanly into our LLM prompt template.\u003C\u002Fp>\u003Cp>Go\u003C\u002Fp>\u003Cpre>\u003Ccode>\u002F\u002F HandleQAStream controls the overall RAG pipeline execution flow\r\nfunc HandleQAStream(ai *services.AIService, vector *services.VectorService) gin.HandlerFunc {\r\n\treturn func(c *gin.Context) {\r\n\t\tvar req QARequest\r\n\t\tif err := c.ShouldBindJSON(&amp;req); err != nil {\r\n\t\t\tc.JSON(http.StatusBadRequest, gin.H{\"error\": \"Invalid request parameters\"})\r\n\t\t\treturn\r\n\t\t}\r\n\r\n\t\tctx := c.Request.Context()\r\n\r\n\t\t\u002F\u002F Step 1: Convert user question into a semantic text embedding vector\r\n\t\tqueryVector, err := ai.CreateEmbedding(ctx, req.Question)\r\n\t\tif err != nil {\r\n\t\t\tc.JSON(http.StatusInternalServerError, gin.H{\"error\": \"Failed to generate embedding vector\"})\r\n\t\t\treturn\r\n\t\t}\r\n\r\n\t\t\u002F\u002F Step 2: Retrieve authorized knowledge base chunks with security filters applied\r\n\t\tchunks, err := vector.SearchRelevantContext(ctx, queryVector, req.Department)\r\n\t\tif err != nil {\r\n\t\t\tc.JSON(http.StatusInternalServerError, gin.H{\"error\": \"Failed to retrieve knowledge base context\"})\r\n\t\t\treturn\r\n\t\t}\r\n\r\n\t\t\u002F\u002F Step 3: Perform Context Injection to ground the LLM's response scope\r\n\t\tcontextText := \"\"\r\n\t\tif len(chunks) &gt; 0 {\r\n\t\t\tcontextText = strings.Join(chunks, \"\\n\\n\")\r\n\t\t} else {\r\n\t\t\tcontextText = \"No relevant context found for this department scope.\"\r\n\t\t}\r\n\r\n\t\tsystemPrompt := \"You are an internal corporate AI assistant. Your sole job is to answer employee questions using the provided Context only. If the answer cannot be found in the Context, state clearly that you cannot find it. Never hallucinate.\"\r\n\t\tuserPrompt := fmt.Sprintf(\"Context:\\n%s\\n\\nQuestion: %s\\nAnswer:\", contextText, req.Question)\r\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch3>3. Server-Sent Events (SSE) Streaming Output\u003C\u002Fh3>\u003Cp>Finally, we configure the HTTP response headers to stay open and feed the response pieces to the frontend interface as soon as they are computed.\u003C\u002Fp>\u003Cp>Go\u003C\u002Fp>\u003Cpre>\u003Ccode>\t\t\u002F\u002F Step 4: Open Chat Completion Stream and configure SSE response pipeline\r\n\t\tstream, err := ai.GetChatStream(ctx, systemPrompt, userPrompt)\r\n\t\tif err != nil {\r\n\t\t\tc.JSON(http.StatusInternalServerError, gin.H{\"error\": \"Failed to initialize completion stream\"})\r\n\t\t\treturn\r\n\t\t}\r\n\t\tdefer stream.Close()\r\n\r\n\t\t\u002F\u002F Set HTTP headers to support persistent text\u002Fevent-stream connections\r\n\t\tc.Header(\"Content-Type\", \"text\u002Fevent-stream\")\r\n\t\tc.Header(\"Cache-Control\", \"no-cache\")\r\n\t\tc.Header(\"Connection\", \"keep-alive\")\r\n\r\n\t\t\u002F\u002F Continuously stream response text tokens out to the frontend client in real-time\r\n\t\tc.Stream(func(w io.Writer) bool {\r\n\t\t\tresp, err := stream.Recv()\r\n\t\t\tif errors.Is(err, io.EOF) {\r\n\t\t\t\tc.SSEvent(\"message\", \"[DONE]\") \u002F\u002F Emit closing signal to inform the UI that streaming is finished\r\n\t\t\t\treturn false\r\n\t\t\t}\r\n\t\t\tif err != nil {\r\n\t\t\t\treturn false\r\n\t\t\t}\r\n\r\n\t\t\tif len(resp.Choices) &gt; 0 {\r\n\t\t\t\tcontent := resp.Choices[0].Delta.Content\r\n\t\t\t\tif content != \"\" {\r\n\t\t\t\t\tc.SSEvent(\"message\", content)\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn true\r\n\t\t})\r\n\t}\r\n}\r\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch2>Assembling the Application (\u003Ccode>main.go\u003C\u002Fcode>)\u003C\u002Fh2>\u003Cp>The \u003Ccode>main.go\u003C\u002Fcode> file acts as the central control room. It loads configuration values, handles secure environment variables, performs \u003Cstrong>Dependency Injection (DI)\u003C\u002Fstrong> to wire up our handlers and services, and spins up our engine using \u003Cstrong>Gin Gonic\u003C\u002Fstrong>.\u003C\u002Fp>\u003Ch3>1. System Initialization &amp; Dependency Injection\u003C\u002Fh3>\u003Cp>We configure the entry point to load sensitive keys and securely instantiate our architecture layout.\u003C\u002Fp>\u003Cp>Go\u003C\u002Fp>\u003Cpre>\u003Ccode>package main\r\n\r\nimport (\r\n\t\"log\"\r\n\t\"os\"\r\n\r\n\t\"internal-ai-tool\u002Fconfig\"\r\n\t\"internal-ai-tool\u002Fhandlers\"\r\n\t\"internal-ai-tool\u002Fservices\"\r\n\t\"github.com\u002Fgin-gonic\u002Fgin\"\r\n)\r\n\r\nfunc main() {\r\n\t\u002F\u002F 1. Fetch credentials from Environment Variables adhering to enterprise safety standards\r\n\topenAIKey := os.Getenv(\"OPENAI_API_KEY\")\r\n\tif openAIKey == \"\" {\r\n\t\tlog.Fatal(\"ERROR: Environment variable OPENAI_API_KEY is not set\")\r\n\t}\r\n\r\n\t\u002F\u002F 2. Initialize connection to Qdrant Vector DB\r\n\tqdrantClient := config.NewQdrantClient(\"localhost\", 6334)\r\n\tdefer qdrantClient.Close() \u002F\u002F Ensure connections close safely when server terminates\r\n\r\n\t\u002F\u002F 3. Execute Dependency Injection (DI) to assemble service instances\r\n\taiService := services.NewAIService(openAIKey)\r\n\tvectorService := services.NewVectorService(qdrantClient)\r\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch3>2. Routing Setup &amp; Server Initialization\u003C\u002Fh3>\u003Cp>We bind our handler to an API route path and hook it up to standard execution loops.\u003C\u002Fp>\u003Cp>Go\u003C\u002Fp>\u003Cpre>\u003Ccode>\t\u002F\u002F 4. Create an HTTP routing instances using Gin Gonic\r\n\tr := gin.Default()\r\n\r\n\t\u002F\u002F Register secure POST route path for streaming internal context answers\r\n\tr.POST(\"\u002Fapi\u002Fv1\u002Fqa\u002Fstream\", handlers.HandleQAStream(aiService, vectorService))\r\n\r\n\t\u002F\u002F Run application listening on predefined port interfaces\r\n\tlog.Println(\"🚀 Enterprise Internal AI Tool server running smoothly on port :8080...\")\r\n\tr.Run(\":8080\")\r\n}\r\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch2>🎯 Daily Mission\u003C\u002Fh2>\u003Cp>Congratulations! You now have a production-ready, clean, and secure enterprise RAG backend architecture ready to interface with a frontend UI.\u003C\u002Fp>\u003Cp>\u003Cstrong>Your Mission:\u003C\u002Fstrong> Spin up this server and use \u003Cstrong>Postman\u003C\u002Fstrong> or \u003Ccode>curl\u003C\u002Fcode> to send a test request. Try swapping the \u003Ccode>\"department\"\u003C\u002Fcode> field in your JSON request body between \u003Ccode>\"Human Resources\"\u003C\u002Fcode> and \u003Ccode>\"Engineering\"\u003C\u002Fcode> using the same question.\u003C\u002Fp>\u003Cp>Observe how the backend \u003Cstrong>Security Filter\u003C\u002Fstrong> handles the authorization rules. Does the AI limit or tailor its response strictly based on the department privileges defined in the Qdrant query? Test it out and prove your backend skills!\u003C\u002Fp>\u003Ch2>FAQ\u003C\u002Fh2>\u003Ch3>Why use Server-Sent Events (SSE) instead of WebSockets for streaming AI responses?\u003C\u002Fh3>\u003Cp>AI chat streaming is inherently a one-way downstream process (server-to-client). SSE runs on standard HTTP protocols, making it lightweight, easier to scale, and more efficient than WebSockets, which are designed for persistent bi-directional communication.\u003C\u002Fp>\u003Ch3>Will query performance degrade as the Qdrant database grows?\u003C\u002Fh3>\u003Cp>Without optimization, latency might increase. We highly recommend setting up \u003Cstrong>Payload Indexing\u003C\u002Fstrong> on fields used in your filters (e.g., \u003Ccode>department\u003C\u002Fcode> and \u003Ccode>status\u003C\u002Fcode>) to keep Qdrant vector queries running within single-digit milliseconds.\u003C\u002Fp>\u003Cdiv data-type=\"horizontalRule\">\u003Chr>\u003C\u002Fdiv>\u003Ch2>Summary\u003C\u002Fh2>\u003Cp>In this episode, we successfully covered:\u003C\u002Fp>\u003Cul>\u003Cli>\u003Cp>Designing a layered RAG architecture using \u003Cstrong>Go (Golang)\u003C\u002Fstrong>.\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>Managing vector database connections and filtering access scopes with \u003Cstrong>Qdrant Vector DB\u003C\u002Fstrong>.\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>Implementing \u003Cstrong>Context Injection\u003C\u002Fstrong> to ground OpenAI GPT-4o models with relevant company facts.\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>Building an asynchronous, real-time streaming HTTP endpoint via \u003Cstrong>Server-Sent Events (SSE)\u003C\u002Fstrong> in Gin Gonic.\u003C\u002Fp>\u003C\u002Fli>\u003C\u002Ful>\u003Cp>\u003Cstrong>Coming Up Next (EP.161): \u003C\u002Fstrong>We've successfully wrapped up our fundamental RAG architecture workshop. But what happens in a real-world enterprise when hundreds of employees upload massive procurement guidelines or onboarding manuals simultaneously? Processing those heavy workloads synchronously over single API calls will inevitably cause timeouts and crash your server.\u003C\u002Fp>\u003Cp>In the next episode, we’ll step into advanced systems design with \u003Cstrong>\"Async AI Tasks: Using Worker Pools to Handle Large-Scale AI Data Extraction.\"\u003C\u002Fstrong> If you're building highly scalable backend architectures, you won't want to miss this one. See you next time, Gophers of Superdev Academy!\u003C\u002Fp>\u003Cp>\u003Cstrong>Follow Superdev Academy on all platforms:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cul>\u003Cli>\u003Cp>\u003Cstrong>🔵 Facebook: \u003C\u002Fstrong>\u003Ca target=\"_blank\" rel=\"noopener\" class=\"ng-star-inserted\" href=\"https:\u002F\u002Fwww.facebook.com\u002Fsuperdev.academy.th\">\u003Cstrong>Superdev Academy Thailand\u003C\u002Fstrong>\u003C\u002Fa>\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>🎬 YouTube: \u003C\u002Fstrong>\u003Ca target=\"_blank\" rel=\"noopener\" class=\"ng-star-inserted\" href=\"https:\u002F\u002Fwww.youtube.com\u002F@SuperdevAcademy\">\u003Cstrong>Superdev Academy Channel\u003C\u002Fstrong>\u003C\u002Fa>\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>📸 Instagram: \u003C\u002Fstrong>\u003Ca target=\"_blank\" rel=\"noopener\" class=\"ng-star-inserted\" href=\"https:\u002F\u002Fwww.instagram.com\u002Fsuperdevacademy\u002F\">\u003Cstrong>@superdevacademy\u003C\u002Fstrong>\u003C\u002Fa>\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>🎬 TikTok: \u003C\u002Fstrong>\u003Ca target=\"_blank\" rel=\"noopener\" class=\"ng-star-inserted\" href=\"https:\u002F\u002Fwww.tiktok.com\u002F@superdevacademy?lang=th-TH\">\u003Cstrong>@superdevacademy\u003C\u002Fstrong>\u003C\u002Fa>\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>🌐 Website: \u003C\u002Fstrong>\u003Ca rel=\"noopener noreferrer\" href=\"https:\u002F\u002Fsuperdevacademy.com\">\u003Cstrong>superdevacademy.com\u003C\u002Fstrong>\u003C\u002Fa>\u003C\u002Fp>\u003C\u002Fli>\u003C\u002Ful>\u003Cp>\u003C\u002Fp>","407o8yp6t5uy_qiooxilknr.png","https:\u002F\u002Ftwsme-r2.tumwebsme.com\u002Fsclblg987654321\u002Fy5daw9sbykzfx6b\u002F407o8yp6t5uy_qiooxilknr.png","2026-07-13 05:33:37.113Z","76qprkevbgfdps8",{"keywords":15,"locale":66,"school_blog":76},[16,23,28,32,36,40,44,49,53,57,61],{"collectionId":17,"collectionName":18,"created":19,"created_by":13,"id":20,"name":21,"updated":22,"updated_by":13},"sclkey987654321","school_keywords","2026-03-04 08:20:11.547Z","ey3puyme01a9bsw","Go","2026-06-07 06:45:07.798Z",{"collectionId":17,"collectionName":18,"created":24,"created_by":13,"id":25,"name":26,"updated":27,"updated_by":13},"2026-03-04 08:20:14.253Z","ah6lvy4x8qe08l5","Golang","2026-06-07 06:45:08.193Z",{"collectionId":17,"collectionName":18,"created":29,"created_by":13,"id":30,"name":31,"updated":29,"updated_by":13},"2026-06-11 16:14:22.575Z","gluay8aj98wheus","RAG",{"collectionId":17,"collectionName":18,"created":33,"created_by":13,"id":34,"name":35,"updated":33,"updated_by":13},"2026-06-30 03:40:49.549Z","lz4vxehenzo8oum","Qdrant",{"collectionId":17,"collectionName":18,"created":37,"created_by":13,"id":38,"name":39,"updated":37,"updated_by":13},"2026-07-13 05:30:10.662Z","txjbbpvtqvsy14j","Gin Gonic",{"collectionId":17,"collectionName":18,"created":41,"created_by":13,"id":42,"name":43,"updated":41,"updated_by":13},"2026-06-09 02:46:11.702Z","y9xcv17enwjxdcw","Server-Sent Events",{"collectionId":17,"collectionName":18,"created":45,"created_by":13,"id":46,"name":47,"updated":48,"updated_by":13},"2026-05-11 08:41:15.880Z","lroded80zwh4qm3"," Design System","2026-06-07 06:49:13.636Z",{"collectionId":17,"collectionName":18,"created":50,"created_by":13,"id":51,"name":52,"updated":50,"updated_by":13},"2026-07-13 05:30:26.261Z","tg35sqbbdingumw","SSE Streaming",{"collectionId":17,"collectionName":18,"created":54,"created_by":13,"id":55,"name":56,"updated":54,"updated_by":13},"2026-07-06 06:33:19.833Z","nri7epnlbj96nyi","Context Injection",{"collectionId":17,"collectionName":18,"created":58,"created_by":13,"id":59,"name":60,"updated":58,"updated_by":13},"2026-07-13 05:30:34.326Z","i0grg21rqz4i7y1","Enterprise AI Tool",{"collectionId":17,"collectionName":18,"created":62,"created_by":13,"id":63,"name":64,"updated":65,"updated_by":13},"2026-03-04 08:44:11.146Z","gms2qr4xg6qv65e","Superdev Academy","2026-06-07 06:46:28.624Z",{"code":67,"collectionId":68,"collectionName":69,"created":70,"flag":71,"id":72,"is_default":73,"label":74,"updated":75},"en","pbc_1989393366","locales","2026-01-22 11:00:02.726Z","twemoji:flag-united-states","qv9c1llfov2d88z",false,"English","2026-04-10 15:42:46.825Z",{"category":77,"collectionId":78,"collectionName":79,"created":80,"expand":81,"id":96,"slug":97,"updated":98,"views":99},"wqxt7ag2gn7xcmk","pbc_2105096300","school_blogs","2026-07-13 05:30:45.321Z",{"category":82},{"blogIds":83,"collectionId":84,"collectionName":85,"created":86,"created_by":13,"id":77,"image":87,"image_alt":88,"image_path":89,"label":90,"name":91,"priority":92,"publish_at":93,"scheduled_at":88,"status":94,"updated":95,"updated_by":13},[],"sclcatblg987654321","school_category_blogs","2026-03-04 08:33:53.210Z","59ty92ns80w_15oc1implw.png","","https:\u002F\u002Ftwsme-r2.tumwebsme.com\u002Fsclcatblg987654321\u002Fwqxt7ag2gn7xcmk\u002F59ty92ns80w_15oc1implw.png",{"en":91,"th":91},"Golang The Series",1,"2026-03-16 04:39:38.440Z","published","2026-06-07 06:45:03.856Z","wqf1daar37kumo1","build-enterprise-rag-internal-ai-tool-golang-qdrant","2026-07-14 06:47:00.941Z",121,"y5daw9sbykzfx6b",[20,25,30,34,38,42,46,51,55,59,63],"2026-07-14 03:00:00.000Z","A complete hands-on guide to building a secure enterprise RAG system using Go, Gin Gonic, and Qdrant Vector DB. Learn how to implement department-level security filtering and real-time response streaming via Server-Sent Events (SSE).","Golang The Series EP.160: Building an Enterprise Internal AI Tool (RAG) with Qdrant and Gin","2026-07-14 03:00:00.166Z",{"th":97,"en":97}]