第 11 期 | RAG 架构实战:检索增强生成的完美闭环 (EN)
🎯 Learning Objectives for This Session
Buckle up, future AI masters! In this first stop of the "Full-Stack LangChain Masterclass," we are diving straight into the core of LangChain: Large Language Models (LLMs) and PromptTemplates. This isn't just a theoretical lesson; it's the first step in laying the foundation for our "Intelligent Support Copilot" project. By the end of this session, you will:
- Thoroughly understand the core role of LLMs in AI applications: Gain insight into how the "brain" of our intelligent support assistant works, and understand how it "thinks" and "communicates."
- Master the loading and invocation of LLMs and ChatModels in LangChain: Learn how to make your code "talk" to top-tier AI models and inject intelligence into our support project.
- Master the construction and advanced techniques of PromptTemplates: Deepen your understanding of the art of prompt engineering, and learn to write clear, efficient instructions that give the LLM a specific "persona" for customer support.
- Combine LLMs with PromptTemplates: Build an initial "intelligent brain" for your support assistant that can understand user intent and provide basic responses.
📖 Core Concepts Explained
Alright everyone, allow this veteran to give you a reality check followed by some encouragement. The AI field is evolving rapidly, but the fundamental principles remain the same. Today, the LLMs and PromptTemplates we are discussing are the "core drivers" of these principles. Imagine your intelligent support assistant not as a cold machine, but as a "digital lifeform" with thoughts, logic, and communication skills. In this session, we are going to equip it with a "brain" and a "voice."
1. LLM: The "Super Brain" of Intelligent Support
What is an LLM? LLM stands for Large Language Model. Simply put, it is a massive neural network trained on vast amounts of text data, capable of understanding and generating human language. You can think of it as a super-scholar who has read all known human books, web pages, and conversations. Not only has it memorized a massive amount of knowledge, but more importantly, it has learned the "patterns" and "logic" of language.
What is the role of an LLM in intelligent support? It acts as the "brain" of our support assistant. When a user asks a question, the LLM is responsible for:
- Understanding user intent: No matter how complex the language a user uses to describe their problem, the LLM attempts to grasp their core request.
- Generating natural language responses: Based on the understood intent and provided knowledge, it generates fluent, accurate responses that align with human communication habits.
- Knowledge reasoning: To a certain extent, the LLM can also reason based on its general knowledge, and even make reasonable inferences when there is no direct matching answer.
How does LangChain encapsulate LLMs? One of LangChain's design philosophies is "abstraction." It encapsulates various LLMs (whether it's OpenAI's GPT series, Google's Gemini, Anthropic's Claude, or open-source models) into a unified interface. This means you only need to change a few lines of code to easily switch between different LLMs without rewriting your entire application logic. This is an absolute godsend for building a flexible and scalable intelligent support system!
- LLMs: Traditional text completion models. They take text as input and output text.
- ChatModels: More focused on conversational scenarios. They take a sequence of messages (System, Human, AI) as input and output an AI message. This is exactly the core component we need for our support project! It better understands conversational context and role-playing.
2. PromptTemplate: The "Magic Spell" for Communicating with AI
If the LLM is the brain, then the PromptTemplate is the "language" and "instructions" we use to communicate with this brain. You can't expect the LLM to magically know what you want it to do out of thin air. It needs clear, explicit, and structured guidance.
Why are PromptTemplates so important?
- Setting roles and behaviors: Do you want your support assistant to be friendly? Professional? Or humorous? A PromptTemplate can establish a "persona" for it.
- Providing context: User questions often require specific background information to yield accurate answers. For example, if a user asks, "When will my order ship?", the assistant needs to know, "Which order number?" PromptTemplates allow us to dynamically inject this context.
- Guiding output formats: Do you want the assistant to reply with a summary? A list of steps? Or a JSON object? A PromptTemplate can explicitly guide the LLM's output format.
- Reusability and standardization: Imagine having to manually concatenate long instructions every time—it's both error-prone and inefficient. PromptTemplates allow us to define reusable templates where we only need to fill in the variables.
The core philosophy of Prompt Engineering: This is not just about writing a few words; it is both an art and a science. Its core lies in: How to use the most concise, clear, and effective way to guide the LLM to produce the high-quality output we expect. In an intelligent support scenario, this means we need to carefully design prompts so the assistant can:
- Accurately understand user questions.
- Provide correct answers by integrating with a knowledge base (which we will cover later).
- Reply in a friendly, professional customer support tone.
- Avoid "hallucinations" and irresponsible answers.
PromptTemplates in LangChain: LangChain provides two core components: PromptTemplate and ChatPromptTemplate.
PromptTemplate: Suitable for simple text-to-text generation.ChatPromptTemplate: Better suited for multi-turn conversations and role-playing. It allows you to define messages for different roles (System, Human, AI), which is crucial for simulating support conversations. We can useSystemMessagePromptTemplateto set the "code of conduct" and "professionalism" of our support assistant.
Mermaid Diagram: Workflow of the Intelligent Support Assistant (V1)
Let's use a diagram to visually see how LLMs and PromptTemplates work together in our intelligent support project. Think of this as the "information bridge" between your users and the support assistant.
graph TD
A[User asks a question] --> B{PromptTemplate}
B -- Inject dynamic data
(User query, Persona) --> C[Construct complete Prompt]
C --> D[LangChain ChatModel / LLM]
D -- Understand & generate response --> E[Support Assistant replies]
E --> F[Present response to user]
subgraph LangChain Core
B
D
end
style A fill:#f9f,stroke:#333,stroke-width:2px
style F fill:#f9f,stroke:#333,stroke-width:2px
style B fill:#bbf,stroke:#333,stroke-width:2px
style C fill:#ccf,stroke:#333,stroke-width:2px
style D fill:#ddf,stroke:#333,stroke-width:2px
style E fill:#eef,stroke:#333,stroke-width:2pxAs you can see from the diagram, the user's question isn't just thrown directly at the LLM. It first goes through the "packaging" and "guidance" of a PromptTemplate, transforming it into instructions that the LLM can understand and process efficiently. This "packaging" process is the crucial step where we endow our intelligent support assistant with "wisdom" and a "soul."
💻 Hands-on Code Practice
No matter how wonderful the theory sounds, nothing beats writing a few lines of code yourself! Now, let's get our hands dirty and lay the first foundation for our "Intelligent Support Copilot" project. We will start with the most basic LLM invocation, gradually introduce PromptTemplates, and ultimately build a support assistant with preliminary "intelligence."
1. Environment Setup
First, ensure you have installed the langchain and openai libraries. If you haven't, please run:
pip install langchain langchain-openai
Or for a TypeScript project:
npm install langchain @langchain/openai
Additionally, you need to set your OpenAI API Key. It is strongly recommended to use environment variables to manage your keys rather than hardcoding them in your code.
Setting environment variables in Python:
export OPENAI_API_KEY="your_openai_api_key_here"
Setting environment variables in TypeScript:
Create a .env file in your project's root directory:
OPENAI_API_KEY="your_openai_api_key_here"
And load it in your code: import 'dotenv/config';
2. Loading and Interacting with ChatModels (Python & TypeScript)
First, let's give our support assistant the ability to "speak." Here, we use ChatOpenAI, which is LangChain's wrapper for OpenAI's chat models, making it a perfect fit for our customer support scenario.
Python Code Example
import os
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage
# Ensure your OPENAI_API_KEY environment variable is set
# os.environ["OPENAI_API_KEY"] = "sk-..." # Not recommended to hardcode here, please use environment variables
print("--- Python: Basic ChatModel Interaction ---")
# Instantiate the ChatModel
# The temperature parameter controls the randomness of the model's output. 0 means more deterministic, 1 means more creative.
# For support scenarios, we usually want accurate and stable replies, so we set a lower temperature.
chat_model = ChatOpenAI(temperature=0.7, model_name="gpt-3.5-turbo")
# Simulate a user query
user_query = "What is your company's return policy?"
print(f"\nUser Query: {user_query}")
# Call the ChatModel, passing in a list of messages
# SystemMessage is used to set the model's behavior or role; HumanMessage is the user's input.
messages = [
SystemMessage(content="You are a friendly and professional intelligent support assistant."),
HumanMessage(content=user_query),
]
# The invoke method is used to synchronously call the model and get the response
response = chat_model.invoke(messages)
print(f"Support Assistant Reply: {response.content}")
# Demonstrate another question to see the model's coherence (although there is no historical memory here, the SystemMessage persists)
user_query_2 = "What if the product has quality issues?"
print(f"\nUser Query: {user_query_2}")
messages_2 = [
SystemMessage(content="You are a friendly and professional intelligent support assistant."),
HumanMessage(content=user_query_2),
]
response_2 = chat_model.invoke(messages_2)
print(f"Support Assistant Reply: {response_2.content}")
TypeScript Code Example
import 'dotenv/config'; // Load environment variables from the .env file
import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage, SystemMessage } from '@langchain/core/messages';
console.log("--- TypeScript: Basic ChatModel Interaction ---");
// Instantiate the ChatModel
// The temperature parameter controls the randomness of the model's output. 0 means more deterministic, 1 means more creative.
// For support scenarios, we usually want accurate and stable replies, so we set a lower temperature.
const chatModel = new ChatOpenAI({
temperature: 0.7,
modelName: "gpt-3.5-turbo",
});
// Simulate a user query
const userQuery = "What is your company's return policy?";
console.log(`\nUser Query: ${userQuery}`);
// Call the ChatModel, passing in a list of messages
// SystemMessage is used to set the model's behavior or role; HumanMessage is the user's input.
const messages = [
new SystemMessage("You are a friendly and professional intelligent support assistant."),
new HumanMessage(userQuery),
];
// The invoke method is used to synchronously call the model and get the response
chatModel.invoke(messages).then((response) => {
console.log(`Support Assistant Reply: ${response.content}`);
});
// Demonstrate another question
const userQuery2 = "What if the product has quality issues?";
console.log(`\nUser Query: ${userQuery2}`);
const messages2 = [
new SystemMessage("You are a friendly and professional intelligent support assistant."),
new HumanMessage(userQuery2),
];
chatModel.invoke(messages2).then((response) => {
console.log(`Support Assistant Reply: ${response.content}`);
});
Execution Result: You will see that the intelligent support assistant provides a preliminary, general answer to your question. At this point, it doesn't have our specific knowledge base yet, so the answer might be quite generic. However, it already possesses the ability to "communicate"!
3. Introducing and Optimizing PromptTemplates (Python & TypeScript)
Now, let's make our support assistant "smarter" and more "professional." We will use ChatPromptTemplate to define a structured prompt, ensuring the assistant adopts a specific "persona" and follows "instructions" when answering questions.
Python Code Example
import os
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate
print("\n--- Python: Introducing and Optimizing ChatPromptTemplate ---")
chat_model = ChatOpenAI(temperature=0.7, model_name="gpt-3.5-turbo")
# Define the System Prompt for the support assistant, setting its role and code of conduct
# This is a crucial step as it defines the assistant's "personality"
system_template = (
"You are a professional intelligent support assistant responsible for answering customer inquiries about the company's products and services. "
"Your answers must be accurate, concise, friendly, and as helpful as possible in solving the customer's problem. "
"If you encounter a question you cannot answer, politely inform the customer that you will transfer them to a human agent. "
"Your answers should be in English."
)
system_message_prompt = SystemMessagePromptTemplate.from_template(system_template)
# Define the user query Prompt, where {user_question} is a variable used to dynamically inject user input
human_message_prompt = HumanMessagePromptTemplate.from_template("{user_question}")
# Combine into a ChatPromptTemplate
# The from_messages method receives a list of message templates
chat_prompt = ChatPromptTemplate.from_messages([
system_message_prompt,
human_message_prompt,
])
# Simulate a user query
user_query = "What payment methods do you support?"
print(f"\nUser Query: {user_query}")
# Fill the user question into the PromptTemplate to generate the final Prompt
# The format_messages method returns a list of messages that can be passed directly to the ChatModel
formatted_prompt_messages = chat_prompt.format_messages(user_question=user_query)
# Call the ChatModel
response = chat_model.invoke(formatted_prompt_messages)
print(f"Support Assistant Reply: {response.content}")
# Demonstrate another question using the same PromptTemplate structure
user_query_2 = "My order number is 12345. Please help me check the shipping status."
print(f"\nUser Query: {user_query_2}")
formatted_prompt_messages_2 = chat_prompt.format_messages(user_question=user_query_2)
response_2 = chat_model.invoke(formatted_prompt_messages_2)
print(f"Support Assistant Reply: {response_2.content}")
# Note: The assistant might not be able to actually check the shipping status here because we haven't integrated external tools or a knowledge base yet.
# However, its answer will follow the "transfer to human agent when unable to answer" rule we set, demonstrating the power of Prompt Engineering.
TypeScript Code Example
import 'dotenv/config';
import { ChatOpenAI } from '@langchain/openai';
import { ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate } from '@langchain/core/prompts';
console.log("\n--- TypeScript: Introducing and Optimizing ChatPromptTemplate ---");
const chatModel = new ChatOpenAI({
temperature: 0.7,
modelName: "gpt-3.5-turbo",
});
// Define the System Prompt for the support assistant, setting its role and code of conduct
// This is a crucial step as it defines the assistant's "personality"
const systemTemplate = (
"You are a professional intelligent support assistant responsible for answering customer inquiries about the company's products and services. "
+ "Your answers must be accurate, concise, friendly, and as helpful as possible in solving the customer's problem. "
+ "If you encounter a question you cannot answer, politely inform the customer that you will transfer them to a human agent. "
+ "Your answers should be in English."
);
const systemMessagePrompt = SystemMessagePromptTemplate.fromTemplate(systemTemplate);
// Define the user query Prompt, where {user_question} is a variable used to dynamically inject user input
const humanMessagePrompt = HumanMessagePromptTemplate.fromTemplate("{user_question}");
// Combine into a ChatPromptTemplate
// The fromMessages method receives a list of message templates
const chatPrompt = ChatPromptTemplate.fromMessages([
systemMessagePrompt,
humanMessagePrompt,
]);
// Simulate a user query
const userQuery = "What payment methods do you support?";
console.log(`\nUser Query: ${userQuery}`);
// Fill the user question into the PromptTemplate to generate the final Prompt
// The formatMessages method returns a list of messages that can be passed directly to the ChatModel
chatPrompt.formatMessages({ user_question: userQuery }).then((formattedPromptMessages) => {
// Call the ChatModel
chatModel.invoke(formattedPromptMessages).then((response) => {
console.log(`Support Assistant Reply: ${response.content}`);
});
});
// Demonstrate another question using the same PromptTemplate structure
const userQuery2 = "My order number is 12345. Please help me check the shipping status.";
console.log(`\nUser Query: ${userQuery2}`);
chatPrompt.formatMessages({ user_question: userQuery2 }).then((formattedPromptMessages2) => {
chatModel.invoke(formattedPromptMessages2).then((response) => {
console.log(`Support Assistant Reply: ${response.content}`);
});
});
Execution Result: You will notice that the support assistant's replies are now more "human-like" and "professional." Even if it doesn't know the specific shipping information, it will politely inform the customer that it will transfer them to a human agent, following the rules we set in the system_template. This is the magic of PromptTemplates! It transforms your AI from a cold machine into an intelligent agent with a "personality" and a "code of conduct."
4. Initial Experience with Chains (Python & TypeScript)
One of the core concepts of LangChain is the "Chain." We can connect different components (like PromptTemplates and LLMs) together like Lego bricks to form a processing pipeline.
Python Code Example
import os
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate
from langchain.chains import LLMChain # Although LLMChain is a legacy class, it's still useful for demonstrating the concept
print("\n--- Python: Initial Experience with Chains ---")
chat_model = ChatOpenAI(temperature=0.7, model_name="gpt-3.5-turbo")
# Still using the previously defined PromptTemplate
system_template = (
"You are a professional intelligent support assistant responsible for answering customer inquiries about the company's products and services. "
"Your answers must be accurate, concise, friendly, and as helpful as possible in solving the customer's problem. "
"If you encounter a question you cannot answer, politely inform the customer that you will transfer them to a human agent. "
"Your answers should be in English."
)
system_message_prompt = SystemMessagePromptTemplate.from_template(system_template)
human_message_prompt = HumanMessagePromptTemplate.from_template("{user_question}")
chat_prompt = ChatPromptTemplate.from_messages([
system_message_prompt,
human_message_prompt,
])
# Create a simple Chain: connect chat_prompt and chat_model
# Modern LangChain recommends using the pipe operator | from LCEL (LangChain Expression Language)
# But here, to demonstrate the legacy Chain concept, we'll mention LLMChain
# In fact, chat_prompt | chat_model achieves a similar effect
chain = chat_prompt | chat_model
# Simulate a user query
user_query = "What is your latest product release plan?"
print(f"\nUser Query: {user_query}")
# Call the Chain, passing in variables
# The invoke method automatically fills user_question into the prompt, then passes the result to the LLM
response = chain.invoke({"user_question": user_query})
print(f"Support Assistant Reply: {response.content}")
user_query_2 = "I need to process a refund, what is the procedure?"
print(f"\nUser Query: {user_query_2}")
response_2 = chain.invoke({"user_question": user_query_2})
print(f"Support Assistant Reply: {response_2.content}")
TypeScript Code Example
import 'dotenv/config';
import { ChatOpenAI } from '@langchain/openai';
import { ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate } from '@langchain/core/prompts';
console.log("\n--- TypeScript: Initial Experience with Chains ---");
const chatModel = new ChatOpenAI({
temperature: 0.7,
modelName: "gpt-3.5-turbo",
});
// Still using the previously defined PromptTemplate
const systemTemplate = (
"You are a professional intelligent support assistant responsible for answering customer inquiries about the company's products and services. "
+ "Your answers must be accurate, concise, friendly, and as helpful as possible in solving the customer's problem. "
+ "If you encounter a question you cannot answer, politely inform the customer that you will transfer them to a human agent. "
+ "Your answers should be in English."
);
const systemMessagePrompt = SystemMessagePromptTemplate.fromTemplate(systemTemplate);
const humanMessagePrompt = HumanMessagePromptTemplate.fromTemplate("{user_question}");
const chatPrompt = ChatPromptTemplate.fromMessages([
systemMessagePrompt,
humanMessagePrompt,
]);
// Create a simple Chain: connect chatPrompt and chatModel
// In LangChain JS, you can directly use the .pipe() method or the LCEL pipe operator |
const chain = chatPrompt.pipe(chatModel);
// Simulate a user query
const userQuery = "What is your latest product release plan?";
console.log(`\nUser Query: ${userQuery}`);
// Call the Chain, passing in variables
// The invoke method automatically fills user_question into the prompt, then passes the result to the LLM
chain.invoke({ user_question: userQuery }).then((response) => {
console.log(`Support Assistant Reply: ${response.content}`);
});
const userQuery2 = "I need to process a refund, what is the procedure?";
console.log(`\nUser Query: ${userQuery2}`);
chain.invoke({ user_question: userQuery2 }).then((response) => {
console.log(`Support Assistant Reply: ${response.content}`);
});
Execution Result: You will find that by using a Chain, the code becomes more concise and the flow clearer. We only need to define the input variables, and the Chain automatically handles formatting the Prompt and calling the LLM. This perfectly embodies LangChain's core mission of simplifying the development of complex AI applications.
坑与避坑指南
Alright, the code is running. Do you feel one step closer to becoming an AI master? Don't celebrate just yet. As an experienced AI architect, it's my duty to remind you that there are many pitfalls on this journey. But knowing them in advance will save you a lot of trouble!
1. The Pitfall of Prompt Engineering: Don't Treat AI as a "Mind Reader"
- Pitfall: Vague instructions. Do you think the LLM can read between the lines? Think again! Broad instructions like "Help me solve a problem" will only result in the LLM giving generic, useless fluff.
- How to Avoid: Be specific, clear, and measurable! Tell the LLM your goal, the role it's playing, the key points to focus on, and even the expected output format. For example, instead of "Help me write an email," use "Please act as a company customer support representative and reply to a customer regarding a delayed order. Keep the tone sincere, and provide a solution along with contact information."
- Pitfall: Insufficient context leading to "confident nonsense." LLMs lack human common sense and real-time information. If the Prompt doesn't provide enough background, it is highly likely to "hallucinate"