news 2026/4/23 20:10:07

LangChainV1.0[06]-Prompt/上下文/结构化输出

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LangChainV1.0[06]-Prompt/上下文/结构化输出

本节完成官方案例: Quickstart->Build a reeal-world agent , 地址:https://docs.langchain.com/oss/python/langchain/quickstart#build-a-real-world-agent, 同时完善相关概念

1.案例中相关概念梳理

这个案例总共提到了6个步骤,其中涉及到了一些概念,这里先对这些概念做一些简短的解释。

1.1 提示词:Prompt

在 LangChain 中,Prompt(提示词)的演进经历了从“简单的字符串”到“结构化消息序列”的过程。

LangChain 将输入抽象为 Messages。每条消息都包含两个核心要素:角色 (Role) 和 内容 (Content)。

消息类型 (Class)对应角色 (Role)**核心用途 ******
SystemMessageSystem设置 AI 的“人格”、背景规则或任务约束。
HumanMessageUser用户输入的指令或问题。
AIMessageAssistant模型生成的响应

如果我们正在构建一个法律咨询机器人,希望它始终以“资深律师”的语气回答,并且不允许讨论食谱。那么像这种 “身份设定”和“禁令”应该放在**SystemMessage **消息中 ,它正是存放这类“全局规则”和“身份设定”的地方。 它就像是给 AI 植入的“出厂设置”,确保它在后续对话中始终维持律师的专业性,并遵守禁令。

那么我们应该怎样来组织这些消息?

在 LangChain 1.0 中,我们通常会将这些消息组合成一个列表 (List)。一个典型的法律对话模板看起来是这样的:

  1. SystemMessage: “你是一位资深律师,只回答法律相关问题,严禁讨论食谱。”
  2. HumanMessage: “你好,我想咨询关于合同纠纷的问题…”
  3. AIMessage: “您好,我是您的法律助手。关于合同纠纷,请问具体涉及到哪方面的条款?”

通过这种结构化的消息序列,模型能更清晰地分辨哪些是指令,哪些是对话历史

1.2 外部工具(@tool)

在 LangChain 中,LLM 本身无法知道模型本身以外的知识,它只能“预测文本”。Tools就是给 AI 安装的“插件”或“手臂”。

@tool装饰器**:这是最简单的创建方式。当你给一个 Python 函数加上这个装饰器时,LangChain 会自动解析函数的名称参数类型和**文档字符串(Docstring)。

AI 并不是直接运行代码,而是通过阅读你的Docstring来判断“我什么时候该用这个工具”。所以,在写@tool时,文档字符串就是写给 AI 看的“使用说明书”。

1.3 运行时上下文 (Runtime Context)

你可以把它理解为职员工作时的“工牌信息”或“环境配置”。 当你在处理多用户请求时,通过 Runtime Context,你可以让 Agent 知道当前是在为哪个用户服务,而不需要把这些琐碎的元数据写进 Prompt 里。

1.4 Agent 记忆 (Short-term Memory)

在前面的章节中,已经了解过记忆管理了。 普通的对话模型只记得“你说过什么”,但 Agent 的记忆更复杂:

  • 它需要记住对话历史(Human 和 AI 的对话)。
  • 它还需要记住思考过程(Intermediate Steps):比如它刚才调用了搜索工具,搜到了什么结果。

1.5 结构化输出 (@dataclass)

当我们要求 LLM “以 JSON 格式输出并包含姓名和年龄”时,LangChain 会利用这些定义好的数据类,把 LLM 吐出来的字符串自动转换成 Python 对象。@dataclass提供了明确的字段名和类型提示。

2.案例代码实现

这里逐步实现案例的每个步骤,帮助理解它是如何运行起来的。会对原案例进行适当的改写。

2.1 定义系统提示词

这里将原案例中的提示词改写为中文:

if__name__=='__main__':SYSTEM_PROMPT="""你是一位专业的天气预报员,说话时还喜欢用双关语。 你可以使用下面两个工具: - get_weather_for_location: 使用它来获取指定地点的天气情况 - get_user_location: 使用它来获取用户的地理位置 如果用户向您询问天气情况,请务必先确认其所在位置。如果从问题中可以推断出他们指的是其所在的具体地点,那么请使用“get_user_location”工具来查找他们的位置。 """

