LangChain 系列文章目录

第一章 LangChain 简介和核心包
第二章 LangChain 提示词工程
第三章 LangChain 工作流
第四章 LangChain服务部署与链路监控
第五章 LangChain 消息管理与聊天历史存储
第六章 LangChain多模态输入与自定义输出


文章目录


前言

本文主要整理 LangChain 多模态输入与自定义输出相关内容,包括多模态图片输入、通义千问多模态调用、多模态工具调用、JSON/XML/YAML/Datetime/List 等结构化输出,以及 model.with_structured_output()model | JsonOutputParser() 的区别。


LangChain多模态输入与自定义输出

一、多模态数据输入

1.多模态模型可以支持图片分析功能。并且以聊天模型的形式作推理。

2.图片的传入方式是以image_url参数的方式传入。

  • 传入方式与模型的接口(如ChatOpenAI())或者模型本身有关,不可以随便自定义传入方式。
  • 部分模型也可以传入多张图片进行对比。
  • image_url的方式传入,就是传入图片的url地址。但也可以使用base64数据代替,多用于本地图片或者网络无法访问图片。

3.操作模型需要使用text的类型传入。

  • 传入的内容是对图片的操作

代码

from langchain_core.messages import HumanMessage
from langchain_openai import ChatOpenAI
import base64
import httpx

image_url1 = "https://www.xxx.com/aa.jpg"
image_url2 = "https://www.xxx.com/bb.jpg"
image_data1 = base64.b64encode(httpx.get(image_url1).content).decode("utf-8")
image_data2 = base64.b64encode(httpx.get(image_url2).content).decode("utf-8")

# 也可以使用本地图片
# image_path1 = "img/aa.jpg"  # 数据中心机柜相关图片
# image_path2 = "img/bb.jpg"  # 备用图片
# with open(image_path1, "rb") as image_file:
#     image_data1 = base64.b64encode(image_file.read()).decode("utf-8")
# with open(image_path2, "rb") as image_file:
#     image_data2 = base64.b64encode(image_file.read()).decode("utf-8")

model = ChatOpenAI(
    model="Qwen/Qwen2.5-VL-72B-Instruct",    # 有图片分析能力的模型
    openai_api_key="************",
    openai_api_base="https://api.siliconflow.cn/v1",
    temperature=0.7,
    max_tokens=8000,
)
message = HumanMessage(
    content=[
        {
            "type": "text",
            "text": "这两张图片的天气一样吗?用中文各自描述一下它们的天气",
        },
        {
            "type": "image_url",
            "image_url": {"url": f"data:image/jpeg;base64,{image_data1}"},
        },
        {
            "type": "image_url",
            "image_url": {"url": f"data:image/jpeg;base64,{image_data2}"},
        },
    ],
)

response = model.invoke(input=[message])
print(response.content)

结果

从提供的两张图片来看,它们的天气情况是不一样的。以下是两张图片的天气描述:

### 第一张图片:
- **天气描述**:这张图片展示了一个晴朗的天气场景。天空清澈,呈现出蓝天白云,阳光明媚,光线充足,整个环境显得非常明亮和清新。草地上绿意盎然,显示出这是一个温暖、宜人的季节,可 能是春季或夏季。整体氛围宁静而生机勃勃。

### 第二张图片:
- **天气描述**:这张图片展示了一个冬季的场景。地面积雪覆盖,建筑物的屋顶和树木上也积满了厚厚的雪。天空显得有些阴沉,可能是清晨或傍晚时分,光线较为柔和但较暗。周围环境被白雪覆 盖,整个画面给人一种寒冷而宁静的感觉,可能是一个寒冷的冬日。

### 总结:
- **是否一样**:这两张图片的天气不一样。第一张图片是晴朗的、温暖的天气,而第二张图片是寒冷的、被雪覆盖的冬季天气。
- **主要差异**:第一张图片是晴天,天气温暖,植被茂盛;第二张图片是雪天,天气寒冷,环境被积雪覆盖。

二、通义千问多模态数据输入

