# PDF 模型工具契约 本文描述当前源码的工具入口。工具提供可验证的材料和确定性动作;如何搜索、阅读、解释和编辑,由会话模型组合。UI 与模型调用同一 Host 领域服务和共享工作副本。只在 UI 中提供的区域 OCR 不注册模型工具。 ## 工具列表 | 工具 | 职责 | | --- | --- | | `pdf_current` | 列出本会话可见 Reader 的文档 ID、标签 ID 和当前页提示 | | `pdf_open` | 为模型取得 PDF 工作副本引用,不打开 Reader 标签 | | `pdf_close` | 释放模型引用,不关闭 UI、不删除草稿 | | `pdf_info` | 读取文件元信息,不创建持久工作副本或模型引用 | | `pdf_status` | 查询已打开文档的修订、未保存状态、冲突及实际可用能力 | | `pdf_page_read` | 读取有界页面文字,并签发可复用证据引用 | | `pdf_page_render` | 生成页面图像和可信图像句柄,不执行 OCR 或识图 | | `pdf_keyword_search` | 精确或显式归一化字符串搜索,返回具体出现位置的引用 | | `pdf_semantic_search` | 使用已配置且可用的 embedding 引擎检索原生文字、OCR 及可选图像描述 | | `pdf_evidence_read` | 重读/缩小已有证据,或解析单页、跨页连续文字范围 | | `pdf_page_jump` | 在已可见的 Reader 中跳转到页或精确引用,并记录多级回退 | | `pdf_form_inspect` | 返回同一快照的编号页图、字段类型、选项及 inspection ID | | `pdf_form_fill` | 原子填写一个已检查页面的草稿 | | `pdf_annotation_list` | 列出当前草稿中的标注、评论及精确 ID | | `pdf_annotation_create` | 根据已解析文字引用或便签区域创建标注 | | `pdf_annotation_update` | 修改已有标注颜色或评论文本,保留其身份与回复关系 | | `pdf_annotation_delete` | 删除指定可编辑标注或评论 | | `pdf_bookmark_list` | 读取 PDF 内置书签树和精确 outline ID | | `pdf_bookmark_edit` | 在一个草稿事务中新增、修改、删除或移动书签 | | `pdf_save` | 经受信用户确认后,将当前草稿覆盖保存到原 PDF | | `ocr_recognize` | 使用配置的 OCR 引擎识别一个可信图像句柄 | | `image_understand` | 使用配置的图像理解后端处理一个可信图像句柄 | 旧的 `pdf_locate`、`pdf_context`、`pdf_ocr_page` 和 `pdf_image_context` 模型入口已移除;其必要能力收敛到搜索、基础读取、证据读取及通用图像工具。不提供总结、问答、翻译等复合 PDF 工具。 ## 文档身份与生命周期 所有资源以执行中的 Agent 会话鉴权;`documentId`、`refId`、`searchId` 和 `inspectionId` 都不能代替文件访问权限。 `working-copy` 是唯一的打开文档表。同一会话、同一规范路径,无论先通过模型还是 UI 打开,都获得同一份文档、草稿、修订和撤销历史。重复的模型打开取得同一个模型持有者,不因工具重试无限增加引用。UI 标签、模型持有者和执行中的操作分别持有引用。 `pdf_close` 仅释放模型持有者。引用归零后可以回收常驻字节和 native 资源,持久草稿仍保留;空闲回收不授权模型关闭用户视图或删除未保存修改。Host 重启后重新打开以获取有效 ID。 `pdf_info` 检查文件本身的元信息,不登记长期模型所有权;`pdf_status` 检查工作副本当前状态,不能用重复打开来替代状态查询。能力报告中的 `supported/available/reason` 是观察结果,执行时仍重新验证。 ## 搜索、读取与证据 `pdf_keyword_search` 搜索字面原文,归一化匹配须显式选择。`pdf_semantic_search` 用于概念、问题或不同表述;没有可用 embedding 引擎时报告不可用,不把关键词结果伪装成语义检索。语义索引复用统一 `PageContent` 的原生文字、OCR 文字及可选图像描述,并保留来源位置。图像首版为整页 `visual-region`,不声称检测出独立图表、表格或公式区域;`pdf_page_read` 与关键词搜索保留各自的读取和精度策略。 开始语义搜索前,先用该 `documentId` 调用 `pdf_status`,仅在 `capabilities.semanticSearch` 为 `true` 时调用 `pdf_semantic_search`。为 `false` 时读取 `capabilityDetails.semanticSearch.reason`;引擎关闭、缺失或不可用时,组合 `pdf_keyword_search` 的相关词搜索与 `pdf_page_read` 的上下文阅读,并明确这是关键词检索和阅读判断,不能称为语义搜索结果。未经用户明确要求,不启用、切换或修改 embedding 引擎及其配置。`pdf_current` 的文档和页提示不代表语义搜索可用。 语义检索已启用且引擎可用时,阅读器或 `pdf_open` 打开文档会触发 Host 后台预建;源字节、页范围、预处理身份和 embedding 配置匹配时,可与搜索及其他已授权会话共享可复用索引、缓存及在途构建。权限、工作副本草稿、查询/续读句柄、结果和证据引用仍按会话隔离,每次访问独立授权;索引不能充当文件访问凭据。`indexScanPages`(文字识别索引)默认开启,在扫描页或损坏原生编码页上调用 `ocrEngine`;`indexImageDescriptions`(视觉描述索引)默认关闭,可与文字识别同时开启。视觉描述复用 `imageEngine` 与 `engineConfigurations.image`,通过索引提示词生成整页描述文字再索引,并非直接嵌入原图;没有独立的视觉描述引擎选择。缺失或不可用的引擎明确报错,不自动切换提供方。描述增加模型调用、耗时和可能的费用。两项本身不启用 embedding。预建不生成查询向量,模型无需占位搜索预热;远程服务可能在预建时收到页图或抽取/派生文字。OCR 成功但空输出表示未识别出文字,不算失败。若预建尚未完成,搜索仍可能等待剩余进度并返回 `hasMore`。一会话关闭最后引用或取消预建,不影响仍有其他订阅者的构建;全部订阅取消后才终止共享构建。普通标注修改不会重复建立未变化的内容索引,常驻索引及持久缓存仍可按预算淘汰。 OCR/图像后端未提供稳定 `cacheIdentity` 时,派生缓存按会话和注册期隔离;提供稳定身份后才允许跨重启复用,实际图像输入及模型/配置变化仍分离缓存。表示 ID 和块 ID 只是内容身份,不授予文件访问,也不替代会话证据 `refId`。 用户可从工具栏手动启动或重试当前文档索引,在任务入口查看当前资源会话内已打开 PDF 的文件名、页面读取及向量分段进度,并取消任务。绿色“索引已就绪”持续显示,直到 Host 移除已关闭文档的任务;其他短暂保存通知仍会消退。取消后,本会话相同来源和配置在本次打开期间不会自动恢复;手动启动、来源或配置变化、关闭后重新打开可恢复预建。该入口使用会话隔离的 `pdf.indexing` RPC,不增加模型工具。 两种搜索首调传 `documentId`、`query` 和范围等初始参数。返回 `searchId` 与 `hasMore`;续调只传同一工具返回的 `{searchId}`。Host 保留查询、范围、版本和进度。未知/过期/错误工具的 ID 必须重新搜索。未完成覆盖不能据此断言无结果或唯一结果;相关检索处理完请求范围后对可用内容统一排名,局部失败时仍可能有候选。 若可选 OCR/图像描述失败但已有有效原生或 OCR 文字,索引保留可用内容,返回 `status: incomplete` 和 `coverage.failedPages`,可以带排名候选。此时 `hasMore: false` 只表示没有后续构建步骤,不表示覆盖完整,也没有可继续的游标;不要重复续调,须显式重建该文档以重试失败步骤。不能宣称索引整体就绪。没有任何有效文字的失败页仍走原有自动重试;成功但空白的 OCR 输出不算失败。 语义结果的 `coverage.qualityWarnings` 按相同问题聚合:`message` 仅出现一次,`affectedPageCount` 是受影响页数,`candidatePages` 精确列出本次命中中受影响的页,`sampleOtherPages` 最多列出 8 个其他页,`omittedPageCount` 明确报告未列出的页数。最多返回 8 类警告,优先呈现影响当前候选的类型;更多类型以 `qualityWarningsOmitted` 汇总,不能把省略当作无问题。`complete` 表示请求范围的索引已完成且无失败页,不表示每个字符都已准确解码;字符缺失与 `failedPages` 的读取/预处理失败分别报告。质量信号按页统计,是否影响具体引文仍需结合原文核对。对相关页的关键文字或符号有疑问时用 `pdf_page_read` 补读,必要时用 `pdf_page_render` 视觉核对。 读取与搜索返回统一的 `refId`。原生文字引用指向某个版本中的具体 UTF-16 文字范围,不能把相同文字的其他出现位置当成该引用。OCR 与图像描述引用保留派生文字快照及近似页/区域位置,不能冒充原生精确文字,也不能直接用于精确文字标注。引用的范围可能大于用户想标记的段落,必须先阅读确认;派生来源需要精确标注时,先重新读取可验证的原生文字,或使用适合该页的区域/页级材料。 `pdf_evidence_read` 有两种输入: ```json {"documentId":"doc-id","refId":"ref-id","quote":"可选:引用内部唯一的精确子段"} ``` ```json { "documentId":"doc-id", "range":{ "start":{"page":3,"quote":"唯一的起点原文"}, "end":{"page":5,"quote":"唯一的终点原文"} } } ``` 连续范围包含起止之间的全部页和文字,最多十页、8,000 UTF-16 字符。Host 验证来源、页文字和原生几何;阅读顺序不明确、引文歧义、文字映射不可靠或范围过大时拒绝,不猜坐标。缩小或解析成功后用返回的最终 `refId` 创建标注和跳转。 证据读取也用于重放保存的图像或派生文字,不能用重新 OCR/识图代替同一次结果。Host 重验会话、文档版本、源文件和附件摘要;已失效的来源应重新读取。OCR 或辅助视觉产生的是派生文字,不冒充精确原生文字,不可直接用于文本标注。 列表工具沿用各自的 `nextCursor/cursor`,游标必须原样传回;文档变化后重新列举。搜索会话与列表游标不可混用。 ## 图像、OCR 与区域交互 模型调用链: ```text pdf_page_render → imageHandle → ocr_recognize / image_understand ``` 页图生成仅生产图像和句柄。当前模型支持视觉时可接收图片自行理解;文本模型也可取得句柄交给后端,但不能声称看过图片。OCR 只提取文字;图像理解通过所选引擎实际执行“图片 + 提示词 → 文字”,均报告来源、限制和有界结果。内置 `dsh-vision` 可跟随会话或指定视觉模型;直接把图片交给视觉会话属于会话编排,不是另一种引擎,也不自动回退到 OCR。 `ocr_recognize` 首调传 `imageHandle`;`image_understand` 首调还需 `question`。两者都可设 `maxChars`。结果返回 `resultId`、`offset/nextOffset`、文字、来源及可用的证据;截断时用同一个工具的 `{resultId,offset:nextOffset}` 继续,不再传图像句柄或问题,也不重新发起识别。结果句柄有会话、工具、期限和来源约束;缺失或过期时重新生成图像/结果。OCR 几何若存在,坐标属于输入图像像素,不是可直接写入 PDF 的位置。 UI 区域 OCR 使用同一个识别服务:框选、裁剪和页面坐标映射由 Reader 链路处理,识别、取消、错误、限额及引擎选择共用 Host 服务。模型工具没有区域 OCR、`region` 或 `bbox` 参数。 普通页图不合成未保存的 Reader 草稿覆盖层,不能用它证明未保存表单值或标注外观已正确。图像生产与派生结果始终携带实际输入的来源约束。 ## 标注、书签与导航 模型新增文本标注只接受已解析引用: ```json { "documentId":"doc-id", "expectedRevision":4, "subtype":"Highlight", "target":{"kind":"reference","refId":"resolved-ref"}, "comment":"可选评论" } ``` 独立便签仍使用 `subtype: "Text"` 和 `target: {kind:"pdf-region",page,rectPdf:[left,bottom,right,top]}`。标注工具不再接受单页引文、起止引文或引用内子引文;这些先交给证据工具。一次连续范围的所有页标注仍属于同一个可撤销事务,失败不部分提交。写入时重新验证来源、修订和原生字形,不能把“已解析”当成永久有效。 完全相同的已有标注可以复用;不同颜色/评论或部分重叠会报告已有 ID,供列表检查或更新。修改、删除使用 `pdf_annotation_list` 返回的完整 ID 和当前修订;更新不通过删后重建实现。评论回复先于父标注删除。 `pdf_bookmark_list` 仅列内置目录。`pdf_bookmark_edit` 接受结构化 `operations` 数组,一次 1–100 项 `outline-add/update/delete/move`。新增可指定父节点与插入位置;删除包括子树;移动位置按移出原子树后计算。仅改名或移动保留既有目的地、外链、样式和折叠状态,显式修改 `page` 才重设目的地。 `pdf_page_jump` 消费最终引用,或物理页码与当前 `contentVersion`;不隐式打开标签。同一文档多个 Reader 时用 `pdf_current` 的 `tabId` 明确目标。成功跳转进入相同的多级阅读回退历史;读取、检索和编辑不自动导航。 ## 表单与保存 `pdf_form_inspect(documentId,page,includeValues?)` 返回两种内容块:精简结构化文本和编号页图。小型透明底 F 编号位于真实可写控件内;图像隐藏 widget 外观、填写值与标注。文本包含字段的原生共享键、类型、`valueType`、约束、选项和编号;`includeValues:true` 才返回实际草稿值。编号和映射来自同一快照,不由模型自己拼接。 `pdf_form_fill` 只接收一个 `inspectionId`、原始 `expectedRevision` 和 `fields:[{label,value}]`,不传页号。文本/单选/下拉为字符串,复选框为布尔,选项列表为字符串数组;清空分别用 `""`、`false`、`[]`。只能填写已暴露的真实可写控件;打印文字不代表控件。共享字段只提交一次。 单页写入是事务边界,不是理解边界。填写前理解相关说明、标题、主体、单位、日期格式及适用条件,必要时补读前页。模型可声明条件与互斥组,Host 只校验所提供规则和类型,不推断业务语义或伪造缺失信息。语义不明时阅读或询问。 可先检查多页再独立填写。Host 排队验证映射和修订;已知独立页填写可以保留无关检查,目标页、共享字段、依赖、UI 编辑、撤销重做或来源变化要求重新检查。每个单页批次一起成功或失败。填写后重新检查实际值;成功写入不代表语义正确。 所有修改直接进入人和模型共用的未保存草稿,不增加预演或人工确认。`pdf_save(documentId,expectedRevision)` 才通过受信交互渠道请求覆盖原文件的批准,再次核对修订、差异和源摘要后原子保存。模型不能以 `approved:true` 授权自己。另存为仍由 UI 路径选择器处理。 ## 验证边界 每次修改应覆盖:工具注册与严格参数、跨会话拒绝、旧引用与源变更、取消/卸载、输出限额、跨页范围完整性、批量失败零修改,以及 Reader 与模型共享草稿。领域测试、DSH 集成、真实 PDFium 和浏览器交互分别验证其边界;代码中的能力声明不等于真实模型或外部阅读器已验收。