LangChain 实践 极简个人知识库问答机器人&自定义大模型封装+通用对话链

LangChain 实践

内容:Model I/O、Prompt/OutputParser、自定义LLM/ChatModel、RAG全链路、多向量检索、Self-querying、MMR、结构化Agent、Function Call Agent

一、入门

项目1:个人知识库问答机器人(极简RAG)

知识点:文档加载器、文本分割、Embedding、FAISS向量库、基础检索、Prompt模板、OutputParser

功能

  • 本地导入 PDF/笔记/Markdown 文档
  • 自动切块、向量化、入库
  • 自然语言提问,基于私有文档回答

重点

把 RAG 完整链路 加载→分割→嵌入→存储→检索→生成 跑通,是所有高级项目的基础。

项目2:自定义大模型封装+通用对话链

知识点:自定义LLM/ChatModel、Model I/O、PromptTemplate、LCEL链式调用、Token统计、缓存

功能

  • 封装通义千问/本地开源模型为 LangChain 标准模型
  • 实现对话、角色人设、调用缓存、记录Token消耗

重点

吃透「自定义LLM继承、_generate/_call、模型参数调优」。

二、进阶

项目3:增强版智能知识库(集成所有高级检索策略)

知识点:语义分块、MMR去重、上下文压缩、多向量检索(摘要/假设问题)、Self-querying元数据检索

功能

  1. 支持语义分割,不按字数硬切
  2. 检索结果 MMR 去重,避免重复内容
  3. 上下文压缩精简冗余文档
  4. 多向量:摘要检索、假设问题检索优化匹配
  5. Self-querying 自动解析时间/分类/评分等元数据过滤

价值

做完这个,RAG所有高级玩法全部吃透

项目4:结构化输出问答器(Pydantic + 自定义OutputParser)

知识点:Pydantic结构化输出、自定义解析器、类NER信息抽取、RunnableLambda

功能

  • 输入自然语言,自动抽取:天气/时间/地址/商品信息
  • 强制固定JSON结构输出,失败自动校验重试

三、高阶

项目5:不依赖Function Call的结构化通用Agent

知识点:结构化Agent、Prompt工程、不支持Function Call模型适配、自定义工具

功能

  • 自己写 Prompt 约束格式:Action:xxx Action Input:xxx
  • 不用模型原生Function Call,纯提示词实现工具调用
  • 接入:计算器、时间查询、本地文档检索工具

落地:结构化Agent不靠模型,靠Prompt实现Agent

项目6:Function Call 智能工具助手

知识点:bind_tools、自定义工具、AgentExecutor、自定义解析器、Response伪装成格式工具

功能

  • 多工具自动调度:查天气、查时间、知识库检索
  • 最后强制调用Response输出结构化JSON
  • 自定义parse解析终止流程

四、企业级项目

项目7:私人AI助手全站项目

整合所有知识点:Model I/O + 高级RAG + 多检索策略 + 结构化&Function双Agent + 缓存 + Token统计

功能

  • 私有知识库问答
  • 多工具智能决策
  • 结构化信息抽取
  • 对话记忆、缓存加速、Token消耗统计
    做完直接可以放到 GitHub 当代表作。

项目1:极简个人知识库问答机器人

掌握本项目就等于掌握 RAG 全流程

项目结构

复制代码
personal-knowledge-qa-bot/
  ├── docs/               # 放你要读取的文档(.txt / .md)
  ├── main.py             # 主程序

第一步:安装依赖

python 复制代码
pip install langchain pypdf faiss-cpu numpy

第二步:完整代码

python 复制代码
# ======================
# 项目1:极简RAG知识库问答
# 流程:加载文档 → 分割 → 向量化 → 存向量库 → 检索 → 回答
# ======================

# 文档加载器:读取本地 txt 文档
from langchain_community.document_loaders import TextLoader

# 文本分割器:把长文本切成小块
from langchain_text_splitters import RecursiveCharacterTextSplitter

# 提示词模板:构造给大模型的提问格式
from langchain_core.prompts import ChatPromptTemplate

