DSPy 2026 - Lập trình LLM thay vì Prompt Engineering với Signatures, Modules, MIPROv2 và BootstrapFinetune cho Multi-Agent Production
Posted on: 4/15/2026 2:10:30 AM
Table of contents
- 1. Vì sao DSPy là thay đổi thế hệ trong cách làm LLM
- 2. Ba trụ cột: Signature, Module, Optimizer
- 3. Signature — viết bài toán như viết type
- 4. Module — từ Predict đến ReAct
- 5. Optimizer — nơi phép màu xảy ra
- 6. Metric — cái giá thực sự của DSPy
- 7. Retrieval Models — RAG theo kiểu DSPy
- 8. Multi-Agent với DSPy — mỗi agent là một Program
- 9. Caching với Redis — cả prompt cache lẫn LM cache
- 10. Observability — đưa trace DSPy vào ClickHouse
- 11. Workflow compile — từ notebook đến production
- 12. DSPy so với LangChain, LlamaIndex và BAML
- 13. Các pattern production đáng dùng năm 2026
- 14. Anti-pattern thường gặp
- 15. Checklist trước khi đưa DSPy vào production
- 16. Tổng kết
- Nguồn tham khảo
Trong suốt ba năm đầu của kỷ nguyên LLM, cả ngành cùng làm một việc: viết prompt thật khéo. Mỗi team có "prompt engineer" riêng, mỗi repo có một thư mục prompts/ chứa hàng chục file text dài dằng dặc, mỗi lần đổi model là cả tuần tinh chỉnh lại. Nhưng đến cuối 2024, một dự án nghiên cứu từ Stanford NLP bắt đầu đặt câu hỏi ngược: Vì sao ta phải viết prompt bằng tay, trong khi ta có thể lập trình bài toán và để compiler sinh ra prompt cho ta? Dự án đó là DSPy — khung lập trình cho LLM nơi ta không viết chuỗi ký tự, ta viết signature và module, rồi để optimizer tìm ra prompt, few-shot example và thậm chí cả trọng số LoRA tốt nhất cho chúng. Đến 2026, DSPy đã trở thành một trong những cách tiếp cận tạo pipeline multi-agent ổn định nhất khi cần chạy qua nhiều model, nhiều nhà cung cấp và nhiều phiên bản dataset. Bài viết này đi sâu vào kiến trúc, các module cốt lõi, optimizer, cách tích hợp với Redis cho caching và ClickHouse cho observability, và những pattern production mà đội engineering nên nắm năm 2026.
1. Vì sao DSPy là thay đổi thế hệ trong cách làm LLM
Muốn hiểu DSPy, hãy nhìn lại vòng đời một tính năng LLM truyền thống. Lập trình viên mở editor, viết một prompt khoảng 400-800 token mô tả nhiệm vụ, nhét vài ví dụ few-shot, chạy thử trên một bộ input mẫu, thấy sai ở edge case thì thêm câu "Hãy chắc chắn rằng..." vào prompt. Ba tuần sau, prompt dài 2000 token với mười lời nhắc nhở chồng lên nhau, không ai dám sửa vì sợ hồi quy, và mỗi khi nhà cung cấp cập nhật model thì mọi thứ phải làm lại từ đầu. Đây không phải là phần mềm — đó là một bộ sưu tập dị bản ngẫu nhiên của một loạt câu mô tả bài toán bằng tiếng Anh.
DSPy đặt câu hỏi: nếu ta coi pipeline LLM là một chương trình — với các đầu vào kiểu rõ ràng, các bước biến đổi có cấu trúc, và một hàm mục tiêu đo lường được — thì prompt chỉ còn là "mã sinh ra tự động" cho một máy ảo đặc biệt tên là LM. Giống như ngày xưa ta không viết assembly, ngày nay ta không nên viết prompt bằng tay. Ta viết signature (tương tự type signature trong Haskell), viết module (tương tự layer trong PyTorch) và gọi optimizer để compile — y hệt việc một compiler C tối ưu vòng lặp, một compiler PyTorch tối ưu graph.
Phát biểu cốt lõi của DSPy
Lập trình viên mô tả bài toán (input/output fields, metric), không mô tả cách giải. Optimizer sẽ tìm ra cách giải tốt nhất trong không gian {prompt, few-shot demos, weights} dựa trên metric và dataset. Khi đổi model, đổi data, đổi metric — ta chỉ chạy lại optimizer, không viết lại chuỗi tự nhiên.
2. Ba trụ cột: Signature, Module, Optimizer
Toàn bộ DSPy xoay quanh ba khái niệm. Nắm chắc chúng là nắm chắc framework.
graph LR
S["Signature
input/output types
+ docstring"] --> M["Module
Predict / CoT / ReAct
+ compose thành Program"]
M --> P["Program
một forward(...) hoàn chỉnh"]
D["Trainset
<=200 mẫu"] --> O
Me["Metric
hàm đo đúng/sai"] --> O["Optimizer
BootstrapFewShot / MIPROv2
BootstrapFinetune"]
P --> O
O --> CP["Compiled Program
prompt + demos + weights
được tối ưu"]
CP --> Prod["Production runtime
module.forward(...)"]
style S fill:#e94560,stroke:#fff,color:#fff
style M fill:#0f3460,stroke:#fff,color:#fff
style O fill:#0f3460,stroke:#fff,color:#fff
style CP fill:#4CAF50,stroke:#fff,color:#fff
Mỗi mảnh giải một vấn đề khác nhau. Signature tách bạch đặc tả khỏi cách thực hiện — ta nói "hàm này nhận câu hỏi và trả ra câu trả lời dạng ngắn", không nói "hãy trả lời súc tích và chính xác". Module là đơn vị thực thi — cùng một signature có thể được phục vụ bởi Predict thuần, ChainOfThought bật reasoning, hoặc ReAct gọi tool. Optimizer là phần làm DSPy khác biệt — nó biến một pipeline viết thô thành phiên bản có prompt/demo/weights được tối ưu trên dataset nhỏ của bạn.
3. Signature — viết bài toán như viết type
Một signature trong DSPy trông như thế này:
import dspy
class AnswerWithCitation(dspy.Signature):
"""Trả lời câu hỏi dựa trên các đoạn ngữ cảnh, kèm trích dẫn đoạn nào."""
context: list[str] = dspy.InputField(desc="Các đoạn văn liên quan, đã được truy xuất")
question: str = dspy.InputField()
answer: str = dspy.OutputField(desc="Câu trả lời ngắn, 1-3 câu")
citation_ids: list[int] = dspy.OutputField(desc="Chỉ số 0-based của các đoạn được dùng")Đây không chỉ là kiểu. Docstring trong lớp là mô tả bài toán ở mức ngữ nghĩa — DSPy dùng nó làm "core instruction" khi sinh prompt. Các field có kiểu Python chuẩn; list, dict, int, enum, thậm chí Pydantic model cho output có cấu trúc. Khi signature được dùng trong một Module, DSPy sẽ tự động:
- Sinh chỉ dẫn về định dạng output dựa trên kiểu (ví dụ: với
list[int]sẽ yêu cầu mảng JSON số nguyên). - Parse output LM về đúng kiểu và validate, retry với instruction cụ thể nếu sai.
- Nối few-shot demos tự động khi optimizer cung cấp.
Tránh docstring mang tính "prompt engineering"
Trong DSPy, docstring chỉ nên mô tả nhiệm vụ, không chứa "hãy trả lời ngắn gọn, đừng dài dòng, đừng thêm lời dẫn...". Những chỉ dẫn đó nên để optimizer tự khám phá. Bạn chỉ nói "trả lời câu hỏi kèm citation", DSPy sẽ tìm ra cách nhắc model phù hợp nhất trên dataset của bạn.
4. Module — từ Predict đến ReAct
Module là đơn vị thực thi một signature. DSPy cung cấp sẵn một tập module đủ cho hầu hết nhu cầu:
- dspy.Predict — gọi LM một lần, parse output. Đây là lớp nền tảng mà các module khác kế thừa.
- dspy.ChainOfThought — thêm một field
reasoning: strtrước các output field. Model buộc phải sinh chuỗi suy luận trước khi trả lời. Là cải tiến nhỏ nhưng thường tăng 5-15% accuracy trên bài toán có reasoning. - dspy.ProgramOfThought — model sinh code Python, chạy code trong , lấy kết quả làm output. Rất mạnh cho bài toán tính toán, phân tích dữ liệu.
- dspy.ReAct — vòng lặp Thought-Action-Observation với tool use. Module tự quản lý history và dừng khi có answer.
- dspy.MultiChainComparison — gọi ChainOfThought nhiều lần với temperature > 0, sau đó aggregate. Xấp xỉ self-consistency.
- dspy.Refine — gọi một module, kiểm tra output bằng một metric, nếu chưa đạt thì gọi lại kèm feedback. Dạng agent tự sửa đơn giản.
Một program DSPy là một class kế thừa dspy.Module, định nghĩa các submodule trong __init__ và compose trong forward:
class RAGAgent(dspy.Module):
def __init__(self, k=5):
super().__init__()
self.retrieve = dspy.Retrieve(k=k)
self.generate = dspy.ChainOfThought(AnswerWithCitation)
def forward(self, question):
ctx = self.retrieve(question).passages
pred = self.generate(context=ctx, question=question)
return dspy.Prediction(
answer=pred.answer,
citations=pred.citation_ids,
context=ctx,
)Nhìn có vẻ không khác LangChain — nhưng điểm mấu chốt là không có chuỗi prompt nào xuất hiện. Khi chạy thô, DSPy sinh prompt tự động từ signature. Khi chạy sau khi compile, prompt đó đã được optimizer tinh chỉnh dựa trên trainset và metric.
5. Optimizer — nơi phép màu xảy ra
Optimizer (gốc gọi là "teleprompter") nhận vào một Program chưa compile, một trainset nhỏ, và một metric; trả về Program đã compile. Có nhiều optimizer ở các mức độ phức tạp khác nhau:
graph TB
U["Uncompiled Program"] --> B1["LabeledFewShot
chèn demo cố định"]
U --> B2["BootstrapFewShot
tự sinh demo bằng teacher model"]
U --> B3["BootstrapFewShotWithRandomSearch
thử nhiều tập demo, chọn bộ tốt nhất"]
U --> B4["MIPROv2
tối ưu đồng thời instruction + demo
bằng Bayesian optimization"]
U --> B5["BootstrapFinetune
sinh dữ liệu training rồi finetune LoRA
cho open model"]
B1 --> C["Compiled Program"]
B2 --> C
B3 --> C
B4 --> C
B5 --> C
style U fill:#888,stroke:#fff,color:#fff
style B4 fill:#e94560,stroke:#fff,color:#fff
style B5 fill:#e94560,stroke:#fff,color:#fff
style C fill:#4CAF50,stroke:#fff,color:#fff
Trong thực tế, ba optimizer được dùng nhiều nhất năm 2026 là:
- BootstrapFewShot — cách rẻ nhất để có một Program nhỉnh hơn viết tay. Ý tưởng: dùng chính program chưa compile như một "teacher", chạy trên trainset, giữ lại các trace đi kèm metric cao, nhúng chúng làm few-shot demos. Hầu như miễn phí, chỉ tốn bằng N lần chạy trainset.
- MIPROv2 — hiện là optimizer SOTA trong DSPy. Nó dùng Bayesian optimization để tìm đồng thời (a) instruction tốt nhất cho từng Predict layer, và (b) tập few-shot demos tốt nhất. MIPROv2 đặc biệt mạnh khi bạn có dataset vài chục đến vài trăm mẫu và metric đo được end-to-end. Giá là thời gian compile tính bằng chục phút đến vài giờ, cùng với một "prompt proposer model" thường là GPT-4o hoặc Claude.
- BootstrapFinetune — khi đích đến là một open-weight model, DSPy sẽ dùng teacher mạnh sinh ra tập training, rồi finetune (thường là LoRA) trên student model. Pipeline DSPy vẫn y nguyên — chỉ weights bên dưới thay đổi.
Tập train nhỏ là đủ
Không giống training truyền thống, DSPy không học từ hàng chục nghìn mẫu. 50-200 mẫu chất lượng thường là đủ cho BootstrapFewShot và MIPROv2. Điểm nghẽn thật sự không phải số lượng mẫu mà là độ tin cậy của metric. Metric yếu thì optimizer cũng chỉ tối ưu sai hướng nhanh hơn.
6. Metric — cái giá thực sự của DSPy
Optimizer chỉ tốt bằng metric. Và viết metric tốt cho bài toán LLM là việc khó nhất — khó hơn viết prompt. Trong DSPy, metric là một hàm Python nhận (example, prediction, trace) và trả về số (hoặc bool). Có ba họ metric hay gặp:
- Exact-match / containment — dùng khi output có ground truth rõ (ví dụ: câu trả lời nằm trong một tập đáp án).
- Programmatic check — dùng cho bài toán có ràng buộc kiểu: JSON hợp lệ, mỗi citation có thật, tổng cộng <= N token, thuộc enum cho phép, v.v.
- LLM-as-judge — dùng chính một signature DSPy khác để chấm điểm. Rất mềm dẻo nhưng phải viết cẩn thận; judge tồi là một nguồn optimize sai phổ biến.
Mẫu metric điển hình cho một RAG agent:
def rag_metric(example, pred, trace=None):
ans_ok = example.answer.lower() in pred.answer.lower()
cites_ok = all(0 <= c < len(pred.context) for c in pred.citations)
format_ok = len(pred.answer.split()) <= 60
return int(ans_ok and cites_ok and format_ok)Lưu ý hàm này trả về số nguyên 0/1. Nhiều optimizer DSPy (BootstrapFewShot, MIPROv2) dùng tín hiệu rời rạc này để chọn trace "tốt" làm demo. Nếu metric của bạn là một hàm liên tục, hãy ngưỡng hóa về nhị phân trước khi đưa cho optimizer — hoặc dùng optimizer hỗ trợ continuous reward như dspy.BootstrapFewShotWithOptuna.
7. Retrieval Models — RAG theo kiểu DSPy
Trong DSPy, retrieval được mô hình hóa như một "Retrieval Model" (RM) — một client cung cấp hàm retrieve(query, k) -> list[Passage]. DSPy cung cấp RM cho hầu hết vector store phổ biến: Chroma, Weaviate, Pinecone, Qdrant, ColBERTv2, và đặc biệt từ 2025 có adapter cho Redis 8 Vector Set. Cấu hình toàn cục như sau:
import dspy
lm = dspy.LM("anthropic/claude-sonnet-4-6", max_tokens=1024)
rm = dspy.RedisVS(
host="redis-cluster.internal",
key="embeddings:knowledge",
embed_model="text-embedding-3-small",
k=5,
)
dspy.configure(lm=lm, rm=rm)Sau đó mọi module gọi dspy.Retrieve(k=5) sẽ dùng Redis Vector Set ở dưới. Giá trị của mô hình này: pipeline DSPy không bị khóa vào một vector store cụ thể — đổi Redis sang Milvus chỉ là đổi một dòng dspy.configure.
8. Multi-Agent với DSPy — mỗi agent là một Program
Điểm mà DSPy tỏa sáng trong kiến trúc multi-agent là tính compose được. Mỗi agent là một dspy.Module độc lập, có signature riêng, có thể compile riêng trên dataset riêng. Một hệ multi-agent là một module cấp cao hơn — chỉ là một program gọi nhiều program khác.
graph LR
U["User Query"] --> R["Router
Predict[QueryType]"]
R -->|factual| RAG["RAGAgent
ChainOfThought"]
R -->|code| CA["CodeAgent
ReAct + tools"]
R -->|analysis| DA["DataAgent
ProgramOfThought"]
RAG --> Agg["Aggregator
Predict[FinalAnswer]"]
CA --> Agg
DA --> Agg
Agg --> Resp["Response
typed output"]
style R fill:#e94560,stroke:#fff,color:#fff
style Agg fill:#4CAF50,stroke:#fff,color:#fff
Mỗi agent có thể được compile với optimizer khác: Router dùng BootstrapFewShot vì signature ngắn, dữ liệu đơn giản; RAGAgent dùng MIPROv2 vì instruction phức tạp hơn; CodeAgent dùng BootstrapFinetune để nhỏ gọn và chạy nhanh trên Qwen2.5-Coder. Một program cha bọc tất cả lại và có thể được compile end-to-end — nghĩa là optimizer nhìn pipeline tổng thể và phân bổ few-shot/instruction cho từng layer dựa trên metric cuối cùng. Đây là điều LangChain không có: bạn không thể "compile" một chain LangChain; bạn chỉ có thể chạy nó.
9. Caching với Redis — cả prompt cache lẫn LM cache
DSPy có sẵn một LM cache nội bộ dựa trên hash của (model, prompt, temperature, stop). Mặc định nó lưu cục bộ bằng diskcache, phù hợp lúc phát triển. Ở production, bạn nên thay bằng Redis để chia sẻ cache giữa các worker:
import dspy
import redis
r = redis.Redis(host="redis-cache.internal", decode_responses=True)
def redis_cache(prompt_hash, fn):
cached = r.get(f"dspy:lm:{prompt_hash}")
if cached:
return cached
out = fn()
r.setex(f"dspy:lm:{prompt_hash}", 86400, out)
return out
dspy.settings.configure(lm=lm, cache=redis_cache)Trên Redis 8 trở lên, ta còn có thêm một tầng nữa là semantic cache — dùng Vector Set để tìm prompt tương tự về ngữ nghĩa chứ không chỉ trùng byte. Điều này đặc biệt hữu ích cho các agent gặp câu hỏi tương tự viết theo nhiều cách khác nhau. Kết hợp hai tầng:
- Lookup tầng 1 — exact cache: hash SHA1 của payload đầy đủ, TTL ngắn (vài giờ), hit rate rất cao với các request lặp hoàn toàn (ví dụ cùng một request đi qua retry).
- Lookup tầng 2 — semantic cache: embed query, VSIM với threshold 0.92, TTL trung bình (vài ngày), bắt được các phiên bản paraphrase.
- Miss cả hai: gọi LM thật, ghi kết quả vào cả hai tầng.
Coi chừng khi cache một agent có tool-use
Với ReAct hoặc các agent có side effect (gọi API ngoài, đổi database), cache nguyên câu trả lời là nguy hiểm — side effect sẽ không xảy ra ở lần cache hit. Chỉ cache các inner Predict layer (ví dụ: Predict sinh Thought, Predict parse Observation), không cache toàn bộ agent.forward.
10. Observability — đưa trace DSPy vào ClickHouse
Mỗi lần một module DSPy chạy, nó tạo ra một trace gồm: prompt cuối cùng gửi đến LM, output, tokens in/out, latency, metric (nếu có). DSPy expose qua dspy.settings.trace và nhiều integration qua OpenTelemetry. Để có dashboard production, pattern thực dụng là đẩy trace sang ClickHouse qua một collector OTLP hoặc push trực tiếp qua HTTP.
Schema tối thiểu cho bảng trace:
CREATE TABLE IF NOT EXISTS dspy_traces (
ts DateTime64(3) DEFAULT now64(3),
run_id String,
program LowCardinality(String),
module LowCardinality(String),
signature LowCardinality(String),
model LowCardinality(String),
input_tokens UInt32,
output_tokens UInt32,
latency_ms UInt32,
metric_score Float32,
compile_id LowCardinality(String),
error String
) ENGINE = MergeTree
PARTITION BY toYYYYMM(ts)
ORDER BY (program, module, ts);Trường compile_id rất đắt giá: mỗi khi ta chạy optimizer và lưu Program compile thành file, sinh ra một id. Dashboard so sánh accuracy/latency giữa hai compile_id trên cùng traffic thực tế là công cụ để quyết định rollout hay rollback một bản optimize. Đây là "A/B test cho prompt" ở cấp độ hệ thống, không còn là chuyện copy-paste hai file prompt rồi đoán.
11. Workflow compile — từ notebook đến production
Một trong những hiểu lầm phổ biến là coi DSPy như thư viện runtime thuần. Thực ra nó có vòng đời hai pha rất giống học máy truyền thống:
program.save(path). File này là artifact deploy — giống như checkpoint model trong ML. Versioning nên đi kèm Git với commit hash của code và hash của trainset.program.load(path) lúc khởi động. Sau đó mỗi request chỉ là program(question=...) — không còn compile, không còn optimizer, không còn teacher model.12. DSPy so với LangChain, LlamaIndex và BAML
| Tiêu chí | DSPy | LangChain / LangGraph | LlamaIndex | BAML |
|---|---|---|---|---|
| Triết lý | Program + optimizer; prompt là output của compile | Chain/graph thủ công, prompt viết tay | Index-first cho RAG, prompt viết tay | Schema-first DSL, prompt do runtime sinh |
| Optimizer tự động | Có — BootstrapFewShot, MIPROv2, BootstrapFinetune | Không — cần framework ngoài (LangSmith prompt lab) | Không | Không, có test-as-you-go |
| Typed I/O | Có — Python type + Pydantic | Một phần qua tool schema | Một phần | Có — mạnh nhất, có test type |
| Multi-agent compose | Program chứa program, compile end-to-end | Graph state machine rõ ràng | Agent module cơ bản | Không — tập trung lớp I/O |
| Đổi model | Đổi dspy.LM, re-compile là xong | Đổi client; prompt có thể vẫn hợp lệ | Đổi LLM object | Đổi config generator |
| Learning curve | Cao ban đầu (cần hiểu signature/optimizer), dễ dần về sau | Trung bình; nhiều khái niệm | Thấp cho RAG cơ bản | Thấp cho việc output có cấu trúc |
| Phù hợp nhất cho | Pipeline cần tối ưu, đo được, đổi model thường xuyên | Workflow graph phức tạp, stateful | Ứng dụng RAG đơn giản đến trung bình | Lớp IO có schema cần type-safe mạnh |
Ba framework này không loại trừ nhau. Một stack production 2026 tương đối phổ biến là: BAML cho lớp IO có cấu trúc ở boundary (API schemas), DSPy cho các agent phức tạp cần tối ưu được, và LangGraph khi cần state machine dài với human-in-the-loop. Chọn một không có nghĩa là bỏ tất cả những cái còn lại.
13. Các pattern production đáng dùng năm 2026
13.1. Ensemble nhiều bản compile
Thay vì deploy một Compiled Program, deploy hai hoặc ba bản tối ưu khác nhau (khác seed, khác teacher, khác optimizer). Router nhận request, gọi song song, vote majority hoặc chọn theo confidence. Chi phí gấp 2-3 lần, accuracy tăng 3-10 điểm trên benchmark khó. Dùng khi chất lượng quan trọng hơn chi phí.
13.2. Shadow deploy
Version mới của compiled program chạy song song với version cũ trong production, nhưng chỉ version cũ trả về client. Trace cả hai vào ClickHouse. So sánh metric sau 24-72 giờ. Nếu new thắng, cutover. Quy trình này phát hiện hồi quy mà eval offline bỏ sót — ví dụ cách user thật hỏi khác cách dataset đặt câu hỏi.
13.3. Ba tầng cache: exact, semantic, prefix
Prefix cache (do nhà cung cấp LLM hỗ trợ qua prompt caching API) giảm chi phí token; exact cache Redis giảm cả latency; semantic cache Redis VS giảm cả hai khi paraphrase nhiều. Ba tầng kết hợp hạ tỷ lệ gọi LM thật xuống 40-60% trong nhiều workload retrieval.
13.4. Theo dõi metric drift
Lưu điểm metric thực tế (khi có feedback) vào ClickHouse. Mỗi tuần tính mean rolling trên từng compile_id. Khi điểm giảm quá một ngưỡng — ví dụ 5% — trigger recompile với trainset mới. Đây là vòng lặp "mlops cho prompt" thực sự khả thi chỉ khi optimizer được tự động hóa, và DSPy cung cấp sẵn điều đó.
13.5. Compile theo budget
MIPROv2 nhận tham số max_bootstrapped_demos, max_labeled_demos, num_trials. Với pipeline production, bắt đầu với budget nhỏ (num_trials=10) để có baseline nhanh, rồi tăng dần khi cần. Đừng chạy full budget ngay; mỗi lần MIPROv2 tốn có thể cả trăm USD tiền API cho teacher model.
14. Anti-pattern thường gặp
- Viết instruction trong docstring rồi đòi optimizer làm gì đó khác — docstring dài quá ngăn không gian tối ưu. Giữ docstring gọn, để optimizer điền phần còn lại.
- Metric là một hàm LLM-as-judge không được hiệu chỉnh — judge không khớp với ground truth thì optimizer cũng đi sai hướng. Luôn đo correlation giữa judge và con người trên ít nhất 30 mẫu trước khi dùng judge làm metric tối ưu.
- Compile trên một model, deploy trên model khác — few-shot demos được tuning cho teacher; chạy với student yếu hơn có thể mất nhiều hơn là được. Luôn compile trên đúng model sẽ chạy ở production, hoặc dùng BootstrapFinetune để đồng bộ student.
- Không lưu compiled program vào version control — file compiled là artifact cần được treat như binary release. Lưu vào S3/artifact registry kèm commit hash của code và hash của trainset.
- Dùng DSPy cho pipeline 1 bước không metric — nếu bài toán chỉ là "gọi LLM parse JSON", BAML hoặc dspy.Predict thuần là đủ; không cần optimizer. DSPy có lợi nhất khi có metric rõ và dataset nhỏ.
15. Checklist trước khi đưa DSPy vào production
- Đã có ít nhất 50 mẫu trainset và 50 mẫu devset độc lập.
- Đã viết metric và đo correlation với đánh giá tay trên 30-50 mẫu.
- Đã compile bằng BootstrapFewShot làm baseline trước khi dùng MIPROv2.
- Đã lưu compiled program ra file JSON và đưa vào artifact registry.
- Đã cấu hình Redis cache (exact + semantic) và đo hit rate trong shadow.
- Đã expose trace sang ClickHouse và có dashboard so sánh theo
compile_id. - Đã chuẩn bị quy trình rollback — load compiled program phiên bản cũ từ file artifact khi metric sụt.
- Đã tài liệu hóa signature và metric trong repo — hai thứ này là "API" thực sự của pipeline DSPy.
- Đã test với ít nhất hai nhà cung cấp LLM khác nhau để xác nhận tính linh hoạt; đừng phụ thuộc ngầm vào một model.
- Đã thiết lập budget cảnh báo cho compile: mỗi MIPROv2 run có thể tốn tiền — biết trước con số đáng giá.
16. Tổng kết
DSPy không phải thư viện cung cấp thêm vài helper cho prompt. Nó là một phát biểu kiến trúc: pipeline LLM nên được lập trình, không được "prompt-engineer". Khi nhìn dưới góc độ này, mọi mảnh trong hệ multi-agent bỗng trở nên có cấu trúc: signature là interface, module là implementation, metric là test, optimizer là compiler, compiled program là artifact, và ClickHouse trace là monitoring. Đây là vòng đời phát triển mà các ngành kỹ thuật khác đã có từ lâu — điều duy nhất mới là nó lần đầu áp dụng được cho hệ thống sinh ngôn ngữ tự nhiên.
Với một đội engineering đang cân nhắc giữa "viết prompt hay hơn" và "chuyển sang lập trình prompt", câu hỏi không còn là "DSPy có tốt hơn không" mà là "bao giờ ta bắt đầu". Càng sớm đưa pipeline vào vòng đời compile-deploy-measure-recompile, càng sớm thoát khỏi trò chơi vô hạn của việc tinh chỉnh chuỗi chữ trong prompts.py. Và với cặp đôi Redis 8 (làm tầng cache và retrieval) cùng ClickHouse (làm tầng observability), các pipeline DSPy production có thể đạt được chi phí, độ trễ và độ tin cậy mà prompt engineering thủ công không bao giờ với tới. Năm 2026, câu hỏi đáng hỏi không phải "prompt của bạn là gì" mà là "compiled program của bạn được optimize lần cuối vào bao giờ, trên metric nào".
Nguồn tham khảo
- DSPy Documentation, Programming—not prompting—Foundation Models — dspy.ai
- Stanford NLP, DSPy GitHub repository — github.com/stanfordnlp/dspy
- Khattab et al., DSPy: Compiling Declarative Language Model Calls into Self-Improving Pipelines, ICLR 2024 — arxiv.org/abs/2310.03714
- Opsahl-Ong et al., Optimizing Instructions and Demonstrations for Multi-Stage Language Model Programs (MIPROv2), 2024 — arxiv.org/abs/2406.11695
- DSPy Docs, Optimizers (formerly Teleprompters) — dspy.ai/learn/optimization/optimizers
- DSPy Docs, Modules — Predict, ChainOfThought, ReAct, ProgramOfThought — dspy.ai/learn/programming/modules
- DSPy Docs, Signatures — dspy.ai/learn/programming/signatures
- Redis Documentation, Vector Sets overview — redis.io/docs/latest/develop/data-types/vector-sets
- ClickHouse Documentation, MergeTree engine — clickhouse.com/docs engines/mergetree
- Anthropic, Prompt caching with Claude — docs.anthropic.com prompt-caching
Structured Outputs & Tool Calling 2026 - Kiến trúc JSON Schema, Constrained Decoding, XGrammar và Tool Registry cho Multi-Agent AI với Redis và ClickHouse
Streaming LLM Infrastructure 2026 - Kiến trúc Token-Level Streaming với SSE, Redis Streams, Resumable Streams và ClickHouse cho Multi-Agent Production
Disclaimer: The opinions expressed in this blog are solely my own and do not reflect the views or opinions of my employer or any affiliated organizations. The content provided is for informational and educational purposes only and should not be taken as professional advice. While I strive to provide accurate and up-to-date information, I make no warranties or guarantees about the completeness, reliability, or accuracy of the content. Readers are encouraged to verify the information and seek independent advice as needed. I disclaim any liability for decisions or actions taken based on the content of this blog.