← Backend← Backend
BackendBackend19 Th7, 2026Jul 19, 202625 phút đọc20 min read

AI cho BackendAI for Backend

Thuộc bộ kiến thức Backend Roadmap.

Tổng quan

AI đã chuyển từ một lĩnh vực chuyên biệt trở thành công cụ hằng ngày của backend engineer, và nó tác động đến công việc theo hai hướng rất khác nhau. Thứ nhất là AI-assisted coding (viết code có AI hỗ trợ): các LLM coding assistant (GitHub Copilot, Cursor, Claude Code, v.v.) thay đổi cách bạn viết, review và tạo documentation cho code backend mỗi ngày. Thứ hai là xây dựng backend có AI: bạn ngày càng xây dựng feature trên nền LLM — semantic search, chatbot, tóm tắt, trích xuất (extraction), phân loại (classification), trả lời dựa trên retrieval, và agent tự động — bằng cách gọi model provider qua API rồi ghép kết quả vào service của mình.

Hai nửa này chia sẻ cùng một nền tảng. Cả hai đều đòi hỏi bạn hiểu LLM là gì, nó làm tốt và không làm được gì một cách đáng tin cậy, cách viết prompt, cách nhận về structured output, và cách kiểm soát cost, latency cùng các failure mode. Note này đi sâu vào cả hai nửa để một backend engineer có thể đi từ “tôi dùng autocomplete gợi ý function” đến “tôi vận hành một RAG service production có eval, caching và guardrail”.

Điều quan trọng là thay đổi tư duy: một LLM không phải database, rule engine hay hàm deterministic. Nó là một bộ sinh văn bản theo xác suất, cực giỏi trong việc hoàn thiện pattern trên ngôn ngữ và code, nhưng lại thiếu tin cậy theo những cách cụ thể và có thể đoán trước. Đối xử với nó giống như các thành phần deterministic bạn đã quen (có input validation, timeout, retry, observability và một fallback path) chính là điều phân biệt một AI feature vững chắc với một demo vỡ trận khi lên production.

Hai nửa nhìn tổng quan

NửaLà gìAi làm việcGiá trị nằm ở đâu
(A) AI-assisted codingLLM assistant hỗ trợ viết/review/document codeModel gợi ý; bạn chịu trách nhiệm tính đúng đắnTốc độ cho boilerplate, test, docs, khám phá code
(B) Backend có AIService của bạn gọi LLM để cấp năng lực cho một featureCode của bạn điều phối model lúc runtimeNăng lực sản phẩm mới trên dữ liệu phi cấu trúc

Kiến thức nền tảng

LLM hoạt động thế nào (ở mức backend developer)

Một LLM (Large Language Model) là mạng neural được huấn luyện để dự đoán token tiếp theo dựa trên các token đứng trước. Bạn không cần phần toán học, nhưng cần mô hình vận hành sau:

LLM làm tốt gì: tóm tắt, soạn thảo, dịch, phân loại, trích xuất theo schema, sinh code, giải thích code, chuyển đổi văn bản giữa các định dạng, trả lời câu hỏi khi được cung cấp context liên quan.

LLM làm kém gì (và thất bại ở đâu):

Provider và model (bức tranh hiện tại, giữ ở mức chung)

Bạn thường dùng LLM từ một provider hosted qua HTTPS API. Các provider lớn tại thời điểm viết:

ProviderHọ modelGhi chú
OpenAIHọ GPTHệ sinh thái rộng, tooling trưởng thành, function calling, JSON mode.
AnthropicHọ ClaudeMạnh về long-context, coding và workload agentic/tool-use.
GoogleHọ GeminiTích hợp sâu với GCP, context window lớn, multimodal.

Lưu ý về độ chính xác: Tên model chính xác, kích thước context window, giá và tham số API thay đổi liên tục và khác nhau giữa các provider. Với bất kỳ chi tiết nào của Anthropic/Claude (model ID, giá, request parameter), hãy coi thông tin ở đây là minh họa và kiểm tra docs chính thức hiện hành — của Anthropic thay đổi rất nhanh (ví dụ tham số thinking/effort, hỗ trợ tham số sampling, và model ID). Điều tương tự áp dụng cho OpenAI và Google.

Bạn cũng có thể tự chạy các open-weight model (Llama, Mistral, Qwen, v.v.) qua runtime như Ollama hoặc vLLM, đánh đổi sự tiện lợi của provider để lấy quyền kiểm soát, data residency và cấu trúc chi phí khác.

