Week 1 -- Day 5:周总结与综合练习

Day 5:周总结与综合练习

本周知识回顾

第一周的学习是一场从零到一的旅程,我们用四天时间走完了 LangChain 从宏观生态到微观组件的完整认知闭环。第一天为你展开了一幅全景地图:LangChain 不是一个单体框架,而是一个由 langchain-core、langchain-community、langchain 主包和 langgraph 四层结构精密咬合的生态体系。基础抽象层的 Runnable 接口和 LCEL 定义了整个框架的"语法",第三方集成层提供了数以百计的外部连接器,业务组件层封装了 Chains、Agents 与 Retrieval 等成熟模式,而最上层的 langgraph 则以图结构驱动有状态的多步骤工作流。2026 年 1.2.14 版本的发布标志着这个生态进入了一个新的成熟阶段,LangGraph 的深度整合、MCP 协议带来的跨框架工具互操作能力,以及 Deep Agents SDK 和 create_agent() 标准入口的引入,都在降低复杂 AI 应用的构建门槛。围绕核心框架,LangSmith 负责可观测性与全生命周期管理,LangSmith Fleet 让非技术人员也能可视化构建 Agent,Deep Agents 则面向长周期、多子 Agent 协作的高级场景,三者与核心框架共同构成了一条从开发到部署再到持续优化的完整链路。

第二天将这张蓝图落地为一行行可运行的代码。你学会了用 pip 安装分层设计的多个子包,理解了为什么 langchain-core、langchain-community、langchain 和 langgraph 要独立发布:因为它们的职责边界截然不同,独立迭代既保证了稳定性也提升了灵活性。通过 .env 文件和 python-dotenv 管理 API 密钥的实践不仅是一个安全习惯,更是一个工程素养的体现:任何可能被提交到版本控制系统的密钥,本质上都已经泄露了。当你亲手用 ChatPromptTemplateChatOpenAIStrOutputParser 通过管道运算符串联出第一个 LCEL 链时,你实际上已经触碰到了 LangChain 设计哲学中最核心的理念:声明式组合。链的定义与链的执行是分离的,你只需要描述"先做什么、再做什么",框架负责一切调度细节。

第三天对这个理念进行了深度的解剖。Runnable 接口是 LangChain 的"通用语言",无论是 Prompt 模板、LLM 封装还是输出解析器,它们都实现了同一套接口,因此在 LCEL 的管道中它们是等价的积木。invokestreambatch 三种调用方式覆盖了从单次同步请求到实时流式输出再到批量并发处理的全部场景,而链的定义本身无需做任何修改。管道运算符 | 的秘密在于 Python 魔术方法的重载,它把一个 Runnable 序列封装为 RunnableSequence 对象,整个过程是惰性的:链的声明阶段不产生任何网络请求,直到你调用 invoke 的那一刻,数据才开始在管道中流动。RunnableParallel 让你把同一份输入同时分发到多条独立路径并发执行,RunnableBranch 则引入了基于谓词函数的动态路由,让链具备了条件判断的能力。这两者配合使用,已经足以构建出相当复杂的 AI 工作流。

第四天将注意力转向了 LLM 应用的"两端":输入端的提示工程和输出端的结构化解析。ChatPromptTemplate.from_messages 提供了一种将对话结构从动态内容中分离出来的方法,system、user、assistant 三种消息角色的组合加上变量占位符,构成了可复用的 prompt 骨架。MessagesPlaceholder 是这个体系中容易被低估但至关重要的概念,它接受的不是单个字符串,而是一个消息对象的列表,这让多轮对话历史的注入变得非常简单:你不需要手动拼接文本,不需要关心历史到底有几条消息,只需要把 HumanMessageAIMessage 组成的列表传进去,LangChain 会自动将它们展开到 prompt 的正确位置。Few-Shot 提示则是一种用示例替代冗长指令的实用技巧,三到五个精心挑选的输入输出对往往比一大段格式说明更能让模型理解你的期望。在输出端,JsonOutputParserPydanticOutputParser 分别提供了字典级别和类型安全级别的结构化解析能力,而 OutputParserException 的存在提醒我们一个重要的工程现实:大语言模型并不总是听话的,健壮的应用必须为解析失败设计降级策略。

综合项目:多语言翻译助手

现在让我们把本周学到的所有知识点整合到一个完整的项目中。这个多语言翻译助手的核心功能是中英文互译,但它的实现方式融合了 LCEL 管道、自定义 Prompt 模板、结构化输出解析以及 Pydantic 类型校验,每一项都是你在本周接触过并亲手实践过的技术。