1.通义千问多模态模型支持分析图片功能。

2.使用MultiModalConversation.call()方法,传入数据

  • 参数messages传入数据的列表。列表每一个元素为一个字典,字典中的键:
    • role为角色,可以为"system"或"user"
    • content为内容,内容也是字典列表
      • "image"为图片的路径,如"file:///opt/lc/aa.txt}“或"file://d:/lc/aa.txt}”
      • "text"为输入的请求字符串
    • messages字典列表如:

代码

[
    {
        "role": "system",
        "content": [
            {"text": "你是达摩院的生活助手机器人。"}
        ]
    },
    {
        "role": "user",
        "content": [
            {"image": "http://XXXX"},
            {"text": "这个图片是哪里?"},
        ]
    }
]
  • 参数model为模型名称字符串,一般是qwen-vl模型
  • 参数api_key为通义千问api字符串

3.定义一个图片处理函数,包装MultiModalConversation.call()方法

4.使用RunnableLambda()包装图片处理函数,实现链的runnable

代码

from langchain.schema.runnable import RunnableLambda
from dashscope import MultiModalConversation
import base64

# 1. 定义图片处理函数
def analyze_image(data: dict) -> str:
    """输入: {'image_path': 'xxx.jpg', 'question': '描述图片'}"""
    response = MultiModalConversation.call(
        model="qwen-vl-max",
        messages=[
            {
                "role": "user",
                "content": [
                    {"image": f"file://{data['image_path']}"},
                    {"text": data["question"]},
                ],
            }
        ],
        api_key="************",
    )
    return response

# 2. 封装为RunnableLambda
image_analyzer = RunnableLambda(analyze_image)

# 3. 组合使用(可接其他LangChain组件)
chain = image_analyzer | RunnableLambda(lambda x: f"分析结果:{x}")

# 4. 调用
result = chain.invoke({
    "image_path": "img/多处接线端没有做铜鼻子,回路编号标识缺失.jpg",
    "question": "图片中有哪些物体?"
})
print(result)

结果

分析结果:{
    "status_code": 200,
    "request_id": "4ecf9e6f-23af-941b-b147-f283d5188321",
    "code": "",
    "message": "",
    "output": {
        "text": null,
        "finish_reason": null,
        "choices": [
            {
                "finish_reason": "stop",
                "message": {
                    "role": "assistant",
                    "content": [
                        {
                            "text": "这张图片展示了一个电气配电箱的内部结构,具体包含以下物体:\n\n### 1. **断路器(Circuit Breakers)**\n   ...结构,包含断路器、电线、接线端子等关键电气元件。这些元件共同作用,实现电力的分配和保护功能。"
                        }
                    ],
                },
            }
        ],
    },
    "usage": {
        "input_tokens": 1245,
        "output_tokens": 443,
        "input_tokens_details": {"text_tokens": 13, "image_tokens": 1232},
        "total_tokens": 1688,
        "output_tokens_details": {"text_tokens": 443},
        "image_tokens": 1232,
        "prompt_tokens_details": {"cached_tokens": 0},
    },
}

三、多模态工具调用

1.部分多模态模型支持工具调用功能。

  • 也就是Function calling功能

2.要使用此类模型调用工具,需要想将工具绑定到模型,然后使用所需类型的内容块调用模型。

  • 使用@tool修饰器定义工具方法,方法必须有工具的作用描述,好让模型理解。
  • 使用模型的bind_tools()方法,绑定工具列表,返回带有工具能力的模型对象
  • 执行推理后,可以使用tool_calls属性获取使用过的工具列表

代码

from typing import Literal
from langchain_core.messages import HumanMessage
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool

@tool
def weather_tool(weather: Literal["春天", "夏天", "秋天", "冬天"]) -> None:
    """描述季节"""
    pass

model = ChatOpenAI(
    model="Pro/deepseek-ai/DeepSeek-V3",    # 有工具调用能力的模型
    openai_api_key="************",
    openai_api_base="https://api.siliconflow.cn/v1",
    temperature=0.7,
    max_tokens=8000,
)

