003、LangChain核心概念初探:Models, Prompts, Output Parsers

张开发
2026/4/7 16:47:11 15 分钟阅读

分享文章

003、LangChain核心概念初探:Models, Prompts, Output Parsers
003、LangChain核心概念初探Models, Prompts, Output Parsers从一次深夜调试说起上周三凌晨两点我在给客户部署一个智能客服原型。代码逻辑看起来没问题但AI返回的答案格式总是飘忽不定——有时候是JSON有时候是一段自由文本还有一次直接给我返回了Markdown表格。下游的业务系统直接崩溃日志里全是解析异常。那一刻我突然意识到直接调用大模型API就像直接操作寄存器写业务逻辑看似灵活实则步步惊心。这就是为什么我们需要LangChain。它提供的不是魔法而是一套工程化的约束和模式。今天我们就拆解其中最基础的三个概念Models、Prompts、Output Parsers。它们分别对应着AI应用的计算单元、输入规范、输出契约。Models不只是换API密钥那么简单很多人以为Models就是简单封装一下OpenAI的调用比如这样# 新手常见写法问题很多fromopenaiimportOpenAI clientOpenAI(api_keyxxx)responseclient.chat.completions.create(modelgpt-3.5-turbo,messages[{role:user,content:你好}])print(response.choices[0].message.content)这种写法在原型阶段没问题但到了生产环境你会发现缺了重试机制、缺了降级策略、缺了成本隔离。LangChain的Models抽象是这样做的fromlangchain_openaiimportChatOpenAIfromlangchain_community.llmsimportTongyi# 阿里云通义千问# 核心是统一的LLM接口llm_gptChatOpenAI(modelgpt-4,temperature0)llm_qwenTongyi(modelqwen-max)# 生产环境一定要配这些llm_prodChatOpenAI(modelgpt-3.5-turbo,temperature0.7,max_retries3,# 网络抖动时重试request_timeout30,# 避免死等max_tokens1000,# 成本控制streamingTrue# 需要流式输出时开)# 关键优势无缝切换模型models{openai:llm_gpt,aliyun:llm_qwen,backup:llm_prod}current_modelmodels.get(os.getenv(LLM_PROVIDER,openai))经验之谈永远不要在生产环境硬编码某个厂商的模型。用LangChain的抽象层哪天GPT-6出来或者公司要求降本切到国产模型你只需要改配置不需要重构业务代码。Prompts把提示词当成代码来管理早期我习惯把提示词直接写在代码字符串里# 千万别这样写维护起来会疯掉promptf 你是一个资深{domain}专家请用{style}风格回答以下问题 问题{question}要求不超过{max_words}字 三个月后产品经理要加需求、改语气、调格式这些散落在各个.py文件里的字符串会让你改到怀疑人生。LangChain的解决方案是模板化fromlangchain.promptsimportChatPromptTemplate,HumanMessagePromptTemplate# 1. 定义模板像写HTML模板一样system_template你是{domain}领域的资深专家回答时使用{style}风格。human_template问题{question}\n要求不超过{max_words}字# 2. 组装成Prompt对象prompt_templateChatPromptTemplate.from_messages([(system,system_template),(human,human_template)])# 3. 动态渲染这才是工程化的样子formatted_promptprompt_template.format_messages(domain云计算,style幽默风趣,question解释什么是容器化,max_words200)# 4. 调用模型responsellm.invoke(formatted_prompt)更高级的玩法是用FewShotPromptTemplate做小样本学习或者用PromptSelector根据模型类型自动选择最优提示词模板。踩过的坑曾经把用户输入直接拼接进提示词结果用户输入里包含}把模板语法破坏了。后来一律用format_messages做安全渲染用户输入自动转义。Output Parsers给AI的输出戴上镣铐开头的调试问题就出在这里。AI的自由发挥是创造力的源泉却是工程系统的噩梦。Output Parsers的核心思想是定义输出结构让AI往里面填数据。fromlangchain.output_parsersimportPydanticOutputParserfrompydanticimportBaseModel,FieldfromtypingimportList# 第一步定义你期望的数据结构classAnswerSchema(BaseModel):这是给AI看的schema描述字段注释要详细summary:strField(description问题的核心总结不超过100字)keywords:List[str]Field(description提取3-5个关键词)confidence:floatField(description答案置信度0-1之间)follow_up_questions:List[str]Field(description建议的后续问题)# 第二步创建解析器parserPydanticOutputParser(pydantic_objectAnswerSchema)# 第三步把格式要求告诉AIpromptChatPromptTemplate.from_messages([(system,你是一个问答助手。\n{format_instructions}),# 关键在这里(human,{question})])# 第四步完整调用链chainprompt|llm|parser# LangChain新语法管道操作resultchain.invoke({question:解释微服务架构的优缺点,format_instructions:parser.get_format_instructions()# 自动生成格式说明})# 现在result是强类型的AnswerSchema对象print(f置信度{result.confidence:.2%})print(f关键词{, .join(result.keywords)})那个凌晨让我崩溃的问题用Output Parsers之后下游系统收到的永远是规整的JSON字段一个不多一个不少。个人习惯我会在schema的Field description里用自然语言详细描述约束比如“用中文回答”“日期格式YYYY-MM-DD”“如果是未知值返回null”。这比写复杂的后处理代码管用得多。组合起来一个可维护的生产级示例importosfromtypingimportOptionalfromlangchain_core.runnablesimportRunnablePassthroughclassQAChain:问答链封装这是我们项目里的真实代码简化版def__init__(self,model_provider:stropenai):# 模型配置集中管理self.llmself._init_llm(model_provider)# 提示词模板独立维护self.promptself._load_prompt_template()# 输出解析器确保接口稳定self.parserself._init_parser()# 组装链prompt - llm - parserself.chain({question:RunnablePassthrough()}|self.prompt|self.llm|self.parser)defquery(self,question:str)-dict:对外暴露的唯一方法try:returnself.chain.invoke(question)exceptExceptionase:# 这里可以加降级逻辑比如切到备用模型returnself._fallback_response(question,e)# 以下私有方法省略...给工程师的几点实在建议早用Output Parsers项目第一天就要定义好输出schema后期加字段比从自由文本里挖数据简单100倍。提示词版本化把prompt template存成.yaml或.json文件和代码一起git管理。我们团队现在有prompts/v1/、prompts/v2/目录每次改提示词都走PR流程。模型隔离层在自己的代码和LangChain之间再包一层我们叫LLMGateway。这样哪天LangChain API变了或者你想换其他框架影响范围可控。温度参数别写死创意场景用0.7逻辑推理用0.2让产品经理通过配置调而不是让你改代码重启服务。流式输出尽早考虑哪怕现在不需要先把streamingTrue加上。等产品说要“打字机效果”时你会感谢自己。最后说句大实话LangChain的这些概念本质上是在解决软件工程的老问题——关注点分离、接口契约、依赖注入。只不过这次我们面对的是一个非确定性的“计算单元”。把它当成一个有点脾气的协作者用工程化的方法约束和协作而不是当成魔法黑盒。本篇代码均为示意片段直接复制可能需调整。实际项目建议用配置中心管理API密钥千万别把key硬编码在代码里。

更多文章