# 输出解析器:把模型返回结果转成字符串
from langchain_core.output_parsers import StrOutputParser

# FAISS 向量库:存储向量 + 快速相似度检索
from langchain_community.vectorstores import FAISS

# 本地开源向量模型(把文本转成向量)
from langchain_huggingface  import HuggingFaceEmbeddings

# 通义千问大模型(生成最终回答)
from langchain_community.llms import Tongyi

# 操作系统底层工具库,此处读取环境变量
import os

# 加载 .env 文件中的密钥
from dotenv import load_dotenv

# ----------------------
# 加载环境变量(API KEY)
# ----------------------
load_dotenv() # 加载项目根目录下的 .env 文件
tongyi_api_key = os.getenv("TONGYI_API_KEY") # 读取通义KEY
print(tongyi_api_key)

# ======================
# 1. 初始化模型
# ======================
# 创建通义大模型实例
llm = Tongyi(api_key=tongyi_api_key)

# 本地 BGE 中文向量模型路径
embeddings_path = "H:\\AI\\Agent\\models\\bge-large-zh-v1.5"
# 创建向量模型实例
embeddings = HuggingFaceEmbeddings(model_name=embeddings_path)

# ======================
# 2. 加载本地文档
# ======================
# 知识库文件路径
DOC_PATH = "./docs/test.txt"
loader = TextLoader(DOC_PATH, encoding="utf-8")
# 读取全文
documents = loader.load()

# ======================
# 3. 文本分割
# ======================
splitter = RecursiveCharacterTextSplitter(
    chunk_size=300,    # 每块大小:300 个字
    chunk_overlap=50,  # 重叠(重叠 50 个字)防止丢失上下文
)
# 切分后的文本块
chunks = splitter.split_documents(documents)

# ======================
# 4. 向量化 + 存入 FAISS 向量库
# ======================
# 把所有文本块 → 向量 → 存入 FAISS
db = FAISS.from_documents(chunks, embeddings)

# 构建检索器(获取最相似的3条)
retriever = db.as_retriever(search_kwargs={"k": 3})

# ======================
# 5. 提示词模板
# ======================
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个文档问答助手,只能根据提供的资料回答,不能编造。\n资料:{context}"),
    ("user", "问题:{question}")
])

# ======================
# 6. 输出解析器
# ======================
parser = StrOutputParser()

# ======================
# 7. RAG 核心链
# ======================
def rag_chain(question):
    # 1. 根据用户问题,检索最相关的文档片段
    docs = retriever.invoke(question)
    context = "\n".join([d.page_content for d in docs])

    # 2. 构造prompt: 把资料 + 问题 填入提示词模板
    formatted_prompt = prompt.invoke({
        "context": context,
        "question": question
    })

    # 3. 大模型生成回答
    response = llm.invoke(formatted_prompt)

    # 4. 解析输出
    return parser.invoke(response)

# ======================
# 8. 启动测试
# ======================
if __name__ == "__main__":
    print("RAG 知识库已启动!输入 'quit' 退出")
    while True:
        question = input("请输入问题:")
        if question.lower() == "quit":
            break
        answer = rag_chain(question)
        print("\n【AI回答】\n", answer)
        print("-"*50)

第三步:使用方法

  1. 新建 docs 文件夹

  2. 在里面放一个 test.txt,随便写一段内容,例如:

    我喜欢骑行,每周3次。
    我最喜欢的小猫是图图。
    我住在南京雨花台区。

  3. 运行 main.py

  4. 提问:

    我喜欢做什么运动?


运行输出


代码流程

python 复制代码
开始
  ↓
【环境准备】加载.env → 获取通义千问API Key
  ↓
【模型初始化】
  ├─ 通义千问LLM(生成回答)
  └─ BGE中文向量模型(文本转向量)
  ↓
【文档加载】读取本地txt文档 → 得到完整文档内容
  ↓
【文本分割】将长文本切分成小块(chunk_size=300,重叠50)
  ↓