2.2 创建工具(@tool)

系统提示词中提到了两个工具:get_weather_for_locationget_user_location.这两个函数上使用了Python 装饰器@tool.

2.2.1 运行时上下文参数

在这些工具具体的实现里面,如果希望获取到一些有用的信息,比如:用户ID(用户业务系统中的ID,或者其他信息)该如何获取?这就要用到运行时上下文(Runtime Context) ,这个运行时上下文中具体包含什么样的数据类型,我们需要定义一个具体的类型,如下代码:

@dataclassclassContext:"""Custom runtime context schema."""user_id:str

@dataclass并不是 LangChain 定义的,它是Python 标准库(从 3.7 版本开始引入)自带的一个装饰器 , 它的核心作用是自动为你生成样板代码

通常在 Python 中,如果你想创建一个简单的类来存储数据,你需要手动写很多方法。对比一下:

不使用**@dataclass**

classContext:def__init__(self,user_id:str):self.user_id=user_iddef__repr__(self):returnf"Context(user_id='{self.user_id}')"

使用@dataclass

fromdataclassesimportdataclass@dataclassclassContext:user_id:str

当你加上这个装饰器时,Python 会自动帮你生成__init__(构造函数)、__repr__(用于打印对象的字符串表示)以及__eq__(用于比较两个对象是否相等)等方法。

上面例子中:Context被用来定义Runtime Context的结构。使用@dataclass有几个好处:

  • 结构清晰:一眼就能看出这个上下文里包含哪些字段(如user_id)及其类型。
  • 类型安全:LangChain 的工具(Tools)可以通过类型检查知道runtime.context到底是什么数据结构。
  • 易于解析:由于它是结构化的,LangChain 内部可以方便地将配置参数(如字典)映射到这个对象上。

2.2.2 定义工具

有了运行时上下文参数,我们在定义工具的时候就可以使用它了。

fromlangchain.toolsimporttool,ToolRuntime@tooldefget_weather_for_location(city:str)->str:"""获取给定城市(city)的天气"""returnf"{city}的天气是晴朗的, 温度是25摄氏度!"@tooldefget_user_location(runtime:ToolRuntime[Context])->str:"""获取用户所在城市."""user_id=runtime.context.user_idreturn"北京"ifuser_id=="1"else"上海"

get_weather_for_location中,虽然没有在注释中写city: 北京,但 LLM 是通过函数签名Docstring的组合来理解:

  • 函数名get_weather_for_location暗示了需要一个位置。
  • 参数名city是一个非常有语义的单词。
  • Docstring"获取给定城市(city)的天气."明确告诉了 LLM 这个函数的作用。

当 LangChain 将这个函数转换成 JSON Schema 发送给 LLM 时,它会告诉模型:“我有一个工具,它需要一个名为city的字符串。” LLM 会根据用户的提问(比如“上海天气如何?”)自动提取出“上海”并填入这个city参数。

第二个工具,get_user_location``ToolRuntime[Context]是如何传递进来的?

这个参数并不是由 LLM 提供的,而是由LangChain 框架在运行时“注入” (Injection)的。当 LangChain 把工具描述发给 LLM 时,它会识别出ToolRuntime这个类型,并自动从发给 LLM 的 Schema 中剔除它。所以 LLM 根本不知道有这个参数,也就不会尝试去填它。

当调用agent.invoke(..., config={"configurable": {"user_id": "1"}})时,框架会将这些配置包装进Context对象,并在执行函数前,自动把当前的运行环境填充到runtime参数里。

LangChain 在执行工具前会进行类型检查 (Type Inspection)。如果发现参数类型是ToolRuntime或其相关子类,它就会去当前的RunnableConfig中寻找匹配的数据填充进去,而剩下的参数则去匹配 LLM 返回的 JSON。

2.3 配置模型

