10-用LangChain构建你的第一个AI应用

39 阅读8分钟

用LangChain构建你的第一个AI应用

从零开始,使用LangChain框架构建完整的AI应用。

前言

LangChain是目前最流行的LLM应用开发框架,它提供了丰富的组件和抽象,让开发者能够快速构建复杂的AI应用。

今天,我们将通过一个实战项目,学习LangChain的核心概念和最佳实践。


一、LangChain核心概念

框架架构

┌─────────────────────────────────────────────────────────────┐
│                    LangChain 架构                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  应用层                                                     │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐        │
│  │   Chains    │  │   Agents    │  │  Callbacks  │        │
│  │  链式调用    │  │  智能代理    │  │  回调处理   │        │
│  └─────────────┘  └─────────────┘  └─────────────┘        │
│                                                             │
│  核心层                                                     │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐        │
│  │   Prompts   │  │    LLMs     │  │  Memory     │        │
│  │   提示模板   │  │   语言模型   │  │   记忆      │        │
│  └─────────────┘  └─────────────┘  └─────────────┘        │
│                                                             │
│  数据层                                                     │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐        │
│  │  Documents  │  │ Embeddings  │  │VectorStores │        │
│  │   文档处理   │  │   向量化    │  │  向量存储    │        │
│  └─────────────┘  └─────────────┘  └─────────────┘        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

核心组件

组件功能说明
Models模型接口支持OpenAI、HuggingFace等
Prompts提示管理模板化、动态提示
Memory记忆系统对话历史、长期记忆
Indexes数据索引文档处理、向量存储
Chains链式调用组合多个组件
Agents智能代理自主决策、工具调用

二、环境搭建

安装依赖

# 安装LangChain
pip install langchain langchain-openai langchain-community

# 安装向量数据库
pip install chromadb

# 安装文档处理
pip install pypdf unstructured

# 安装其他工具
pip install tiktoken python-dotenv

配置环境

# .env文件
OPENAI_API_KEY=your-api-key

# 加载环境变量
from dotenv import load_dotenv
load_dotenv()

三、基础组件使用

1. LLM调用

from langchain_openai import OpenAI, ChatOpenAI

# 文本补全模型
llm = OpenAI(temperature=0.7)
result = llm.invoke("什么是机器学习?")
print(result)

# 聊天模型(推荐)
chat = ChatOpenAI(model="gpt-4", temperature=0.7)

from langchain.schema import HumanMessage, SystemMessage

messages = [
    SystemMessage(content="你是一个AI科普作者"),
    HumanMessage(content="请用简单的话解释神经网络")
]
response = chat.invoke(messages)
print(response.content)

2. Prompt模板

from langchain.prompts import PromptTemplate, ChatPromptTemplate

# 简单模板
template = PromptTemplate.from_template("请用{style}的风格解释{topic}")
prompt = template.format(style="幽默", topic="量子力学")
print(prompt)

# 聊天模板
chat_template = ChatPromptTemplate.from_messages([
    ("system", "你是一位{role}"),
    ("human", "{question}")
])

messages = chat_template.format_messages(
    role="资深Python工程师",
    question="什么是装饰器?"
)

3. 输出解析

from langchain.output_parsers import StructuredOutputParser, ResponseSchema

# 定义输出结构
response_schemas = [
    ResponseSchema(name="name", description="产品名称"),
    ResponseSchema(name="price", description="产品价格"),
    ResponseSchema(name="features", description="产品特点列表")
]

parser = StructuredOutputParser.from_response_schemas(response_schemas)
format_instructions = parser.get_format_instructions()

prompt = PromptTemplate(
    template="""
    请分析以下产品信息并提取结构化数据。

    {format_instructions}

    产品描述:{product_desc}
    """,
    input_variables=["product_desc"],
    partial_variables={"format_instructions": format_instructions}
)