【向量化 + 向量库存储】
  文本块 → 向量模型 → 向量
  向量 → 存入FAISS本地向量库
  ↓
【构建检索器】设置从向量库召回最相似的3条内容
  ↓
【提示词模板】定义系统提示(根据资料回答,不编造)
  ↓
【输出解析】将模型返回结果转为字符串
  ↓
【启动问答循环】等待用户输入问题
  ↓
┌───────────────────────────────────┐
│          RAG 核心问答链            │
│  1. 用户问题 → 检索器 → 匹配相关文档 │
│  2. 文档内容 + 问题 → 填入提示词    │
│  3. 提示词 → 通义千问 → 生成回答    │
│  4. 回答解析 → 输出给用户           │
└───────────────────────────────────┘
  ↓
循环问答,直到输入 quit → 结束

项目知识点

✅ 文档加载

✅ 文本切块

✅ 向量库 FAISS

✅ 基础检索

✅ Prompt 模板

✅ 输出解析

完整 RAG 流水线


项目2:自定义大模型封装+通用对话链

考察内容

用到知识点:自定义LLM/ChatModel、Model I/O、PromptTemplate、LCEL链式调用、Token统计、缓存

功能

  • 封装通义千问/本地开源模型为 LangChain 标准模型
  • 实现对话、角色人设、调用缓存、记录Token消耗

适合练什么

吃透「自定义LLM继承、_generate/_call、模型参数调优」。

方案分析

有 3 种调用大模型的方案(从最简单 → 最底层)

  • 方案 1:直接使用 LangChain 现成封装(项目 1 用的)

    • 最简单 → 0 底层知识 → 开箱即用
    • from langchain_community.llms import Tongyi llm = Tongyi(api_key="...") llm.invoke("你好")
    • 原理
      • 已经写好了整个 LLM 类,实现了 _call(),帮你发 HTTP, 帮你解析返回结果
      • 你要做的内容:只调用(不关心底层)
  • 方案2:自定义 LLM + 官方 SDK(中等难度)

    • 自己写 LLM 类,但借用 SDK 发请求
    • 原理
      • 你要做的内容:继承 LangChain 的 LLM 基类,实现 call(), 在 _call () 里调用官方 SDK(dashscope)
      • 不用自己写 HTTP
    • 优点
      • 学会自定义 LLM
      • 不用处理复杂网络请求
  • 方案3:自定义 LLM + 纯原生 HTTP(最底层 ✅️)

    • 原理:自己写:请求头、请求体、POST、JSON解析、获取回答内容、处理异常(真正的从零封装大模型)
    • 优点
      • 彻底懂大模型调用原理
      • 真正掌握 "封装"
      • 任何模型都能接

方案3 自定义LLM + 原生HTTP调用

前置准备

安装依赖
python 复制代码
pip install langchain-core requests python-dotenv

python-dotenv介绍

安装

python 复制代码
pip install python-dotenv

导入

python 复制代码
from dotenv import load_dotenv
import os

# 加载项目根目录里的 .env 文件,把里面的密钥、配置读到环境变量里:
load_dotenv()  
api_key = os.getenv("OPENAI_API_KEY")
.env文件

已有通义千问API_KEY,存入.env文件

对话API

通义千问对话 API 地址:

https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation

BaseLLM规范

必须继承

python 复制代码
from langchain_core.language_models.llms import BaseLLM

必须实现

  1. _llm_type()
    • → 返回模型标识,框架识别用
  2. _generate()
    • → 框架真正强制的核心方法,处理批量请求

_call() 是什么?

  • 是辅助方法,方便写单条调用逻辑
  • 不是必须,但写了会让代码更干净
  • 会被 _generate 内部调用

HTTP 调用格式

针对通义千问模型的调用格式如下。

请求头

python 复制代码
headers = {
    "Authorization": f"Bearer {self.api_key}",
    "Content-Type": "application/json"
}

请求体

python 复制代码
body = {
    "model": self.model_name,
    "input": {
        "messages": [
            {"role": "system", "content": "你是智能助手"},
            {"role": "user", "content": prompt}
        ]
    },
    "parameters": {
        "result_format": "message",
        "temperature": self.temperature,
        "max_tokens": self.max_tokens
    }
}