fromlangchain.chat_modelsimportinit_chat_model model=init_chat_model(model="ollama:deepseek-v3.1:671b-cloud",base_url="http://127.0.0.1:11434")

2.4 定义响应结构化数据

我们希望agent响应数据的时候,自动将模型响应的数据构建成我们自己定义的数据结构,现在只需要自定义一个类接口,需要注意的是编写注释中语义要明确

@dataclassclassResponseFormat:"""agent响应数据的 schema."""# 有趣,调皮风格的回应 (必须)punny_response:str# 常规的回应weather_conditions:str|None=None

2.5 添加记忆管理

这里使用内存记忆,这个在上一章已经介绍过了。

fromlanggraph.checkpoint.memoryimportInMemorySaver checkpointer=InMemorySaver()

2.6 完整可运行代码

fromdataclassesimportdataclassfromlangchain.agentsimportcreate_agentfromlangchain.chat_modelsimportinit_chat_modelfromlangchain.toolsimporttool,ToolRuntimefromlanggraph.checkpoint.memoryimportInMemorySaver@dataclassclassContext:""" 自定义运行时上下文参数数据结构 """user_id:str@tooldefget_weather_for_location(city:str)->str:''' 获取给定城市(city)的天气 :param city: 城市 :return: '''returnf"{city}的天气是晴朗的, 温度是25摄氏度!"@tooldefget_user_location(runtime:ToolRuntime[Context])->str:"""获取用户所在城市."""user_id=runtime.context.user_idreturn"北京"ifuser_id=="1"else"上海"@dataclassclassResponseFormat:"""Response schema for the agent."""# A punny response (always required)punny_response:str# Any interesting information about the weather if availableweather_conditions:str|None=Noneif__name__=='__main__':# 1. 定义系统提示词SYSTEM_PROMPT=""" 你是一位专业的天气预报员,说话时还喜欢用双关语。 你可以使用下面两个工具: - get_weather_for_location: 使用它来获取指定地点的天气情况 - get_user_location: 使用它来获取用户的地理位置 如果用户向您询问天气情况,请务必先确认其所在位置。如果从问题中可以推断出他们指的是其所在的具体地点,那么请使用“get_user_location”工具来查找他们的位置。 """# 2. 创建工具:上面的 get_weather_for_location 和 get_user_location 函数# 3. 配置模型model=init_chat_model(model="ollama:qwen3-next:80b-cloud",base_url="http://127.0.0.1:11434")# 4.定义响应结构化数据 上面的 ResponseFormat# 5. 记忆管理checkpointer=InMemorySaver()# 6. 创建 Agentagent=create_agent(model=model,# 模型system_prompt=SYSTEM_PROMPT,# SystemMessagetools=[get_user_location,get_weather_for_location],# 工具context_schema=Context,# 运行时上下文参数数据结构Context是自定义的类response_format=ResponseFormat,# 响应结构化数据checkpointer=checkpointer# 记忆管理)# 7. 会话,运行时上下文参数config={"configurable":{"thread_id":"123456"}}runtime_context=Context(user_id="1000")# 8. HumanMessage,第一次提问prompt_first=[{"role":"user","content":"今天天气如何?"}]# 9. 运行response=agent.invoke({"messages":prompt_first},config=config,context=runtime_context)# 10. 输出结构化响应# print(response)formessageinresponse["messages"]:message.pretty_print()print("*"*80)# 11. HumanMessage,第二次提问prompt_second=[{"role":"user","content":"谢谢!"}]response=agent.invoke({"messages":prompt_second},config=config,context=runtime_context)# 12. 输出结构化响应# print(response)formessageinresponse["messages"]:message.pretty_print()

输出:

=============== Human Message ============== 今天天气如何? =============== Ai Message ============== Tool Calls: get_user_location (0b540a6d-8c75-4bf9-8ea5-cfba99c81e6b) Call ID: 0b540a6d-8c75-4bf9-8ea5-cfba99c81e6b Args: =============== Tool Message ============== Name: get_user_location 上海 =============== Ai Message ============== Tool Calls: get_weather_for_location (5897eba2-5ec1-4e7d-ba82-6b2603ebec17) Call ID: 5897eba2-5ec1-4e7d-ba82-6b2603ebec17 Args: city: 上海 =============== Tool Message ============== Name: get_weather_for_location 上海 的天气是晴朗的, 温度是25摄氏度! =============== Ai Message ============== { "punny_response": "上海今天晴空万里,25度,真是‘晴’人好天气,记得‘晒’出好心情!", "weather_conditions": { "condition": "晴朗", "temperature": "25摄氏度" } } ******************************************************************************** =============== Human Message ============== 今天天气如何? =============== Ai Message ============== Tool Calls: get_user_location (0b540a6d-8c75-4bf9-8ea5-cfba99c81e6b) Call ID: 0b540a6d-8c75-4bf9-8ea5-cfba99c81e6b Args: =============== Tool Message ============== Name: get_user_location 上海 =============== Ai Message ============== Tool Calls: get_weather_for_location (5897eba2-5ec1-4e7d-ba82-6b2603ebec17) Call ID: 5897eba2-5ec1-4e7d-ba82-6b2603ebec17 Args: city: 上海 =============== Tool Message ============== Name: get_weather_for_location 上海 的天气是晴朗的, 温度是25摄氏度! =============== Ai Message ============== { "punny_response": "上海今天晴空万里,25度,真是‘晴’人好天气,记得‘晒’出好心情!", "weather_conditions": { "condition": "晴朗", "temperature": "25摄氏度" } } =============== Human Message ================================= 谢谢! =============== Ai Message ================================== 不客气!上海今天晴空万里,25度,祝你“晴”心如意,“暖”意融融!☀️

可以看到结果输出的时候,被格式化成了JSON格式。注意如果发现结果没有被正确的结构化输出,有可能是模型并不支持结构化输出。比如我将上面的模型更换成deepseek-v3.1:671b-cloud就发现它无法进行结构化输出。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 11:19:17

为什么90%的团队都低估了Falco规则的重要性?揭开容器监控失效真相

第一章:为什么90%的团队都低估了Falco规则的重要性?揭开容器监控失效真相在容器化环境快速扩张的今天,安全监控却常常停留在表面。Falco作为开源的运行时安全工具,能够实时检测异常行为,但多数团队仅依赖其默认规则集&…

作者头像 李华
网站建设 2026/4/23 12:34:03

StripFran 对比三角形带与数据抽取、采样模型的不同

一:主要的知识点 1、说明 本文只是教程内容的一小段,因博客字数限制,故进行拆分。主教程链接:vtk教程——逐行解析官网所有Python示例-CSDN博客 2、知识点纪要 本段代码主要涉及的有①vtkMaskPolyData抽取点或单元格 二&#…

作者头像 李华
网站建设 2026/4/23 9:53:51

Docker Rollout配置实战(从入门到精通的3大核心要点)

第一章:Docker Rollout概述Docker Rollout 是一种基于容器化技术的应用部署策略,旨在实现应用版本的平滑升级与回滚。通过将应用程序及其依赖打包进轻量级、可移植的容器中,Docker Rollout 能够确保开发、测试与生产环境的一致性,…

作者头像 李华
网站建设 2026/4/23 9:56:32

Codeforces Rating提升秘籍:每天用VibeThinker练一道C题

Codeforces Rating提升秘籍:每天用VibeThinker练一道C题 在算法竞赛的世界里,Rating的每一次跃升背后,都是对思维极限的反复锤炼。无论是Codeforces上的红名梦,还是ICPC赛场上的金牌冲刺,选手们最常面对的问题从来不是…

作者头像 李华
网站建设 2026/4/23 14:47:48

基于Android的医院健康管理平台的设计与实现

本医院健康管理平台基于Android系统开发,综合运用Java语言、MySQL数据库以及协同过滤推荐算法等关键技术,旨在为患者和管理员提供高效、便捷的健康管理服务。在患者功能方面,支持注册登录后,利用协同过滤推荐算法实现健康文章与视…

作者头像 李华