ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Instructor 文档智能分段实战指南:用 Cohere command-a 将长文档切成语义完整的 Section

Instructor 文档智能分段实战指南:用 Cohere command-a 将长文档切成语义完整的 Section Instructor 文档智能分段实战指南用 Cohere command-a 将长文档切成语义完整的 Section【免费下载链接】instructorstructured outputs for llms项目地址: https://gitcode.com/GitHub_Trending/in/instructor本文是一份基于 Instructor 结构化输出能力的文档分段Document Segmentation实战指南。文章以SectionStructuredDocument两个 Pydantic 模型为骨架讲解如何让长上下文 LLM以 Cohere command-a 为例把任意长文档切成一个个围绕单一核心概念的分段并在分段后无损还原原始文本。读完本文你将掌握一套不依赖正则规则、可复用在论文、教程、代码文档等任意场景的分段流水线。为什么需要 LLM 驱动的文档分段很多时候我们需要把一份长文档拆分成有意义的段落且每个段落都围绕一个核心概念展开——例如把一篇技术教程切分成可以按课时讲授的若干主题块。传统的基于长度或规则的文本分割器text-splitter并不可靠它们通常依赖空行、标题等表面特征一旦文档中出现代码片段或数学公式这些内容既不能简单地在\n\n处切开会破坏语法完整性也难以为每种文档类型编写大量专门规则。幸运的是具备足够长上下文窗口的 LLM 天然适合完成这项任务它能够理解语义边界判断哪里是一个概念的结束、另一个概念的开始。这正是 Instructor 的用武之地我们用 Pydantic 模型声明分段结果的 schema让 LLM 以结构化输出的形式返回分段边界从而把不可靠的文本切分升级为可校验、可复现的语义切分。定义分段的数据结构分段任务的输出模型非常简单只有两个类Section描述文档中的一段。包含该段主题标题以及该段在文档中的起止行号。StructuredDocument封装一份文档全部分段的容器。from pydantic import BaseModel, Field from typing import List class Section(BaseModel): title: str Field(descriptionmain topic of this section of the document) start_index: int Field(descriptionline number where the section begins) end_index: int Field(descriptionline number where the section ends) class StructuredDocument(BaseModel): obtains meaningful sections, each centered around a single concept/topic sections: List[Section] Field(descriptiona list of sections of the document)这里有一个非常关键的设计LLM 只返回分段的起止行号而不是分段正文。Field(description...)中给每个字段补充了语义描述帮助 LLM 准确理解start_index/end_index的含义。这样做的直接好处是避免 LLM 重新生成原文——模型不参与内容创作自然不会有改写、遗漏或幻觉文本混入输出极其轻量——即使输入文档有几千行结构化输出也只是若干个小整数区间无损还原——分段正文完全从原始文档按行号切取保证与原文逐字一致。文档预处理给每一行编号为了让 LLM 能够用行号引用文档位置我们需要在送入模型前对文档做一次预处理把每一行前面加上行号标记如[0]、[1]、[2]同时维护一张行号 - 原文的映射表供后续还原使用。def doc_with_lines(document): document_lines document.split(\n) document_with_line_numbers line2text {} for i, line in enumerate(document_lines): document_with_line_numbers f[{i}] {line}\n line2text[i] line return document_with_line_numbers, line2text这个函数返回两个东西document_with_line_numbers带行号标记的文档文本直接作为用户消息发送给 LLMline2text字典{行号: 原文}在分段完成后用于把start_index–end_index区间映射回真实文本。行号从 0 开始连续编号LLM 在 system prompt 中会被告知方括号里的数字即行号。使用 Instructor Cohere 提取分段接下来是核心环节创建 Instructor 客户端让 LLM 从带行号的文档中提取StructuredDocument。import instructor # Apply the patch to the cohere client # enables response_model keyword client instructor.from_provider(cohere/command-r-plus) system_prompt f\ You are a world class educator working on organizing your lecture notes. Read the document below and extract a StructuredDocument object from it where each section of the document is centered around a single concept/topic that can be taught in one lesson. Each line of the document is marked with its line number in square brackets (e.g. [1], [2], [3], etc). Use the line numbers to indicate section start and end. def get_structured_document(document_with_line_numbers) - StructuredDocument: return client.create( modelcommand-a-03-2025, response_modelStructuredDocument, messages[ { role: system, content: system_prompt, }, { role: user, content: document_with_line_numbers, }, ], ) # type: ignore这段代码有四个要点客户端初始化instructor.from_provider(...)是 Instructor 提供的统一工厂方法传入形如cohere/command-a-03-2025的provider/model字符串即可自动完成 provider 识别、客户端创建与补丁patch注入从而让client.create支持response_model关键字。从当前仓库源码看该方法实现在 instructor/v2/auto_client.py要求模型字符串必须是provider/model-name格式并支持async_client、cache、mode等扩展参数。System prompt 设计这里把 LLM 塑造成整理讲义的世界级教育家明确要求每个 section 围绕一个可在一节课内讲完的单一概念并强调方括号内的数字是行号必须用行号标注起止。Prompt 的角色设定与输出约束直接决定了分段质量。模型选择command-a-03-2025是 Cohere 的 256k 长上下文模型足以容纳一篇完整教程command-r-plus则为相对轻量的备选。两者的选择取决于你的文档长度与预算。返回类型标注- StructuredDocument让 IDE 与类型检查器能感知返回值结构response_modelStructuredDocument则让 Instructor 在运行时完成校验与反序列化。根据起止行号还原分段正文拿到StructuredDocument后借助预处理阶段的line2text映射把每个分段的起止行号区间还原成真实文本def get_sections_text(structured_doc, line2text): segments [] for s in structured_doc.sections: contents [] for line_id in range(s.start_index, s.end_index): contents.append(line2text.get(line_id, )) segments.append( { title: s.title, content: \n.join(contents), start: s.start_index, end: s.end_index, } ) return segments注意两点实现细节循环使用range(s.start_index, s.end_index)即左闭右开区间第end_index行本身不包含在本段内分段之间不会重叠使用line2text.get(line_id, )兜底即便 LLM 偶尔给出越界行号也不会抛异常只会得到空行增强了健壮性。每个返回的 segment 是一个包含title、content、start、end的字典既便于阅读也方便后续接知识图谱、向量化索引等下游任务。完整示例切分一篇 Transformer 教程下面把上述类与函数串起来演示如何分段 Sebastian Raschka 的《Self-Attention from Scratch》教程。我们使用trafilatura包抓取并抽取网页正文from trafilatura import fetch_url, extract import instructor from pydantic import BaseModel, Field from typing import List def doc_with_lines(document): document_lines document.split(\n) document_with_line_numbers line2text {} for i, line in enumerate(document_lines): document_with_line_numbers f[{i}] {line}\n line2text[i] line return document_with_line_numbers, line2text client instructor.from_provider(cohere/command-r-plus) system_prompt f\ You are a world class educator working on organizing your lecture notes. Read the document below and extract a StructuredDocument object from it where each section of the document is centered around a single concept/topic that can be taught in one lesson. Each line of the document is marked with its line number in square brackets (e.g. [1], [2], [3], etc). Use the line numbers to indicate section start and end. class Section(BaseModel): title: str Field(descriptionmain topic of this section of the document) start_index: int Field(descriptionline number where the section begins) end_index: int Field(descriptionline number where the section ends) class StructuredDocument(BaseModel): obtains meaningful sections, each centered around a single concept/topic sections: List[Section] Field(descriptiona list of sections of the document) def get_structured_document(document_with_line_numbers) - StructuredDocument: return client.create( modelcommand-a-03-2025, response_modelStructuredDocument, messages[ { role: system, content: system_prompt, }, { role: user, content: document_with_line_numbers, }, ], ) # type: ignore def get_sections_text(structured_doc, line2text): segments [] for s in structured_doc.sections: contents [] for line_id in range(s.start_index, s.end_index): contents.append(line2text.get(line_id, )) segments.append( { title: s.title, content: \n.join(contents), start: s.start_index, end: s.end_index, } ) return segments url https://sebastianraschka.com/blog/2023/self-attention-from-scratch.html downloaded fetch_url(url) document extract(downloaded) document_with_line_numbers, line2text doc_with_lines(document) structured_doc get_structured_document(document_with_line_numbers) segments get_sections_text(structured_doc, line2text)运行之后segments就是一个按语义切分好的分段列表。例如第 6 个分段的标题与内容如下print(segments[5][title]) Introduction to Multi-Head Attention print(segments[5][content]) Multi-Head Attention In the very first figure, at the top of this article, we saw that transformers use a module called multi-head attention. How does that relate to the self-attention mechanism (scaled-dot product attention) we walked through above? In the scaled dot-product attention, the input sequence was transformed using three matrices representing the query, key, and value. These three matrices can be considered as a single attention head in the context of multi-head attention. The figure below summarizes this single attention head we covered previously: As its name implies, multi-head attention involves multiple such heads, each consisting of query, key, and value matrices. This concept is similar to the use of multiple kernels in convolutional neural networks. To illustrate this in code, suppose we have 3 attention heads, so we now extend the \(d \times d\) dimensional weight matrices so \(3 \times d \times d\): In: h 3 multihead_W_query torch.nn.Parameter(torch.rand(h, d_q, d)) multihead_W_key torch.nn.Parameter(torch.rand(h, d_k, d)) multihead_W_value torch.nn.Parameter(torch.rand(h, d_v, d)) Consequently, each query element is now \(3 \times d_q\) dimensional, where \(d_q24\) (here, lets keep the focus on the 3rd element corresponding to index position 2): In: multihead_query_2 multihead_W_query.matmul(x_2) print(multihead_query_2.shape) Out: torch.Size([3, 24]) 可以看到含数学公式与 PyTorch 代码块的整块内容被完整保留在一个分段里标题也准确概括为 Introduction to Multi-Head Attention——这正是基于语义的分段相对朴素文本切分的核心价值。同样的方法可以迁移到任意需要把复杂长文档拆分成语义块的其他领域。源码级的实现原理from_provider 与 from_cohere为了让你在排查问题或扩展功能时心里有底这里结合当前仓库源码说明这条流水线背后的实现机制。统一入口instructor.from_provider其实现在 instructor/v2/auto_client.py 中逻辑是按/拆分模型字符串得到 provider 名与模型名再从 provider 注册表instructor/v2/core/provider_specs.pyCohere 的别名注册为cohere查找对应的from_cohere工厂函数最终返回一个 Instructor 实例。它支持async_clientTrue返回异步客户端、cache注入缓存适配器、mode覆盖 provider 默认模式。Cohere 适配层instructor.providers.cohere.client是一个兼容门面真实实现在 instructor/v2/providers/cohere/client.py 的from_cohere。从源码可以看到它同时兼容 Cohere 的 V1cohere.Client/AsyncClient与 V2cohere.ClientV2/AsyncClientV2SDK并做两件事模式归一化将 Cohere 特有的工具调用模式归一化为通用的Mode.TOOLS等模式并校验该模式已在 Cohere 的注册表中注册未注册则抛出ModeError补丁注入通过patch_v2包装client.chat/client.chat_stream使create方法获得response_model结构化输出能力同时根据客户端版本自动切换消息格式——V2 使用 OpenAI 兼容的messages格式V1 使用messagechat_history格式。仓库内的可运行参考实现见 examples/cohere/cohere.py它演示了from_cohere的完整用法含temperature0以提升确定性更详细的 Cohere 集成说明安装pip install instructor[cohere]、导出CO_API_KEY等见 docs/integrations/cohere.md。使用要点与注意事项行号格式要与 prompt 约定一致预处理阶段用什么格式编号[0]起始、每行一个编号prompt 中就要如实说明两者不一致会显著降低分段准确率。区间语义要统一start_index包含、end_index排他左闭右开的约定应在Field(description)中写清楚并保证get_sections_text的range逻辑与之一致。长文档与大模型分段质量依赖模型的上下文长度与指令遵循能力。若使用其他模型或更长文档需评估上下文窗口是否足够本文使用的 command-a 系列模型具备 256k 长上下文是这类任务的合适选择。确定性优先分段属于定位类任务而非生成类任务在预算允许时建议将temperature设为 0参考 examples/cohere/cohere.py 的做法以获得稳定、可复现的边界结果。越界兜底line2text.get(line_id, )保证了极端情况下流水线不会崩溃但在生产环境建议额外校验start_index end_index并过滤空分段。延伸阅读本文的分段结果可以直接作为下游任务的数据源仓库中提供了相关配套指南知识图谱构建 —— 从文档构建知识图谱实体解析 —— 识别并对齐实体列表抽取 —— 抽取多个对象嵌套结构 —— 复杂层级模型建模from_provider 客户端配置 —— 更全面的客户端初始化方式【免费下载链接】instructorstructured outputs for llms项目地址: https://gitcode.com/GitHub_Trending/in/instructor创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表