基础自定义LLM

2-langchain-model-customize/SelfTongyiLLM.py

python 复制代码
from langchain_core.language_models.llms import BaseLLM
from typing import Optional, List, Any
from langchain_core.callbacks import CallbackManagerForLLMRun
import requests
from langchain_core.outputs import LLMResult, Generation


class SelfTongyiLLM(BaseLLM):
    # 自定义可配置参数
    api_key: Optional[str] = None
    model_name: str = "qwen-turbo"
    temperature: float = 0.7
    max_tokens: int = 1024
    base_url: str = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation"

    # 1:模型标识
    @property
    def _llm_type(self) -> str:
        return "self_define_tongyi_http"

    # 2. 核心调用方法(重点写这里)
    def _generate(
        self,
        prompts: List[str],
        stop: Optional[List[str]] = None,
        run_manager: Optional[CallbackManagerForLLMRun] = None,
        **kwargs: Any,
    ) -> LLMResult:
        # 直接取第一个提问,日常invoke只会传一个
        prompt = prompts[0]


        # 1. 拼接headers请求头
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }
        # 2. 组装messages对话结构
        messages = [
            {"role": "system", "content": "你是一个智能聊天助手"},
            {"role": "user", "content": prompt}
        ]
        # 3. 构造完整请求体body
        body = {
            "model": self.model_name,
            "input": { "messages": messages },
            "parameters": {
                "result_format": "message",
                "temperature": self.temperature,
                "max_tokens": self.max_tokens
            }
        }
        # 4. 发起请求 + 异常捕获
        try:
            response = requests.post(
                url=self.base_url,
                headers=headers,
                json=body,
            )

            # 接口报错时抛出异常
            response.raise_for_status()

            # 6 解析响应json,提取回答文本
            result = response.json()
            # 7. 返回最终字符串结果
            answer  = result["output"]["choices"][0]["message"]["content"]
        except Exception as e:
            # 5 捕获异常
            answer = f"调用失败:{str(e)}"

        # 按框架固定格式返回结果
        return LLMResult(generations=[[Generation(text=answer)]])

2-langchain-model-customize/main.py

python 复制代码
from SelfTongyiLLM import SelfTongyiLLM
import os
from dotenv import load_dotenv

load_dotenv()
tongyi_api_key = os.getenv("TONGYI_API_KEY") # 读取通义KEY

if __name__ == "__main__":
    llm = SelfTongyiLLM(
        api_key=tongyi_api_key,
        model_name="qwen-turbo"
    )

    res = llm.invoke("介绍下你自己")
    print(res)

运行 main.py 结果

自定义ChatModel

LLM 与 ChatModel的区别

  • BaseLLM:只接收纯文本字符串,适合单轮短句问答
  • BaseChatModel:接收结构化消息对象(System/Human/AI),天生支持多轮对话、人设角色

项目做对话模型,优先继承 BaseChatModel,淘汰旧的 BaseLLM.

实现

python 复制代码
from langchain_core.language_models.chat_models import BaseChatModel
from typing import Optional, List, Any, Dict
from langchain_core.callbacks import CallbackManagerForLLMRun
import requests
from langchain_core.outputs import ChatResult, ChatGeneration
from langchain_core.messages import BaseMessage, AIMessage, SystemMessage, HumanMessage


