From a95e27a29024ed6982f48e93e092af2b7bffff08 Mon Sep 17 00:00:00 2001 From: YANG JIANKUAN Date: Tue, 3 Nov 2026 19:32:25 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=AF=BB=E6=A0=87=E8=A1=A5=E9=BD=90?= =?UTF-8?q?=E4=BA=94=E7=B1=BB=E6=B8=85=E5=8D=95=E4=B8=8E=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E5=9F=BA=E6=9C=AC=E4=BF=A1=E6=81=AF=EF=BC=8C=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=9B=BD=E5=8A=A8=E6=A0=B7=E6=9C=AC=E6=9A=B4=E9=9C=B2=E7=9A=84?= =?UTF-8?q?=204=20=E4=B8=AA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 文字层 v2:页眉页脚带单独成句;写到页尾的句子和下一页首句接续(出处两页各一条) - 结构识别(最小版):十进制编号、冒号引出的列表找上级条款;上级条款写明无效的,条目照样认定 - 抽取:资格、评分、日期与递交、技术参数、项目基本信息各一份提示词;实质性条款出 v2 (认上级条款、补「做无效响应处理」等写法);各类取值核对;按首句合并; 拆开被合成一条的列举条款;描述标注方式的句子和表头碎片不当条款;* 号标注 - 项目:解析出的名称、编号、采购人、预算、截止时间、分包写回项目,用户改过的不覆盖 - 前端:清单项显示分值、参数要求、日期、证明材料 - 测试 40 个,新增两页虚构样例 Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 3 +- apps/web/src/components/ChecklistItemRow.vue | 27 +- apps/web/src/lib/labels.ts | 38 + docs/architecture/api-design.md | 2 +- docs/architecture/architecture.md | 7 +- prompts/extract.project_info/v1.md | 69 ++ prompts/extract.qualification/v1.md | 59 ++ prompts/extract.schedule/v1.md | 58 ++ prompts/extract.scoring/v1.md | 57 ++ prompts/extract.substantive/v2.md | 56 ++ prompts/extract.tech_param/v1.md | 63 ++ server/src/bid_doctor/ai/providers/base.py | 2 +- server/src/bid_doctor/ai/providers/fake.py | 165 +++- .../bid_doctor/ai/providers/openai_compat.py | 4 +- server/src/bid_doctor/ai/service.py | 2 +- server/src/bid_doctor/checklist/extract.py | 788 +++++++++++++++--- server/src/bid_doctor/checklist/models.py | 1 + server/src/bid_doctor/checklist/service.py | 126 ++- server/src/bid_doctor/document/citations.py | 54 +- .../bid_doctor/document/pipeline/runner.py | 61 +- .../bid_doctor/document/pipeline/structure.py | 68 ++ .../document/pipeline/text_layer.py | 92 +- server/src/bid_doctor/project/extracted.py | 119 +++ server/src/bid_doctor/project/models.py | 24 +- server/src/bid_doctor/project/service.py | 46 +- server/src/bid_doctor/project/workbench.py | 4 +- server/tests/fixtures/tender-full.pdf | Bin 0 -> 235656 bytes server/tests/test_parse_flow.py | 41 + server/tests/test_units.py | 165 +++- 29 files changed, 1971 insertions(+), 230 deletions(-) create mode 100644 prompts/extract.project_info/v1.md create mode 100644 prompts/extract.qualification/v1.md create mode 100644 prompts/extract.schedule/v1.md create mode 100644 prompts/extract.scoring/v1.md create mode 100644 prompts/extract.substantive/v2.md create mode 100644 prompts/extract.tech_param/v1.md create mode 100644 server/src/bid_doctor/document/pipeline/structure.py create mode 100644 server/src/bid_doctor/project/extracted.py create mode 100644 server/tests/fixtures/tender-full.pdf diff --git a/AGENTS.md b/AGENTS.md index 80d2407..c9d30b2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -196,4 +196,5 @@ samples/ - 2026-11-03:物理数据模型 v0.1(`docs/architecture/data-model.md` + `schema-draft.sql`,40 张表,PostgreSQL 16 上 16 项行为测试通过)。DM1–DM7 全部按建议确认:句子放对象存储不进库、外键都带租户、每个项目一把数据密钥、手机号不存明文、规则问题可以只挂规则编号、清单新增「报价」类别。 - 2026-11-03:接口契约 v0.1(`docs/architecture/api-design.md` + `openapi.yaml`,OpenAPI 3.1,64 个路径、81 个操作),覆盖 MVP 原型 13 页的主流程,运营后台另行定义。API1–API5 全部按建议确认:骨架搭好前以 `openapi.yaml` 为准,之后以 FastAPI 生成的快照为准;事件流用 fetch 读;异地登录确认后挤掉旧设备;扣点说明和实际不一致时拒绝、不扣点;报告后台生成。 - 2026-11-03:工程骨架完成(分支 `feat/engineering-skeleton`)。「上传 → 解析 → 显示清单」最小链路在真实样本 S02、S06 和虚构样例上跑通:分片上传、按文件头识别格式、Gotenberg 转 PDF、文字层与稳定句子编号、实质性条款抽取(假模型 + 三道校验 + 标注类规则兜底)、并入清单;额度预扣 / 结算 / 退回、行级安全策略、对象加密都有测试。后端 30 个测试、pyright 严格模式、import 规则全部通过;前端 5 个页面按设计规范 v0.2 实现,类型检查和构建通过。搭建中的调整记在架构 §13(SK1–SK8),SK1、SK2 待确认。 -- 下一步:读标的修改类接口(采纳、修改、删除、确认清单)和 pdf.js 原文查看器;之后是查标(M3)。骨架留下的 TODO 见架构 §13 末尾。 +- 2026-11-03:测试环境接上真实模型(DeepSeek,配置在不进 git 的根目录 `.env`)。读标补齐五类清单和项目基本信息的抽取,修了国动样本暴露的 4 个问题(标注规则碎片、页码混入与跨页拆分、无效写法漏认、上级标题下的无效情形),见架构 §13 SK9、SK10。 +- 下一步:在 S01–S10 上做标注集评测(各类召回率、准确率),再做读标的修改类接口和 pdf.js 原文查看器;之后是查标(M3)。 diff --git a/apps/web/src/components/ChecklistItemRow.vue b/apps/web/src/components/ChecklistItemRow.vue index a87d7ae..42e1a35 100644 --- a/apps/web/src/components/ChecklistItemRow.vue +++ b/apps/web/src/components/ChecklistItemRow.vue @@ -5,7 +5,7 @@ */ import { computed } from "vue"; import CitationBlock from "./CitationBlock.vue"; -import { category, IMPORTANCE_MARK, IMPORTANCE_NAME, pendingReason } from "@/lib/labels"; +import { category, IMPORTANCE_MARK, IMPORTANCE_NAME, pendingReason, specFacts } from "@/lib/labels"; import type { ChecklistItem } from "@/lib/queries"; const props = defineProps<{ item: ChecklistItem; pending?: boolean }>(); @@ -16,6 +16,8 @@ const more = computed(() => props.item.citations.length - shown.value.length); const mark = computed(() => IMPORTANCE_MARK[props.item.importance] ?? ""); /** AI 摘要和标题不同时才单独显示,避免重复 */ const summary = computed(() => (props.item.summary && props.item.summary !== props.item.title ? props.item.summary : "")); +/** 结构化取值:分值、参数要求、日期等,都来自原文(取值校验通过的才进正式清单) */ +const facts = computed(() => specFacts(props.item.spec));