model_with_tools = model.bind_tools([weather_tool])

message = HumanMessage(
    content=("用中文帮我确认一下季节:很冷,我穿了很多衣服,而且湖面都结冰了"),
)
response = model_with_tools.invoke([message])
print(response)
print("-----------------------------------")
print(response.tool_calls)

结果

content = "根据你的描述,天气非常寒冷,湖面结冰,而且你穿了很多衣服,这些特点非常符合冬季的特征。让我帮你确认一下季节的描述。"
additional_kwargs = {
    "tool_calls": [
        {
            "id": "01970d9299eabc3260b13c3fdb70a12e",
            "function": {"arguments": '{"weather":"冬天"}', "name": "weather_tool"},
            "type": "function",
        }
    ],
    "refusal": None,
}
response_metadata = {
    "token_usage": {
        "completion_tokens": 52,
        "prompt_tokens": 179,
        "total_tokens": 231,
        "completion_tokens_details": {
            "accepted_prediction_tokens": None,
            "audio_tokens": None,
            "reasoning_tokens": 0,
            "rejected_prediction_tokens": None,
        },
        "prompt_tokens_details": None,
    },
    "model_name": "Pro/deepseek-ai/DeepSeek-V3",
    "system_fingerprint": "",
    "id": "01970d92845417d1450ca1119b199c75",
    "service_tier": None,
    "finish_reason": "tool_calls",
    "logprobs": None,
}
id = "run--ea0f134a-75c9-40f2-b397-d2efd38ba09b-0"
tool_calls = [
    {
        "name": "weather_tool",
        "args": {"weather": "冬天"},
        "id": "01970d9299eabc3260b13c3fdb70a12e",
        "type": "tool_call",
    }
]
usage_metadata = {
    "input_tokens": 179,
    "output_tokens": 52,
    "total_tokens": 231,
    "input_token_details": {},
    "output_token_details": {"reasoning": 0},
}
-----------------------------------
[
    {
        "name": "weather_tool",
        "args": {"weather": "冬天"},
        "id": "01970d9299eabc3260b13c3fdb70a12e",
        "type": "tool_call",
    }
]

四、JSON输出

1.使用输出解析器来通过提示指定任意的JSON模式输出。

  • 解析器本质就是通过提示词的文字约束来指定输出格式
  • 注意:大型语言模型是有泄漏的抽象(有可能生成不是完全符合规格的格式)。

2.Pydantic版本

  • 从langchain-core 0.3.0版本开始,LangChain内部使用了Pydantic v2。此前的langchain_core.pydantic_v1模块是为了兼容Pydantic v1版本而存在的,现在不再建议使用。
  • 使用时,直接从Pydantic导入所需的类或函数。
from langchain_core.pydantic_v1 import BaseModel
改为
from pydantic import BaseModel

3.使用Pydantic定义一个JSON格式约束类。再使用JsonOutputParser()方法的pydantic_object参数输入这个类,用来定义输出的JSON格式。

  • JSON格式类的每个属性就是一个返回的JSON字段
  • 使用Pydantic的Field(description)方法来描述JSON字段,用于被模型理解

4.使用JsonOutputParser()方法,创建一个用于提示并解析JSON输出对象

  • 一般JsonOutputParse与Pydantic一起使用

5.在PromptTemplate()中设置partial_variables参数,让JSON的描述说明参与prompt中

  • partial_variables参数,用于设置模板中的某些变量使用某个固定值。
  • partial_variables参数中的format_instructions用于设置输出格式的说明
  • parser.get_format_instructions()会返回一段说明文本,告诉模型如何按照JSON格式类的结构输出JSON格式的数据

6.JsonOutputParser在2个地方参与了链条

  • 一个是Prompt时,JsonOutputParser提供了说明文档(非必要当有Prompt时,最好添加,如果没有也是可以的,就没有太准确)
  • 一个是Output时,JsonOutputParser约束了输出

代码

from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import PromptTemplate
# from langchain_core.pydantic_v1 import BaseModel, Field
from pydantic import BaseModel, Field
from langchain_openai import ChatOpenAI