class SelfTongyiChatModel(BaseChatModel):
    # 模型配置参数
    api_key: Optional[str] = None
    model_name: str = "qwen-turbo"
    temperature: float = 0.7
    max_tokens: int = 1024
    base_url: str = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation"

    # 固定必填:声明模型类型
    @property
    def _llm_type(self) -> str:
        return "custom_tongyi_chat_model"

    # 核心必填方法:预留_generate空实现
    def _generate(
        self,
        messages: List[BaseMessage],
        stop: Optional[List[str]] = None,
        run_manager: Optional[CallbackManagerForLLMRun] = None,
        **kwargs: Any
    ) -> ChatResult:
        # 1. 转换消息格式
        api_messages = self._convert_langchain_msg_to_api(messages)

        # 2. 请求头
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }

       # 3. 构造完整请求体body
        body = {
            "model": self.model_name,
            "input": { "messages": api_messages },
            "parameters": {
                "result_format": "message",
                "temperature": self.temperature,
                "max_tokens": self.max_tokens
            }
        }

        # 4. 发起请求 + 异常捕获
        try:
            response = requests.post(
                url=self.base_url,
                headers=headers,
                json=body,
            )

            # 接口报错时抛出异常
            response.raise_for_status()

            # 6 解析响应json,提取回答文本
            result = response.json()
            # 7. 返回最终字符串结果
            answer  = result["output"]["choices"][0]["message"]["content"]
        except Exception as e:
            # 5 捕获异常
            answer = f"调用失败:{str(e)}"

        # 封装成LangChain标准返回格式
        return ChatResult(
            generations=[ChatGeneration(message=AIMessage(content=answer))]
        )

    def _convert_langchain_msg_to_api(self, messages: List[BaseMessage]) -> List[Dict]:
        msg_list = []
        for msg in messages:
            if isinstance(msg, SystemMessage):
                role = "system"
            elif isinstance(msg, HumanMessage):
                role = "user"
            elif isinstance(msg, AIMessage):
                role = "assistant"
            else:
                role = "user"
            msg_list.append({"role": role, "content": msg.content})
        return msg_list

单轮测试调用

python 复制代码
from langchain_core.messages import SystemMessage, HumanMessage

from SelfTongyiLLM import SelfTongyiLLM
from SelfTongyiChatModel import SelfTongyiChatModel
import os
from dotenv import load_dotenv

load_dotenv()
tongyi_api_key = os.getenv("TONGYI_API_KEY") .                                                                        # 读取通义KEY

if __name__ == "__main__":
    # LLM
    # llm = SelfTongyiLLM(
    #     api_key=tongyi_api_key,
    #     model_name="qwen-turbo"
    # )
    #
    # res = llm.invoke("介绍下你自己")
    # print(res)

    # ChatModel
    chat_model = SelfTongyiChatModel(api_key=tongyi_api_key)
    # 构造人设+用户提问
    msg_list = [
        SystemMessage(content="你是资深Python开发工程师"),
        HumanMessage(content="简单介绍LCEL是什么")
    ]
    result = chat_model.invoke(msg_list)
    print(result.content)

_convert_langchain_msg_to_api: 通义 API 需要 [{"role":"xxx","content":"xxx"}] 格式,将转换函数_convert_langchain_msg_to_api加到SelfTongyiChat类内部

  • SystemMessage = 角色人设
  • HumanMessage = 用户提问
  • AIMessage = 模型历史回答
python 复制代码
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage

def _convert_langchain_msg_to_api(self, messages: List[BaseMessage]) -> List[Dict]:
    msg_list = []
    for msg in messages:
        if isinstance(msg, SystemMessage):
            role = "system"
        elif isinstance(msg, HumanMessage):
            role = "user"
        elif isinstance(msg, AIMessage):
            role = "assistant"
        else:
            role = "user"
        msg_list.append({"role": role, "content": msg.content})
    return msg_list

【观察】 LLM vs ChatModel

对比项 自定义LLM(BaseLLM) 自定义ChatModel(BaseChatModel)
继承父类 BaseLLM BaseChatModel
接收入参 字符串列表prompts 消息对象列表messages
传参方式 invoke(纯文本) invoke([系统消息+用户消息])
消息处理 内置固定系统提示词 自动转换角色消息,灵活配置
返回类型 LLMResult+纯文本 ChatResult+AI消息对象
多轮对话 不友好 原生支持
LCEL适配 一般 完美适配
使用场景 简单文本生成 对话、RAG、Agent、业务问答

接入PromptTemplate提示词模板

使用模板统一管理人设和输入。

模板解耦人设,业务修改只改模板不用改调用逻辑。