Gọi provider từ backend là một HTTPS request có xác thực bình thường. Về mặt khái niệm:

# Pattern giống nhau giữa các provider: auth bằng API key, gửi messages, đọc reply.
import os
from openai import OpenAI  # minh họa; mỗi provider có SDK riêng

client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])

resp = client.chat.completions.create(
    model="gpt-4o-mini",  # kiểm tra model ID hiện hành của từng provider
    messages=[
        {"role": "system", "content": "You are a terse backend assistant."},
        {"role": "user", "content": "Explain idempotency keys in one paragraph."},
    ],
    temperature=0.2,
)
print(resp.choices[0].message.content)

Giữ API key trong biến môi trường hoặc secrets manager — không bao giờ để trong source control hay code phía client. Gọi provider cũng là một dependency qua mạng: hãy cho nó timeout, retry có backoff, và một fallback path.

AI-assisted coding so với coding truyền thống

Coding truyền thống là bạn viết từng dòng. AI-assisted coding chèn một LLM vào vòng lặp — dưới dạng autocomplete inline (Copilot), chat/agent tích hợp editor (Cursor), hoặc một agentic coding tool ở terminal (Claude Code) có thể đọc file, chạy lệnh và sửa nhiều file.

Nó thay đổi workflow backend thế nào:

Điểm mạnh: tốc độ cho công việc đã đặc tả rõ, ít tính mới; một “con vịt cao su” tốt để bàn thiết kế; dịch giữa các ngôn ngữ/framework rất giỏi.

Rủi ro (hãy xem trọng):

Kỷ luật review là thực hành không thể bỏ qua. Bạn vẫn là tác giả và là engineer chịu trách nhiệm. Đọc mọi dòng được sinh ra như thể một junior vừa nộp lên: có compile không, có đúng không, có an toàn không, có khớp convention của dự án không, có test chưa? Giữ thay đổi nhỏ và dễ review, chạy test suite, và không bao giờ merge code bạn không giải thích được. AI tăng throughput của bạn; nó không chuyển giao trách nhiệm.

Khái niệm chính

Kỹ thuật prompting cho backend

Với việc dùng theo kiểu programmatic (không phải chat), prompt là một phần của code và nên được version, test, và coi như một contract.

System: You are an extraction service. Return ONLY JSON matching the schema.
Do not invent values. If a field is absent, use null.

User:
<schema>{ "name": string, "email": string|null, "plan": "free"|"pro"|null }</schema>
<document>
Hi, I'm Jane Doe (jane@co.com). We'd like the Pro plan.
</document>

Structured output, JSON mode, function calling / tool use

Văn bản tự do khó tiêu thụ theo kiểu programmatic. Hai cơ chế liên quan cho bạn output có thể parse bằng máy, đáng tin cậy:

Structured output / JSON mode — Ràng buộc model chỉ phát ra JSON tuân theo schema bạn cung cấp. Các provider hiện đại có thể đảm bảo output hợp lệ schema (strict mode) chứ không chỉ yêu cầu. Điều này loại bỏ cả một lớp bug parsing.

Function calling / tool use — Bạn mô tả các tool (function) mà model được phép gọi, mỗi tool có tên, mô tả và tham số theo JSON schema. Thay vì trả lời trực tiếp, model có thể phản hồi bằng một yêu cầu gọi tool có cấu trúc. Backend của bạn thực thi function thật (query DB, gọi API, tính toán), trả kết quả về, và model tiếp tục. Đây là cách bạn cho một LLM truy cập dữ liệu mới, hệ thống nội bộ, và tính toán đáng tin cậy.

Vòng lặp: gửi prompt + định nghĩa tool → model trả về một tool call → code của bạn chạy tool → gửi kết quả về → model tạo câu trả lời cuối (lặp khi cần).

{
  "tools": [
    {
      "name": "get_order_status",
      "description": "Look up the current status of an order by its ID.",
      "input_schema": {
        "type": "object",
        "properties": {
          "order_id": { "type": "string", "description": "e.g. ord_789" }
        },
        "required": ["order_id"]
      }
    }
  ]
}
// Model, khi được hỏi "where is order ord_789?", trả lời bằng một tool call:
{
  "type": "tool_use",
  "name": "get_order_status",
  "input": { "order_id": "ord_789" }
}
// Backend của bạn chạy function thật, rồi trả về:
{ "type": "tool_result", "content": "{\"status\": \"shipped\", \"eta\": \"2026-07-22\"}" }
// Model sau đó tạo câu trả lời tự nhiên đã được grounded.