# 使用
input_prompt = prompt.format(product_desc="iPhone 15 Pro,售价8999元,特点是钛金属边框、A17芯片、钛金属边框")
output = llm.invoke(input_prompt)
parsed = parser.parse(output)
print(parsed)

四、Chain链式调用

LLMChain

from langchain.chains import LLMChain

# 创建链
chain = LLMChain(
    llm=chat,
    prompt=chat_template,
    verbose=True  # 显示执行过程
)

# 执行
result = chain.run(role="AI专家", question="什么是大语言模型?")
print(result)

SequentialChain

from langchain.chains import SequentialChain

# 第一步:生成文章大纲
outline_chain = LLMChain(
    llm=chat,
    prompt=PromptTemplate.from_template("为以下主题生成文章大纲:{topic}"),
    output_key="outline"
)

# 第二步:根据大纲写文章
article_chain = LLMChain(
    llm=chat,
    prompt=PromptTemplate.from_template("根据以下大纲写一篇800字的文章:\n{outline}"),
    output_key="article"
)

# 组合链
overall_chain = SequentialChain(
    chains=[outline_chain, article_chain],
    input_variables=["topic"],
    output_variables=["outline", "article"],
    verbose=True
)

result = overall_chain({"topic": "人工智能的未来"})
print(result["article"])

LCEL (LangChain Expression Language)

from langchain_core.output_parsers import StrOutputParser

# 使用LCEL语法(推荐)
chain = chat_template | chat | StrOutputParser()

result = chain.invoke({
    "role": "AI助手",
    "question": "什么是LangChain?"
})
print(result)

# 添加更多步骤
from langchain_core.runnables import RunnablePassthrough

chain = (
    {"question": RunnablePassthrough()}
    | PromptTemplate.from_template("请详细回答:{question}")
    | chat
    | StrOutputParser()
)

result = chain.invoke("什么是向量数据库?")

五、Memory记忆系统

对话记忆

from langchain.memory import ConversationBufferMemory

# 创建记忆
memory = ConversationBufferMemory(
    memory_key="chat_history",
    return_messages=True
)

# 添加对话
memory.save_context(
    {"input": "你好,我叫张三"},
    {"output": "你好张三,很高兴认识你!"}
)

# 获取记忆
print(memory.load_memory_variables({}))
# {'chat_history': [HumanMessage(content='你好,我叫张三'), AIMessage(content='你好张三,很高兴认识你!')]}

# 在Chain中使用
from langchain.chains import ConversationChain

conversation = ConversationChain(
    llm=chat,
    memory=memory,
    verbose=True
)

conversation.predict(input="你还记得我的名字吗?")
# 输出: 当然记得,你叫张三!

记忆类型对比

# 1. 完整记忆(可能超出上下文长度)
from langchain.memory import ConversationBufferMemory

# 2. 窗口记忆(只保留最近N轮)
from langchain.memory import ConversationBufferWindowMemory
window_memory = ConversationBufferWindowMemory(k=5)  # 保留最近5轮

# 3. 摘要记忆(压缩历史对话)
from langchain.memory import ConversationSummaryMemory
summary_memory = ConversationSummaryMemory(llm=chat)

# 4. 实体记忆(记住实体信息)
from langchain.memory import ConversationEntityMemory
entity_memory = ConversationEntityMemory(llm=chat)

# 5. 向量存储记忆(语义检索历史)
from langchain.memory import VectorStoreRetrieverMemory

六、实战项目:文档问答助手

项目架构

┌─────────────────────────────────────────────────────────────┐
│                    文档问答助手                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 文档加载 ─→ 2. 文档分割 ─→ 3. 向量化 ─→ 4. 向量存储    │
│                                                             │
│  5. 问题输入 ─→ 6. 向量检索 ─→ 7. 上下文构建 ─→ 8. LLM生成 │
│                                                             │
└─────────────────────────────────────────────────────────────┘

完整代码实现

from langchain.document_loaders import PyPDFLoader, TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
import os