python 复制代码
# 导入提示词模板
from langchain_core.prompts import ChatPromptTemplate

# 定义对话模板
chat_prompt = ChatPromptTemplate.from_messages([
    ("system", "你是专业前端架构师,回答简洁精炼"),
    ("human", "{user_input}")
])

# 接入提示词模板
prompt_value = chat_prompt.format_messages(user_input="讲一下Vue2核心原理")
chat_model = SelfTongyiChatModel(api_key=tongyi_api_key)
res = chat_model.invoke(prompt_value)
print(res.content)

运行结果

python 复制代码
Vue2 核心原理主要包括以下几点:

1. **响应式系统**:通过 `Object.defineProperty` 或 `Proxy`(Vue3)实现数据劫持,结合 `Dep` 和 `Watcher` 实现数据变化时触发视图更新。

2. **虚拟 DOM**:通过 `render` 函数生成虚拟节点(VNode),用于高效更新真实 DOM。

3. **编译过程**:将模板编译为 `render` 函数,支持指令、表达式等语法。

4. **组件系统**:基于构造函数和原型链实现组件化开发,支持 props、events、slot 等机制。

5. **生命周期钩子**:如 `created`、`mounted` 等,用于控制组件行为。

总结:Vue2 通过响应式数据 + 虚拟 DOM + 编译机制实现高效、灵活的前端框架。

LCEL 链式调用

  • 链式结构:提示模板 | 自定义聊天模型 | 结果解析器
  • |管道符串联流程
python 复制代码
# 导入解析器
from langchain_core.output_parsers import StrOutputParser

# LCEL链式调用
chat_model = SelfTongyiChatModel(api_key=tongyi_api_key)
chain = chat_prompt | chat_model | StrOutputParser()
resp = chain.invoke({"user_input": "说说原型链继承优缺点"})
print(resp)

运行结果

python 复制代码
原型链继承优点:  
- 实现简单,共享父类方法,节省内存。  
- 对象间共享属性和方法,提高效率。

缺点:  
- 所有实例共享引用类型属性,修改可能影响其他实例。  
- 无法实现多继承。  
- 无法在创建时传参,初始化不够灵活。

token消耗统计

从通义返回体读取 usage 字段,统计输入 / 输出 token,实现用量监控,方便后续计费统计。

修改_generate里成功逻辑:

python 复制代码
 # 6 解析响应json,提取回答文本
 result = response.json()
 # 7. 返回最终字符串结果
 answer  = result["output"]["choices"][0]["message"]["content"]
 # 新增token统计
 usage_info = result.get("usage", {})
 input_token = usage_info.get("input_tokens", 0)
 output_token = usage_info.get("output_tokens", 0)
 total_token = usage_info.get("total_tokens", 0)
 print(f"输入Token:{input_token} 输出Token:{output_token} 总Token:{total_token}")

运行结果

python 复制代码
输入Token:34 输出Token:77 总Token:111
原型链继承优点:  
- 实现简单,共享父类方法,节省内存。  
- 对象间共享属性和方法,提高性能。

缺点:  
- 父类引用类型属性会被所有子类共享,修改会影响所有实例。  
- 无法实现多继承。  
- 子类实例无法向父类构造函数传参。

输入Token:34,已经包含了给 LLM 的所有人设、系统提示词、用户问题,是完整的总输入消耗。

LangChain 缓存

缓存是 LangChain 内置自带的核心技术,完全属于 LangChain 功能,不需要写代码存 Redis / 文件。

LangChain 支持一键开启全局缓存,相同的提问会直接读缓存,不再调用大模型,0 Token 消耗,速度极快。

LangChain 自带 3 种最常用缓存:

  • 内存缓存(InMemoryCache):重启程序消失 ------ 本节使用该缓存方式
  • 本地文件缓存(SQLiteCache):持久化保存
  • Redis 缓存(RedisCache):分布式高性能
python 复制代码
from langchain_core.globals import set_llm_cache
from langchain_community.cache import InMemoryCache
import time