Lưu ý bảo mật: hãy coi tool call là request không đáng tin. Validate tham số, enforce authorization cho end user (không phải cho model), chặn các hành động phá hoại/không thể hoàn tác sau một bước xác nhận, và áp timeout cùng rate limit. Model quyết định gọi gì; backend của bạn quyết định có được phép hay không.

Embedding và vector database

Một embedding là một vector số thực có độ dài cố định (ví dụ 768–3072 chiều) biểu diễn ý nghĩa của một đoạn văn bản. Các văn bản có nghĩa gần nhau ánh xạ tới vector gần nhau, nên độ tương đồng ngữ nghĩa trở thành khoảng cách hình học. Bạn sinh embedding bằng cách gọi một embeddings model/endpoint.

Similarity search — Để tìm văn bản liên quan tới một query, embed query rồi tìm các vector đã lưu gần nó nhất, thường bằng cosine similarity (góc giữa các vector) hoặc dot product. Cách này truy xuất theo nghĩa, không phải keyword — “how do I reset my password” khớp với “account recovery steps” dù không có từ chung.

Một vector database lưu embedding và làm approximate-nearest-neighbor (ANN) search nhanh trên hàng triệu vector.

Lựa chọnLoạiGhi chú
pgvectorExtension của PostgresGiữ vector trong SQL DB sẵn có; tuyệt vời khi bạn đã chạy Postgres và muốn một hệ thống duy nhất. Xem ./09-nosql-databases.md cho bức tranh data-store rộng hơn.
PineconeManaged SaaSHosted hoàn toàn, scale tốt, không cần ops; cost theo mức dùng.
QdrantOpen-source + cloudViết bằng Rust, nhanh, filter phong phú; self-host hoặc managed.
WeaviateOpen-source + cloudModule tích hợp sẵn, hybrid (keyword + vector) search.
-- pgvector: lưu và query embedding ngay trong Postgres
CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE docs (
  id       bigserial PRIMARY KEY,
  content  text,
  embedding vector(1536)         -- khớp với dimension của embedding model
);

-- Tìm 5 chunk tương đồng nhất với vector query (<=> là cosine distance)
SELECT id, content
FROM docs
ORDER BY embedding <=> '[0.021, -0.44, ...]'::vector
LIMIT 5;

Mẹo thực tế: embedding model đã tạo ra vector phải là model dùng để query nó (dimension và ngữ nghĩa phải khớp); lưu metadata hữu ích cùng vector để filter; và cân nhắc hybrid search (kết hợp vector similarity với keyword/BM25) để tăng precision cho tên, ID và từ khóa chính xác.

RAG (Retrieval-Augmented Generation)

RAG neo câu trả lời của LLM vào dữ liệu của bạn bằng cách retrieval các document liên quan tại thời điểm query rồi đưa vào prompt. Đây là cách tiêu chuẩn để một LLM trả lời câu hỏi về thông tin riêng tư, theo lĩnh vực, hoặc cập nhật mà không cần huấn luyện lại model.

Pipeline:

Ingestion (offline):
  documents → chunk → embed từng chunk → lưu vector (+ metadata) vào vector DB

Lúc query (online):
  câu hỏi user → embed → retrieve top-k chunk tương đồng → dựng prompt
              → (câu hỏi + context đã retrieve) → LLM → câu trả lời grounded (kèm citation)
  1. Chunk — Cắt document thành các đoạn (ví dụ vài trăm token, thường có overlap). Chiến lược chunking ảnh hưởng mạnh đến chất lượng.
  2. Embed — Biến mỗi chunk thành vector.
  3. Store — Lưu vector + metadata nguồn vào vector DB.
  4. Retrieve — Embed câu hỏi, lấy top-k chunk gần nhất (tùy chọn filter theo metadata, tùy chọn re-rank).
  5. Generate — Prompt LLM với câu hỏi các chunk đã retrieve, yêu cầu nó chỉ trả lời dựa trên context được cung cấp và trích dẫn nguồn.

