📰 来源:Towards Data Science | 📅 翻译日期:2026年7月6日
🔗 原文:查看原文
🤖 翻译:DeepSeek AI · 仅供参考
生成砖块简介
本文是企业文档智能系列中生成砖块的第二部分,该系列从四个砖块构建企业RAG系统:文档解析、问题解析、检索和生成。第8A篇文章(回答合约)声明了类型化模式家族和ANSWER_REGISTRY,它将每个答案形状映射到其模式。本部分构建了填充合约的调用:输入一个ParsedQuestion;调度器从注册表中选择模式,从固定的BASE加上片段组合系统提示,构建用户提示,调用模型,并保留完整轨迹。调用后对答案的处理是第8C篇文章(验证)。
生成是第四个砖块。初次阅读的读者可以从各自文章中了解前三个砖块:
- 文档解析:将PDF转换为结构化表格:第5A篇(PDF中读取的内容)和第5B篇(关系数据模型)。
- 问题解析:将用户字符串转换为类型化的
ParsedQuestion:第6A篇(论点)、第6B篇(提取)和第6C篇(调度)。 - 检索:将段落过滤到应包含答案的范围:第7A篇(检索作为过滤)、第7B篇(锚点检测)和第7C篇(LLM仲裁器)。
本文在系列中的位置:第8篇(生成),提示组装部分,位于第二部分(四个砖块)——图片来源:作者
📓 可运行配套笔记本在GitHub上:doc-intel/notebooks-vol1。
公共配套代码仓库doc-intel/notebooks-vol1——图片来源:作者
1. 从简要到提示:调度器
每个问题形状一个提示,在调用时组合。这就是调度器。替代方案是每个RAG代码库容易陷入的巨型提示:一个系统提示同时处理金额、日期、列表、表格和自由文本。每次调用都会增加一个新的条件子句(“如果答案是日期,使用ISO 8601;如果是金额,使用ISO 4217;如果是列表,每个元素一行……”),模型每次都读取所有内容,两个月后没人记得哪个子句是为哪个情况添加的。
我们将构建的调度器取代了这种混乱。合约:输入一个ParsedQuestion;输出三样东西:模式(根据expected_answer_shape从ANSWER_REGISTRY中选择)、系统提示(固定的BASE加上简要请求的片段)和用户提示(问题+关键词+标注的段落行)。它调用模型,在轨迹上保留完整的原始响应,返回类型化结果。添加一个新形状就添加一个片段;添加一个新约束就添加一个片段;没有组合爆炸。
一个ParsedQuestion输入,一个类型化调用输出:调度器组合模式、系统提示和用户提示,然后返回答案及其轨迹——图片来源:作者我们排除了替代方案:一个包含所有子句的巨型提示(浪费token,当表格格式化子句渗入金额答案时难以调试),或者每个形状独立的N个提示(每个形状更清晰,但跨所有模板重复了横切约束(格式、区分、必须消除歧义),并且每次更改都需要重新同步)。
1.1 简要:ParsedQuestion
调度器读取由问题解析砖块产生的ParsedQuestion。完整模式是关系型的(嵌套Pydantic对象,不是扁平简要):keywords(类型化对象,不是字符串)、expected_answer_shape、decomposition、scope_filters、执行计划、解析注释,以及为后续砖块(检索:RetrievalQuery和生成:GenerationBrief)做的两个准备。
本文读取该对象的三个部分:expected_answer_shape(选择模式和形状片段)、generation(携带格式约束、近似候选项之间的消歧和要区分的值)和keywords(在用户提示中回显,以便模型标记哪些出现在检索到的段落中)。边界清晰:形状检测位于问题解析中;模板组装位于此处。
我们在下面内联构建ParsedQuestion;包的当前源码src/docintel/question/parse_question.py只提供最小版本。一旦问题解析砖块完成其完整实现,模式和调度器将升级到包中。
class Keyword(BaseModel):
text: str
weight: float = 1.0
source: Literal['direct','llm_expansion','expert_dictionary'] = 'direct'
is_regex: bool = False
class RetrievalQuery(BaseModel):
main_query: str
rewrites: list[str] = []
anchor_keywords: list[str] = []
section_hint: str | None = None
scope: str = 'default'
class GenerationBrief(BaseModel):
original_question: str
format_constraint: dict[str, str] = {}
disambiguation: str | None = None
must_distinguish: list[Distinction] = []
class ExecutionPlan(BaseModel):
use_toc_navigation: bool = True
use_keyword_retrieval: bool = True
use_embeddings: bool = False
iterate_on_feedback: bool = True
max_iterations: int = 3
class ParsedQuestion(BaseModel):
original_question: str
keywords: list[Keyword]
expected_answer_shape: Literal['text','amount','date','boolean','list','table']
decomposed_subquestions: list[str] = []
activations: ExecutionPlan = ExecutionPlan()
parsing_notes: list[str] = []
suggested_clarification: str | None = None
retrieval: RetrievalQuery
generation: GenerationBrief1.2 来自问题的结构提示
用户的问题措辞限制了检索范围,而无需在管道上添加任何新标志。当问题说“在第1页”、“第5页到第7页”或“在定价表上”时,该指针位于ParsedQuestion.structural_hints上。检索读取它并过滤搜索空间。没有chunk_strategy="passthrough",没有绕过参数,没有特殊的短文档路径。操作员通过在问题中编写来控制范围。
该字段每种格式携带一个列表。本文仅针对PDF,因此pages_hint是此处相关的字段;系列后期涉及的其他格式的兄弟字段暂不讨论:
class Str
评论已关闭