@xdev-asia/xdev-knowledge-mcp 1.0.58 → 1.0.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (15) hide show
  1. package/content/blog/ai/nvidia-dli-generative-ai-chung-chi-va-lo-trinh-hoc.md +894 -0
  2. package/content/series/luyen-thi/luyen-thi-nvidia-dli-generative-ai/chapters/01-deep-learning-foundations/lessons/01-bai-1-pytorch-neural-network-fundamentals.md +790 -0
  3. package/content/series/luyen-thi/luyen-thi-nvidia-dli-generative-ai/chapters/01-deep-learning-foundations/lessons/02-bai-2-transformer-architecture-attention.md +984 -0
  4. package/content/series/luyen-thi/luyen-thi-nvidia-dli-generative-ai/chapters/02-diffusion-models/lessons/01-bai-3-unet-architecture-denoising.md +1111 -0
  5. package/content/series/luyen-thi/luyen-thi-nvidia-dli-generative-ai/chapters/02-diffusion-models/lessons/02-bai-4-ddpm-forward-reverse-diffusion.md +1007 -0
  6. package/content/series/luyen-thi/luyen-thi-nvidia-dli-generative-ai/chapters/02-diffusion-models/lessons/03-bai-5-clip-text-to-image-pipeline.md +1037 -0
  7. package/content/series/luyen-thi/luyen-thi-nvidia-dli-generative-ai/chapters/03-llm-applications-rag/lessons/01-bai-6-llm-inference-pipeline-design.md +929 -0
  8. package/content/series/luyen-thi/luyen-thi-nvidia-dli-generative-ai/chapters/03-llm-applications-rag/lessons/02-bai-7-rag-retrieval-augmented-generation.md +1099 -0
  9. package/content/series/luyen-thi/luyen-thi-nvidia-dli-generative-ai/chapters/03-llm-applications-rag/lessons/03-bai-8-rag-agent-build-evaluate.md +1249 -0
  10. package/content/series/luyen-thi/luyen-thi-nvidia-dli-generative-ai/chapters/04-agentic-ai-customization/lessons/01-bai-9-agentic-ai-multi-agent-systems.md +1357 -0
  11. package/content/series/luyen-thi/luyen-thi-nvidia-dli-generative-ai/chapters/04-agentic-ai-customization/lessons/02-bai-10-llm-evaluation-lora-fine-tuning.md +1867 -0
  12. package/content/series/luyen-thi/luyen-thi-nvidia-dli-generative-ai/index.md +237 -0
  13. package/data/quizzes/nvidia-dli-generative-ai.json +350 -0
  14. package/data/quizzes.json +14 -0
  15. package/package.json +1 -1