RAG giảm hallucination (model có sẵn sự kiện trước mặt), cho phép citation, và cập nhật tức thì khi bạn ingest lại dữ liệu. Failure mode: nếu retrieval trả về sai chunk thì câu trả lời sẽ sai (“garbage in, garbage out”), nên chất lượng retrieval là nơi tốn công engineering nhất.

RAG so với fine-tuning — một quyết định bạn sẽ gặp đi gặp lại:

Khía cạnhRAGFine-tuning
Mục đíchBơm kiến thức/sự kiện tại thời điểm queryDạy hành vi, phong cách, định dạng
Độ tươi dữ liệuTức thì — ingest lại để cập nhậtCũ — cần huấn luyện lại
Chi phí thay đổiRẻ (cập nhật index)Đắt (huấn luyện lại)
Truy vếtCó thể trích dẫn nguồn đã retrieveMờ đục — kiến thức nằm ẩn bên trong
Hợp nhất choQ&A trên tài liệu, sự kiện mới/riêng tưGiọng điệu nhất quán, thuật ngữ ngành, định dạng output, tiết kiệm latency/token
HallucinationThấp hơn (grounded)Không giải quyết trực tiếp

Quy tắc ngón tay cái: dùng RAG cho kiến thức, fine-tuning cho hành vi, và bắt đầu bằng RAG (kèm prompting tốt) trước khi nghĩ đến fine-tuning — nó rẻ hơn, lặp nhanh hơn và đáp ứng phần lớn nhu cầu. Hai cách bổ trợ cho nhau, không loại trừ nhau.

AI agent

Một agent là một LLM được giao một mục tiêu, một tập tool, và quyền tự chủ để quyết định dùng tool nào, theo thứ tự nào, qua nhiều bước — thay vì tạo một phản hồi duy nhất. Cốt lõi là agent loop:

reason → act → observe → (lặp cho đến khi xong)

  reason:  model quyết định bước tiếp theo dựa trên mục tiêu + lịch sử
  act:     nó gọi một tool (search, chạy code, query DB, gọi API)
  observe: backend của bạn chạy tool và đưa kết quả trở lại
  loop:    tiếp tục cho đến khi đạt mục tiêu hoặc gặp điều kiện dừng

Agent cũng cần memory: ngắn hạn (scratchpad/hội thoại đang chạy trong context window) và dài hạn (state được lưu qua các session, thường trong database hoặc vector store). SDK của provider ngày càng cung cấp helper kiểu agent/tool-runner để chạy vòng lặp này thay bạn, nên bạn chỉ cung cấp tool và mục tiêu thay vì tự viết vòng lặp.

Agent tỏa sáng ở đâu: các task nhiều bước, mở, khó đặc tả đầy đủ từ đầu (nghiên cứu, thay đổi code khắp một repo, workflow nhiều hệ thống).

Rủi ro — agent khuếch đại mọi failure mode của LLM:

Chỉ dùng agent khi task thật sự cần tool use mở, do model điều khiển; một lần gọi đơn hoặc một workflow cố định, do code kiểm soát thì đơn giản hơn, rẻ hơn và dễ đoán hơn — hãy ưu tiên chúng khi đủ dùng.

MCP (Model Context Protocol)

MCP (Model Context Protocol) là một chuẩn mở để kết nối các ứng dụng AI với tool, nguồn dữ liệu và context bên ngoài. Hãy hình dung nó như một bộ chuyển đổi phổ quát: thay vì viết keo dán riêng cho từng tích hợp model-với-tool, bạn expose năng lực của mình một lần dưới dạng một MCP server, và bất kỳ client có MCP nào (một app AI, IDE, hay agent) đều dùng được.

Vấn đề nó giải quyết: nếu không có chuẩn, mỗi tổ hợp (app AI × nguồn dữ liệu/tool) cần code tích hợp riêng — bùng nổ M×N. MCP biến việc này thành M+N: mỗi tool viết phần server một lần; mỗi app AI viết phần client một lần; chúng liên thông với nhau.

Một MCP server expose:

Với backend engineer, MCP nghĩa là bạn có thể bọc các service nội bộ (một database, hệ thống ticket, một API nội bộ) thành một MCP server và để các app LLM tiêu thụ chúng qua một protocol nhất quán, với auth và transport được xử lý bởi chuẩn thay vì phát minh lại mỗi lần. Hãy tra specification hiện hành vì protocol đang được phát triển tích cực.

Best Practices