我们从定义数据结构开始。Pydantic 的 BaseModel 用来描述翻译结果应该包含哪些字段:源语言、目标语言、原文、译文,以及一个用于衡量翻译质量的置信度分数。这里的每个字段都通过 Field 装饰器附带了中文描述,这些描述会通过 get_format_instructions() 自动生成为一段格式说明文字,告诉大语言模型它需要输出什么样的 JSON 结构。

然后是 Prompt 的设计。与 Day 2 中那个简单的单行模板不同,翻译助手需要一个精心编写的 system prompt 来约束模型的行为。system 消息中我们告诉模型它是一名专业翻译,需要严格控制输出格式,并在翻译前先判断源语言是什么。结合 Few-Shot 的思想,我们也可以在 prompt 中注入翻译示例,让模型通过具体的输入输出对来理解期望的翻译质量和风格。format_instructions 通过 partial 方法预先绑定到模板中,这样调用时只需传入 source_texttarget_lang 两个变量。

链的组装沿用了 LCEL 的标准三段式结构:prompt 格式化输入,llm 执行翻译推理,parser 将模型的 JSON 输出解析为 TranslationResult 实例。这里选择 PydanticOutputParser 而非 JsonOutputParser,是因为我们希望在解析阶段就获得类型安全保障,如果模型输出的 confidence 字段是个字符串 "0.95",Pydantic 会自动将其转换为浮点数 0.95,如果某个必需字段缺失,则会立即抛出清晰的校验错误。同时,我们还在链的调用外层包裹了 try-except 来捕获 OutputParserException,当解析失败时提供一个包含原始输出和错误信息的友好提示,而不是让程序直接崩溃。

以下是完整的实现代码:

from dotenv import load_dotenv
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.exceptions import OutputParserException
from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field

load_dotenv()

# 1. 定义翻译结果的结构化模型
class TranslationResult(BaseModel):
    source_lang: str = Field(description="源语言,如'中文'或'英文'")
    target_lang: str = Field(description="目标语言,如'中文'或'英文'")
    original: str = Field(description="用户输入的原文,原样保留")
    translated: str = Field(description="翻译后的文本")
    confidence: float = Field(description="翻译置信度,0.0~1.0之间的浮点数")

# 2. 准备 Pydantic 解析器
parser = PydanticOutputParser(pydantic_object=TranslationResult)

# 3. 构建 Prompt 模板
prompt = ChatPromptTemplate.from_messages([
    ("system", """你是一名资深的多语言翻译专家,精通中文和英文。

你的任务是:
1. 判断用户输入文本的源语言(中文或英文)
2. 将文本翻译为目标语言
3. 对翻译质量给出 0.0~1.0 之间的置信度评分

翻译要求:信、达、雅——准确传达原意,语句自然流畅,符合目标语言的表达习惯。

请严格遵循以下输出格式,不要附加任何额外的文字说明。

{format_instructions}"""),
    ("user", "请将以下文本翻译成{target_lang}:\n\n{source_text}")
])

# 4. 将格式说明绑定到模板中
prompt = prompt.partial(format_instructions=parser.get_format_instructions())

# 5. 初始化 LLM
llm = ChatOpenAI(
    model="Qwen/Qwen3.6-35B-A3B",
    temperature=0,
    base_url="https://api.siliconflow.cn/v1"
)

# 6. 用 LCEL 管道组装链
chain = prompt | llm | parser

# 7. 封装一个健壮的翻译函数
def translate(source_text: str, target_lang: str = "英文") -> TranslationResult | dict:
    """执行翻译,返回 TranslationResult 或包含错误信息的字典。"""
    try:
        result: TranslationResult = chain.invoke({
            "source_text": source_text,
            "target_lang": target_lang
        })
        return result
    except OutputParserException as e:
        return {"error": "模型输出格式异常,无法解析为 TranslationResult", "detail": str(e)}
    except Exception as e:
        return {"error": "翻译失败", "detail": str(e)}

# 8. 测试:中文 → 英文
print("=" * 50)
print("【中文 → 英文】")
result1 = translate("人工智能正在深刻改变我们与世界互动的方式。", "英文")
if isinstance(result1, TranslationResult):
    print(f"源语言: {result1.source_lang}")
    print(f"目标语言: {result1.target_lang}")
    print(f"原文: {result1.original}")
    print(f"译文: {result1.translated}")
    print(f"置信度: {result1.confidence}")
else:
    print(f"翻译出错: {result1}")