model = ChatOpenAI(
    model="Pro/deepseek-ai/DeepSeek-V3",
    openai_api_key="************",
    openai_api_base="https://api.siliconflow.cn/v1",
)

# 定义json的数据结构。
class Joke(BaseModel):
    setup: str = Field(description="设置笑话的问题")
    punchline: str = Field(description="解决笑话的答案")

# 设置json解析器使用pydantic的数据格式。
parser = JsonOutputParser(pydantic_object=Joke)
# 获取解析器的说明文档字符串
json_instructions = parser.get_format_instructions()
print(json_instructions)

prompt = PromptTemplate(
    template="回答用户的查询。\n{format_instructions}\n{query}\n",
    input_variables=["query"],
    partial_variables={"format_instructions": json_instructions},
)
chain = prompt | model | parser
response = chain.invoke({"query": "告诉我一个笑话。"})
print(response)

结果

# 此部分是JsonOutputParser预定的内容字符串,用于描述什么是json格式,给模型理解
The output should be formatted as a JSON instance that conforms to the JSON schema below.

As an example, for the schema {"properties": {"foo": {"title": "Foo", "description": "a list of strings", "type": "array", "items": {"type": "string"}}}, "required": ["foo"]}
the object {"foo": ["bar", "baz"]} is a well-formatted instance of the schema. The object {"properties": {"foo": ["bar", "baz"]}} is not well-formatted.     

Here is the output schema:
```
{"properties": {"setup": {"description": "设置笑话的问题", "title": "Setup", "type": "string"}, "punchline": {"description": "解决笑话的答案", "title": "Punchline", "type": "string"}}, "required": ["setup", "punchline"]}
```

{'setup': '为什么电脑很冷?', 'punchline': '因为它有很多Windows(窗户)!'}

7.JsonOutputParser支持流式输出。

  • JsonOutputParser和PydanticOutputParser之间的一个关键区别是 JsonOutputParser输出解析器支持流式处理部分块。
    • 之前JSON输出需要使用PydanticOutputParser()

代码

from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import PromptTemplate
from pydantic import BaseModel, Field
from langchain_openai import ChatOpenAI

model = ChatOpenAI(
    model="Pro/deepseek-ai/DeepSeek-V3",
    openai_api_key="************",
    openai_api_base="https://api.siliconflow.cn/v1",
)

class Joke(BaseModel):
    setup: str = Field(description="设置笑话的问题")
    punchline: str = Field(description="解决笑话的答案")

joke_query = "告诉我一个笑话。"
parser = JsonOutputParser(pydantic_object=Joke)
json_instructions = parser.get_format_instructions()

prompt = PromptTemplate(
    template="回答用户的查询。\n{format_instructions}\n{query}\n",
    input_variables=["query"],
    partial_variables={"format_instructions": json_instructions},
)
chain = prompt | model | parser
for s in chain.stream({"query": joke_query}):
    print(s)

结果

{}
{'setup': '为什么'}
{'setup': '为什么鸡'}
{'setup': '为什么鸡不能过'}
{'setup': '为什么鸡不能过马路?'}
{'setup': '为什么鸡不能过马路?', 'punchline': ''}
{'setup': '为什么鸡不能过马路?', 'punchline': '因为对面'}
{'setup': '为什么鸡不能过马路?', 'punchline': '因为对面有K'}
{'setup': '为什么鸡不能过马路?', 'punchline': '因为对面有KFC!'}

8.没有使用Pydantic

  • 可以在没有Pydantic情况下使用JsonOutputParser。也会输出JSON数据,但没有指定JSON字段格式。
  • 直接JsonOutputParser()不传参数

代码

from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI

model = ChatOpenAI(
    model="Pro/deepseek-ai/DeepSeek-V3",
    openai_api_key="************",
    openai_api_base="https://api.siliconflow.cn/v1",
    temperature=0.7,
    max_tokens=8000,
)