# 开启内存缓存
set_llm_cache(InMemoryCache())

# LangChain缓存
chat_model = SelfTongyiChatModel(api_key=tongyi_api_key)
chain = chat_prompt | chat_model | StrOutputParser()
print(time.time())
print("第 1 次提问")
resp = chain.invoke({"user_input": "说说原型链继承优缺点"})
print(resp)
print(time.time())
print("第 2 次提问")
resp = chain.invoke({"user_input": "说说原型链继承优缺点"})
print(resp)
print(time.time())

运行结果

python 复制代码
1779282110.4766881
第 1 次提问
输入Token:34 输出Token:78 总Token:112
原型链继承优点:  
- 实现简单,共享父类属性和方法。  
- 内存高效,所有实例共享原型上的属性和方法。

缺点:  
- 原型中引用类型会被所有实例共享,修改会影响其他实例。  
- 无法实现多继承。  
- 无法在创建时传递参数给父类构造函数。
1779282111.9036255
第 2 次提问
原型链继承优点:  
- 实现简单,共享父类属性和方法。  
- 内存高效,所有实例共享原型上的属性和方法。

缺点:  
- 原型中引用类型会被所有实例共享,修改会影响其他实例。  
- 无法实现多继承。  
- 无法在创建时传递参数给父类构造函数。
1779282111.9046257

开始 1779282110.4766881

第1次回答结束 1779282111.9036255

第2次回答结束 1779282111.9046257

多轮连续对话(带上下文)

使用MessagesPlaceholder占位对话历史。

python 复制代码
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, HumanMessagePromptTemplate

# 支持历史对话的模板
history_prompt = ChatPromptTemplate.from_messages([
    SystemMessage(content="亲切聊天助手"),
    MessagesPlaceholder(variable_name="chat_history"),
    HumanMessagePromptTemplate.from_template("{question}")
    # HumanMessage(content="{question}") # ❌️
])

# 构建多轮链
chat_model = SelfTongyiChatModel(api_key=tongyi_api_key)
multi_chain = history_prompt | chat_model | StrOutputParser()
# 第一轮
history = []
ans1 = multi_chain.invoke({"chat_history": history, "question": "你好"})
print(ans1)
# 存入历史
history.extend([HumanMessage(content="你好"), AIMessage(content=ans1)])
# 第二轮带上下文
ans2 = multi_chain.invoke({"chat_history": history, "question": "你都会什么"})
print(ans2)

运行结果

python 复制代码
[{'role': 'system', 'content': '亲切聊天助手'}, {'role': 'user', 'content': '你好'}]
输入Token:21 输出Token:23 总Token:44
你好呀!😊 很高兴见到你!今天过得怎么样呀?有什么想和我聊聊的吗?
[{'role': 'system', 'content': '亲切聊天助手'}, {'role': 'user', 'content': '你好'}, {'role': 'assistant', 'content': '你好呀!😊 很高兴见到你!今天过得怎么样呀?有什么想和我聊聊的吗?'}, {'role': 'user', 'content': '你都会什么'}]
输入Token:57 输出Token:164 总Token:221
你好呀!我是一个人工智能助手,可以和你聊天、解答问题、分享知识,还能陪你一起玩游戏、讲笑话呢!😊

虽然我不能像真人一样有真实的感受,但我可以尽量用温暖、真诚的方式和你交流。我可以:

- 和你聊天,听你说说你的想法和心情
- 帮你解答一些问题,比如学习、生活、工作上的困惑
- 给你推荐一些有趣的书、电影、音乐
- 陪你一起玩文字游戏、猜谜语、讲故事
- 在你需要的时候给你一些安慰和鼓励

当然啦,我也有很多不懂的地方,如果你问我不会的问题,我会如实告诉你,然后我们一起想办法找到答案,好不好?🌟

你想和我聊些什么呢?或者有什么想让我帮你做的事情吗?

总结

项目定位

基于 LangChain 生态,自研封装国产通义千问大模型 ,打造一套符合LangChain标准、可扩展、可商用的对话式大模型调用框架,同时预留本地开源模型接入能力,兼顾学习实战与工程落地。