class DocumentQA:
    def __init__(self, persist_directory="./chroma_db"):
        """初始化文档问答系统"""
        self.embeddings = OpenAIEmbeddings()
        self.llm = ChatOpenAI(model="gpt-4", temperature=0)
        self.persist_directory = persist_directory
        self.vectorstore = None

    def load_documents(self, file_paths):
        """加载文档"""
        documents = []

        for file_path in file_paths:
            if file_path.endswith('.pdf'):
                loader = PyPDFLoader(file_path)
            else:
                loader = TextLoader(file_path, encoding='utf-8')

            documents.extend(loader.load())

        print(f"已加载 {len(documents)} 个文档")
        return documents

    def split_documents(self, documents, chunk_size=1000, chunk_overlap=200):
        """分割文档"""
        text_splitter = RecursiveCharacterTextSplitter(
            chunk_size=chunk_size,
            chunk_overlap=chunk_overlap,
            separators=["\n\n", "\n", "。", "!", "?", " ", ""]
        )

        chunks = text_splitter.split_documents(documents)
        print(f"已分割为 {len(chunks)} 个文档块")
        return chunks

    def build_vectorstore(self, chunks):
        """构建向量存储"""
        self.vectorstore = Chroma.from_documents(
            documents=chunks,
            embedding=self.embeddings,
            persist_directory=self.persist_directory
        )
        print("向量存储构建完成")

    def load_vectorstore(self):
        """加载已有的向量存储"""
        self.vectorstore = Chroma(
            persist_directory=self.persist_directory,
            embedding_function=self.embeddings
        )

    def create_qa_chain(self):
        """创建问答链"""
        # 自定义Prompt
        prompt_template = """
        你是一个专业的问答助手。请根据提供的参考资料回答问题。

        要求:
        1. 仅基于参考资料回答,不要添加外部知识
        2. 如果资料中没有相关信息,请明确说明
        3. 回答要准确、简洁、完整
        4. 如果引用内容,请注明来源

        参考资料:
        {context}

        问题:{question}

        回答:"""

        PROMPT = PromptTemplate(
            template=prompt_template,
            input_variables=["context", "question"]
        )

        # 创建问答链
        qa_chain = RetrievalQA.from_chain_type(
            llm=self.llm,
            chain_type="stuff",
            retriever=self.vectorstore.as_retriever(
                search_type="mmr",
                search_kwargs={"k": 4}
            ),
            chain_type_kwargs={"prompt": PROMPT},
            return_source_documents=True
        )

        return qa_chain

    def ask(self, question):
        """提问"""
        if not self.vectorstore:
            raise ValueError("请先构建或加载向量存储")

        qa_chain = self.create_qa_chain()
        result = qa_chain({"query": question})

        return {
            "answer": result["result"],
            "sources": [
                {
                    "content": doc.page_content[:200] + "...",
                    "source": doc.metadata.get("source", "未知")
                }
                for doc in result["source_documents"]
            ]
        }

# 使用示例
def main():
    # 创建系统
    qa_system = DocumentQA()

    # 加载并处理文档
    documents = qa_system.load_documents(["doc1.pdf", "doc2.txt"])
    chunks = qa_system.split_documents(documents)
    qa_system.build_vectorstore(chunks)

    # 问答
    while True:
        question = input("\n请输入问题(输入'quit'退出):")
        if question.lower() == 'quit':
            break

        result = qa_system.ask(question)
        print(f"\n回答:{result['answer']}")
        print(f"\n来源:{len(result['sources'])} 个文档")

if __name__ == "__main__":
    main()

七、实战项目:智能客服机器人

完整实现

from langchain_openai import ChatOpenAI
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationalRetrievalChain
from langchain.prompts import PromptTemplate
from langchain.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings

class CustomerServiceBot:
    def __init__(self, knowledge_base_path):
        """初始化客服机器人"""
        self.llm = ChatOpenAI(model="gpt-4", temperature=0)

        # 加载知识库
        self.vectorstore = Chroma(
            persist_directory=knowledge_base_path,
            embedding_function=OpenAIEmbeddings()
        )

        # 创建记忆
        self.memory = ConversationBufferMemory(
            memory_key="chat_history",
            return_messages=True,
            output_key="answer"
        )

        # 创建对话链
        self.qa_chain = self._create_chain()

    def _create_chain(self):
        """创建对话检索链"""
        # 客服Prompt
        prompt = PromptTemplate.from_template("""
你是一个专业、友好的客服助手。请根据知识库内容回答客户问题。

指导原则:
1. 回答要专业、准确、友好
2. 如果知识库中没有答案,诚实告知并提供替代建议
3. 对于投诉类问题,表示理解并提供解决方案
4. 必要时引导客户联系人工客服

历史对话:
{chat_history}

相关知识:
{context}

客户问题:{question}

回答:""")

        chain = ConversationalRetrievalChain.from_llm(
            llm=self.llm,
            retriever=self.vectorstore.as_retriever(search_kwargs={"k": 3}),
            memory=self.memory,
            combine_docs_chain_kwargs={"prompt": prompt},
            return_source_documents=True,
            verbose=True
        )

        return chain

    def chat(self, message):
        """对话"""
        result = self.qa_chain({"question": message})

        response = {
            "answer": result["answer"],
            "sources": [
                doc.metadata.get("source", "知识库")
                for doc in result.get("source_documents", [])
            ]
        }

        return response

    def reset(self):
        """重置对话"""
        self.memory.clear()

# 使用示例
bot = CustomerServiceBot("./knowledge_base")

# 对话
print(bot.chat("你们的产品支持退换货吗?"))
print(bot.chat("退款多久能到账?"))
print(bot.chat("我买的商品有质量问题,怎么处理?"))

八、最佳实践

性能优化

# 1. 使用缓存
from langchain.cache import InMemoryCache
import langchain
langchain.llm_cache = InMemoryCache()

# 2. 批量处理
from langchain.schema import Document
batch_docs = [Document(page_content=text) for text in texts]
vectorstore = Chroma.from_documents(batch_docs, embeddings)

# 3. 异步调用
async def async_process(questions):
    tasks = [chain.ainvoke({"question": q}) for q in questions]
    results = await asyncio.gather(*tasks)
    return results

# 4. 流式输出
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler

llm = ChatOpenAI(
    streaming=True,
    callbacks=[StreamingStdOutCallbackHandler()]
)

错误处理

from langchain.callbacks import StdOutCallbackHandler
from langchain.chains import LLMChain

def safe_invoke(chain, inputs, max_retries=3):
    """安全的链调用"""
    for attempt in range(max_retries):
        try:
            return chain.invoke(inputs)
        except Exception as e:
            print(f"第{attempt + 1}次尝试失败: {e}")
            if attempt == max_retries - 1:
                raise
            time.sleep(2 ** attempt)  # 指数退避

    return None

小结

组件用途关键API
Models模型调用ChatOpenAI, OpenAI
Prompts提示管理PromptTemplate
Chains链式调用LLMChain, RetrievalQA
Memory记忆系统ConversationBufferMemory
VectorStores向量存储Chroma, FAISS
Retrievers检索器vectorstore.as_retriever()

思考与练习

  1. 思考题

    • LCEL语法相比传统Chain有什么优势?
    • 如何选择合适的Memory类型?
  2. 动手练习

    • 构建一个基于PDF文档的问答系统
    • 为问答系统添加对话记忆功能
  3. 延伸阅读


下期预告

下一篇文章,我们将深入探讨:本地部署大模型:Ollama实战指南

会解答这些问题:

  • 如何在本地运行开源大模型?
  • Ollama的核心功能有哪些?
  • 如何优化本地模型性能?

关注专栏,不错过后续更新!


作者:ECH00O00 本文首发于掘金专栏《AI科普实验室》 欢迎评论区交流讨论,点赞收藏就是最大的鼓励 ❤️