Đưa AI feature lên production phần lớn là kỷ luật backend thông thường áp dụng cho một dependency khác thường. Các mối quan tâm dưới đây là nơi AI feature hay vỡ trong thực tế.

Cost và token budgeting

Caching

Rate limit, latency và streaming

Eval (đánh giá)

Guardrail và prompt injection (phòng thủ)

PII và quyền riêng tư dữ liệu

Vệ sinh engineering tổng quát

Tài liệu tham khảo

Part of the Backend Roadmap knowledge base.

Overview

AI has moved from a specialist niche into the everyday toolbox of the backend engineer, and it now touches the work in two very different ways. First, AI-assisted coding: LLM-powered assistants (GitHub Copilot, Cursor, Claude Code, and others) change how you actually write, review, and document backend code day to day. Second, AI-powered backends: you increasingly build features on top of LLMs — semantic search, chatbots, summarization, extraction, classification, retrieval-augmented answers, and autonomous agents — by calling model providers over an API and wiring the results into your services.

These two halves share the same foundations. Both require understanding what an LLM is, what it can and cannot do reliably, how to prompt it, how to get structured output back, and how to keep cost, latency, and failure modes under control. This note covers both halves in depth so that a backend engineer can go from “I use an autocomplete that suggests functions” to “I ship a production RAG service with evals, caching, and guardrails.”

The mental shift is important: an LLM is not a database, a rules engine, or a deterministic function. It is a probabilistic text generator that is astonishingly good at pattern-completion over language and code, and unreliable in specific, predictable ways. Treating it like the deterministic components you already know (with input validation, timeouts, retries, observability, and a fallback path) is what separates a robust AI feature from a demo that breaks in production.

The two halves at a glance

HalfWhat it isWho does the workWhere the value is
(A) AI-assisted codingLLM assistants help you write/review/document codeThe model suggests; you own correctnessSpeed on boilerplate, tests, docs, exploration
(B) AI-powered backendsYour service calls an LLM to power a featureYour code orchestrates the model at runtimeNew product capabilities over unstructured data

Fundamentals

How LLMs work (at a backend-developer level)

An LLM (Large Language Model) is a neural network trained to predict the next token given the tokens that came before it. You do not need the math, but you do need the operational model:

What LLMs are good at: summarization, drafting, translation, classification, extraction into a schema, code generation, explaining code, transforming text between formats, answering questions when given the relevant context.

What LLMs are bad at (and where they fail):

Providers and models (current landscape, kept general)

You typically consume LLMs from a hosted provider over an HTTPS API. The major providers as of writing:

ProviderModel familyNotes
OpenAIGPT familyBroad ecosystem, mature tooling, function calling, JSON mode.
AnthropicClaude familyStrong on long-context, coding, and agentic/tool-use workloads.
GoogleGemini familyDeep GCP integration, large context windows, multimodal.

Accuracy note: Exact model names, context-window sizes, pricing, and API parameters change frequently and differ per provider. For any Anthropic/Claude specifics (model IDs, pricing, request parameters), treat the details here as illustrative and check the current official docs — Anthropic’s evolve quickly (e.g. thinking/effort parameters, sampling-parameter support, and model IDs). The same caution applies to OpenAI and Google.

You can also run open-weight models (Llama, Mistral, Qwen, etc.) yourself via runtimes like Ollama or vLLM, trading provider convenience for control, data residency, and different cost dynamics.

Calling a provider from a backend is an ordinary authenticated HTTPS request. Conceptually:

# Pattern is the same across providers: auth via API key, send messages, read the reply.
import os
from openai import OpenAI  # illustrative; each provider ships its own SDK

client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])

resp = client.chat.completions.create(
    model="gpt-4o-mini",  # check current model IDs per provider
    messages=[
        {"role": "system", "content": "You are a terse backend assistant."},
        {"role": "user", "content": "Explain idempotency keys in one paragraph."},
    ],
    temperature=0.2,
)
print(resp.choices[0].message.content)

Keep API keys in environment variables or a secrets manager — never in source control or client-side code. Calls to a provider are also a networked dependency: give them timeouts, retries with backoff, and a fallback path.

AI-assisted coding vs traditional coding

Traditional coding is you writing every line. AI-assisted coding inserts an LLM into the loop — as inline autocomplete (Copilot), an editor-integrated chat/agent (Cursor), or a terminal/agentic coding tool (Claude Code) that can read files, run commands, and make multi-file edits.