@@ -0,0 +1,929 @@
1
+ ---
2
+ id: 019c9619-nv01-p3-l06
3
+ title: 'Bài 6: LLM Inference Pipeline Design'
4
+ slug: bai-6-llm-inference-pipeline-design
5
+ description: >-
6
+ LLM inference parameters: temperature, top-k, top-p.
7
+ NVIDIA NIM microservices cho triển khai model.
8
+ LangChain LCEL pipeline.
9
+ Gradio & LangServe: build UI + API.
10
+ Dialog management & multi-turn conversation.
11
+ duration_minutes: 75
12
+ is_free: true
13
+ video_url: null
14
+ sort_order: 6
15
+ section_title: "Part 3: LLM Applications & RAG"
16
+ course:
17
+ id: 019c9619-nv01-7001-c001-nv0100000001
18
+ title: 'Luyện thi NVIDIA DLI — Generative AI with Diffusion Models & LLMs'
19
+ slug: luyen-thi-nvidia-dli-generative-ai
20
+ ---
21
+
22
+ <h2 id="1-gioi-thieu-part-3">1. Từ Diffusion Models sang LLM Applications</h2>
23
+
24
+ <p>Trong Part 2, chúng ta đã làm chủ <strong>Diffusion Models</strong> — từ forward/reverse process đến CLIP-guided generation. Giờ sang Part 3, trọng tâm chuyển sang <strong>Large Language Models (LLMs)</strong> và cách xây dựng ứng dụng thực tế: inference pipeline, RAG, chatbot.</p>
25
+
26
+ <p>Bài này tập trung vào <strong>LLM Inference Pipeline Design</strong> — cách điều khiển output của LLM thông qua sampling parameters, triển khai model với <strong>NVIDIA NIM</strong>, xây pipeline với <strong>LangChain LCEL</strong>, và build UI/API với <strong>Gradio</strong> + <strong>LangServe</strong>.</p>
27
+
28
+ <blockquote><p><strong>Exam tip:</strong> Đề thi NVIDIA DLI rất hay hỏi về inference parameters (temperature, top-k, top-p) và khi nào dùng NIM vs framework khác. Nắm chắc bảng so sánh ở cuối bài.</p></blockquote>
29
+
30
+ <figure><img src="/storage/uploads/2026/04/nvidia-dli-bai6-llm-inference-pipeline.png" alt="LLM Inference Pipeline — Prompt Template, NIM, LCEL Chain, Gradio UI" loading="lazy" /><figcaption>LLM Inference Pipeline — Prompt Template, NIM, LCEL Chain, Gradio UI</figcaption></figure>
31
+
32
+ <h2 id="2-llm-inference-fundamentals">2. LLM Inference Fundamentals</h2>
33
+
34
+ <h3 id="2-1-autoregressive-generation">2.1. Autoregressive Generation</h3>
35
+
36
+ <p>LLM sinh text theo cơ chế <strong>autoregressive</strong>: mỗi bước, model dự đoán token tiếp theo dựa trên tất cả token trước đó. Quá trình lặp lại cho đến khi gặp <strong>stop token</strong> hoặc đạt <strong>max_tokens</strong>.</p>
37
+
38
+ <pre><code class="language-text">
39
+ Autoregressive Generation Flow
40
+ ═══════════════════════════════
41
+
42
+ Input: "Hà Nội là"
43
+
44
+
45
+ ┌─────────────────────┐
46
+ │ LLM Forward Pass │
47
+ │ P(token | context) │
48
+ └──────────┬──────────┘
49
+
50
+
51
+ ┌─────────────────────┐
52
+ │ Sampling Strategy │──► temperature, top-k, top-p
53
+ │ Select next token │
54
+ └──────────┬──────────┘
55
+
56
+
57
+ token = "thủ"
58
+
59
+
60
+ Input: "Hà Nội là thủ"
61
+
62
+
63
+ ┌─────────────────────┐
64
+ │ LLM Forward Pass │
65
+ └──────────┬──────────┘
66
+
67
+
68
+ token = "đô"
69
+
70
+
71
+ ... lặp lại đến &lt;EOS&gt; hoặc max_tokens
72
+ </code></pre>
73
+
74
+ <h3 id="2-2-sampling-parameters">2.2. Sampling Parameters</h3>
75
+
76
+ <p>Ba tham số quan trọng nhất kiểm soát tính sáng tạo của output:</p>
77
+
78
+ <table>
79
+ <thead>
80
+ <tr><th>Parameter</th><th>Range</th><th>Tác dụng</th><th>Giá trị thấp</th><th>Giá trị cao</th></tr>
81
+ </thead>
82
+ <tbody>
83
+ <tr><td><strong>temperature</strong></td><td>0.0 – 2.0</td><td>Điều chỉnh entropy của phân phối xác suất</td><td>Deterministic, lặp lại</td><td>Sáng tạo, random hơn</td></tr>
84
+ <tr><td><strong>top_k</strong></td><td>1 – vocab_size</td><td>Giới hạn chỉ xét top K token có xác suất cao nhất</td><td>Chọn lọc hơn, ít đa dạng</td><td>Nhiều lựa chọn hơn</td></tr>
85
+ <tr><td><strong>top_p</strong></td><td>0.0 – 1.0</td><td>Nucleus sampling: chỉ xét tokens có cumulative prob ≤ p</td><td>Chỉ token chắc chắn nhất</td><td>Xét nhiều token hơn</td></tr>
86
+ </tbody>
87
+ </table>
88
+
89
+ <pre><code class="language-text">
90
+ Token Sampling Process (temperature + top-p)
91
+ ═════════════════════════════════════════════
92
+
93
+ Raw logits: [2.1, 1.8, 0.5, 0.3, -1.0, -2.5, ...]
94
+
95
+
96
+ ┌──────────────┐
97
+ │ ÷ temperature │ (temp=0.7 → sharper)
98
+ └──────┬───────┘
99
+
100
+
101
+ Scaled probs: [0.35, 0.28, 0.12, 0.09, 0.08, 0.05, 0.03]
102
+
103
+
104
+ ┌──────────────┐
105
+ │ top-p=0.8 │ cumsum: 0.35→0.63→0.75→0.84 ✓
106
+ │ Keep top 4 │ → loại bỏ token 5,6,7...
107
+ └──────┬───────┘
108
+
109
+
110
+ Filtered: [0.41, 0.33, 0.14, 0.12] (re-normalized)
111
+
112
+
113
+ Random sample → token "thủ"
114
+ </code></pre>
115
+
116
+ <h3 id="2-3-other-parameters">2.3. Các tham số khác</h3>
117
+
118
+ <table>
119
+ <thead>
120
+ <tr><th>Parameter</th><th>Mô tả</th><th>Use case</th></tr>
121
+ </thead>
122
+ <tbody>
123
+ <tr><td><strong>max_tokens</strong></td><td>Giới hạn số token output tối đa</td><td>Kiểm soát chi phí, latency</td></tr>
124
+ <tr><td><strong>stop</strong></td><td>Dừng generation khi gặp chuỗi này</td><td>Structured output, function calling</td></tr>
125
+ <tr><td><strong>repetition_penalty</strong></td><td>Phạt token đã xuất hiện (>1.0 = phạt nặng)</td><td>Tránh lặp từ/câu</td></tr>
126
+ <tr><td><strong>frequency_penalty</strong></td><td>Giảm xác suất theo tần suất xuất hiện</td><td>Output đa dạng hơn</td></tr>
127
+ <tr><td><strong>presence_penalty</strong></td><td>Phạt nếu token đã xuất hiện ít nhất 1 lần</td><td>Khuyến khích chủ đề mới</td></tr>
128
+ </tbody>
129
+ </table>
130
+
131
+ <blockquote><p><strong>Exam tip:</strong> Câu hỏi hay gặp: "Muốn output luôn giống nhau (deterministic), set parameter nào?" → <strong>temperature = 0.0</strong>. Nếu hỏi "giảm lặp từ" → dùng <strong>repetition_penalty > 1.0</strong> hoặc <strong>frequency_penalty > 0</strong>.</p></blockquote>
132
+
133
+ <h2 id="3-nvidia-nim">3. NVIDIA NIM (NVIDIA Inference Microservices)</h2>
134
+
135
+ <h3 id="3-1-nim-la-gi">3.1. NIM là gì?</h3>
136
+
137
+ <p><strong>NVIDIA NIM</strong> là bộ <strong>pre-optimized inference containers</strong> cho phép deploy LLM/multimodal models với hiệu năng cao nhất trên GPU NVIDIA. NIM đã tích hợp sẵn <strong>TensorRT-LLM</strong>, quantization, và tối ưu memory.</p>
138
+
139
+ <p>Đặc điểm chính:</p>
140
+ <ul>
141
+ <li><strong>OpenAI-compatible API</strong> — drop-in replacement, dùng openai client gọi thẳng</li>
142
+ <li><strong>TensorRT-LLM backend</strong> — tối ưu kernel cho NVIDIA GPU</li>
143
+ <li><strong>Continuous batching</strong> — xử lý nhiều request cùng lúc hiệu quả</li>
144
+ <li><strong>gRPC + REST API</strong> — flexible integration</li>
145
+ <li><strong>Multi-GPU support</strong> — tensor parallelism tự động</li>
146
+ </ul>
147
+
148
+ <h3 id="3-2-nim-architecture">3.2. NIM Architecture</h3>
149
+
150
+ <pre><code class="language-text">
151
+ NVIDIA NIM Architecture
152
+ ════════════════════════
153
+
154
+ ┌─────────────────────────────────────────────┐
155
+ │ NIM Container │
156
+ │ │
157
+ │ ┌──────────┐ ┌──────────────────────┐ │
158
+ │ │ REST API │ │ gRPC Endpoint │ │
159
+ │ │ :8000 │ │ :8001 │ │
160
+ │ └─────┬────┘ └──────────┬───────────┘ │
161
+ │ │ │ │
162
+ │ └────────┬───────────┘ │
163
+ │ ▼ │
164
+ │ ┌──────────────────────────────────┐ │
165
+ │ │ Request Router &amp; Batcher │ │
166
+ │ │ (Continuous Batching) │ │
167
+ │ └──────────────┬───────────────────┘ │
168
+ │ ▼ │
169
+ │ ┌──────────────────────────────────┐ │
170
+ │ │ TensorRT-LLM Engine │ │
171
+ │ │ ┌────────┐ ┌────────────────┐ │ │
172
+ │ │ │ KV Cache│ │ Paged Attention│ │ │
173
+ │ │ └────────┘ └────────────────┘ │ │
174
+ │ └──────────────┬───────────────────┘ │
175
+ │ ▼ │
176
+ │ ┌──────────────────────────────────┐ │
177
+ │ │ NVIDIA GPU(s) │ │
178
+ │ │ A100 / H100 / L40S │ │
179
+ │ └──────────────────────────────────┘ │
180
+ └─────────────────────────────────────────────┘
181
+ </code></pre>
182
+
183
+ <h3 id="3-3-pull-run-nim">3.3. Pull &amp; Run NIM Container</h3>
184
+
185
+ <pre><code class="language-python">
186
+ # Pull và chạy NIM container cho Llama-3
187
+ # Yêu cầu: NVIDIA GPU, Docker + NVIDIA Container Toolkit
188
+
189
+ # Terminal command:
190
+ # docker run -it --rm --gpus all \
191
+ # -p 8000:8000 \
192
+ # -e NGC_API_KEY=$NGC_API_KEY \
193
+ # nvcr.io/nim/meta/llama-3.1-8b-instruct:latest
194
+ </code></pre>
195
+
196
+ <h3 id="3-4-call-nim-api">3.4. Gọi NIM API</h3>
197
+
198
+ <pre><code class="language-python">
199
+ from openai import OpenAI
200
+
201
+ # NIM tương thích OpenAI API — chỉ cần đổi base_url
202
+ client = OpenAI(
203
+ base_url="http://localhost:8000/v1",
204
+ api_key="not-used" # NIM local không cần key
205
+ )
206
+
207
+ response = client.chat.completions.create(
208
+ model="meta/llama-3.1-8b-instruct",
209
+ messages=[
210
+ {"role": "system", "content": "Bạn là trợ lý AI hữu ích."},
211
+ {"role": "user", "content": "Giải thích Transformer architecture"}
212
+ ],
213
+ temperature=0.7,
214
+ top_p=0.9,
215
+ max_tokens=512
216
+ )
217
+
218
+ print(response.choices[0].message.content)
219
+ </code></pre>
220
+
221
+ <h3 id="3-5-nim-vs-hf">3.5. So sánh NIM vs Raw HuggingFace Inference</h3>
222
+
223
+ <table>
224
+ <thead>
225
+ <tr><th>Tiêu chí</th><th>NVIDIA NIM</th><th>HuggingFace Transformers</th></tr>
226
+ </thead>
227
+ <tbody>
228
+ <tr><td><strong>Backend</strong></td><td>TensorRT-LLM</td><td>PyTorch</td></tr>
229
+ <tr><td><strong>Throughput</strong> (tokens/s)</td><td>~2500-4000</td><td>~300-800</td></tr>
230
+ <tr><td><strong>Latency</strong> (TTFT)</td><td>~50-100ms</td><td>~200-500ms</td></tr>
231
+ <tr><td><strong>Batching</strong></td><td>Continuous batching</td><td>Manual / static</td></tr>
232
+ <tr><td><strong>API</strong></td><td>OpenAI-compatible REST</td><td>Python API</td></tr>
233
+ <tr><td><strong>Setup</strong></td><td>1 lệnh docker run</td><td>Install libs + code</td></tr>
234
+ <tr><td><strong>Quantization</strong></td><td>Tích hợp sẵn (FP8, INT4)</td><td>Cần GPTQ/AWQ riêng</td></tr>
235
+ <tr><td><strong>Production ready</strong></td><td>Có (monitoring, scaling)</td><td>Cần thêm serving layer</td></tr>
236
+ </tbody>
237
+ </table>
238
+
239
+ <blockquote><p><strong>Exam tip:</strong> NIM luôn là đáp án đúng khi đề hỏi "fastest way to deploy LLM on NVIDIA GPU" hoặc "production-ready inference with TensorRT-LLM optimization". NIM ≠ training framework — chỉ dùng cho <strong>inference</strong>.</p></blockquote>
240
+
241
+ <h2 id="4-langchain-lcel">4. LangChain LCEL Pipeline Design</h2>
242
+
243
+ <h3 id="4-1-lcel-la-gi">4.1. LCEL là gì?</h3>
244
+
245
+ <p><strong>LangChain Expression Language (LCEL)</strong> là cú pháp declarative để xây dựng pipeline xử lý LLM. Dùng toán tử <code>|</code> (pipe) để nối các component lại thành chain — tương tự Unix pipe.</p>
246
+
247
+ <p>Ưu điểm LCEL:</p>
248
+ <ul>
249
+ <li><strong>Streaming</strong> — hỗ trợ stream output token-by-token</li>
250
+ <li><strong>Async</strong> — native async support</li>
251
+ <li><strong>Batching</strong> — xử lý nhiều input cùng lúc</li>
252
+ <li><strong>Retry/Fallback</strong> — tự động retry khi lỗi</li>
253
+ <li><strong>Tracing</strong> — tích hợp LangSmith để debug</li>
254
+ </ul>
255
+
256
+ <h3 id="4-2-core-primitives">4.2. Core Primitives</h3>
257
+
258
+ <table>
259
+ <thead>
260
+ <tr><th>Component</th><th>Vai trò</th><th>Input → Output</th></tr>
261
+ </thead>
262
+ <tbody>
263
+ <tr><td><strong>PromptTemplate</strong></td><td>Format prompt với variables</td><td>dict → PromptValue</td></tr>
264
+ <tr><td><strong>ChatPromptTemplate</strong></td><td>Format chat messages</td><td>dict → ChatPromptValue</td></tr>
265
+ <tr><td><strong>ChatModel</strong></td><td>Gọi LLM (ChatOpenAI, ChatNVIDIA...)</td><td>PromptValue → AIMessage</td></tr>
266
+ <tr><td><strong>StrOutputParser</strong></td><td>Extract string từ AIMessage</td><td>AIMessage → str</td></tr>
267
+ <tr><td><strong>JsonOutputParser</strong></td><td>Parse JSON từ output</td><td>AIMessage → dict</td></tr>
268
+ <tr><td><strong>RunnablePassthrough</strong></td><td>Pass input qua không đổi</td><td>any → any</td></tr>
269
+ <tr><td><strong>RunnableLambda</strong></td><td>Wrap function thành Runnable</td><td>any → any</td></tr>
270
+ <tr><td><strong>RunnableParallel</strong></td><td>Chạy nhiều chain song song</td><td>dict → dict</td></tr>
271
+ </tbody>
272
+ </table>
273
+
274
+ <h3 id="4-3-lcel-pipeline-diagram">4.3. LCEL Pipeline Flow</h3>
275
+
276
+ <pre><code class="language-text">
277
+ LCEL Pipeline Architecture
278
+ ════════════════════════════
279
+
280
+ Simple Chain:
281
+ ─────────────
282
+ {"topic": "AI"}
283
+
284
+
285
+ ┌───────────────┐ ┌─────────────┐ ┌────────────────┐
286
+ │ PromptTemplate │──►│ ChatModel │──►│ StrOutputParser │──► "AI là..."
287
+ │ "Explain {topic}"│ │ (ChatNVIDIA) │ │ │
288
+ └───────────────┘ └─────────────┘ └────────────────┘
289
+
290
+ prompt | llm | parser
291
+ LCEL: prompt | llm | parser
292
+
293
+
294
+ Parallel Chain (RunnableParallel):
295
+ ───────────────────────────────────
296
+ {"topic": "AI"}
297
+
298
+ ┌────────┴────────┐
299
+ ▼ ▼
300
+ ┌──────────────┐ ┌──────────────┐
301
+ │ chain_summary│ │ chain_quiz │
302
+ │ prompt | llm │ │ prompt | llm│
303
+ └──────┬───────┘ └──────┬───────┘
304
+ │ │
305
+ └────────┬────────┘
306
+
307
+ {"summary": "...", "quiz": "..."}
308
+ </code></pre>
309
+
310
+ <h3 id="4-4-lcel-code">4.4. Code: LCEL Chain</h3>
311
+
312
+ <pre><code class="language-python">
313
+ from langchain_core.prompts import ChatPromptTemplate
314
+ from langchain_core.output_parsers import StrOutputParser
315
+ from langchain_nvidia_ai_endpoints import ChatNVIDIA
316
+
317
+ # 1. Khởi tạo components
318
+ prompt = ChatPromptTemplate.from_messages([
319
+ ("system", "Bạn là chuyên gia {domain}. Trả lời ngắn gọn."),
320
+ ("human", "{question}")
321
+ ])
322
+
323
+ llm = ChatNVIDIA(
324
+ model="meta/llama-3.1-8b-instruct",
325
+ temperature=0.3,
326
+ top_p=0.9,
327
+ max_tokens=512
328
+ )
329
+
330
+ parser = StrOutputParser()
331
+
332
+ # 2. Tạo chain bằng LCEL pipe syntax
333
+ chain = prompt | llm | parser
334
+
335
+ # 3. Invoke (đồng bộ)
336
+ result = chain.invoke({
337
+ "domain": "deep learning",
338
+ "question": "Transformer self-attention hoạt động thế nào?"
339
+ })
340
+ print(result)
341
+
342
+ # 4. Stream (token-by-token)
343
+ for chunk in chain.stream({
344
+ "domain": "deep learning",
345
+ "question": "So sánh RNN và Transformer"
346
+ }):
347
+ print(chunk, end="", flush=True)
348
+ </code></pre>
349
+
350
+ <h3 id="4-5-advanced-lcel">4.5. Advanced: RunnableParallel &amp; RunnableLambda</h3>
351
+
352
+ <pre><code class="language-python">
353
+ from langchain_core.runnables import (
354
+ RunnablePassthrough,
355
+ RunnableParallel,
356
+ RunnableLambda
357
+ )
358
+
359
+ # Custom function wrapped thành Runnable
360
+ def word_count(text: str) -> dict:
361
+ return {"text": text, "word_count": len(text.split())}
362
+
363
+ # Parallel chain: vừa summarize vừa đếm từ
364
+ parallel_chain = RunnableParallel(
365
+ summary=prompt | llm | parser,
366
+ metadata=RunnableLambda(
367
+ lambda x: f"Query: {x['question']}"
368
+ )
369
+ )
370
+
371
+ # Chain với passthrough — giữ input gốc qua pipeline
372
+ chain_with_context = (
373
+ RunnablePassthrough.assign(
374
+ answer=prompt | llm | parser
375
+ )
376
+ )
377
+
378
+ # Invoke parallel
379
+ result = parallel_chain.invoke({
380
+ "domain": "AI",
381
+ "question": "Generative AI là gì?"
382
+ })
383
+ # result = {"summary": "...", "metadata": "Query: Generative AI là gì?"}
384
+ </code></pre>
385
+
386
+ <blockquote><p><strong>Exam tip:</strong> Khi đề cho code LCEL và hỏi "output type là gì", hãy trace từng bước: PromptTemplate → PromptValue, ChatModel → AIMessage, StrOutputParser → str. <strong>Nếu quên parser</strong>, output sẽ là AIMessage object (không phải string).</p></blockquote>
387
+
388
+ <h2 id="5-gradio-langserve">5. Build UI với Gradio &amp; API với LangServe</h2>
389
+
390
+ <h3 id="5-1-gradio-chatbot">5.1. Gradio: Rapid Chatbot UI</h3>
391
+
392
+ <p><strong>Gradio</strong> cho phép tạo web UI cho ML models chỉ với vài dòng code. Component <code>gr.ChatInterface</code> đặc biệt phù hợp cho chatbot.</p>
393
+
394
+ <pre><code class="language-python">
395
+ import gradio as gr
396
+ from langchain_core.prompts import ChatPromptTemplate
397
+ from langchain_core.output_parsers import StrOutputParser
398
+ from langchain_nvidia_ai_endpoints import ChatNVIDIA
399
+
400
+ # Setup chain
401
+ prompt = ChatPromptTemplate.from_messages([
402
+ ("system", "Bạn là trợ lý AI thân thiện."),
403
+ ("human", "{message}")
404
+ ])
405
+ llm = ChatNVIDIA(model="meta/llama-3.1-8b-instruct")
406
+ chain = prompt | llm | StrOutputParser()
407
+
408
+ # Gradio handler
409
+ def respond(message, history):
410
+ """Handle chat message — history là list of [user, bot] pairs."""
411
+ response = chain.invoke({"message": message})
412
+ return response
413
+
414
+ # Launch UI
415
+ demo = gr.ChatInterface(
416
+ fn=respond,
417
+ title="NVIDIA NIM Chatbot",
418
+ description="Chatbot powered by Llama 3.1 via NIM",
419
+ examples=["Generative AI là gì?", "So sánh GAN và Diffusion"],
420
+ theme="soft"
421
+ )
422
+ demo.launch(server_port=7860)
423
+ </code></pre>
424
+
425
+ <h3 id="5-2-langserve-api">5.2. LangServe: Expose Chain as REST API</h3>
426
+
427
+ <p><strong>LangServe</strong> biến bất kỳ LCEL chain nào thành REST API với docs tự động (Swagger). Phù hợp cho production deployment.</p>
428
+
429
+ <pre><code class="language-python">
430
+ # === Server (server.py) ===
431
+ from fastapi import FastAPI
432
+ from langserve import add_routes
433
+ from langchain_core.prompts import ChatPromptTemplate
434
+ from langchain_core.output_parsers import StrOutputParser
435
+ from langchain_nvidia_ai_endpoints import ChatNVIDIA
436
+
437
+ app = FastAPI(title="LLM API")
438
+
439
+ # Tạo chain
440
+ chain = (
441
+ ChatPromptTemplate.from_messages([
442
+ ("system", "Trợ lý AI chuyên về {domain}."),
443
+ ("human", "{question}")
444
+ ])
445
+ | ChatNVIDIA(model="meta/llama-3.1-8b-instruct")
446
+ | StrOutputParser()
447
+ )
448
+
449
+ # Expose chain tại /chat endpoint
450
+ add_routes(app, chain, path="/chat")
451
+
452
+ # Run: uvicorn server:app --port 8080
453
+ </code></pre>
454
+
455
+ <pre><code class="language-python">
456
+ # === Client (client.py) ===
457
+ from langserve import RemoteRunnable
458
+
459
+ # Kết nối đến LangServe endpoint
460
+ chain = RemoteRunnable("http://localhost:8080/chat")
461
+
462
+ # Invoke giống như local chain
463
+ result = chain.invoke({
464
+ "domain": "machine learning",
465
+ "question": "Overfitting là gì?"
466
+ })
467
+ print(result)
468
+
469
+ # Stream cũng hoạt động
470
+ for chunk in chain.stream({
471
+ "domain": "NLP",
472
+ "question": "Tokenization hoạt động thế nào?"
473
+ }):
474
+ print(chunk, end="")
475
+ </code></pre>
476
+
477
+ <pre><code class="language-text">
478
+ Gradio + LangServe Deployment Pattern
479
+ ═══════════════════════════════════════
480
+
481
+ Browser (User) Mobile App / Service
482
+ │ │
483
+ ▼ ▼
484
+ ┌──────────────┐ ┌──────────────┐
485
+ │ Gradio UI │ │ REST Client │
486
+ │ :7860 │ │ │
487
+ └──────┬───────┘ └──────┬───────┘
488
+ │ │
489
+ └────────┬────────────────┘
490
+
491
+ ┌──────────────────┐
492
+ │ LangServe API │
493
+ │ FastAPI :8080 │
494
+ │ /chat/invoke │
495
+ │ /chat/stream │
496
+ └────────┬─────────┘
497
+
498
+ ┌──────────────────┐
499
+ │ LCEL Chain │
500
+ │ prompt|llm|parser│
501
+ └────────┬─────────┘
502
+
503
+ ┌──────────────────┐
504
+ │ NVIDIA NIM │
505
+ │ :8000 │
506
+ └──────────────────┘
507
+ </code></pre>
508
+
509
+ <blockquote><p><strong>Exam tip:</strong> Gradio = <strong>prototyping/demo UI</strong>, LangServe = <strong>production REST API</strong>. Nếu đề hỏi "fastest way to demo a chatbot" → Gradio. "Expose chain for multiple clients" → LangServe. Hai cái có thể dùng cùng nhau.</p></blockquote>
510
+
511
+ <h2 id="6-dialog-management">6. Dialog Management &amp; Multi-turn Conversation</h2>
512
+
513
+ <h3 id="6-1-memory-types">6.1. Các loại Memory</h3>
514
+
515
+ <p>Chatbot cần <strong>nhớ</strong> context từ các lượt hội thoại trước. LangChain cung cấp nhiều loại memory:</p>
516
+
517
+ <table>
518
+ <thead>
519
+ <tr><th>Memory Type</th><th>Cách hoạt động</th><th>Ưu điểm</th><th>Nhược điểm</th></tr>
520
+ </thead>
521
+ <tbody>
522
+ <tr><td><strong>ConversationBufferMemory</strong></td><td>Lưu toàn bộ lịch sử</td><td>Không mất thông tin</td><td>Token count tăng nhanh</td></tr>
523
+ <tr><td><strong>ConversationBufferWindowMemory</strong></td><td>Giữ N lượt gần nhất</td><td>Kiểm soát token</td><td>Mất context cũ</td></tr>
524
+ <tr><td><strong>ConversationSummaryMemory</strong></td><td>Tóm tắt lịch sử bằng LLM</td><td>Nén thông tin hiệu quả</td><td>Tốn thêm LLM call</td></tr>
525
+ <tr><td><strong>ConversationSummaryBufferMemory</strong></td><td>Tóm tắt cũ + giữ nguyên gần đây</td><td>Cân bằng chi tiết/nén</td><td>Phức tạp hơn</td></tr>
526
+ </tbody>
527
+ </table>
528
+
529
+ <h3 id="6-2-message-types">6.2. Message Types</h3>
530
+
531
+ <p>LangChain dùng typed messages để phân biệt vai trò:</p>
532
+
533
+ <pre><code class="language-python">
534
+ from langchain_core.messages import (
535
+ SystemMessage,
536
+ HumanMessage,
537
+ AIMessage
538
+ )
539
+
540
+ messages = [
541
+ SystemMessage(content="Bạn là trợ lý AI."),
542
+ HumanMessage(content="Xin chào!"),
543
+ AIMessage(content="Chào bạn! Tôi có thể giúp gì?"),
544
+ HumanMessage(content="Giải thích attention mechanism"),
545
+ ]
546
+ </code></pre>
547
+
548
+ <h3 id="6-3-multi-turn-code">6.3. Code: Multi-turn Chatbot với Memory</h3>
549
+
550
+ <pre><code class="language-python">
551
+ from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
552
+ from langchain_core.output_parsers import StrOutputParser
553
+ from langchain_core.chat_history import InMemoryChatMessageHistory
554
+ from langchain_core.runnables.history import RunnableWithMessageHistory
555
+ from langchain_nvidia_ai_endpoints import ChatNVIDIA
556
+
557
+ # 1. Prompt có slot cho message history
558
+ prompt = ChatPromptTemplate.from_messages([
559
+ ("system", "Bạn là trợ lý AI. Trả lời ngắn gọn."),
560
+ MessagesPlaceholder(variable_name="history"),
561
+ ("human", "{input}")
562
+ ])
563
+
564
+ llm = ChatNVIDIA(model="meta/llama-3.1-8b-instruct")
565
+ chain = prompt | llm | StrOutputParser()
566
+
567
+ # 2. Session store — mỗi user một history riêng
568
+ session_store = {}
569
+
570
+ def get_session_history(session_id: str):
571
+ if session_id not in session_store:
572
+ session_store[session_id] = InMemoryChatMessageHistory()
573
+ return session_store[session_id]
574
+
575
+ # 3. Wrap chain với message history
576
+ chain_with_history = RunnableWithMessageHistory(
577
+ chain,
578
+ get_session_history,
579
+ input_messages_key="input",
580
+ history_messages_key="history"
581
+ )
582
+
583
+ # 4. Chat — cùng session_id giữ context
584
+ config = {"configurable": {"session_id": "user-123"}}
585
+
586
+ r1 = chain_with_history.invoke(
587
+ {"input": "Tên tôi là Minh"},
588
+ config=config
589
+ )
590
+ print(r1) # "Xin chào Minh!..."
591
+
592
+ r2 = chain_with_history.invoke(
593
+ {"input": "Tên tôi là gì?"},
594
+ config=config
595
+ )
596
+ print(r2) # "Tên bạn là Minh." ← nhớ context!
597
+ </code></pre>
598
+
599
+ <h3 id="6-4-window-memory">6.4. Window Memory Pattern</h3>
600
+
601
+ <pre><code class="language-text">
602
+ Window Memory (k=3): Chỉ giữ 3 lượt gần nhất
603
+ ═══════════════════════════════════════════════
604
+
605
+ Turn 1: User: "Xin chào" ─┐
606
+ Turn 2: AI: "Chào bạn!" │ ← bị loại khi turn > 3+k
607
+ Turn 3: User: "Tôi là Minh" │
608
+ Turn 4: AI: "Chào Minh!" ─┘
609
+
610
+ Turn 5: User: "Giải thích CNN" ─┐
611
+ Turn 6: AI: "CNN là..." │ ← giữ lại
612
+ Turn 7: User: "So với RNN?" ─┘
613
+
614
+ Prompt gửi đi chỉ gồm: [System] + [Turn 5,6,7] + [Turn 8 input]
615
+ → Tiết kiệm token, nhưng mất context "tên là Minh"
616
+ </code></pre>
617
+
618
+ <blockquote><p><strong>Exam tip:</strong> "Chatbot quên context sau vài lượt" → đang dùng <strong>BufferWindowMemory quá nhỏ</strong> hoặc không có memory. "Token limit exceeded" → chuyển sang <strong>ConversationSummaryMemory</strong> để nén lịch sử.</p></blockquote>
619
+
620
+ <h2 id="7-comparison-table">7. So sánh Inference Frameworks</h2>
621
+
622
+ <table>
623
+ <thead>
624
+ <tr><th>Feature</th><th>NVIDIA NIM</th><th>vLLM</th><th>TGI (HuggingFace)</th><th>Ollama</th></tr>
625
+ </thead>
626
+ <tbody>
627
+ <tr><td><strong>Backend</strong></td><td>TensorRT-LLM</td><td>PagedAttention</td><td>PyTorch + Flash</td><td>llama.cpp</td></tr>
628
+ <tr><td><strong>GPU Required</strong></td><td>NVIDIA (A100/H100)</td><td>NVIDIA</td><td>NVIDIA</td><td>Không (CPU OK)</td></tr>
629
+ <tr><td><strong>Throughput</strong></td><td>Cao nhất</td><td>Rất cao</td><td>Cao</td><td>Thấp</td></tr>
630
+ <tr><td><strong>Quantization</strong></td><td>FP8, INT4 tích hợp</td><td>AWQ, GPTQ</td><td>GPTQ, bitsandbytes</td><td>GGUF</td></tr>
631
+ <tr><td><strong>API</strong></td><td>OpenAI-compatible</td><td>OpenAI-compatible</td><td>Custom + Messages</td><td>OpenAI-compatible</td></tr>
632
+ <tr><td><strong>Setup</strong></td><td>Docker (NGC)</td><td>pip install</td><td>Docker</td><td>1 binary</td></tr>
633
+ <tr><td><strong>Best for</strong></td><td>Enterprise, production</td><td>Research, high-throughput</td><td>HF ecosystem</td><td>Local dev, laptop</td></tr>
634
+ <tr><td><strong>NVIDIA optimized</strong></td><td>✅ Sâu nhất</td><td>✅ Tốt</td><td>Một phần</td><td>❌</td></tr>
635
+ </tbody>
636
+ </table>
637
+
638
+ <blockquote><p><strong>Exam tip:</strong> Đề thi NVIDIA DLI sẽ ưu tiên <strong>NIM</strong> cho mọi câu hỏi deployment production. "Best performance on NVIDIA GPU" → NIM. "Quick local testing on laptop" → Ollama. "Open-source high throughput" → vLLM.</p></blockquote>
639
+
640
+ <h2 id="8-cheat-sheet">8. Cheat Sheet</h2>
641
+
642
+ <table>
643
+ <thead>
644
+ <tr><th>Concept</th><th>Key Point</th></tr>
645
+ </thead>
646
+ <tbody>
647
+ <tr><td>temperature = 0.0</td><td>Deterministic output (lặp lại)</td></tr>
648
+ <tr><td>temperature = 1.0+</td><td>Creative, random hơn</td></tr>
649
+ <tr><td>top_p = 0.1</td><td>Chỉ chọn token chắc chắn nhất</td></tr>
650
+ <tr><td>top_k = 50</td><td>Giới hạn 50 token candidates</td></tr>
651
+ <tr><td>NIM</td><td>Pre-optimized container, TensorRT-LLM, OpenAI API</td></tr>
652
+ <tr><td>LCEL pipe</td><td>prompt | llm | parser</td></tr>
653
+ <tr><td>RunnableParallel</td><td>Chạy nhiều chain cùng lúc</td></tr>
654
+ <tr><td>Gradio</td><td>Demo UI, gr.ChatInterface</td></tr>
655
+ <tr><td>LangServe</td><td>REST API từ LCEL chain, FastAPI</td></tr>
656
+ <tr><td>BufferMemory</td><td>Lưu toàn bộ history → token tăng nhanh</td></tr>
657
+ <tr><td>SummaryMemory</td><td>Nén history bằng LLM → tiết kiệm token</td></tr>
658
+ <tr><td>WindowMemory (k=N)</td><td>Giữ N lượt gần nhất</td></tr>
659
+ <tr><td>MessagesPlaceholder</td><td>Slot trong prompt cho chat history</td></tr>
660
+ <tr><td>RunnableWithMessageHistory</td><td>Wrap chain + session-based memory</td></tr>
661
+ </tbody>
662
+ </table>
663
+
664
+ <h2 id="9-practice-questions">9. Practice Questions</h2>
665
+
666
+ <p><strong>Q1: Build LCEL Chain với Streaming</strong></p>
667
+ <p>Viết LCEL chain dùng <code>PromptTemplate</code> → <code>ChatNVIDIA</code> → <code>StrOutputParser</code>. Prompt nhận <code>topic</code>, yêu cầu LLM giải thích topic đó. Thêm streaming output.</p>
668
+
669
+ <details>
670
+ <summary>Xem đáp án Q1</summary>
671
+
672
+ <pre><code class="language-python">
673
+ from langchain_core.prompts import ChatPromptTemplate
674
+ from langchain_core.output_parsers import StrOutputParser
675
+ from langchain_nvidia_ai_endpoints import ChatNVIDIA
676
+
677
+ # Tạo prompt template
678
+ prompt = ChatPromptTemplate.from_messages([
679
+ ("system", "Bạn là giáo viên AI. Giải thích dễ hiểu."),
680
+ ("human", "Giải thích chi tiết về: {topic}")
681
+ ])
682
+
683
+ # Tạo LLM
684
+ llm = ChatNVIDIA(
685
+ model="meta/llama-3.1-8b-instruct",
686
+ temperature=0.5,
687
+ max_tokens=1024
688
+ )
689
+
690
+ # Tạo parser
691
+ parser = StrOutputParser()
692
+
693
+ # LCEL chain
694
+ chain = prompt | llm | parser
695
+
696
+ # Invoke (trả kết quả 1 lần)
697
+ result = chain.invoke({"topic": "Diffusion Models"})
698
+ print(result)
699
+
700
+ # Stream (token-by-token) — dùng .stream() thay vì .invoke()
701
+ for chunk in chain.stream({"topic": "Diffusion Models"}):
702
+ print(chunk, end="", flush=True)
703
+
704
+ # Giải thích:
705
+ # - .invoke() gọi chain và đợi toàn bộ output
706
+ # - .stream() trả về iterator, mỗi chunk là 1 phần output
707
+ # - StrOutputParser cho phép stream vì nó pass-through string chunks
708
+ # - Nếu dùng JsonOutputParser, stream sẽ trả partial JSON
709
+ </code></pre>
710
+ </details>
711
+
712
+ <p><strong>Q2: Configure NIM &amp; So sánh Temperature</strong></p>
713
+ <p>Gọi NIM endpoint dùng OpenAI client. Cùng 1 prompt, so sánh output khi <code>temperature=0.0</code> vs <code>temperature=1.0</code>. Chạy mỗi cấu hình 3 lần và quan sát sự khác biệt.</p>
714
+
715
+ <details>
716
+ <summary>Xem đáp án Q2</summary>
717
+
718
+ <pre><code class="language-python">
719
+ from openai import OpenAI
720
+
721
+ # Kết nối NIM endpoint
722
+ client = OpenAI(
723
+ base_url="http://localhost:8000/v1",
724
+ api_key="not-used"
725
+ )
726
+
727
+ prompt_msg = [
728
+ {"role": "system", "content": "Trả lời ngắn gọn trong 1-2 câu."},
729
+ {"role": "user", "content": "Tại sao bầu trời có màu xanh?"}
730
+ ]
731
+
732
+ print("=== Temperature = 0.0 (Deterministic) ===")
733
+ for i in range(3):
734
+ resp = client.chat.completions.create(
735
+ model="meta/llama-3.1-8b-instruct",
736
+ messages=prompt_msg,
737
+ temperature=0.0, # Luôn chọn token có xác suất cao nhất
738
+ max_tokens=100
739
+ )
740
+ print(f"Run {i+1}: {resp.choices[0].message.content}")
741
+ # → 3 lần cho output GIỐNG NHAU
742
+
743
+ print("\n=== Temperature = 1.0 (Creative) ===")
744
+ for i in range(3):
745
+ resp = client.chat.completions.create(
746
+ model="meta/llama-3.1-8b-instruct",
747
+ messages=prompt_msg,
748
+ temperature=1.0, # Phân phối rộng, random hơn
749
+ max_tokens=100
750
+ )
751
+ print(f"Run {i+1}: {resp.choices[0].message.content}")
752
+ # → 3 lần cho output KHÁC NHAU
753
+
754
+ # Key insight:
755
+ # - temp=0.0: greedy decoding, reproducible, dùng cho factual tasks
756
+ # - temp=1.0: sampling rộng hơn, creative, dùng cho brainstorming
757
+ # - NIM dùng OpenAI-compatible API nên client code giống hệt
758
+ </code></pre>
759
+ </details>
760
+
761
+ <p><strong>Q3: Multi-turn Chatbot với Memory</strong></p>
762
+ <p>Tạo chatbot sử dụng <code>ConversationBufferMemory</code> tích hợp vào LCEL chain thông qua <code>RunnableWithMessageHistory</code>. Bot phải nhớ tên user từ lượt trước.</p>
763
+
764
+ <details>
765
+ <summary>Xem đáp án Q3</summary>
766
+
767
+ <pre><code class="language-python">
768
+ from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
769
+ from langchain_core.output_parsers import StrOutputParser
770
+ from langchain_core.chat_history import InMemoryChatMessageHistory
771
+ from langchain_core.runnables.history import RunnableWithMessageHistory
772
+ from langchain_nvidia_ai_endpoints import ChatNVIDIA
773
+
774
+ # 1. Prompt với history placeholder
775
+ prompt = ChatPromptTemplate.from_messages([
776
+ ("system", "Bạn là trợ lý thân thiện. Nhớ thông tin user đã chia sẻ."),
777
+ MessagesPlaceholder(variable_name="history"),
778
+ ("human", "{input}")
779
+ ])
780
+
781
+ # 2. Chain
782
+ llm = ChatNVIDIA(model="meta/llama-3.1-8b-instruct", temperature=0.3)
783
+ chain = prompt | llm | StrOutputParser()
784
+
785
+ # 3. Session store
786
+ store = {}
787
+ def get_history(session_id: str):
788
+ if session_id not in store:
789
+ store[session_id] = InMemoryChatMessageHistory()
790
+ return store[session_id]
791
+
792
+ # 4. Wrap với message history
793
+ chatbot = RunnableWithMessageHistory(
794
+ chain,
795
+ get_history,
796
+ input_messages_key="input",
797
+ history_messages_key="history"
798
+ )
799
+
800
+ # 5. Test multi-turn
801
+ cfg = {"configurable": {"session_id": "demo-001"}}
802
+
803
+ print(chatbot.invoke({"input": "Tên tôi là Lan"}, config=cfg))
804
+ # → "Xin chào Lan! Rất vui được gặp bạn..."
805
+
806
+ print(chatbot.invoke({"input": "Tên tôi là gì?"}, config=cfg))
807
+ # → "Tên bạn là Lan." ← Bot nhớ context!
808
+
809
+ print(chatbot.invoke({"input": "Tôi thích machine learning"}, config=cfg))
810
+ # → "Tuyệt vời Lan! Machine learning là..."
811
+
812
+ # Kiểm tra history đã lưu
813
+ history = store["demo-001"]
814
+ for msg in history.messages:
815
+ print(f"{msg.type}: {msg.content[:50]}...")
816
+ </code></pre>
817
+ </details>
818
+
819
+ <p><strong>Q4: Gradio ChatInterface + LangChain</strong></p>
820
+ <p>Tạo Gradio UI chatbot sử dụng <code>gr.ChatInterface</code>, backend là LCEL chain gọi NIM. Hỗ trợ streaming response.</p>
821
+
822
+ <details>
823
+ <summary>Xem đáp án Q4</summary>
824
+
825
+ <pre><code class="language-python">
826
+ import gradio as gr
827
+ from langchain_core.prompts import ChatPromptTemplate
828
+ from langchain_core.output_parsers import StrOutputParser
829
+ from langchain_nvidia_ai_endpoints import ChatNVIDIA
830
+
831
+ # 1. Setup LCEL chain
832
+ prompt = ChatPromptTemplate.from_messages([
833
+ ("system", "Bạn là trợ lý AI chuyên về deep learning."),
834
+ ("human", "{message}")
835
+ ])
836
+ llm = ChatNVIDIA(
837
+ model="meta/llama-3.1-8b-instruct",
838
+ temperature=0.7
839
+ )
840
+ chain = prompt | llm | StrOutputParser()
841
+
842
+ # 2. Streaming handler cho Gradio
843
+ def respond_stream(message, history):
844
+ """
845
+ Gradio ChatInterface gọi function này.
846
+ - message: tin nhắn mới của user
847
+ - history: list of [user_msg, bot_msg] pairs
848
+ Yield từng chunk để Gradio hiển thị streaming.
849
+ """
850
+ partial = ""
851
+ for chunk in chain.stream({"message": message}):
852
+ partial += chunk
853
+ yield partial # Gradio cập nhật UI mỗi lần yield
854
+
855
+ # 3. Launch Gradio app
856
+ demo = gr.ChatInterface(
857
+ fn=respond_stream,
858
+ title="🤖 DL Assistant (NIM-powered)",
859
+ description="Hỏi bất kỳ câu nào về Deep Learning",
860
+ examples=[
861
+ "Transformer hoạt động thế nào?",
862
+ "So sánh CNN và ViT",
863
+ "Batch Normalization dùng để làm gì?"
864
+ ],
865
+ theme="soft"
866
+ )
867
+
868
+ demo.launch(server_port=7860, share=False)
869
+
870
+ # Truy cập: http://localhost:7860
871
+ # Gradio sẽ hiển thị streaming response real-time
872
+ </code></pre>
873
+ </details>
874
+
875
+ <p><strong>Q5: Debug — Chain trả về output rỗng</strong></p>
876
+ <p>Code bên dưới chạy nhưng output luôn rỗng hoặc là object không mong muốn. Tìm và sửa lỗi.</p>
877
+
878
+ <pre><code class="language-python">
879
+ # BUG: chain trả về AIMessage object thay vì string
880
+ from langchain_core.prompts import ChatPromptTemplate
881
+ from langchain_core.output_parsers import JsonOutputParser # ← Hmm...
882
+ from langchain_nvidia_ai_endpoints import ChatNVIDIA
883
+
884
+ prompt = ChatPromptTemplate.from_messages([
885
+ ("system", "Trả lời ngắn gọn bằng tiếng Việt."),
886
+ ("human", "{question}")
887
+ ])
888
+
889
+ llm = ChatNVIDIA(model="meta/llama-3.1-8b-instruct")
890
+ chain = prompt | llm | JsonOutputParser() # ← Lỗi ở đây
891
+
892
+ result = chain.invoke({"question": "AI là gì?"})
893
+ print(result) # → Error hoặc output rỗng/lạ
894
+ </code></pre>
895
+
896
+ <details>
897
+ <summary>Xem đáp án Q5</summary>
898
+
899
+ <pre><code class="language-python">
900
+ # PHÂN TÍCH LỖI:
901
+ # - Prompt yêu cầu LLM trả lời bằng text thuần (tiếng Việt)
902
+ # - Nhưng parser là JsonOutputParser → expect JSON format
903
+ # - LLM trả về "AI là trí tuệ nhân tạo..." (not JSON)
904
+ # - JsonOutputParser cố parse → fail hoặc trả output rỗng
905
+
906
+ # SỬA: Thay JsonOutputParser bằng StrOutputParser
907
+
908
+ from langchain_core.prompts import ChatPromptTemplate
909
+ from langchain_core.output_parsers import StrOutputParser # ← FIX!
910
+ from langchain_nvidia_ai_endpoints import ChatNVIDIA
911
+
912
+ prompt = ChatPromptTemplate.from_messages([
913
+ ("system", "Trả lời ngắn gọn bằng tiếng Việt."),
914
+ ("human", "{question}")
915
+ ])
916
+
917
+ llm = ChatNVIDIA(model="meta/llama-3.1-8b-instruct")
918
+ chain = prompt | llm | StrOutputParser() # ← StrOutputParser
919
+
920
+ result = chain.invoke({"question": "AI là gì?"})
921
+ print(result) # → "AI (Artificial Intelligence) là trí tuệ nhân tạo..."
922
+
923
+ # RULE: OutputParser type PHẢI match output format:
924
+ # - Text thuần → StrOutputParser
925
+ # - JSON output (prompt phải yêu cầu JSON) → JsonOutputParser
926
+ # - Structured output → PydanticOutputParser
927
+ # Nếu mismatch → chain fail silently or raise error
928
+ </code></pre>
929
+ </details>