林子雨编著《AI编程与智能体开发》(访问教材官网)
7.5 LangChain快速入门
7.5.1 环境准备
推荐使用如下命令构建Python虚拟环境并安装依赖包:
python -m venv langchain-env # 在当前目录下创建一个名字叫langchain-env的独立 Python 环境,用来隔离项目依赖,不污染系统全局Python
langchain-env\Scripts\activate
pip install -U langchain langchain-ollama langchain-community langchain-text-splitters faiss-cpu
7.5.2 第一个LangChain程序
# langchain_call_model.py
from langchain_ollama import ChatOllama
model = ChatOllama(
model="gemma4:e4b",
temperature=0,
base_url="http://127.0.0.1:11434"
)
response = model.invoke("请用一句话解释什么是检索增强生成。")
print(response.content)
如果采用云端大模型,比如阿里的千问大模型,则可以采用类似如下代码:
# langchain_cloud_qwen.py
from langchain_community.chat_models import ChatTongyi
model = ChatTongyi(
model="qwen-turbo",
temperature=0,
api_key="你的阿里云API_KEY"
)
response = model.invoke("请用一句话解释什么是检索增强生成。")
print(response.content)
7.5.3 一个简易的智能体实例
# langchain_simple_agent.py
from langchain.agents import create_agent
from langchain_ollama import ChatOllama
def search_notice(topic: str) -> str:
"""
查询课程相关通知信息
根据输入的主题关键词,返回对应的课程通知内容
Args:
topic: 要查询的通知主题(如:实验报告、课程答疑)
Returns:
对应的通知详情字符串
"""
notices = {
"实验报告": "实验报告需在本周五18:00前提交到教学平台。",
"课程答疑": "课程答疑安排在周三下午 4 点,地点为实验楼302。",
}
for keyword, notice in notices.items():
if keyword in topic:
return notice
return notices.get(topic, "暂未查询到相关通知。")
def build_agent():
model = ChatOllama(
model="gemma4:e4b",
temperature=0,
base_url="http://127.0.0.1:11434",
)
return create_agent(
model=model,
tools=[search_notice],
system_prompt="你是一名课程助理,必要时调用工具查询课程通知。",
)
def main() -> None:
agent = build_agent()
result = agent.invoke(
{"messages": [{"role": "user", "content": "请帮我查询实验报告的提交要求。"}]}
)
print(result)
if __name__ == "__main__":
main()
7.6 LangChain的消息
7.6.3 消息实操
以下示例演示多轮对话中消息的创建与使用,场景模拟了教师与助手的连续提问。具体代码如下:
# langchain_messages_demo.py
from langchain_ollama import ChatOllama
from dotenv import load_dotenv
import os
load_dotenv()
llm = ChatOllama(
model=os.getenv("OLLAMA_MODEL", "gemma4:e4b"),
base_url=os.getenv("OLLAMA_BASE_URL", "http://localhost:11434"),
temperature=0.7
)
# 构建多轮对话消息列表
messages = [
{"role": "system", "content": "你是一个专业的Python教学助手,回答简洁明了。"},
{"role": "user", "content": "什么是装饰器?"},
{"role": "assistant", "content": "装饰器是Python中用于修改函数或类行为的特殊语法。"},
{"role": "user", "content": "请举例说明"}
]
response = llm.invoke(messages)
print(f"AI响应: {response.content}")
c7.6.4 消息在智能体中的流转
以下示例完整演示四种消息类型在一次工具调用中的流转过程:
# langchain_agent_messages_demo.py
from dotenv import load_dotenv
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage, ToolMessage
from langchain_core.tools import tool
from langchain_ollama import ChatOllama
import os
load_dotenv()
@tool
def calculate(a: float, b: float, operator: str) -> str:
"""
执行基础数学计算
参数:
a: 第一个数字
b: 第二个数字
operator: 运算符,支持 "+"、"-"、"*"、"/"
"""
if operator == "+":
return str(a + b)
elif operator == "-":
return str(a - b)
elif operator == "*":
return str(a * b)
elif operator == "/" and b != 0:
return str(a / b)
return "不支持的操作"
llm = ChatOllama(
model=os.getenv("OLLAMA_MODEL", "gemma4:e4b"),
base_url=os.getenv("OLLAMA_BASE_URL", "http://localhost:11434"),
temperature=0.7
)
llm_with_tools = llm.bind_tools([calculate])
# ── 步骤 1:用户输入封装为 HumanMessage ──────────────────────
messages = [
SystemMessage(content="你是计算助手,使用 calculate 工具完成计算。"),
HumanMessage(content="计算 15 加 27 等于多少"),
]
print(f"步骤1 消息数: {len(messages)},类型: {[type(m).__name__ for m in messages]}")
# ── 步骤 2:模型判断需要调用工具,返回含 tool_calls 的 AIMessage ──
ai_response = llm_with_tools.invoke(messages)
print(f"\n步骤2 模型回复类型: {type(ai_response).__name__}")
print(f" 工具调用指令: {ai_response.tool_calls}")
# ── 步骤 3:执行工具,结果封装为 ToolMessage ─────────────────
tool_call = ai_response.tool_calls[0]
tool_result = calculate.invoke(tool_call["args"])
tool_message = ToolMessage(content=tool_result, tool_call_id=tool_call["id"])
print(f"\n步骤3 工具执行结果: {tool_message.content}")
# ── 步骤 4:把完整历史交给模型,生成自然语言最终回复 ──────────
messages_full = messages + [ai_response, tool_message]
final_response = llm_with_tools.invoke(messages_full)
print(f"\n步骤4 最终回复: {final_response.content}")
print(f"\n完整消息链类型: {[type(m).__name__ for m in messages_full]}")
7.7 LangChain的提示词
7.7.3 提示词模板
在LangChain中,可以使用 PromptTemplate类创建简单的提示词。提示词模板可以内嵌任意数量的模板参数,然后通过参数值格式化模板内容。下面是一个代码实例:
# langchain_string_prompt.py
from langchain_core.prompts import PromptTemplate
# 定义一个提示模板,包含adjective和content两个模板变量,模板变量使用{}包括起来
prompt_template = PromptTemplate.from_template(
"给我讲一个关于{content}的{adjective}故事。"
)
# 通过模板参数格式化提示模板
result = prompt_template.format(adjective="童话", content="一千零一夜")
print(result)
下面是一个关于聊天消息提示词模板的具体代码实例:
# langchain_chat_prompt.py
from langchain_core.prompts import ChatPromptTemplate
chat_template = ChatPromptTemplate.from_messages(
[
("system", "你是一位人工智能助手,你的名字是{name}。"),
("human", "你能做什么"),
("ai", "设计课程,谢谢!"),
("human", "{user_input}"),
]
)
messages = chat_template.format_messages(name="教学助手",
user_input="你的名字叫什么?")
print(messages)
MessagesPlaceholder提示词模板负责在特定位置添加消息列表。在上面的 ChatPromptTemplate中,我们看到了如何格式化两条消息,每条消息都是一个字符串。如果我们希望用户传入一个消息列表,就可以通过MessagesPlaceholder的方式将其插入到特定位置。下面是一个具体代码实例:
# langchain_messagesplaceholder_prompt.py
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.prompts import MessagesPlaceholder
from langchain_core.messages import HumanMessage
prompt_template = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant"),
#可以传入一组消息
MessagesPlaceholder("msgs")
])
result = prompt_template.invoke({"msgs": [HumanMessage(content="您好!"),
HumanMessage(content="langchain!")]})
print(result)
7.7.4 LCEL:把提示词串进链式调用
学完ChatPromptTemplate,可以自然引出 LCEL(LangChain Expression Language),它用管道符“|”把提示词、模型、输出解析器串成一条“流水线”,是LangChain推荐的现代写法。
这里对比两种不同的写法:
# 传统写法:手动分三步
messages = chat_prompt.format_messages(role="Python工程师", question="装饰器")
response = llm.invoke(messages)
result = response.content # 手动取 .content
# LCEL写法:用管道符“|”串成链,一步完成
from langchain_core.output_parsers import StrOutputParser
chain = chat_prompt | llm | StrOutputParser()
result = chain.invoke({"role": "Python工程师", "question": "装饰器"})
以下示例演示流式输出,在需要“打字机效果”的界面中常用:
# langchain_stream_demo.py
from dotenv import load_dotenv
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_ollama import ChatOllama
import os
load_dotenv()
llm = ChatOllama(
model=os.getenv("OLLAMA_MODEL", "gemma4:e4b"),
base_url=os.getenv("OLLAMA_BASE_URL", "http://localhost:11434"),
temperature=0.7
)
chain = ChatPromptTemplate.from_messages([
("system", "你是一个简洁的学习助手,回答不超过100字。"),
("human", "{question}")
]) | llm | StrOutputParser()
# 流式输出:逐块打印,不等待全部生成完成
print("助手:", end="", flush=True)
for chunk in chain.stream({"question": "什么是递归?用一句话解释"}):
print(chunk, end="", flush=True)
print()
7.8 LangChain的工具
7.8.3 自定义工具实操
以下示例演示如何封装一个支持四则运算的计算器工具,并通过invoke方法验证正常与异常两种情况。
# langchain_tool_calculator.py
from langchain_core.tools import tool
from typing import Union
@tool
def calculate(a: Union[int, float], b: Union[int, float], operator: str) -> str:
"""
执行基础数学计算,支持加减乘除。
参数:
a: 第一个数字
b: 第二个数字
operator: 运算符,可选值: "+", "-", "*", "/"
返回:
计算结果字符串,格式:"结果: {数值}"
示例:
calculate(10, 5, "+") → "结果: 15"
"""
try:
if operator == "+":
result = a + b
elif operator == "-":
result = a - b
elif operator == "*":
result = a * b
elif operator == "/":
if b == 0:
return "错误:除数不能为零"
result = a / b
else:
return f"错误:不支持的运算符 '{operator}'"
return f"结果: {result}"
except Exception as e:
return f"错误:{str(e)}"
print(calculate.invoke({"a": 10, "b": 5, "operator": "+"}))
print(calculate.invoke({"a": 10, "b": 0, "operator": "/"}))
以下示例演示如何封装日期加减运算工具,同时兼容YYYY-MM-DD和YYYY/MM/DD 两种常见日期格式。
# langchain_date_tool.py
from langchain_core.tools import tool
from datetime import datetime, timedelta
from typing import Union
@tool
def calculate_date(date_str: str, days: int) -> str:
"""
计算指定日期加减天数后的日期。
参数:
date_str: 原始日期,格式支持 YYYY-MM-DD 或 YYYY/MM/DD
days: 要加减的天数(正数加,负数减)
返回:
计算后的日期字符串,格式:"YYYY-MM-DD"
示例:
calculate_date("2026-01-01", 10) → "2026-01-11"
calculate_date("2026-01-01", -5) → "2025-12-27"
"""
formats = ["%Y-%m-%d", "%Y/%m/%d"]
for fmt in formats:
try:
original = datetime.strptime(date_str, fmt)
target = original + timedelta(days=days)
return target.strftime("%Y-%m-%d")
except ValueError:
continue
return f"错误:日期格式无效,请使用 YYYY-MM-DD 或 YYYY/MM/DD"
print(calculate_date.invoke({"date_str": "2026-04-18", "days": 7}))
print(calculate_date.invoke({"date_str": "2026/04/18", "days": -30}))
以下示例演示文件写入工具的实现,包括目录自动创建、追加/覆盖两种写入模式,以及权限异常的友好处理:
# langchain_file_tool.py
from langchain_core.tools import tool
from pathlib import Path
@tool
def write_to_file(file_path: str, content: str, mode: str = "append") -> str:
"""
将文本内容写入本地文件。
参数:
file_path: 文件路径(支持相对路径和绝对路径)
content: 要写入的文本内容
mode: 写入模式,"append"(追加)或 "overwrite"(覆盖)
返回:
操作结果字符串
示例:
write_to_file("test.txt", "Hello", "overwrite") → "成功写入test.txt"
"""
try:
path = Path(file_path)
path.parent.mkdir(parents=True, exist_ok=True) # 自动创建缺失的目录层级
write_mode = "a" if mode == "append" else "w"
with open(file_path, write_mode, encoding="utf-8") as f:
f.write(content + "\n")
return f"成功:{'追加写入' if mode == 'append' else '覆盖写入'}{file_path}"
except PermissionError:
return f"错误:权限不足,无法写入{file_path}"
except Exception as e:
return f"错误:{str(e)}"
print(write_to_file.invoke({
"file_path": "output/test.txt",
"content": "LangChain工具测试",
"mode": "overwrite"
}))
7.8.4 工具在智能体中的调用
这里给出一个包含工具调用的最小可运行智能体示例。虽然该智能体只有一个工具,但已经具备“理解问题、决定是否调用工具、返回最终答案”的基本能力。具体代码如下:
# langchain_agent_tool.py
from langchain.agents import create_agent
from langchain.tools import tool
from langchain_ollama import ChatOllama
SYSTEM_PROMPT = """
你是高校《AI编程和智能体开发》课程助教。
请准确回答学生问题;涉及分数计算时必须调用工具。
"""
@tool
def calculate_final_score(attendance: float, lab: float, exam: float) -> str:
"""按照总评 = 平时考勤*0.1 + 实验*0.4 + 期末考试*0.5 计算成绩。"""
total = attendance * 0.1 + lab * 0.4 + exam * 0.5
return f"根据课程规则,总评成绩为 {total:.2f} 分。"
def build_agent():
model = ChatOllama(
model="gemma4:e4b",
temperature=0,
base_url="http://127.0.0.1:11434",
)
return create_agent(
model=model,
tools=[calculate_final_score],
system_prompt=SYSTEM_PROMPT,
)
agent = build_agent()
response = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "我的考勤 95,实验 88,期末 90,请计算总评。"
}
]
}
)
print(response["messages"][-1].content)
7.8.5 MCP:标准化的工具与上下文接入协议
运行示例前,需要在当前Python环境中安装MCP SDK、LangChain MCP适配器以及本章前面使用过的LangChain和模型接入包,具体安装命令如下:
# 安装依赖示例
pip install mcp langchain-mcp-adapters
构建课程通知MCP服务器的代码如下:
# mcp_course_server.py
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("course-assistant")
COURSE_NOTICES = {
"实验报告": "实验报告需在本周五22:00前提交,文件名格式为:学号-姓名-实验4.docx。",
"课程答疑": "本周三19:30在线答疑,主题为LangChain工具调用与RAG案例。",
"期末复习": "期末复习资料将在第16周发布,重点包括RAG、工具调用和智能体状态管理。"
}
@mcp.tool()
def query_course_notice(topic: str) -> str:
"""
根据主题查询课程通知。
参数:
topic: 通知主题,例如“实验报告”“课程答疑”或“期末复习”
返回:
与主题相关的课程通知;若没有命中,则返回可查询主题列表。
"""
if topic in COURSE_NOTICES:
return COURSE_NOTICES[topic]
topics = "、".join(COURSE_NOTICES.keys())
return f"未找到主题“{topic}”。当前可查询主题包括:{topics}。"
if __name__ == "__main__":
mcp.run(transport="stdio")
有了MCP服务器以后,LangChain应用可以通过MCP适配器加载工具。下面的示例演示如何连接上面的课程通知服务器,并把MCP工具交给LangChain智能体使用。为了与本章前面的示例保持一致,这里仍以本地Ollama模型为例;如果读者使用其他大模型服务,只需要替换模型初始化部分。具体代码如下:
# langchain_use_mcp_tool.py
import asyncio
from langchain.agents import create_agent
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_ollama import ChatOllama
async def main():
client = MultiServerMCPClient({
"course": {
"command": "python",
"args": ["mcp_course_server.py"],
"transport": "stdio",
}
})
tools = await client.get_tools()
print("已加载工具:", [tool.name for tool in tools])
model = ChatOllama(model="gemma4:e4b")
agent = create_agent(model=model, tools=tools)
result = await agent.ainvoke({
"messages": [
{
"role": "user",
"content": "请查询实验报告的提交要求,并用一句话提醒学生。"
}
]
})
print(result["messages"][-1].content)
if __name__ == "__main__":
asyncio.run(main())
7.9 LangChain的状态和持久化
7.9.2 短期状态:让同一会话能够连续对话
下面的示例使用InMemorySaver在内存中保存状态。它适合演示和本地调试,但程序退出后状态会丢失。具体代码如下:
# langchain_short_term_state_demo.py
from dotenv import load_dotenv
from langchain.agents import create_agent
from langchain_ollama import ChatOllama
from langgraph.checkpoint.memory import InMemorySaver
import os
load_dotenv()
llm = ChatOllama(
model=os.getenv("OLLAMA_MODEL", "gemma4:e4b"),
base_url=os.getenv("OLLAMA_BASE_URL", "http://localhost:11434"),
temperature=0.2,
)
# InMemorySaver把状态保存在当前Python进程的内存中。
checkpointer = InMemorySaver()
agent = create_agent(
model=llm,
tools=[],
checkpointer=checkpointer,
system_prompt="你是一个课程学习助手,回答要简洁准确。",
)
config = {"configurable": {"thread_id": "student-001"}}
result1 = agent.invoke(
{"messages": [{"role": "user", "content": "我叫小林,正在学习LangChain。"}]},
config=config,
)
print(result1["messages"][-1].content)
result2 = agent.invoke(
{"messages": [{"role": "user", "content": "我正在学习什么?"}]},
config=config,
)
print(result2["messages"][-1].content)
7.9.3 持久化检查点:让程序重启后仍能延续状态
使用SQLite检查点前,需要安装额外依赖。可以在虚拟环境中执行如下命令:
pip install langgraph-checkpoint-sqlite
下面的示例把检查点写入本地SQLite数据库文件。第一次运行程序时,用户告诉助手自己的课程;再次运行程序时,只要使用相同的数据库文件和thread_id,助手就可以继续读取先前保存的会话状态。具体代码如下:
# langchain_persistent_state_demo.py
from dotenv import load_dotenv
from langchain.agents import create_agent
from langchain_ollama import ChatOllama
from langgraph.checkpoint.sqlite import SqliteSaver
import os
load_dotenv()
llm = ChatOllama(
model=os.getenv("OLLAMA_MODEL", "gemma4:e4b"),
base_url=os.getenv("OLLAMA_BASE_URL", "http://localhost:11434"),
temperature=0.2,
)
config = {"configurable": {"thread_id": "student-001"}}
with SqliteSaver.from_conn_string("langchain_state.db") as checkpointer:
agent = create_agent(
model=llm,
tools=[],
checkpointer=checkpointer,
system_prompt="你是一个课程学习助手,回答要简洁准确。",
)
result = agent.invoke(
{"messages": [{"role": "user", "content": "我这学期选了人工智能导论。"}]},
config=config,
)
print(result["messages"][-1].content)
result = agent.invoke(
{"messages": [{"role": "user", "content": "请提醒我刚才提到的课程名称。"}]},
config=config,
)
print(result["messages"][-1].content)
7.9.4 长期记忆:不要把所有业务信息都放进聊天历史
一种常见做法是:把稳定的用户信息保存在业务存储中,每次调用智能体前只取出与当前任务相关的少量信息,放入系统提示词或运行时上下文。下面用一个简化的字典模拟长期用户信息,说明长期记忆如何与短期会话状态配合使用:
# langchain_profile_context_demo.py
from dotenv import load_dotenv
from langchain.agents import create_agent
from langchain_ollama import ChatOllama
from langgraph.checkpoint.memory import InMemorySaver
import os
load_dotenv()
user_profiles = {
"student-001": {
"name": "小林",
"course": "人工智能导论",
"level": "大学本科二年级",
}
}
def build_system_prompt(user_id: str) -> str:
profile = user_profiles.get(user_id, {})
return (
"你是一个课程学习助手。"
f"当前学生姓名:{profile.get('name', '未知')};"
f"课程:{profile.get('course', '未知')};"
f"学习阶段:{profile.get('level', '未知')}。"
"回答时结合这些背景,但不要编造未提供的信息。"
)
llm = ChatOllama(
model=os.getenv("OLLAMA_MODEL", "gemma4:e4b"),
base_url=os.getenv("OLLAMA_BASE_URL", "http://localhost:11434"),
temperature=0.2,
)
user_id = "student-001"
agent = create_agent(
model=llm,
tools=[],
checkpointer=InMemorySaver(),
system_prompt=build_system_prompt(user_id),
)
result = agent.invoke(
{"messages": [{"role": "user", "content": "请给我一个适合当前课程的复习建议。"}]},
config={"configurable": {"thread_id": user_id}},
)
print(result["messages"][-1].content)
7.10 案例:课程资料问答助手
文档加载和文本切分的示例代码如下:
# langchain_text_load_split.py
from pathlib import Path
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
data_dir = Path("course_docs")
docs = []
for path in data_dir.glob("*.txt"):
loader = TextLoader(str(path), encoding="utf-8")
docs.extend(loader.load())
splitter = RecursiveCharacterTextSplitter(
chunk_size=350,
chunk_overlap=80
)
splits = splitter.split_documents(docs)
print(f"原始文档数:{len(docs)}")
print(f"切分后片段数:{len(splits)}")
7.10.4向量索引构建
在本案例中,使用FAISS作为本地向量索引工具。它不依赖额外数据库服务,便于在个人计算机上复现。需要指出的是,向量索引并不直接生成答案,它只负责找出最相关的知识片段。真正的答案仍由大模型在阅读这些片段后生成。索引构建过程如下所示:
# langchain_index_build.py
from langchain_community.vectorstores import FAISS
from langchain_ollama import OllamaEmbeddings
from pathlib import Path
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
data_dir = Path("course_docs")
docs = []
for path in data_dir.glob("*.txt"):
loader = TextLoader(str(path), encoding="utf-8")
docs.extend(loader.load())
splitter = RecursiveCharacterTextSplitter(
chunk_size=350,
chunk_overlap=80
)
splits = splitter.split_documents(docs)
print(f"原始文档数:{len(docs)}")
print(f"切分后片段数:{len(splits)}")
embeddings = OllamaEmbeddings(model="nomic-embed-text", base_url="http://127.0.0.1:11434")
vector_store = FAISS.from_documents(
documents=splits,
embedding=embeddings
)
retriever = vector_store.as_retriever(search_kwargs={"k": 5})
print(retriever)
7.10.5检索问答链实现
这一实现方式本质上属于2-Step RAG,即检索步骤先于生成步骤且流程固定。它的优点是结构清晰、执行稳定。若未来需要把系统升级为更灵活的知识智能体,还可以把检索器进一步封装为工具,由智能体决定何时检索。检索问答链的核心代码如下:
# langchain_find_answer_chain.py
from langchain_community.vectorstores import FAISS
from langchain_ollama import OllamaEmbeddings
from pathlib import Path
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
from langchain_core.runnables import RunnablePassthrough
from langchain_ollama import ChatOllama
data_dir = Path("course_docs")
docs = []
for path in data_dir.glob("*.txt"):
loader = TextLoader(str(path), encoding="utf-8")
docs.extend(loader.load())
splitter = RecursiveCharacterTextSplitter(
chunk_size=350,
chunk_overlap=80
)
splits = splitter.split_documents(docs)
print(f"原始文档数:{len(docs)}")
print(f"切分后片段数:{len(splits)}")
embeddings = OllamaEmbeddings(model="nomic-embed-text", base_url="http://127.0.0.1:11434")
vector_store = FAISS.from_documents(
documents=splits,
embedding=embeddings
)
retriever = vector_store.as_retriever(search_kwargs={"k": 5})
print(retriever)
def format_docs(documents):
return "\n\n".join(doc.page_content for doc in documents)
prompt = ChatPromptTemplate.from_template(
"""你是一名课程助教。请结合给定资料回答问题。
如果资料中没有足够信息,请明确说明“资料中未给出充分信息”。
资料内容:
{context}
用户问题:
{question}
"""
)
model = ChatOllama(model="gemma4:e4b", temperature=0, base_url="http://127.0.0.1:11434")
parser = StrOutputParser()
rag_chain = (
{
"context": retriever | format_docs,
"question": RunnablePassthrough()
}
| prompt
| model
| parser
)
answer = rag_chain.invoke("MapReduce 在课程资料中被描述为什么?")
print(answer)