核心技术栈

  1. 底层框架:LangChain 标准生态
  2. 模型来源:阿里通义千问HTTP接口
  3. 自定义实现:继承BaseChatModel实现自研对话模型
  4. 链式语法:LCEL 管道式调用
  5. 辅助能力:Prompt模板、Token统计、内存缓存、多轮对话

已完成核心模块

  1. 自定义对话模型(核心根基)
  • 放弃旧BaseLLM,使用标准**BaseChatModel**对话基类
  • 实现必填:_llm_type标识 + 核心_generate请求逻辑
  • 内置消息转换器:LangChain结构化消息 ↔ 通义API请求格式
  • 封装统一请求头、请求体、异常捕获,兼容性强
  • 支持自由配置:模型名、温度、最大生成长度等参数
  1. Model I/O 标准化
  • 输入:SystemMessage/HumanMessage/AIMessage标准对话消息
  • 输出:LangChain统一ChatResult结构,无缝对接所有LangChain组件
  1. PromptTemplate 提示词工程
  • 解耦角色人设、固定话术与动态用户输入
  • 支持单轮提示词、多轮对话历史占位模板
  • 业务修改只改模板,不动调用代码
  1. LCEL 链式调用(主流写法)
  • 链式流程:提示模板 | 自定义模型 | 结果解析器
  • 极简调用,易拼接流程、易扩展中间组件
  1. 实用工程能力
    1. Token用量统计:读取接口返回usage,统计输入/输出/总Token,用于计费、限流
    2. 全局缓存机制:内存缓存重复问答,减少接口请求、节省API额度
    3. 多轮上下文对话:通过消息列表维护聊天历史,实现连续对话

项目价值

  1. 吃透LangChain自定义模型底层继承规则
  2. 掌握_generate底层调用逻辑,理解框架模型运行原理
  3. 熟练区分LLM与ChatModel使用场景
  4. 精通Prompt模板设计、LCEL工业级链式开发
  5. 学会大模型项目必备:Token计费、调用缓存、异常容错
  6. 具备国产大模型私有化封装实战经验

后续拓展方向

  1. 加入stream流式输出,实现打字机聊天效果
  2. 接入Ollama本地模型,做到云端+本地模型一键切换
  3. 封装Memory记忆组件,自动管理对话上下文
  4. 接入Redis持久化缓存
  5. 增加请求重试、超时、请求队列、参数校验
  6. 封装成工具类,提供全局统一调用入口
  7. 对接Web/命令行/前端页面,做成完整聊天机器人

项目优势

不依赖官方SDK,纯手写HTTP自研封装,彻底吃透底层调用逻辑,不受官方接口版本迭代限制,自由度极高,是前端转AI全栈、学习大模型工程化最扎实的实战项目。

相关推荐
njsgcs7 小时前
那langchain的reAct是怎么实现的
人工智能·langchain
不懂的浪漫7 小时前
RAG 后端接口设计:为什么 ingest 要异步,query 要同步?
rag
swipe7 小时前
Elasticsearch 全文检索工程教程:倒排索引、IK 分词器与 BM25 从原理到落地
面试·langchain·llm
SuniaWang8 小时前
《Agentx专栏》02-技术选型:预算有限时如何做出正确的技术决策
java·spring·架构·langchain·milvus·agenx·opl
一条泥憨鱼8 小时前
能够让AI做事的“Skill“有什么奥秘
人工智能·ai·agent·rag·skill·mcp
唐璜Taro8 小时前
LangChain与LangGraph多Agent实战:从工具链到工作流编排(上)
langchain·agent·langgraph
deephub10 小时前
推理 → 行动 → 观察:用 LangChain + Python 实现一个智能体循环
人工智能·python·langchain·大语言模型·agent
嫣然细雨红尘路10 小时前
LangChain学习笔记(一)
笔记·学习·langchain
阿拉伯柠檬10 小时前
大语言模型 LLM
人工智能·python·语言模型·自然语言处理·langchain