# 9. 测试:英文 → 中文
print("=" * 50)
print("【英文 → 中文】")
result2 = translate(
    "The greatest glory in living lies not in never falling, but in rising every time we fall.",
    "中文"
)
if isinstance(result2, TranslationResult):
    print(f"源语言: {result2.source_lang}")
    print(f"目标语言: {result2.target_lang}")
    print(f"原文: {result2.original}")
    print(f"译文: {result2.translated}")
    print(f"置信度: {result2.confidence}")
else:
    print(f"翻译出错: {result2}")

print("=" * 50)

运行这段代码,你会看到两段翻译结果以结构化的方式输出,每一个 TranslationResult 实例中的字段都是类型安全的,你可以在 IDE 中通过点号访问 result1.confidence 并获得完整的自动补全提示。从架构视角来看,这个项目完整复现了第二天的 LCEL 管道模式,运用了第三天的 invoke 调用方式(你也可以轻松地将它改为 stream 或 batch),使用了第四天的 PydanticOutputParser 做结构化输出和类型校验,并且将 MessagesPlaceholder 和 Few-Shot 的思想融入了 prompt 的设计之中。它虽然只有不到一百行代码,但已经是一个具备工程价值的、可扩展的 LLM 应用原型。

进一步扩展的思路

这个翻译助手远非终点,而是一个可供持续迭代的起点。你可以考虑引入 RunnableBranch 来实现智能语言检测,先让链判断用户输入的语言,再自动决定翻译方向,用户无需手动指定 target_lang。你也可以用 RunnableParallel 来同时发起中译英和英译中的双向翻译,让用户一次性看到两种语言的对照结果。如果你希望保留翻译历史,MessagesPlaceholder 可以在 prompt 中插入之前的翻译记录,让模型在翻译新句子时参考之前的风格和用词习惯。更进一步,你还可以用 LangSmith 来追踪每条翻译链的执行轨迹,记录耗时和 token 消耗,为后续的 prompt 调优积累数据。这些扩展方向都不是新的知识点,它们只是把本周已经掌握的积木以不同的方式重新组合,而这正是 LCEL 设计哲学的终极体现:学会少数几个核心抽象,就能应对无限多的业务场景。

练习任务

今天的练习围绕知识的内化与产出展开。首先,你需要完成上述综合项目,确保代码在你的环境中可以正常运行,并尝试至少三组不同难度和领域的文本进行翻译测试,比如技术文档、文学段落和日常对话,观察模型在不同语境下的翻译质量和置信度变化。其次,撰写第 1 周的学习笔记,不少于 500 字。笔记不应是对本教程的简单摘抄,而应该是你对 LangChain 核心概念的个人化理解和重新表述。试着用自己的语言描述 Runnable 接口的意义、管道运算符的惰性本质、MessagesPlaceholder 与普通占位符的本质区别,以及结构化输出解析对工程实践的重要性。第三,浏览第 2 周的大纲:RAG(检索增强生成)内容预览,思考第一周学到的 LCEL 管道模式和 prompt 模板技巧将如何应用于知识库问答场景。RAG 本质上就是在 prompt | llm | parser 的管道前端插入一个检索步骤,而 LCEL 的可组合性让这种插入变得极其简单,你只需要把 retriever 作为第一个组件接入管道即可。

考核点

今天的验收围绕四个维度展开。项目验收方面,你需要提交完整可运行的翻译助手项目代码,该代码应支持中英双向互译,输出结构化的 TranslationResult 而非自由文本,并至少包含对解析异常的基本处理。学习笔记方面,提交不少于 500 字的第 1 周总结笔记,内容应涵盖对 LangChain 生态架构、LCEL 核心概念、提示工程技巧以及输出解析的个人理解,鼓励结合自己的学习心得和遇到的问题进行反思。知识回顾方面,你应当能够口头回答以下六个问题:LangChain 的四大核心层级各是什么、各由哪个包实现?管道运算符 | 为什么是惰性的,这种设计有什么工程收益?RunnableParallelRunnableBranch 分别解决什么场景的问题?MessagesPlaceholder 与普通的花括号占位符有什么本质区别?Few-Shot 提示的核心思想是什么,它与直接写一段文字描述相比有什么优势?PydanticOutputParser 相比 JsonOutputParser 的优势是什么,为什么工程中推荐前者?综合能力方面,你应当能够演示如何将 Day 1 至 Day 4 的知识点整合到翻译助手项目中,并清楚地说明每个技术点在项目中的具体体现位置,比如 PydanticOutputParser 在哪里用到了、prompt 模板的结构是如何设计的、管道链的组装顺序为什么是这个样子。只有你能解释清楚为什么这样写,而不是仅仅知道怎么写,才算真正完成了第一周的学习目标。

posted @ 2026-07-04 23:48  喵叔哟  阅读(2)  评论(0)    收藏  举报