LangChain 基础入门:模型调用、Prompt 模板与链式编排 环境准备 1 pip install langchain langchain-community langchain-ollama dashscope chromadb
各依赖的作用如下:
langchain:LangChain 核心框架
langchain-community:社区维护的模型、工具、加载器等
langchain-ollama:调用本地 Ollama 模型
dashscope:调用通义千问、DashScope 相关能力
chromadb:向量数据库,常用于文档检索和记忆存储
如果你使用 OpenAI 或其他在线模型,还需要额外安装对应 SDK,并配置 API Key。 例如 OpenAI 通常需要设置环境变量 OPENAI_API_KEY。
如果你调用的是通义千问,通常需要提前配置 DASHSCOPE_API_KEY,并注意网络代理问题;有些环境下开代理可能会导致请求失败。
LangChain 是什么 LangChain 可以理解为一个“LLM 应用开发框架”。 它帮我们把大模型相关的常见能力封装起来,比如:
Prompt 组织与模板化
模型调用
多轮对话管理
记忆机制
文档加载与检索
Agent / 工具调用
链式调用(Chain / LCEL)
简单来说,LangChain 的目标不是“替你训练模型”,而是帮助你更方便地“把模型用起来”。
LangChain 主要支持三类模型:
LLMs 输入一段字符串,输出一段字符串。
Chat Models 输入一组消息,输出一条回复,更适合多轮对话。
Embedding Models 把文本转换成向量,用于语义检索、相似度搜索、RAG 等场景。
最基本的模型调用 直接调用文本模型
1 2 3 4 5 6 7 8 from langchain_community.llms.tongyi import Tongyillm = Tongyi( model="qwen-max" , ) res = llm.invoke("你好哈哈" ) print (res)
调用本地 Ollama 模型1 2 3 4 5 6 7 8 from langchain_ollama import OllamaLLMllm = OllamaLLM( model="qwen3:4b" , ) res = llm.invoke("你是谁,可以干什么?" ) print (res)
本地模型有时会比较吃 CPU 或内存,如果运行压力太大,可以做两类优化:
1 2 3 4 llm = OllamaLLM( model="qwen3:4b" , num_predict=50 , )
流式输出 很多时候我们不想等模型一次性返回完,而是希望它边生成边输出。 这时可以使用 stream()。1 2 3 4 5 6 from langchain_community.llms.tongyi import Tongyillm = Tongyi(model="qwen-max" ) for chunk in llm.stream("你是谁,可以干什么?" ): print (chunk, end="" , flush=True )
流式输出的好处是:
流式输出(SSE)的实现
模型在收到客户端发送过来的消息的时候,根据提示词+已经生成token预测下一个token,之后选择最好的token作为delta发送,客户端收到后拼接成结果直到结束
聊天模型 聊天模型和普通 LLM 最大的区别在于: 它接收的不是单纯字符串,而是消息列表 。
消息通常有三种角色:
SystemMessage:系统提示词,用来设定身份、风格、规则
HumanMessage:用户输入
AIMessage:助手历史回复
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 from langchain_community.llms.tongyi import Tongyifrom langchain_core.messages import AIMessage, SystemMessage, HumanMessagellm = Tongyi(model="qwen-max" ) messages = [ SystemMessage(content="你是一个小说续写助手,只能续写 100 字。" ), HumanMessage(content="小说:我是小米" ), AIMessage(content="" ), ] for chunk in llm.stream(messages): print (chunk, end="" , flush=True )
这里的 messages 需要是正确的消息对象,或者符合 LangChain 规范的消息格式。 规范的写法应该使用消息对象,或者 ("system", "...")、("user", "...") 这种格式。
Embedding:把文本变成向量 Embedding 模型的作用是把文本映射成高维向量。 这样就可以计算文本之间的语义相似度,用于:
语义检索
文档问答
推荐系统
向量数据库检索(后续的RAG)示例:DashScope Embedding
1 2 3 4 5 6 7 8 9 from langchain_community.embeddings import DashScopeEmbeddingsembeddings = DashScopeEmbeddings() print (embeddings.embed_query("我是小明,我今年21岁了" ))print (embeddings.embed_documents([ "小明" , "小绿" ]))
一般来说:
embed_query():把单条查询文本转成向量
embed_documents():把文档列表转成向量列表
模板 PromptsTemplate PromptTemplate 适合简单场景。 它本质上就是一个带变量的字符串模板。
示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from langchain_core.prompts import PromptTemplatefrom langchain_community.llms.tongyi import Tongyitemplate = "你是{role},擅长{skill}。接下来请按照你的身份回答问题。" prompt = PromptTemplate.from_template(template) formatted_prompt = prompt.format ( role="登山者" , skill="攀岩" ) print (formatted_prompt)model = Tongyi(model="qwen-max" ) for chunk in model.stream(formatted_prompt): print (chunk, end="" , flush=True )
适用场景
ChatPromptTemplate:适合对话场景 如果做的是聊天机器人、多轮问答、带上下文的对话,ChatPromptTemplate 会更合适。
示例:一个简单的医生助手 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 from langchain_community.llms.tongyi import Tongyifrom langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholderclass Doctor : def __init__ (self, major: str = "" ): self .major = major self .client = Tongyi(model="qwen-max" ) self .messages = [] self .standard = ChatPromptTemplate.from_messages([ ("system" , "You are a doctor, mainly major in {major}, please answer user's question professionally." ), MessagesPlaceholder(variable_name="conversation" ) ]) self .chain = self .standard | self .client def ask (self, msg: str ): self .messages.append(("user" , msg)) prompt_value = self .standard.invoke({ "major" : self .major, "conversation" : self .messages }) stream = self .chain.stream(prompt_value.to_messages()) full_response = "" for chunk in stream: print (chunk, end="" , flush=True ) full_response += chunk print () self .messages.append(("assistant" , full_response)) return full_response def get_history (self ): return self .messages if __name__ == "__main__" : d = Doctor(major="口腔" ) answer = d.ask("17岁孩子口腔牙齿很不整齐,影响形象怎么办" ) print ("\n对话历史:" ) for role, content in d.get_history(): print (f"{role} : {content} " )
这个例子的核心思想
system 负责设定身份和规则
conversation 保存历史消息
chain = prompt | model 形成调用链
stream() 可以边生成边输出
在做下优化,变成流式输出 如果对话轮次很多,直接把全部历史塞进 prompt 会越来越长。 这时可以用向量检索,从历史对话里找“最相关”的内容。
将每一轮用户和助手消息存入 Chroma
用户新提问时,先检索最相似的历史片段
把这些片段重新组装成上下文
再交给大模型生成回答
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 from langchain_community.llms.tongyi import Tongyifrom langchain_community.embeddings import DashScopeEmbeddingsfrom langchain_chroma import Chromafrom langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholderclass Doctor : def __init__ (self, major: str = "" ): self .major = major self .client = Tongyi(model="qwen-max" ) self .embeddings = DashScopeEmbeddings(model="text-embedding-v1" ) self .vectorstore = Chroma( collection_name="chat_history" , embedding_function=self .embeddings, persist_directory=None ) self .messages = [] self .standard = ChatPromptTemplate.from_messages([ ("system" , "You are a doctor, mainly major in {major}, please answer user's question professionally." ), MessagesPlaceholder(variable_name="conversation" ) ]) self .chain = self .standard | self .client def _add_message (self, role: str , content: str ): self .messages.append((role, content)) self .vectorstore.add_texts( texts=[content], metadatas=[{"role" : role}] ) def ask (self, msg: str ): docs = self .vectorstore.similarity_search(msg, k=10 ) conversation = [] for doc in docs: role = doc.metadata.get("role" , "user" ) conversation.append((role, doc.page_content)) conversation.append(("user" , msg)) stream = self .chain.stream({ "major" : self .major, "conversation" : conversation }) full_response = "" for chunk in stream: print (chunk, end="" , flush=True ) full_response += chunk print () self ._add_message("user" , msg) self ._add_message("assistant" , full_response) return full_response def get_history (self ): return self .messages if __name__ == "__main__" : d = Doctor(major="口腔" ) d.ask("17岁孩子口腔牙齿很不整齐,影响形象怎么办" ) d.ask("刚刚是不是有人问了你问题,那个孩子是几岁来着" ) d.ask("总结问问题的人的特点" ) print ("\n对话历史:" ) for role, content in d.get_history(): print (f"{role} : {content} " )
这段代码的作用
它解决的是“长对话记忆”问题:
不是把所有历史都拼进去
而是只找和当前问题最相关的历史
更省 token,也更适合长对话
后续可能出现历史过长有幻觉问题,可以另外起一个功能函数,整合历史记录 个人有两个想法:
便利所有的历史记录,先喂给AI / 想出一个数学公式 让其提取计算模型对话历史中拟合的向量中值,之后删除太过偏离的向量对应的消息
建图,例如对于一个医生agent,关键词比如role = doctor, major = 口腔,以这个role为中心建图,可以采取向量相似度或者消息重合度连边,对于在同一个图内的设置消息的“相似度”,设置相似度 p,选取相似度 <= p的消息作为前置prompts输入模型
FewShotPromptTemplate:少样本提示词 少样本提示词的核心思想是: 先给模型几个示例,让它模仿这些示例的回答方式,再处理新的问题。
比如你给模型 3 组“问题 + 回答”,它就会更容易学会你的风格。
固定格式输出
分类任务
翻译风格统一
模仿特定回答模板
特性
PromptTemplate
ChatPromptTemplate
FewShotPromptTemplate
输出类型
字符串
消息列表
字符串
适用模型
LLM
ChatModel
LLM / 组合式聊天模板
主要用途
单轮问答
多轮对话
少样本学习
消息结构
无
system / user / assistant
示例驱动
变量插值
支持
支持
支持
组合能力
一般
很强
可与其他模板组合
LCEL:链式编排 LangChain 的一个重要能力是链式组合,也叫 LCEL。 你可以把多个步骤串起来:
1 template1 | model | parser | template2 | model
常见用途:
提示词生成
模型调用
输出解析
二次加工
多步任务流
例如:
1 2 chain = prompt | model result = chain.invoke({"name" : "小明" })
如果是流式输出:
1 2 for chunk in chain.stream({"name" : "小明" }): print (chunk, end="" , flush=True )