How it changes backend workflows:

Strengths: speed on well-specified, low-novelty work; a strong “rubber duck” for design discussion; excellent at translating between languages/frameworks.

Risks (take these seriously):

Review discipline is the non-negotiable practice. You remain the author and the accountable engineer. Read every generated line as if a junior submitted it: does it compile, is it correct, is it secure, does it match project conventions, is it tested? Keep changes small and reviewable, run the test suite, and never merge code you cannot explain. AI raises your throughput; it does not transfer accountability.

Key Concepts

Prompting techniques for backend use

For programmatic use (not chat), prompts are part of your code and should be versioned, tested, and treated as a contract.

System: You are an extraction service. Return ONLY JSON matching the schema.
Do not invent values. If a field is absent, use null.

User:
<schema>{ "name": string, "email": string|null, "plan": "free"|"pro"|null }</schema>
<document>
Hi, I'm Jane Doe (jane@co.com). We'd like the Pro plan.
</document>

Structured outputs, JSON mode, function calling / tool use

Free-form text is hard to consume programmatically. Two related mechanisms give you machine-parseable, reliable output:

Structured outputs / JSON mode — Constrain the model to emit JSON conforming to a schema you supply. Modern providers can guarantee schema-valid output (strict mode) rather than merely requesting it. This removes an entire class of parsing bugs.

Function calling / tool use — You describe tools (functions) the model may call, each with a name, description, and JSON-schema parameters. Instead of answering directly, the model can respond with a structured request to call a tool. Your backend executes the real function (query a DB, hit an API, do arithmetic), returns the result, and the model continues. This is how you give an LLM access to fresh data, private systems, and reliable computation.

The loop: send prompt + tool definitions → model returns a tool call → your code runs the tool → send the result back → model produces the final answer (repeat as needed).

{
  "tools": [
    {
      "name": "get_order_status",
      "description": "Look up the current status of an order by its ID.",
      "input_schema": {
        "type": "object",
        "properties": {
          "order_id": { "type": "string", "description": "e.g. ord_789" }
        },
        "required": ["order_id"]
      }
    }
  ]
}
// The model, asked "where is order ord_789?", replies with a tool call:
{
  "type": "tool_use",
  "name": "get_order_status",
  "input": { "order_id": "ord_789" }
}
// Your backend runs the real function, then returns:
{ "type": "tool_result", "content": "{\"status\": \"shipped\", \"eta\": \"2026-07-22\"}" }
// The model then produces a grounded natural-language answer.

Security note: treat tool calls as untrusted requests. Validate arguments, enforce authorization for the end user (not the model), gate destructive/irreversible actions behind confirmation, and apply timeouts and rate limits. The model decides what to call; your backend decides whether it may.

Embeddings and vector databases

An embedding is a fixed-length vector of floats (e.g. 768–3072 dimensions) that represents the meaning of a piece of text. Texts with similar meaning map to nearby vectors, so semantic similarity becomes geometric distance. You generate embeddings by calling an embeddings model/endpoint.

Similarity search — To find text relevant to a query, embed the query and find the stored vectors closest to it, typically by cosine similarity (angle between vectors) or dot product. This retrieves by meaning, not keywords — “how do I reset my password” matches “account recovery steps” even with no shared words.

A vector database stores embeddings and does fast approximate-nearest-neighbor (ANN) search over millions of vectors.

OptionTypeNotes
pgvectorPostgres extensionKeep vectors in your existing SQL DB; great when you already run Postgres and want one system. See ./09-nosql-databases.md for the broader data-store landscape.
PineconeManaged SaaSFully hosted, scales well, no ops; usage-based cost.
QdrantOpen-source + cloudRust-based, fast, rich filtering; self-host or managed.
WeaviateOpen-source + cloudBuilt-in modules, hybrid (keyword + vector) search.
-- pgvector: store and query embeddings inside Postgres
CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE docs (
  id       bigserial PRIMARY KEY,
  content  text,
  embedding vector(1536)         -- match your embedding model's dimension
);

-- Find the 5 chunks most similar to a query vector (<=> is cosine distance)
SELECT id, content
FROM docs
ORDER BY embedding <=> '[0.021, -0.44, ...]'::vector
LIMIT 5;

Practical tips: the embedding model that wrote a vector must be the one that queries it (dimensions and semantics must match); store useful metadata alongside vectors for filtering; and consider hybrid search (combine vector similarity with keyword/BM25) for better precision on names, IDs, and exact terms.

