第 19 期 | 结构化数据输出:用 Pydantic 约束 LLM (EN)
🎯 Learning Objectives
Hey there, future AI masters! Welcome back to our LangChain Full-Stack Masterclass. In the last session, we discussed basic LLM calls and the art of Prompt Engineering. You probably feel pretty comfortable "chatting" with AI now, right? But stopping at single-turn conversations is like having a Swiss Army knife and only using it to peel apples. Our goal is to build an Intelligent Support Copilot. It needs to do much more than just peel apples—it must handle complex, multi-step user requests.
So, in this session, we will dive deep into one of LangChain's core concepts: Chains. This is the key to evolving your AI application from a "single-celled organism" into a "multi-organ system"!
By the end of this session, you will:
- Thoroughly understand the core concept of Chains in LangChain and their irreplaceable role in complex tasks. Say goodbye to single LLM calls and step into multi-step intelligent orchestration.
- Master the use cases and construction methods of different chains (especially
LLMChainandSequentialChain). Know exactly when to use which "chain" to solve specific problems in your support copilot. - Learn to build multi-step processing logic in our Support Copilot, significantly improving its ability to understand and resolve complex issues. For example, how to make it first understand the problem, then look up information, and finally provide a solution.
- Learn how to efficiently connect different LangChain components (LLMs, Prompts, Parsers, etc.) to achieve production-grade complex task automation. Make your support copilot truly come alive!
Ready? Let's enter the world of Chains and unleash the true power of LangChain!
📖 Concept Breakdown
Chains: The "Backbone" of LLM Applications
Imagine you are building an intelligent customer support bot. A user asks: "My order number is #XYZ123, why hasn't it shipped yet?"
A simple LLM call might try to answer directly, but it doesn't know what "order #XYZ123" is, nor does it have the permissions to look it up. It might just say: "I cannot check specific order information, please contact human support." This is obviously not the "intelligent" support we want.
To solve this problem, we need to:
- Extract Intent and Key Information: The user wants to check their order status, and the order ID is #XYZ123.
- Execute External Actions: Call our internal order query API, passing in the order ID.
- Process API Results: Parse the shipping status, estimated time of arrival (ETA), etc., returned by the API.
- Generate a Friendly Response: Format the queried information into language the user can easily understand.
This series of steps requires an organized workflow to coordinate. In LangChain, this "organized workflow" is a Chain.
The core idea of a Chain is to combine multiple LLM calls or other tools (like data queries, API calls, code execution) into an ordered, multi-step logical flow to complete more complex tasks. It's like an assembly line: each station is responsible for specific processing, and the output of the previous station becomes the input for the next.
Why Do We Need Chains?
- Overcome LLM Limitations: LLMs are inherently stateless; they process one request at a time. Chains give LLM applications "memory" and the ability to perform "multi-step reasoning."
- Task Decomposition and Orchestration: Break down a massive, complex task into a series of smaller tasks, where each small task is handled by a chain or a component within a chain.
- Integrate External Capabilities: Make the LLM more than just a "chatbot." Allow it to seamlessly integrate with your business systems, databases, and APIs to fetch real-time data and execute actual operations.
- Improve Maintainability and Scalability: Modular design makes it easy to modify, debug, and expand functionalities.
Types and Structures of Chains
LangChain provides a variety of built-in chains, but the two most fundamental and commonly used are:
LLMChain: This is the cornerstone of all chains. It combines aPromptTemplateand aBaseLLM(orBaseChatModel). Its job is simply to take the template and inputs, send a request to the LLM, and get the output. You can think of it as "a templated LLM call."- Input: Data required for the template variables.
- Output: Text generated by the LLM.
SequentialChain(and its simplified versionSimpleSequentialChain): This is the key to linking multiple chains together.SimpleSequentialChain: The simplest way to link chains. The complete output of the previous chain serves as the complete input for the next chain. Suitable for linear, branchless, simple workflows.SequentialChain: More powerful and flexible. It allows you to define the input and output variables for each chain, supports multiple inputs and outputs, and can pass intermediate results to specific subsequent chains. This is crucial for complex flow control.
Chain Workflows in the Intelligent Support Copilot
Imagine our Intelligent Support Copilot. When a user asks a complex question, it might go through the following chained processing:
graph TD
A[User Query: "Why hasn't my order #12345 shipped yet?"] --> B(Intent Recognition & Info Extraction)
B --> C{LLMChain: Extract Order ID & Intent}
C -- Extracted: ID="12345", Intent="Check Shipping" --> D(Business Data Query)
D --> E{Tool/Function: Call Order API}
E -- Returned: Status="Shipped", Courier="SF Express", ETA="Tomorrow" --> F(Result Analysis & Response Generation)
F --> G{LLMChain: Summarize & Generate Friendly Response}
G -- Response: "Your order #12345 shipped today via SF Express and is expected to arrive tomorrow." --> H[Reply to User]
subgraph "LangChain Chain Processing"
C
E
G
endDiagram: Chain Processing Workflow of the Intelligent Support Copilot
In this diagram, we see how a complex request is broken down into multiple steps and collaboratively completed by different components (including LLMChain and Tool/Function, which we will learn about later). In this session, we will focus primarily on using LLMChain and SequentialChain to orchestrate pure text processing logic.
For example, a basic text processing chain could be:
LLMChain(Issue Classifier): Inputs the user query, outputs the issue category (e.g., "Returns", "Tech Support", "Order Inquiry").LLMChain(Information Extractor): Inputs the user query and issue category, outputs key entities (e.g., order ID, product name, fault description).LLMChain(Draft Response Generator): Inputs the issue category and extracted info, generates a preliminary response draft.
These LLMChains can be linked together using a SequentialChain to achieve a much smarter customer service interaction.
💻 Practical Code Drill
Alright, enough theory, let's roll up our sleeves! Now, let's build an actual chained processing logic for our "Intelligent Support Copilot."
Scenario Setup: Our copilot needs to handle user inquiries regarding product faults. It must not only identify the product and the type of fault but also provide preliminary, personalized troubleshooting suggestions or guidance based on that information.
Core Challenge: This requires two or more steps of LLM reasoning:
- Understand user intent and extract key info: What product did the user say is broken? What kind of fault is it?
- Generate suggestions based on the info: Provide appropriate next steps tailored to the specific product and fault.
We will use SequentialChain to elegantly solve this problem.
Preparation (Python)
First, ensure you have installed the necessary libraries:
pip install langchain openai python-dotenv
Then, set up your OpenAI API Key. We usually manage sensitive information via environment variables:
# Example .env file content
# OPENAI_API_KEY="sk-your_openai_api_key_here"
import os
from dotenv import load_dotenv
load_dotenv() # Load environment variables from the .env file
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain, SequentialChain
# Initialize a chat model instance
# temperature controls model creativity, 0 means more deterministic
llm = ChatOpenAI(temperature=0.7, model="gpt-4o") # Recommended to use powerful models like gpt-4o or gpt-3.5-turbo
Step 1: Build the First Chain - Issue Classification and Info Extraction
This chain is responsible for identifying the product and fault type from the user's input.
# 1. Define the first Prompt Template: Issue Classification and Information Extraction
# input_variables: Original user query (customer_query)
# output_variables: Extracted product name (product_name) and issue type (issue_type)
prompt_template_1 = PromptTemplate(
input_variables=["customer_query"],
template="""You are a professional customer support assistant. Please carefully read the following user query and try to extract the product name and issue type mentioned by the user. If uncertain, indicate 'Unknown'.
Please return the result in JSON format, for example: {{"product": "Product Name", "issue_type": "Issue Type"}}
User Query: {customer_query}
"""
)
# 2. Create the first LLMChain instance
# output_key: The output of this chain will be stored in a variable named "extracted_info"
chain_1 = LLMChain(
llm=llm,
prompt=prompt_template_1,
output_key="extracted_info", # CRITICAL! This output_key will serve as the input for the next chain
verbose=True # Enable verbose to see the intermediate steps of the chain
)
Step 2: Build the Second Chain - Troubleshooting Suggestion Generation
This chain will receive the product and fault information extracted by the previous chain, and then generate specific suggestions.
# 3. Define the second Prompt Template: Troubleshooting Suggestions
# input_variables: Needs to receive the output from the previous chain (product_name, issue_type)
prompt_template_2 = PromptTemplate(
input_variables=["extracted_info"], # Note: Here it receives the content of the output_key from the previous chain
template="""You are an experienced technical support expert. You have received information about a user's product issue:
{extracted_info}
Based on this information, please provide the user with preliminary troubleshooting suggestions or next-step guidance. If the product or issue type is unknown, provide general advice.
Please keep the tone friendly and professional, and provide clear actionable steps.
"""
)
# 4. Create the second LLMChain instance
# output_key: The output of this chain will be stored in a variable named "final_suggestion"
chain_2 = LLMChain(
llm=llm,
prompt=prompt_template_2,
output_key="final_suggestion",
verbose=True
)
Step 3: Link All Chains - Using SequentialChain
Now, we will link these two LLMChains together to form a complete processing pipeline.
# 5. Create SequentialChain
# chains: A list of chains arranged in execution order
# input_variables: The initial input for the entire SequentialChain. Here it is the original user query (customer_query)
# output_variables: The final output of the entire SequentialChain. Here we want the final suggestion (final_suggestion)
overall_chain = SequentialChain(
chains=[chain_1, chain_2],
input_variables=["customer_query"],
output_variables=["final_suggestion"],
verbose=True # Enable verbose to see the execution process of the entire SequentialChain
)
# 6. Run our Intelligent Support Copilot!
customer_query_1 = "My Xiaomi robot vacuum won't charge, is the battery broken?"
response_1 = overall_chain.invoke({"customer_query": customer_query_1})
print("\n--- Customer Query 1 ---")
print(f"User: {customer_query_1}")
print(f"Copilot Suggestion:\n{response_1['final_suggestion']}")
print("\n" + "="*50 + "\n")
customer_query_2 = "My Lenovo laptop boots up to a black screen, what should I do?"
response_2 = overall_chain.invoke({"customer_query": customer_query_2})
print("\n--- Customer Query 2 ---")
print(f"User: {customer_query_2}")
print(f"Copilot Suggestion:\n{response_2['final_suggestion']}")
print("\n" + "="*50 + "\n")
customer_query_3 = "My phone screen is shattered, can it be fixed?"
response_3 = overall_chain.invoke({"customer_query": customer_query_3})
print("\n--- Customer Query 3 ---")
print(f"User: {customer_query_3}")
print(f"Copilot Suggestion:\n{response_3['final_suggestion']}")
print("\n" + "="*50 + "\n")
customer_query_4 = "I don't know what's wrong with my device."
response_4 = overall_chain.invoke({"customer_query": customer_query_4})
print("\n--- Customer Query 4 ---")
print(f"User: {customer_query_4}")
print(f"Copilot Suggestion:\n{response_4['final_suggestion']}")
Code Breakdown:
output_keyinLLMChain: This is the key to allowingSequentialChainto pass variables correctly! The output ofchain_1is named"extracted_info", and this exact name must match the variable name expected byinput_variablesinprompt_template_2ofchain_2.input_variablesandoutput_variablesinSequentialChain:input_variablesdefines the initial inputs the entireSequentialChainneeds to receive when it runs.output_variablesdefines which variables the entireSequentialChainwill ultimately return.
verbose=True: This parameter is a lifesaver for debugging chains! It prints out the inputs, outputs, and LLM call processes for each chain, helping you understand exactly how data flows through the pipeline. It is highly recommended to always keep this enabled during development and debugging.
Through this practical exercise, you've seen how to break down a complex customer service inquiry into logically clear steps, and use LangChain's chain mechanism to link them together, granting our Intelligent Support Copilot powerful "multi-step reasoning" capabilities!
坑与避坑指南 (Pitfalls & Best Practices)
While the chain mechanism is powerful, there are some "pitfalls" in real-world applications that we need to be aware of in advance to avoid stepping on landmines. As your senior mentor, I don't want you taking unnecessary detours!
Input/Output Variable Mismatch (The Naming Game)
- The Pitfall: This is the most common error with
SequentialChain! Theoutput_keyof one chain must perfectly match the variable name expected by theinput_variablesin thePromptTemplateof the next chain. Case sensitivity or typos will cause the chain to fail with aKeyError. - Best Practice:
- Standardize Naming: Adopt clear, consistent naming conventions for
output_keyandinput_variablesright from the start. verbose=Trueis your best friend: Always enableverbose=Truewhen debugging. It clearly prints the inputs and outputs of each chain, allowing you to see at a glance if variables are passing correctly.- Test Step-by-Step: If a chain is very long, test each
LLMChainindependently first to ensure they work correctly on their own before combining them.
- Standardize Naming: Adopt clear, consistent naming conventions for
- The Pitfall: This is the most common error with
Over-chaining and Performance Issues (The Chain Reaction Overload)
- The Pitfall: Theoretically, you can link chains infinitely, but every LLM call introduces latency and cost. If a task could be completed with one slightly more complex Prompt but is instead broken down into seven or eight simple
LLMChains, you are wasting resources and degrading the user experience. - Best Practice:
- Appropriate Task Granularity: Balance is key. Break tasks down small enough so the LLM can handle them reliably, but don't over-decompose. If one
LLMChaincan do the job, don't use two. - Parallel Thinking: Some steps don't necessarily require strict sequential order. In the future, we will learn how to execute tasks in parallel.
- Caching Strategies: For repetitive LLM calls, consider using LangChain's caching mechanisms to reduce actual API calls.
- Appropriate Task Granularity: Balance is key. Break tasks down small enough so the LLM can handle them reliably, but don't over-decompose. If one
- The Pitfall: Theoretically, you can link chains infinitely, but every LLM call introduces latency and cost. If a task could be completed with one slightly more complex Prompt but is instead broken down into seven or eight simple
Context Loss and Incomplete Information Transfer (The Amnesia Effect)
- The Pitfall: In a multi-step chain, if an intermediate chain fails to pass crucial information completely to subsequent chains, the LLM might "forget" important context, leading to poor response quality or logical errors. For example, if step one extracts an order ID, but step two's Prompt template doesn't include that order ID, it cannot use it.
- Best Practice:
- Explicit Passing: Ensure every
PromptTemplateincludes all the contextual information it needs. - Careful
output_variablesDesign: In aSequentialChain, carefully defineoutput_variablesto ensure all variables needed by subsequent chains are correctly exported from intermediate chains. - Prompt Engineering: Design Prompts that summarize and retain key information, such as explicitly referencing previously extracted info in the response.
- Explicit Passing: Ensure every
Error Handling and Robustness (The Fragile Chain)
- The Pitfall: If any LLM call within the chain fails (e.g., API rate limits, network errors, LLM returning malformed formats), the execution of the entire chain will halt. Production-grade applications must handle these exceptions gracefully.
- Best Practice:
try-exceptWrappers: When callingchain.invoke(), use Python'stry-exceptblocks to catch potential exceptions.- Prompt Resilience: When designing Prompts, anticipate that the LLM might return unexpected formats. Prompt it to return specific error messages upon failure, or use output parsers to increase robustness.
- Retry Mechanisms: For temporary API errors, consider implementing simple retry logic.
Remember, building AI applications is an iterative process. Experiment boldly, debug carefully, and you will master these advanced techniques!
📝 Session Summary
Congratulations, future AI architects! In this session, we deeply explored the crucial Chains mechanism in LangChain.
We theoretically understood why chains are the "backbone" for building complex LLM applications, and how they grant our Intelligent Support Copilot the ability to perform "multi-step reasoning" and "task orchestration." We focused on the foundational building block, LLMChain, and learned how to use SequentialChain to elegantly link multiple LLMChains together to complete a complex workflow—from user query classification and information extraction to final suggestion generation.
Through practical coding, you built a working prototype of a support copilot capable of handling product troubleshooting. It is no longer a simple Q&A machine, but an intelligent agent capable of multi-step reasoning based on context.
Finally, we previewed the potential "pitfalls" you might encounter during chain-based development and how to cleverly avoid them. These advanced insights will be invaluable assets on your future development journey.
By mastering Chains, you have truly stepped into the core world of LangChain. Next up, we will build upon this foundation and add even more superpowers to our Intelligent Support Copilot, such as how to make it interact with external Tools, fetch real-time data, and even autonomously decide its action paths!
Keep up the great work, and see you in the next session!