joke_query = "告诉我一个笑话。"
parser = JsonOutputParser()
json_instructions = parser.get_format_instructions()

prompt = PromptTemplate(
    template="A回答用户的查询。\n{format_instructions}\n{query}\n",
    input_variables=["query"],
    partial_variables={"format_instructions": json_instructions},
)
chain = prompt | model | parser
response = chain.invoke({"query": joke_query})
print(response)

结果

{'response': {'content': '为什么电脑经常感冒?因为它总是开着窗户(Windows)!', 'language': 'zh', 'category': 'joke', 'source': 'user_request'}}

五、XML输出

1.使用XML格式需要先安装defusedxml:

  • 安装defusedxml包:pip install defusedxml

2.使用输出解析器来通过提示指定任意的XML模式输出。

  • 解析器本质就是通过提示词的文字约束来指定输出格式
  • 注意:大型语言模型是有泄漏的抽象(有可能生成不是完全符合规格的格式)。

3.使用XMLOutputParser()方法,创建一个用于提示并解析XML输出对象

  • 不与Pydantic一起使用
  • 参数tag用于指定XML具有的字段列表。如果不指定会随机字段。

4.在PromptTemplate()中设置partial_variables参数

  • partial_variables参数,用于设置模板中的某些变量使用某个固定值。
  • partial_variables参数中的format_instructions用于设置输出格式的说明
  • parser.get_format_instructions()会返回一段说明文本字符串,告诉模型如何按照XML格式类的结构输出XML格式的数据

5.输出

  • 输出格式虽然指定了XML格式,但是还是会带有其他描述的语言
  • 使用parser.parse(response.content)可以将描述性语言去掉,但是有可能得不到XML,而是得到一个JSON数据

代码

from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import XMLOutputParser
# pip install defusedxml

model = ChatOpenAI(
    model="Pro/deepseek-ai/DeepSeek-V3",
    openai_api_key="************",
    openai_api_base="https://api.siliconflow.cn/v1",
    temperature=0.7,
    max_tokens=8000,
)
actor_query = "生成周星驰的简化电影作品列表,按照最新的时间降序"

# 设置xml解析器,使用tag指定数据格式。
parser = XMLOutputParser(tags=["movies", "actor", "film", "name", "genre"])
xml_instructions = parser.get_format_instructions()
print(xml_instructions)

prompt = PromptTemplate(
    template="回答用户的查询。\n{format_instructions}\n{query}\n",
    input_variables=["query"],
    partial_variables={"format_instructions": xml_instructions},
)
chain = prompt | model
response = chain.invoke({"query": actor_query})
print(response.content)
xml_output = parser.parse(response.content)
print(xml_output)

结果

# 此部分就是XmlOutputParser预定的内容,用于描述什么是json格式,给模型理解
The output should be formatted as a XML file.
1. Output should conform to the tags below.
2. If tags are not given, make them on your own.
3. Remember to always open and close all the tags.

As an example, for the tags ["foo", "bar", "baz"]:
1. String "<foo>
   <bar>
      <baz></baz>
   </bar>
</foo>" is a well-formatted instance of the schema.
2. String "<foo>
   <bar>
   </foo>" is a badly-formatted instance.
3. String "<foo>
   <tag>
   </tag>
</foo>" is a badly-formatted instance.

Here are the output tags:
```
['movies', 'actor', 'film', 'name', 'genre']
```

# 返回的结果了包含了文字性描述,而不是一个纯xml
以下是根据您的要求生成的周星驰电影作品简化列表,按照最新时间降序排列的XML格式输出:

```xml
<movies>
    <actor>
        <name>周星驰</name>
        <film>
            <name>美人鱼</name>
            <genre>喜剧/爱情/奇幻</genre>
        </film>
        <film>
            <name>西游·降魔篇</name>
            <genre>喜剧/奇幻/冒险</genre>
        </film>
        <film>
            <name>长江7号</name>
            <genre>喜剧/科幻/家庭</genre>
        </film>
        <film>
            <name>功夫</name>
            <genre>喜剧/动作/犯罪</genre>
        </film>
        <film>
            <name>少林足球</name>
            <genre>喜剧/运动</genre>
        </film>
        <film>
            <name>喜剧之王</name>
            <genre>喜剧/剧情/爱情</genre>
        </film>
    </actor>
</movies>
```