RAG (Retrieval-Augmented Generation)

RAG grounds an LLM’s answer in your data by retrieving relevant documents at query time and putting them in the prompt. It is the standard way to make an LLM answer questions about private, domain-specific, or up-to-date information without retraining the model.

The pipeline:

Ingestion (offline):
  documents → chunk → embed each chunk → store vectors (+ metadata) in a vector DB

Query time (online):
  user question → embed → retrieve top-k similar chunks → build prompt
                → (question + retrieved context) → LLM → grounded answer (with citations)
  1. Chunk — Split documents into passages (e.g. a few hundred tokens, often with overlap). Chunking strategy strongly affects quality.
  2. Embed — Turn each chunk into a vector.
  3. Store — Persist vectors + source metadata in a vector DB.
  4. Retrieve — Embed the query, fetch the top-k nearest chunks (optionally filter by metadata, optionally re-rank).
  5. Generate — Prompt the LLM with the question and the retrieved chunks, instructing it to answer only from the provided context and to cite sources.

RAG reduces hallucination (the model has the facts in front of it), lets you cite sources, and updates instantly when you re-ingest data. Failure modes: if retrieval returns the wrong chunks, the answer is wrong (“garbage in, garbage out”), so retrieval quality is where most of the engineering effort goes.

RAG vs fine-tuning — a decision you will face repeatedly:

DimensionRAGFine-tuning
PurposeInject knowledge/facts at query timeTeach behavior, style, format
Data freshnessInstant — re-ingest to updateStale — requires retraining
Cost to changeCheap (update the index)Expensive (retrain)
TraceabilityCan cite retrieved sourcesOpaque — knowledge baked in
Best forQ&A over docs, up-to-date/private factsConsistent tone, domain jargon, output format, latency/token savings
HallucinationLower (grounded)Not directly addressed

Rule of thumb: use RAG for knowledge, fine-tuning for behavior, and start with RAG (plus good prompting) before reaching for fine-tuning — it is cheaper, faster to iterate, and covers most needs. The two are complementary, not mutually exclusive.

AI agents

An agent is an LLM given a goal, a set of tools, and the autonomy to decide which tools to use, in what order, over multiple steps — instead of producing a single response. The core is the agent loop:

reason → act → observe → (repeat until done)

  reason:  the model decides the next step given the goal + history
  act:     it calls a tool (search, run code, query DB, call an API)
  observe: your backend runs the tool and feeds the result back
  loop:    continue until the goal is met or a stop condition triggers

Agents also need memory: short-term (the running conversation/scratchpad within the context window) and long-term (state persisted across sessions, often in a database or a vector store). Provider SDKs increasingly ship agent/tool-runner helpers that drive this loop for you, so you supply the tools and the goal rather than hand-writing the loop.

Where agents shine: open-ended, multi-step tasks that are hard to fully specify up front (research, code changes across a repo, multi-system workflows).

Risks — agents amplify every LLM failure mode:

Reach for an agent only when the task genuinely needs open-ended, model-driven tool use; a single call or a fixed, code-controlled workflow is simpler, cheaper, and more predictable — prefer them whenever they suffice.

MCP (Model Context Protocol)

MCP (Model Context Protocol) is an open standard for connecting AI applications to external tools, data sources, and context. Think of it as a universal adapter: instead of writing bespoke glue for every model-to-tool integration, you expose your capability once as an MCP server, and any MCP-capable client (an AI app, IDE, or agent) can use it.

The problem it solves: without a standard, every combination of (AI app × data source/tool) needs custom integration code — an M×N explosion. MCP turns this into M+N: each tool implements the server side once; each AI app implements the client side once; they interoperate.

An MCP server exposes:

For a backend engineer, MCP means you can wrap your internal services (a database, a ticketing system, an internal API) as an MCP server and have LLM apps consume them through a consistent protocol, with the auth and transport concerns handled by the standard rather than reinvented each time. Check the current specification for details, as the protocol is actively evolving.

Best Practices

Shipping AI features to production is mostly ordinary backend discipline applied to an unusual dependency. The concerns below are where AI features tend to fail in the real world.

Cost and token budgeting

Caching

Rate limits, latency, and streaming

Evals (evaluation)

Guardrails and prompt injection (defensive)

PII and data privacy

General engineering hygiene

References