AI- RAG笔记05 - 生成
Chapter5 生成:格式化生成
本文学习来源 all-in-rag
个人学习笔记整理总结,有错误或者遗漏希望大家指正
预览
LLM 的输出不能只适合人阅读,还要适合程序解析、校验、渲染和执行。
格式化生成负责把“模型会说话”变成“系统能处理”。
格式化生成的核心价值是把自然语言能力接到工程系统里:
用户输入
-> LLM 理解意图
-> 输出 JSON / Pydantic 对象 / tool_calls
-> 代码解析、校验、调用工具或渲染页面
-> 系统继续执行确定性的业务逻辑
1. 为什么需要格式化生成
普通 LLM 输出通常是自然语言文本,适合阅读,但不适合直接被代码稳定处理。
例如电商客服里,用户问:
推荐几款适合程序员的键盘
如果模型只返回一段介绍文字,前端很难直接渲染商品卡片。更理想的输出是:
[
{
"name": "键盘名称",
"price": 399,
"features": ["机械轴", "蓝牙", "多设备切换"],
"url": "https://example.com"
}
]
格式化生成适合这些场景:
- RAG 结果结构化展示:把答案拆成标题、摘要、引用来源、相关条目。
- 自然语言转 API 参数:把“查明天上海到北京的航班”解析成出发地、目的地和日期。
- 信息抽取:从文章、合同、简历、客服记录中抽取实体、时间、地点、事件。
- Agent 工具调用:让模型判断该调用哪个工具,并生成工具参数。
关键点:
自然语言输出解决“表达”问题。
结构化输出解决“程序继续处理”问题。
2. LangChain Output Parsers
LangChain 的 OutputParsers 负责两件事:
- 在 Prompt 中注入格式要求。
- 在模型返回后,把文本解析成目标结构。
常见解析器:
StrOutputParser:把模型输出当字符串返回。JsonOutputParser:把 JSON 字符串解析成 Python 字典或列表。PydanticOutputParser:用 Pydantic 模型定义结构,并做类型校验。
2.1 PydanticOutputParser 的核心流程
定义 Pydantic Schema
-> 创建 PydanticOutputParser
-> 生成 format_instructions
-> 注入 PromptTemplate
-> prompt | llm | parser
-> 得到 Pydantic 对象
例如代码示例
class PersonInfo(BaseModel):
name: str = Field(description="人物姓名")
age: int = Field(description="人物年龄")
skills: List[str] = Field(description="技能列表")
这里的 Field(description=...) 不只是给人看的注释,它会进入格式指令,影响模型怎么理解字段。
2.2 format_instructions 做了什么
parser.get_format_instructions() 会把 Pydantic 模型转换成类似 JSON Schema 的说明,然后塞进 Prompt。
可以理解成:
开发者定义 Python 类型
-> parser 转成模型可理解的格式说明
-> LLM 按格式说明输出 JSON
-> parser 再把 JSON 校验成 Python 对象
这比单纯写一句“请输出 JSON”更稳,因为 Schema 更明确,而且返回后还有校验。
2.3 解析与校验
PydanticOutputParser 收到模型输出后会做两层处理:
- 先把文本解析成 JSON。
- 再用 Pydantic 模型做字段和类型校验。
如果输出是:
{
"name": "张三",
"age": 30,
"skills": ["Python", "Go语言"]
}
最终会得到:
PersonInfo(name='张三', age=30, skills=['Python', 'Go语言'])
注意:
Output Parser 不能保证模型一定生成正确格式。
它的价值是增强约束,并在格式错误时尽早暴露问题。
3. LlamaIndex 的结构化输出
LlamaIndex 在 RAG 流程里更强调响应合成和结构化输出。
3.1 Response Synthesizer
在普通 RAG 中,检索器召回 Nodes 后,Response Synthesizer 会负责组织上下文并生成答案。
常见模式:
refine:逐块处理检索内容,迭代改进答案。compact:尽量把更多上下文压缩进单次 LLM 调用。
默认情况下,它生成的是自然语言文本。
3.2 Pydantic Programs
当需要结构化数据时,LlamaIndex 主要使用 Pydantic Programs。
核心流程:
定义 Pydantic 模型
-> LlamaIndex 转成格式约束
-> LLM 生成结构化结果
-> Pydantic 校验
-> 返回 Pydantic 对象
如果底层模型支持 Function Calling,LlamaIndex 会优先使用工具调用能力;如果不支持,则退回到把 JSON Schema 注入 Prompt 的方式。
这和 LangChain 的思想接近:
Schema 是开发者和模型之间的结构化契约。
4. 不依赖框架的实现思路
不用 LangChain 或 LlamaIndex,也可以通过提示词工程控制输出格式。
常用方法:
- 明确要求 JSON:要求“只返回 JSON,不要解释,不要 Markdown”。
- 提供 JSON Schema:写清楚字段名、类型、含义、是否必填。
- 提供 few-shot 示例:给出输入和标准输出,让模型模仿。
- 使用语法约束:本地模型可通过 GBNF 等 grammar 强制输出合法语法。
一个最小提示模板:
请从文本中抽取人物信息。
只返回 JSON,不要输出解释。
JSON 字段:
- name: string,人物姓名
- age: number,人物年龄
- skills: string[],技能列表
文本:
张三今年30岁,他擅长Python和Go语言。
这种方式实现简单,但缺点也明显:
- 约束主要靠 Prompt,稳定性依赖模型遵循指令的能力。
- 需要自己写解析、校验、重试和错误处理。
- 字段变多后,提示词容易变长,也容易遗漏边界情况。
5. Function Calling
Function Calling 也叫 Tool Calling。它不是让模型真的执行函数,而是让模型生成一个结构化的“调用请求”。
核心分工:
模型负责判断:该不该调用工具,调用哪个工具,参数是什么。
代码负责执行:解析 tool_calls,调用真实 API,把结果返回给模型。
5.1 工作流程
完整链路是:
1. 代码定义 tools
2. 用户提问
3. 模型返回 tool_calls
4. 代码解析工具名和参数
5. 代码实际执行工具
6. 代码把工具结果作为 role=tool 的消息发回模型
7. 模型基于工具结果生成最终回答
代码示例中的工具定义是天气查询:
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定地点的天气信息",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市和省份,例如:杭州市, 浙江省",
}
},
"required": ["location"]
},
}
},
]
用户问:
杭州今天天气怎么样?
模型不会直接查天气,而是可能返回:
tool_name: get_weather
arguments: {"location": "杭州市, 浙江省"}
然后代码模拟执行工具:
24℃,晴朗
最后把这个工具结果发回模型,由模型组织成自然语言回答。
5.2 Function Calling 的关键理解
最重要的一点:
LLM 不执行工具,应用程序执行工具。
模型输出的是意图和参数。真正的网络请求、数据库查询、文件操作、支付、发消息,都必须由代码完成。
所以 Function Calling 的可靠性来自两部分:
- 模型原生支持结构化工具调用,比纯文本 JSON 更稳定。
- 代码层可以对工具名、参数、权限、返回结果做严格控制。
5.3 Function Calling 的优势
相比“让模型直接输出 JSON”,Function Calling 更适合需要执行动作的场景。
优势:
- 结构更稳定:模型返回的是标准化的
tool_calls。 - 意图识别更强:模型可以从多个工具中选择合适工具。
- 可以接外部系统:API、数据库、搜索、订单系统、文件系统都可以变成工具。
- 适合构建 Agent:Agent 的核心就是规划、调用工具、观察结果、继续推理。
但也要注意:
Function Calling 不是安全边界。
安全边界必须由代码侧实现。
例如:
- 工具参数要校验。
- 工具调用要有权限控制。
- 高风险动作要二次确认。
- 外部 API 错误要可恢复。
- 工具返回不能盲目信任,需要清洗或标注来源。
6. 总结
| 方案 | 适合场景 | 优点 | 局限 |
|---|---|---|---|
| Prompt 要求 JSON | 简单结构化输出 | 实现成本最低 | 稳定性一般,需要自己解析校验 |
| JSON Schema + few-shot | 字段较明确的信息抽取 | 比纯指令更清晰 | 仍依赖模型遵循提示 |
| LangChain Output Parser | Python 应用内结构化抽取 | 能生成格式指令并自动解析 | 格式错误时仍需重试策略 |
| Pydantic 模型 | 强类型结构化结果 | 字段、类型、描述集中管理 | Schema 设计质量很关键 |
| LlamaIndex Pydantic Programs | RAG 响应需要结构化 | 与检索和响应合成结合好 | 依赖 LlamaIndex 工作流 |
| Function Calling | 需要调用工具或 API | 原生工具调用,适合 Agent | 工具执行、安全和错误处理都在代码侧 |
| Grammar 约束 | 本地模型强约束输出 | token 级别限制格式 | 配置成本更高,灵活性较低 |
选择建议:
只需要抽取结构化字段:优先 PydanticOutputParser / Pydantic Programs。
需要调用外部工具:优先 Function Calling。
只做轻量 demo:Prompt + JSON Schema 可以够用。
本地模型且必须严格合法 JSON:考虑 grammar 约束。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)