projectName 最新一份公告,KV 的 key-value 模型比 D1 更轻量;Worker 现有 ADMIN_TOKEN 鉴权可复用到公告管理接口。UpdateNotifier,版本检查间隔为 30 分钟;可在同一个定时器里并行检查更新和远程公告,但更新继续用 Toast,公告使用独立 Dialog,避免同时触发时互相覆盖。MarkdownRenderer,公告渲染必须传 allowRawHtml={false},避免远程公告里的 HTML 被渲染成 DOM。requestJson() 默认 GET + Bearer token;公告发布需要扩展为支持 POST/DELETE JSON body。DELETE /api/notice 会触发浏览器 CORS 预检,Worker 的 Access-Control-Allow-Methods 必须包含 DELETE,否则 Dashboard 停用公告会失败。clientCreatedAt 和配置枚举字段,保持旧数据兼容。analytics.ts 只支持 app_open/page_view,并从 user_config 读取 analytics_client_id;可继续通过 window.yibiao.config.load() 读取 analytics_created_at 和配置项。/api/latest?limit=50 调用,Worker 已改为分页 page/pageSize=10,需要同步页面分页控件。blob8 的 analytics_created_at 作为 cohort 日期;为兼容旧事件和异常空值,SQL 中使用 toDateOrNull(blob8),旧数据不会进入留存口径但仍可进入总客户端/活跃客户端。DocumentAnalysisPage 仍是“导入文件 + AI 解析项目概述/评分要求”的旧交互,导入后没有 Markdown 渲染原始提取内容。client/electron/services/fileService.cjs 只用 mammoth.extractRawText 和 pdf-parse 做纯文本提取,未按配置中的 file_parser.provider 分流,也未使用 tools/doc2markdown-node 的 Markdown 还原逻辑。file_parser.provider 已存在,值为 local、mineru-accurate-api、mineru-agent-api,但文件解析服务没有读取配置。tools/doc2markdown-node/src/convert.js 是 ESM,实现包括 Markdown 编码识别、DOCX->HTML->GFM Markdown、PDF 文本/表格提取、DOC/WPS 经 LibreOffice 转 DOCX。要“100%还原”本地解析,应复用该模块而不是重写简化版。POST https://mineru.net/api/v1/agent/parse/file 获取 task_id/file_url,PUT 上传,GET /parse/{task_id} 轮询,完成后下载 markdown_url。无需 Token。POST https://mineru.net/api/v4/file-urls/batch 带 Bearer Token 获取 batch_id/file_url,PUT 上传,GET /extract-results/batch/{batch_id} 轮询,下载 full_zip_url,从 zip 中读取 full.md 或任意 .md。tools/doc2markdown-node/src/convert.js 复制到 client/electron/services/doc2markdown/convert.mjs,运行时不再依赖 tools/ 目录。react-markdown、remark-gfm、rehype-raw,用于展示 GFM 表格和 DOCX 转换保留下来的 HTML 表格。TechnicalPlanHome 内存状态;切换到设置页会卸载页面导致丢失。technicalPlanStorage.ts 使用 Renderer localStorage,不适合保存招标文件 Markdown 这类大文本,应迁移到 Electron Main 的 userData 文件。electron/ipc/index.cjs,preload 暴露集中在 electron/preload.cjs,Renderer 类型来自 src/vite-env.d.ts 引用的 shared/types。ai.chat() 只能一次性返回;已新增 OpenAI-compatible SSE 解析通道,通过 ai:stream-chat IPC 和 window.yibiao.ai.streamChat() 向 Renderer 推送 chunk。backend/app/services/outline_service.py 和 backend/app/utils/prompts/outline_prompts.py:自由模式包含一次性生成、失败切分步生成、审核和二次生成;对齐模式先提取技术评分大类,再按大类生成二三级目录并审核。已迁入 client 的 outlineWorkflow.ts 与 outlinePrompts.ts。useTechnicalPlanWorkflow 中跳过运行中任务状态下的 debounce/卸载保存,避免写入竞争。OpenAIUtil.collect_json_response() 的完整链路;后端每一步 JSON 调用都在同一函数内执行解析、Pydantic schema 校验、业务 validator、JSON 修复和最多 3 轮重试,而 client 此前把业务校验放在 requestJson() 外部,导致校验失败不能进入修复/重试。/api/content/generate-chapter-stream 的契约很轻:请求包含 chapter、parent_chapters、sibling_chapters、project_overview,服务端用 build_chapter_content_messages() 后以 temperature=0.7 流式返回纯正文 chunk。frontend/src/pages/ContentEdit.tsx 已实现可参考的叶子节点收集、父级章节查找、同级章节查找、5 并发生成和 Word 导出 payload 构造;但旧版依赖浏览器 SSE、file-saver 和本地草稿缓存,client 需要改为 Main 后台任务与工作区文件存储。/api/document/export-word 的 payload 是 { project_name?, project_overview?, outline },其中 outline 节点包含 id/title/description/children/content;导出服务只对叶子节点渲染 content,Markdown 支持标题、列表、表格行、粗体/斜体/代码。exportService.cjs 是未实现占位;preload.cjs 已暴露 window.yibiao.export.exportWord(payload),但 Main 侧还需要实现保存对话框和 docx 写入。.content-shell 原先使用底部大 padding 给 toolbar 预留空间,技术方案页通过 :has(.technical-workbench) 再额外处理;Step02 和设置页也有页面级底部 padding,会造成内容高度被压缩且不符合悬浮覆盖要求。FloatingToolbar 同时可由 AppShell 和页面内部渲染;将 .content-shell 设为相对定位和隐藏溢出后,页面内部 toolbar 可以继续相对内容区域悬浮,页面内容则由各自根容器或工作区承担内部滚动。FB208/OpenBidKit_Yibiao,electron-builder 可直接使用 GitHub provider 上传安装包和 latest.yml / latest-mac.yml 更新元数据。electron-updater 在普通 Node 环境 require 会访问 Electron app,因此必须在 app.isPackaged 后懒加载;开发模式跳过更新检查。winCodeSign 资源编辑链路,当前 Windows 用户没有创建符号链接权限会导致解压失败;关闭 win.signAndEditExecutable 后 NSIS 安装包验证通过。gh release create 创建了正式 Release,但 electron-builder --publish always 默认以 draft 发布类型工作,日志显示 existingType=release publishingType=draft,因此所有安装包、blockmap 和 latest*.yml 都被跳过上传。Full Changelog 根因:GitHub --generate-notes 在没有可识别 PR/分组内容时只生成比较链接;改为 workflow 内显式用 git log 生成提交列表更可控。Build renderer 报 TS2688: Cannot find type definition file for 'plist':干净 CI 环境或重跑旧 tag 时缺少 @types/plist,但 TypeScript 会从 electron-builder 相关类型链路发现 plist 类型引用;已将 @types/plist 显式加入 devDependency,并在 workflow npm ci 后 npm install --no-save @types/plist 以兼容重跑旧 tag。Re-run all jobs 会重跑旧 run 当时的 workflow 文件和 tag 提交,不会使用 main 上后来修复的 workflow;要补发已有 tag,必须用 workflow_dispatch 从 main 手动运行并输入 tag。Yibiao-${version}-${os}-${arch}.${ext}。-2.0.1-win-x64.exe SHA256 与 GitHub Release digest 一致,7-Zip 可识别为 NSIS/Electron 安装包;本地启动后进程保持运行且有响应窗口标题,说明安装器没有崩溃。用户看不到窗口更可能是安全扫描延迟、窗口在后方/任务栏,或旧产物以 - 开头导致识别体验差。winCodeSign 解压权限问题关闭了 win.signAndEditExecutable,导致 electron-builder 不会把 assets/icon.ico 写入 exe 资源;macOS 则缺少 assets/icon.icns。已恢复 Windows exe 资源编辑,并在 macOS workflow 中用 sips + iconutil 从 assets/icon_256.png 生成 assets/icon.icns 后打包。contentGenerationTask.cjs 里流式生成纯正文;ContentEditPage 已用 ReactMarkdown + remark-gfm 展示 Markdown,因此表格可直接由正文 Markdown 承载。image_model 下增加状态字段,并由设置页测试结果写回配置。yibiao-asset://generated-images/... 可避免把大 base64 长期写入 technical_plan.json。pako: 编码不是直接压缩 Mermaid 源码;它会先反序列化 { code, mermaid } JSON 状态,再用 pako inflate 解压。客户端应使用 zlib.deflateSync(JSON.stringify({ code, mermaid: { theme: 'default' } })) 后转 base64url,不能用 deflateRawSync 直接压缩源码。mermaid 代码块;Renderer 通过动态导入 mermaid 本地渲染预览,Word 导出时 Main 再通过 mermaid.ink 转 PNG 并上报进度。docx 包默认只声明 png/jpeg/jpg/bmp/gif 图片 content type,不包含 WebP;生成图如果保存为 .webp,导出前应在 Electron Main 运行时用 nativeImage 转 PNG。rehypeRaw 后,Word 导出需要处理常见 HTML 节点,否则会出现预览可见但导出丢失或降级的问题;当前已覆盖 br/img/table/list/blockquote/strong/em/code 等常见标签。& 多节点连接简写、未加引号的中文节点标签在 mermaid.ink 可返回 PNG,但前端 mermaid.render() 仍可能报 lexical error。因此生成侧校验除了 mermaid.ink 渲染结果,还需要加前端兼容性规则,要求中文标签写成 A["中文"]、不用分号、不用 & 简写。remark-gfm 解析前的原始 Markdown 形态:模型可能把标题和表格写在同一行,或把多行表格压缩成一行;导出层先拆分并补空行后,压缩表格 smoke test 可生成真实 <w:tbl>。numId。| :--- | ... | :--- | 每日运行简报 | ... |。按表头列数拆出分隔行和后续数据行后,remark-gfm 能正常生成 table AST。contentPlans 只存在于 contentGenerationTask.cjs 的运行时 Map 中,任务结束后不会写入 technical_plan.json。需要将最终配图决策持久化为 contentGenerationPlans,单章才可跳过重新编排。planAll()。htmlNodeToDocxBlocks() 原先把 div/section/article 统一走 htmlInlineRuns(),会让容器内的 table/ul/ol/blockquote/img 等块级内容丢失 Word 原生结构。修复后仅当容器存在块级子节点时递归到 htmlNodesToDocxBlocks(),纯内联容器仍保持单段落输出。| :--- | ... | 被拆成 | :--- | ... |,导致表头/分隔行/数据行缩进不一致,remark-gfm 不能把它识别成嵌套表格。保留空白前缀缩进后,列表内表格可以导出为 Word 原生表格。filtered_blocks.json;筛后内容编号为 block;AI 先全文抽条目,再用全文 + 首轮条目做补充;程序合并并生成 ID;页面输入每批匹配条目数;分批提交固定全文 + 当前批次条目做强相关匹配;遗漏 block 走最多两轮补漏;最终程序回填正文。knowledgeBaseService.cjs 上传后立即后台处理文档:复制原文件、转换 Markdown、按 B00001 旧块切分、分 chunk 让 AI 直接输出 items + source_block_ids,随后程序回填正文写 items.json。需要替换为“准备分析 -> 用户输入每批条目数 -> 继续匹配”的两阶段流程。list/createFolder/uploadDocuments/readMarkdown/readItems/onEvent,需要增加读取分析快照/报告和启动分批匹配的接口。pending/copying/converting/analyzing/saving/success/error 状态;新流程需要新增候选条目已生成但未匹配的状态,以及报告字段。aiService.collectJsonResponse() 已提供 JSON 解析、修复和最多重试能力,知识库新流程应继续复用它;开发者模式下 AI 日志会保存完整请求,可用于验证稳定前缀和 prompt cache 结构。KnowledgeBasePage.tsx 当前已经有内部“列表/条目详情/Markdown详情”状态切换,适合继续扩展出分析调试详情页,不必引入 URL 路由。content.md、blocks.json、filtered_blocks.json、candidate_items.json、match_result.json、report.json、items.json。其中 match_result.json 保存批次匹配、补漏、新增条目、AI 舍弃和 system_discarded_after_retry。startMatching。user 多消息:全文 block 是第一条 user message,任务要求和当前批次变量放后续 user message;不再使用 system prompt,避免 system 内容破坏前缀缓存收益。logs/knowledge-base/<documentId>.jsonl,用于定位“卡住”发生在 AI 调用前、AI 调用后、normalizer/validator、状态更新还是保存阶段。outline-mode-switch,generateOutline() 直接用 outlineMode 启动后台任务;知识库列表可直接通过 window.yibiao.knowledgeBase.list() 获取 folders/documents,其中 KnowledgeDocument.status === 'success' 可作为可选条件。startOutlineGeneration({ overview, requirements, mode }) 保持不变,参考知识库文档 ID 只先保存到前端状态。K000001 递增,多文档之间会重复;目录节点的 knowledge_item_ids 必须保存为 document_id::item_id 才能在后续正文生成阶段唯一定位。client/electron/services/outlineGenerationTask.cjs,且已使用 collectJsonResponse 做修复/重试;需要直接改该文件,而不是旧 Renderer outlineWorkflow.ts。generateChildrenMessages() / generateAlignedChildrenMessages() 注入按一级目录筛选的知识条目,且自由模式检测到知识库后强制走 fallback;这会导致同一知识条目跨一级目录重复引用,必须由后置全局 Patch 替代。knowledge_item_ids;Patch prompt 使用纯 user 多消息,normalizer 拒绝完整 outline,只接受二三级 bindings 和一级/二级 parent additions;应用后再统一重编号并全局去重。knowledge_item_ids 的根因已确认:模型照抄 prompt 示例中的 document_id::K000001 占位 ID,而真实 ID 是 doc-00665a28-...::K000001;同时模型把部分 additions 挂到三级目录,违反“不能新增四级目录”的规则。新实现通过真实白名单提示和严格校验修复该类问题。bindings 和 knowledge_item_ids,与当前目标“只补缺失三级目录”冲突。新方案应完全不向模型暴露知识 ID,只把知识条目的标题/摘要作为参考文本。knowledge.item_ids 自然产生,不需要全局唯一性约束。id/title/resume,不提交 content;当前实现只落盘 knowledge.item_ids,正文生成阶段暂不消费知识库正文。knowledge.item_ids 定位条目,但给正文模型只传 content;知识库素材放在项目概述之后、章节上下文之前,可让相同素材组合在不同章节间尽量复用服务商 prompt cache 前缀。查看原文 和全文 Markdown 内容渲染阶段;openDocument() 的点击、IPC 读取、JSON 解析、setItemsPreview、条目列表整体渲染和下一帧可见尚未被记录,这正是继续定位用户感知慢点的缺口。document-items trace 后,条目页会记录 click:open-document、ipc:read:start/end、items:metrics、state:set-items-preview、React Profiler、dom:commit、dom:next-frame-visible 和 Long Task;可与 document-markdown、item-source trace 直接对比。sourceItem ? 原文页 : 列表 会用原文视图替换条目列表,列表 DOM 被卸载;改为 Dialog 后列表保持挂载,关闭原文不会丢失滚动位置。preserveImages: true;元数据横向对比仅覆盖投标文件。DOCX 元数据来自 docProps/core.xml、app.xml、custom.xml,PDF 元数据来自 pdf-parse 的 getInfo(),DOC/WPS 当前正文可通过既有 LibreOffice 链路提取,元数据先保留文件系统信息。created_at/modified_at/accessed_at/created/modified 同一天出现于多份投标文件时用橙色高亮;非时间类重复仍用红色高亮。.doc/.wps 元数据增强可通过 cfb 读取 OLE Compound File,重点流名是 \u0005SummaryInformation 和 \u0005DocumentSummaryInformation;前者覆盖作者、最后作者、模板、创建/修改/打印时间、页数、字数、应用程序等,后者覆盖类别、公司、管理者、应用版本、字节/行/段落/字符数、自定义属性等。wps:* 疑似字段,并在 UI 中标为“疑似 WPS 用户/账号”。Title/Author/Creator/Producer;pdf-parse 的 getInfo() 同时返回 info、可迭代 metadata、fingerprints、permission,原始 PDF 字节中还可能有增量更新残留的 /Author、/Creator、/ModDate 等记录。.doc/.wps 采用双来源读取更稳:原始 OLE/HPSF 属性优先,LibreOffice 转 .docx 后的 docProps/* 作为补充;转换失败时仍保留 OLE 已读字段并记录 metadata_error。duplicateCheckService.cjs 已在 Step02 并发提取正文和元数据,正文保存到 userData/workspace/duplicate-check/contents/<fileId>.md;目录分析应复用这些 Markdown,不重新解析原文件。# 标题次之,短行编号/粗体/第X章等语义标题兜底。duplicate-check/contents/*.md:先移除 Markdown/HTML 图片,再按句号、问号、感叹号、分号和换行拆句;投标文件内先去重,再用全局 Map 聚合跨文件重复,避免文件两两全文比较。yibiao-asset://imported-images/...,Main 侧可根据协议根目录解析回文件并计算 SHA256,筛出完全一致图片。<table>,而正文分句前直接 replace(/<[^>]+>/g, ' ') 删除 <td>/<tr>/<p> 等结构标签,导致“无偏离”“2”“供应商的报价应包括...”等不同单元格被压成同一段文本。isLikelyMergedTableSentence() 这类按“无偏离/特别要求/交货地点”等业务词跳过整句的规则属于特例过滤,会误伤真实商务响应内容;正确修复是保留表格结构边界,让单元格独立分句。<td>/<th> 单元格提取,单元格内 <p>/<li>/<br> 作为内部边界;Markdown 管道表按 GFM 表格行列解析;普通 Markdown 段落再按自然句末符分句。9e2770208cb01a044c6e6eaff29328cc83faf75c.md 中“供应商的报价应包括...”前后块已变为 无偏离、2、正文单元格、投标应答单元格、无偏离、3,模拟重复分析中 无偏离\s*\d+、表头串联类异常重复句数量为 0。特别要求:交货时要求供应商就所投产品提供产品说明书... 未被招标白名单排除的根因不是招标原文缺失,而是招标文件分句为 3.特别要求:...,投标文件分句为 特别要求:...,此前 normalized 完全相等匹配无法跨过句首序号差异。GB/T 29768-2013、30天、3年、第2包 等业务数字。3. 后的目标句,投标文件中该目标句命中白名单 5 次,模拟重复分析 targetInDuplicates=false;总重复句由 132 降到 116。1. 供应商资格要求证明文件、(四)法定代表人资格证明书 等仍出现的原因不是 normalized 未去序号;当前缓存中这些项的 normalized 已去序号,UI 展示的是保留原文的 sentence。真正问题是此前先用原句做 isInformativeContentSentence(),短标题加上序号后长度达标进入投标重复池,而招标文件同名无序号短标题长度不足没有进入白名单。normalized 再判断信息量;同时句首序号剥离支持 Markdown 转义 1\.、全角数字、括号/圈号后额外分隔符和 第一章/第1节/第二部分。真实缓存模拟结果:招标白名单句子数 717,投标正文句子数 6167,命中招标白名单 1997,重复句 105,截图短标题重复项 0,normalized 句首序号残留 0,目标“特别要求”重复项 0。