注:
1. 列表仅包含周星驰作为主演的代表性电影(非导演作品)
2. 时间顺序已调整为从新到旧(2016年《美人鱼》最早)
3. 每个电影包含名称和主要类型标签
4. 所有标签均按要求完整闭合

# 将xml内容转成了json
{'movies': [{'actor': [{'name': '周星驰'}, {'film': [{'name': '美人鱼'}, {'genre': '喜剧/爱情/奇幻'}]}, {'film': [{'name': '西游·降魔篇'}, {'genre': '喜剧/奇幻/冒险'}]}, {'film': [{'name': '长江7号'}, {'genre': '喜剧/科幻/家庭'}]}, {'film': [{'name': '功夫'}, {'genre': '喜剧/动作/犯罪'}]}, {'film': [{'name': '少 林足球'}, {'genre': '喜剧/运动'}]}, {'film': [{'name': '喜剧之王'}, {'genre': '喜剧/剧情/爱情'}]}]}]}
  • 如果修改最后链条和输出

代码

...
chain = prompt | model | parser
response = chain.invoke({"query": actor_query})
print(response)

结果

{'movies': [{'actor': [{'name': '周星驰'}, {'film': [{'name': '美人鱼'}, {'genre': '喜剧/奇幻'}]}, {'film': [{'name': '西游降魔篇'}, {'genre': '喜剧/奇幻/冒险'}]}, {'film': [{'name': '长江七号'}, {'genre': '喜剧/科幻'}]}, {'film': [{'name': '功夫'}, {'genre': '喜剧/动作'}]}, {'film': [{'name': ' 少林足球'}, {'genre': '喜剧/运动'}]}]}]}

6.XmlOutputParser支持流式输出。

代码

...
for s in chain.stream({"query": actor_query}):
    print(s)

六、YAML输出

1.使用输出解析器来通过提示指定任意的YAML模式输出。

  • 解析器本质就是通过提示词的文字约束来指定输出格式
  • 注意:大型语言模型是有泄漏的抽象(有可能生成不是完全符合规格的格式)。

2.使用Pydantic定义一个YAML格式类。使用YamlOutputParser()方法的pydantic_object参数输入这个类,用来定义输出的YAML格式。

  • YAML格式类的每个属性就是一个返回的YAML字段
  • 可以不使用pydantic_object参数,字段会随机。
  • 使用Pydantic的Field(description)方法来描述YAML字段,用于被模型理解

3.使用YamlOutputParser()方法,创建一个用于提示并解析YAML输出对象

  • 一般YamlOutputParse与Pydantic一起使用

4.在PromptTemplate()中设置partial_variables参数

  • partial_variables参数,用于设置模板中的某些变量值在后续保持不变。
  • partial_variables参数中的format_instructions用于设置输出格式的说明
  • parser.get_format_instructions()会返回一段说明文本,告诉模型如何按照YAML格式类的结构输出YAML格式的数据

代码

from langchain.output_parsers import YamlOutputParser
from langchain_core.prompts import PromptTemplate
# from langchain_core.pydantic_v1 import BaseModel, Field
from pydantic import BaseModel, Field
from langchain_openai import ChatOpenAI

model = ChatOpenAI(
    model="Pro/deepseek-ai/DeepSeek-V3",
    openai_api_key="************",
    openai_api_base="https://api.siliconflow.cn/v1",
    temperature=0.7,
    max_tokens=8000,
)

# 定义yaml数据结构。
class Joke(BaseModel):
    setup: str = Field(description="设置笑话的问题")
    punchline: str = Field(description="解答笑话的答案")

# 设置yaml解析器使用pydantic的数据格式。
parser = YamlOutputParser(pydantic_object=Joke)
# 使用解析器的说明文档
yaml_instructions = parser.get_format_instructions()
print(yaml_instructions)

prompt = PromptTemplate(
    template="回答用户的查询。\n{format_instructions}\n{query}\n",
    input_variables=["query"],
    partial_variables={"format_instructions": yaml_instructions},
)
chain = prompt | model               # 无需再加入 | parser
response = chain.invoke({"query": "告诉我一个笑话。"})
print(response.content)              # 字符串
print(parser.parse(response.content))

结果

# 此部分就是YamlOutputParser预定的内容,用于描述什么是json格式,给模型理解
The output should be formatted as a YAML instance that conforms to the given JSON schema below.

# Examples
## Schema
```
{"title": "Players", "description": "A list of players", "type": "array", "items": {"$ref": "#/definitions/Player"}, "definitions": {"Player": {"title": "Player", "type": "object", "properties": {"name": {"title": "Name", "description": "Player name", "type": "string"}, "avg": {"title": "Avg", "description": "Batting average", "type": "number"}}, "required": ["name", "avg"]}}}
```
## Well formatted instance
```
- name: John Doe
  avg: 0.3
- name: Jane Maxfield
  avg: 1.4
```

## Schema
```
{"properties": {"habit": { "description": "A common daily habit", "type": "string" }, "sustainable_alternative": { "description": "An environmentally friendly alternative to the habit", "type": "string"}}, "required": ["habit", "sustainable_alternative"]}
```
## Well formatted instance
```
habit: Using disposable water bottles for daily hydration.
sustainable_alternative: Switch to a reusable water bottle to reduce plastic waste and decrease your environmental footprint.
```

Please follow the standard YAML formatting conventions with an indent of 2 spaces and make sure that the data types adhere strictly to the following JSON schema:
```
{"properties": {"setup": {"description": "\u8bbe\u7f6e\u7b11\u8bdd\u7684\u95ee\u9898", "title": "Setup", "type": "string"}, "punchline": {"description": "\u89e3\u7b54\u7b11\u8bdd\u7684\u7b54\u6848", "title": "Punchline", "type": "string"}}, "required": ["setup", "punchline"]}
```

Make sure to always enclose the YAML output in triple backticks (```). Please do not add anything other than valid YAML output!

# 返回的结果了包含了文字性描述,而不是一个纯yaml
```
setup: 为什么鸡会过马路?
punchline: 为了证明它不是一只鸭子!
```

# 将yaml内容转成了json
setup='为什么鸡会过马路?' punchline='为了证明它不是一只鸭子!'

七、Datetime格式输出

1.使用DatetimeOutputParser()方法,创建一个用于提示并解析Datetime输出对象

2.操作与JsonOutputParser()相似。就是引用的地方不一样

代码

from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate
from langchain.output_parsers import DatetimeOutputParser

model = ChatOpenAI(
    model="Pro/deepseek-ai/DeepSeek-V3",
    openai_api_key="************",
    openai_api_base="https://api.siliconflow.cn/v1",
    temperature=0.7,
    max_tokens=8000,
)

parser = DatetimeOutputParser()
dt_instructions = parser.get_format_instructions()
print(dt_instructions)

prompt = PromptTemplate(
    template="回答用户的问题。\n{question}\n{format_instructions}\n",
    input_variables=["question"],
    partial_variables={"format_instructions": dt_instructions},
)
chain = prompt | model | parser
response = chain.invoke({"question": "比特币什么时候成立的"})
print(response)

结果

# 此部分就是DatetimeOutputParser预定的内容,描述什么是Datetime格式,给模型理解
Write a datetime string that matches the following pattern: '%Y-%m-%dT%H:%M:%S.%fZ'.

Examples: 1253-02-06T18:00:15.621147Z, 0943-03-04T21:41:32.570698Z, 0901-04-04T21:50:53.770031Z    

Return ONLY this string, no other words!

2009-01-03 18:15:05

八、List格式输出

1.使用CommaSeparatedListOutputParser()方法,创建一个用于提示并解析列表输出对象

2.操作与DatetimeOutputParser()相似。

代码

from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate
from langchain.output_parsers import CommaSeparatedListOutputParser

model = ChatOpenAI(
    model="Pro/deepseek-ai/DeepSeek-V3",
    openai_api_key="************",
    openai_api_base="https://api.siliconflow.cn/v1",
    temperature=0.7,
    max_tokens=8000,
)

parser = CommaSeparatedListOutputParser()
csl_instructions = parser.get_format_instructions()
print(csl_instructions)

prompt = PromptTemplate(
    template="List Five {subject}.\n{format_instructions}\n",
    input_variables=["subject"],
    partial_variables={"format_instructions": csl_instructions},
)
chain = prompt | model | parser
response = chain.invoke({"subject": "冰淇淋口味"})
print(response)

结果

Your response should be a list of comma separated values, eg: `foo, bar, baz` or `foo,bar,baz`

['vanilla', 'chocolate', 'strawberry', 'mint chocolate chip', 'cookies and cream']

九、model.with_structured_output()与model | JsonOutputParser()的区别

1.说明:

  • with_structured_output(schema)方法的schema参数:定义输出结构契约,也就是Pydantic库的BaseModel子类。
  • 对比不单止是JsonOutputParser(),XMLOutputParser(),YamlOutputParser()

2.model.with_structured_output()是模型级结构约束(强约束)

  • with_structured_output()只能接Chat模型,不能接普通文本LLM模型。
  • 本质机制
    • 把schema转成模型能理解的约束
    • 强制模型只输出符合schema的内容
    • 模型直接返回schema指定结构的实例
  • 过程:Prompt -> Model(受schema约束) -> 合法对象 -> Pydantic校验 -> 指定格式对象
  • with_structured_output(schema)方法的参数
    • schema:定义输出的结构化格式(可以是Pydantic BaseModel继承类)必选
    • method:指定生成结构化输出方式的字符串,可选
      • auto:自动选择方式(优先用模型原生结构化输出能力,如OpenAI的response_format)(默认)。
      • json_mode:强制使用模型的JSON模式(仅支持部分模型,如 GPT-4)。
      • function_calling:通过函数调用方式生成结构化输出(兼容性最强)。
    • strict:是否严格schema模式。默认False,尝试不匹配的输出。
    • include_raw:是否返回原始模型响应,默认False。
  • 示例

代码

...
class Plan(BaseModel):
    steps: list[str]
    tool: str
res = prompt | model.with_structured_output(Plan)
...

3.model | JsonOutputParser()是后处理解析(弱约束)

  • JsonOutputParser()能接Chat模型,也能接普通文本LLM模型。
  • 本质机制
    • 模型自由输出文本
    • LangChain尝试从文本中提取JSON:json.loads(…)
    • 成功就返回dict,失败就报错
  • 过程:Prompt -> Model(自由发挥) -> 文本(可能含有json) -> 正则/json.loads -> dict或报错
  • 示例

代码

...
from langchain.output_parsers import JsonOutputParser
chain = prompt| model | JsonOutputParser()
...

4.核心差异对比

with_structured_output JsonOutputParser
约束点 模型生成阶段 生成之后解析
是否使用schema Pydantic/TypedDict 不使用
是否强类型
格式错误 极少
是否自动修复 失败模型会重试或修复 基本不能
使用范围 生成环境 Demo/快速脚本
LangGraph/Agent 推荐 不稳定

总结

提示:这里对文章进行总结:

本文整理了 LangChain 在多模态输入与自定义输出方面的常用方法。多模态部分重点说明了图片 URL、Base64、本地文件路径以及通义千问 MultiModalConversation.call() 的调用方式;自定义输出部分重点说明了 JsonOutputParser、XMLOutputParser、YamlOutputParser、DatetimeOutputParser、CommaSeparatedListOutputParser,以及 with_structured_output() 和后处理解析器之间的差异。实际项目中,建议根据模型是否支持原生结构化输出来选择方案:如果模型支持 schema 级约束,优先使用 with_structured_output();如果只是需要后处理解析,可以使用对应 OutputParser。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