docs: 工程骨架落地记录(架构 §13)与 AGENTS.md 工程一节
- 架构 §13:SK1–SK8,SK1(事件流用 Valkey stream)、SK2(测试不用 testcontainers)待确认 - api-design §10:接口实现进度 - openapi.yaml:ChecklistItem 的必填字段在同一层声明,生成的类型不再是可选 - AGENTS.md:目录结构、常用命令、编码约定、当前进度 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
49
AGENTS.md
49
AGENTS.md
@@ -7,7 +7,7 @@
|
|||||||
- **产品名**:标博士(代码仓库名 `bid-doctor`)
|
- **产品名**:标博士(代码仓库名 `bid-doctor`)
|
||||||
- **一句话定位**:站在**投标方**一侧,贯穿「寻标 → 读标 → 写标 → 查标 → 投标」全生命周期的 AI 辅助工具。产品是「副驾驶」,不是「自动驾驶」。
|
- **一句话定位**:站在**投标方**一侧,贯穿「寻标 → 读标 → 写标 → 查标 → 投标」全生命周期的 AI 辅助工具。产品是「副驾驶」,不是「自动驾驶」。
|
||||||
- **口号**:标博士,你的AI投标专家(固定写法,见决策 B5)。
|
- **口号**:标博士,你的AI投标专家(固定写法,见决策 B5)。
|
||||||
- **当前阶段**:需求分析、品牌与 VI 已完成,产品界面设计规范 v0.1、MVP 可点击原型 v0.1、技术架构设计 v0.2(选型已定,待技术验证 S1–S4)已完成,尚无代码。工作流:需求分析 ✅ → 主题 / 品牌设计 ✅ → 产品界面设计规范 ✅ → 原型设计 ✅ → UI 设计 → 技术架构 ✅ → 实现 → 测试。
|
- **当前阶段**:需求分析、品牌与 VI、产品界面设计规范 v0.2、MVP 原型 v0.2、技术架构 v0.3(含物理数据模型、接口契约)已完成;工程骨架已搭好,进入实现阶段。工作流:需求分析 ✅ → 主题 / 品牌设计 ✅ → 产品界面设计规范 ✅ → 原型设计 ✅ → UI 设计 ✅ → 技术架构 ✅ → 实现(进行中)→ 测试。
|
||||||
- **需求来源**:当前有效版本是 `docs/招投标AI工具PRD-v0.9.1.html`(`docs/招投标AI工具PRD.html` 是 v0.9 原稿,保留作对照),再加上 `docs/decisions.md` 和 `docs/analysis/` 里已确认的 MVP 细化需求。三者冲突时:decisions.md > analysis > PRD。发现矛盾或缺失时,记录问题并向用户确认,不要自行假设。
|
- **需求来源**:当前有效版本是 `docs/招投标AI工具PRD-v0.9.1.html`(`docs/招投标AI工具PRD.html` 是 v0.9 原稿,保留作对照),再加上 `docs/decisions.md` 和 `docs/analysis/` 里已确认的 MVP 细化需求。三者冲突时:decisions.md > analysis > PRD。发现矛盾或缺失时,记录问题并向用户确认,不要自行假设。
|
||||||
- **工作语言**:文档、界面文案、提交说明默认使用简体中文;代码标识符、文件名使用英文。
|
- **工作语言**:文档、界面文案、提交说明默认使用简体中文;代码标识符、文件名使用英文。
|
||||||
|
|
||||||
@@ -74,7 +74,46 @@
|
|||||||
- **已确认的选型**(AR1–AR3、AR5–AR9):模块化单体;前端用 Vue 3 + TypeScript + Vite 单页应用;数据库 PostgreSQL;文件存储用 S3 协议 + 本地磁盘驱动;单一仓库。详见 `docs/architecture/architecture.md` 第 11 节。
|
- **已确认的选型**(AR1–AR3、AR5–AR9):模块化单体;前端用 Vue 3 + TypeScript + Vite 单页应用;数据库 PostgreSQL;文件存储用 S3 协议 + 本地磁盘驱动;单一仓库。详见 `docs/architecture/architecture.md` 第 11 节。
|
||||||
- **Valkey**(AR4):只存丢了也没关系的数据(限速、验证码、推送、缓存);权威数据(含会话、任务队列)都在 PostgreSQL。
|
- **Valkey**(AR4):只存丢了也没关系的数据(限速、验证码、推送、缓存);权威数据(含会话、任务队列)都在 PostgreSQL。
|
||||||
- **UI 组件**(AR10):不引入带样式的 UI 框架(如 Element Plus),视觉自己写,样式只来自 `tokens.css`;复杂交互用无样式组件库 Reka UI 打底;不用 Tailwind。
|
- **UI 组件**(AR10):不引入带样式的 UI 框架(如 Element Plus),视觉自己写,样式只来自 `tokens.css`;复杂交互用无样式组件库 Reka UI 打底;不用 Tailwind。
|
||||||
- 工程骨架搭好后,在本文件回填构建、运行、测试命令和目录结构。
|
- 构建、运行、测试命令和代码目录见下方「工程」一节。
|
||||||
|
|
||||||
|
## 工程
|
||||||
|
|
||||||
|
骨架见技术架构 §13。后端 Python 3.12+(uv workspace),前端 Vue 3 + Vite(pnpm workspace)。
|
||||||
|
|
||||||
|
```
|
||||||
|
server/ 后端:api 和 worker 是同一个包、两种进程(bid-doctor api | worker | migrate)
|
||||||
|
src/bid_doctor/
|
||||||
|
core/ 配置、数据库与租户事务、错误码、加密、对象存储、队列、事件流、权限点
|
||||||
|
identity/ billing/ project/ document/ checklist/ ai/ audit/ notification/ 业务模块(架构 §3.2)
|
||||||
|
app.py worker.py cli.py
|
||||||
|
migrations/ Alembic;sql/0001_baseline/ 是 data-model 的建表脚本,按模块拆分
|
||||||
|
tests/ 集成测试(真实 PostgreSQL + 行级安全策略)和单元测试;fixtures/ 是虚构的小样例
|
||||||
|
apps/web/ 用户端单页应用
|
||||||
|
packages/api-client/ 由 docs/architecture/openapi.yaml 生成的类型 + 客户端(不手写接口类型)
|
||||||
|
prompts/ 带版本号的提示词(已发布的版本不再修改)
|
||||||
|
deploy/compose/ Docker Compose;deploy/docker/ 镜像
|
||||||
|
```
|
||||||
|
|
||||||
|
常用命令(都在仓库根目录执行):
|
||||||
|
|
||||||
|
| 做什么 | 命令 |
|
||||||
|
|---|---|
|
||||||
|
| 装依赖 | `uv sync` 和 `pnpm install --ignore-scripts` |
|
||||||
|
| 起依赖(PostgreSQL、Valkey、Gotenberg) | `docker compose -f deploy/compose/compose.yaml up -d postgres valkey gotenberg` |
|
||||||
|
| 建表 / 升级数据库 | `cd server && ../.venv/bin/bid-doctor migrate` |
|
||||||
|
| 起后端 | `cd server && ../.venv/bin/bid-doctor api`(:8009),另开一个终端 `../.venv/bin/bid-doctor worker` |
|
||||||
|
| 起前端 | `pnpm dev`(:5173,`/api` 代理到 :8009)。dev 环境短信验证码固定是 `000000` |
|
||||||
|
| 后端检查 | `cd server && ../.venv/bin/ruff check src tests migrations && ../.venv/bin/ruff format --check src tests migrations && ../.venv/bin/pyright && ../.venv/bin/lint-imports && ../.venv/bin/pytest -q` |
|
||||||
|
| 前端检查 | `pnpm typecheck && pnpm build` |
|
||||||
|
| 改了 openapi.yaml 之后 | `pnpm gen:api`,重新生成的 `schema.d.ts` 一起提交(CI 会比对) |
|
||||||
|
| 全部用容器跑 | `docker compose -f deploy/compose/compose.yaml --profile app up -d --build` |
|
||||||
|
|
||||||
|
约定:
|
||||||
|
|
||||||
|
- 配置只走 `BD_` 前缀的环境变量(`.env.example`);`BD_ENV=prod` 时检测到默认密钥、dev 短信、假模型会拒绝启动。
|
||||||
|
- 模型默认用确定性的假模型(`BD_AI_PROVIDER=fake`),测试和 CI 不调用真实模型。
|
||||||
|
- 数据访问都在 `tenant_tx(ctx)` 里;表结构以迁移为准,ORM 模型不声明外键,写入有先后时要自己 `flush`。
|
||||||
|
- 测试样例只能用虚构内容:真实招标文件样本不进 git(`samples/tenders/README.md`)。
|
||||||
|
|
||||||
## 品牌与设计资产(已定)
|
## 品牌与设计资产(已定)
|
||||||
|
|
||||||
@@ -115,7 +154,7 @@ samples/
|
|||||||
tenders/ # 真实招标文件样本(README 是清单;原始文件不进 git,用 fetch.sh 获取)
|
tenders/ # 真实招标文件样本(README 是清单;原始文件不进 git,用 fetch.sh 获取)
|
||||||
```
|
```
|
||||||
|
|
||||||
以上目录按阶段推进时再创建,不要提前建空目录。代码目录结构在技术架构确定后补充到本文件。
|
以上目录按阶段推进时再创建,不要提前建空目录。代码目录见「工程」一节。
|
||||||
|
|
||||||
## 参考开源项目(opensource-ref/)
|
## 参考开源项目(opensource-ref/)
|
||||||
|
|
||||||
@@ -131,7 +170,6 @@ samples/
|
|||||||
- 每个阶段产出落到 `docs/` 对应目录,并在本文件「当前进度」中更新一行。
|
- 每个阶段产出落到 `docs/` 对应目录,并在本文件「当前进度」中更新一行。
|
||||||
- 用户确认的决策写入 `docs/decisions.md`(日期 + 决策 + 理由),不要只留在对话里。
|
- 用户确认的决策写入 `docs/decisions.md`(日期 + 决策 + 理由),不要只留在对话里。
|
||||||
- 设计与文案产出提交前,对照上方「产品红线」自查一遍。
|
- 设计与文案产出提交前,对照上方「产品红线」自查一遍。
|
||||||
- 技术栈确定后,在本文件补充:构建 / 运行 / 测试命令、代码规范、目录结构。
|
|
||||||
|
|
||||||
## 当前进度
|
## 当前进度
|
||||||
|
|
||||||
@@ -157,4 +195,5 @@ samples/
|
|||||||
- 2026-11-03:VI 手册第 20 页报告页脚去掉「保证」字样,改为与设计规范 §7.17 一致的「辅助检查结果,仅供投标前自查参考」,PDF 已重出。
|
- 2026-11-03:VI 手册第 20 页报告页脚去掉「保证」字样,改为与设计规范 §7.17 一致的「辅助检查结果,仅供投标前自查参考」,PDF 已重出。
|
||||||
- 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/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:接口契约 v0.1(`docs/architecture/api-design.md` + `openapi.yaml`,OpenAPI 3.1,64 个路径、81 个操作),覆盖 MVP 原型 13 页的主流程,运营后台另行定义。API1–API5 全部按建议确认:骨架搭好前以 `openapi.yaml` 为准,之后以 FastAPI 生成的快照为准;事件流用 fetch 读;异地登录确认后挤掉旧设备;扣点说明和实际不一致时拒绝、不扣点;报告后台生成。
|
||||||
- 下一步:搭工程骨架(仓库结构、CI、Compose 一键启动、「上传 → 解析 → 显示清单」最小链路),见 `docs/architecture/architecture.md` 第 12 节。CI 里补上 OpenAPI 的完整校验(Redocly 或 Spectral)。
|
- 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 末尾。
|
||||||
|
|||||||
@@ -179,3 +179,17 @@
|
|||||||
| API3 | 异地多设备并发登录怎么「提示并限制」 | **新设备登录时提示「账号已在其他设备登录」,用户确认后挤掉旧设备**。另一种做法是旧设备不退出就不让新设备登录,但用户换了电脑、忘了退出时就登不上了 |
|
| API3 | 异地多设备并发登录怎么「提示并限制」 | **新设备登录时提示「账号已在其他设备登录」,用户确认后挤掉旧设备**。另一种做法是旧设备不退出就不让新设备登录,但用户换了电脑、忘了退出时就登不上了 |
|
||||||
| API4 | 扣点说明和实际扣点不一致时 | **拒绝并刷新说明,不扣点**(§4.1)。用户看到「不扣点」,结果却扣了,这种情况必须杜绝 |
|
| API4 | 扣点说明和实际扣点不一致时 | **拒绝并刷新说明,不扣点**(§4.1)。用户看到「不扣点」,结果却扣了,这种情况必须杜绝 |
|
||||||
| API5 | 报告怎么生成 | **后台生成**,完成后推送事件。用 Chromium 生成 PDF 要几秒到十几秒,不放在请求里同步做(架构:api 只做短事务) |
|
| API5 | 报告怎么生成 | **后台生成**,完成后推送事件。用 Chromium 生成 PDF 要几秒到十几秒,不放在请求里同步做(架构:api 只做短事务) |
|
||||||
|
|
||||||
|
## 10. 实现进度(工程骨架)
|
||||||
|
|
||||||
|
骨架阶段只实现了「上传 → 解析 → 显示清单」这条最小链路要用的接口。`server/tests/test_contract.py` 保证已实现的接口都在契约里;契约里还没实现的,按模块列在这里,实现一个划掉一个。
|
||||||
|
|
||||||
|
| 模块 | 已实现 | 还没实现 |
|
||||||
|
|---|---|---|
|
||||||
|
| 登录与账号 | 发验证码、注册、登录(含挤下线确认)、退出、`GET /me` | 修改显示名、在线会话列表和下线、注销账号 |
|
||||||
|
| 额度与套餐 | `GET /plans`、`GET /quota` | 额度流水、开通申请 |
|
||||||
|
| 项目 | 列表(不含筛选和分页)、新建、详情 | 基本信息字段修改与补遗确认、状态变更、删除、分包的增删改和选包 |
|
||||||
|
| 上传与文档 | 分片上传全套、挂到项目、文档列表和详情、逐页摘要、一页的句子、签名下载、发起解析、解析进度、取消 | 删除文档、人工建出处 |
|
||||||
|
| 清单 | 清单概况、清单项列表(只按区域筛选) | 修改、采纳、删除恢复、替换出处、补遗更新确认、修改记录、确认清单、版本 |
|
||||||
|
| 检查、报告、资质 | — | 全部(查标阶段) |
|
||||||
|
| 通知与事件 | 事件流、通知列表 | 标为已读、反馈 |
|
||||||
|
|||||||
@@ -839,4 +839,21 @@ S1–S4 风险最高,建议先做;S8(桌面外壳 PoC)等桌面版立项
|
|||||||
1. 做技术验证 S1–S4,结论写进本文件,并据此确定 FR-M2-05、FR-M2-06 的目标值。
|
1. 做技术验证 S1–S4,结论写进本文件,并据此确定 FR-M2-05、FR-M2-06 的目标值。
|
||||||
2. ~~物理数据建模~~:✅ 已完成,见 `data-model.md`、`schema-draft.sql`(DM1–DM7 已确认)。
|
2. ~~物理数据建模~~:✅ 已完成,见 `data-model.md`、`schema-draft.sql`(DM1–DM7 已确认)。
|
||||||
3. ~~定接口契约~~:✅ 已完成,见 `api-design.md`、`openapi.yaml`(API1–API5 已确认)。
|
3. ~~定接口契约~~:✅ 已完成,见 `api-design.md`、`openapi.yaml`(API1–API5 已确认)。
|
||||||
4. 搭工程骨架:仓库结构、CI、Compose 一键启动、一个贯通「上传 → 解析 → 显示清单」的最小链路。
|
4. ~~搭工程骨架~~:✅ 已完成,见 §13 和 AGENTS.md「工程」一节。
|
||||||
|
|
||||||
|
## 13. 工程骨架的落地记录(2026-11-03)
|
||||||
|
|
||||||
|
搭骨架时按代码实际情况做了下面这些调整或补充。SK1、SK2 改了上文的写法,待确认;其余是实现细节,记在这里备查。
|
||||||
|
|
||||||
|
| # | 事项 | 做法 | 状态 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| SK1 | 进度推送(§3.4) | 用 **Valkey stream** 代替发布 / 订阅:一个结构同时解决实时推送和断线补发(按 `Last-Event-ID` 续读),多个 api 实例都能读到。Valkey 丢了也只是没有推送,前端轮询兜底 | 待确认 |
|
||||||
|
| SK2 | 接口集成测试(§8) | 不用 testcontainers,改为连 Compose 起的 PostgreSQL,每次测试会话重建 `bid_doctor_test` 库;CI 里用 GitHub Actions 的服务容器。更快,本地和 CI 走同一套配置 | 待确认 |
|
||||||
|
| SK3 | 建任务和入队在同一个事务里(§3.4) | Procrastinate 的 `defer` 用的是它自己的连接,进不了我们的事务,所以直接调用它的 SQL 函数 `procrastinate_defer_jobs_v1`(`core/queue.py`);它的表放在 public schema。pyproject 锁定了大版本,升级时要核对 | 已实现 |
|
||||||
|
| SK4 | 上传会话(§5.2 第 7 条) | 上传会话的元数据放在 Valkey(24 小时过期),分片按企业级数据密钥加密后写进对象存储;挂到项目上时换成项目密钥 | 已实现 |
|
||||||
|
| SK5 | ORM 模型 | 表结构以迁移为准,ORM 模型只做列映射、不声明外键,所以写入顺序要自己 `flush` 保证(测试里踩到过两次) | 编码约定 |
|
||||||
|
| SK6 | 文字层 | 有的 PDF(如 Chromium 生成的)把汉字映射成康熙部首码位(「目」成了 U+2F6C),引文校验会对不上;文字层把康熙部首和部首补充区换回常用汉字,其他字符不动 | 已实现 |
|
||||||
|
| SK7 | 斜体下划线(07 F1) | 只有文件的「标注规则说明」声明过斜体下划线标注,才按它认定实质性条款;没声明的文件里,标题、表头也常是斜体或下划线(S02 实测踩到) | 已实现 |
|
||||||
|
| SK8 | 登录验证码 | 登录和注册共用一个验证码;会话建好之后才作废,用户确认挤下线(API3)或发现「还没注册」时不用重新获取 | 已实现 |
|
||||||
|
|
||||||
|
还没做、留了 TODO 的:转换镜像的字体包和 WPS 旧版 DOC 预处理(S1)、表格按单元格切句和页眉页脚识别(S3)、OCR(S5)、结构识别和分包识别、模型路由表和限流、PDF 阅读器、读标的修改类接口、查标全部。
|
||||||
|
|||||||
@@ -2524,6 +2524,12 @@ components:
|
|||||||
- type: object
|
- type: object
|
||||||
required: [categories, title]
|
required: [categories, title]
|
||||||
properties:
|
properties:
|
||||||
|
categories:
|
||||||
|
type: array
|
||||||
|
minItems: 1
|
||||||
|
uniqueItems: true
|
||||||
|
items: { $ref: '#/components/schemas/Category' }
|
||||||
|
title: { type: string, minLength: 1, maxLength: 200 }
|
||||||
citation_ids:
|
citation_ids:
|
||||||
type: array
|
type: array
|
||||||
items: { $ref: '#/components/schemas/Id' }
|
items: { $ref: '#/components/schemas/Id' }
|
||||||
@@ -2535,6 +2541,13 @@ components:
|
|||||||
- type: object
|
- type: object
|
||||||
required: [id, categories, title, importance, origin, zone, citations, edited_fields, updated_at]
|
required: [id, categories, title, importance, origin, zone, citations, edited_fields, updated_at]
|
||||||
properties:
|
properties:
|
||||||
|
categories:
|
||||||
|
type: array
|
||||||
|
minItems: 1
|
||||||
|
uniqueItems: true
|
||||||
|
items: { $ref: '#/components/schemas/Category' }
|
||||||
|
title: { type: string, minLength: 1, maxLength: 200 }
|
||||||
|
importance: { $ref: '#/components/schemas/Importance' }
|
||||||
id: { $ref: '#/components/schemas/Id' }
|
id: { $ref: '#/components/schemas/Id' }
|
||||||
summary:
|
summary:
|
||||||
type: [string, 'null']
|
type: [string, 'null']
|
||||||
|
|||||||
Reference in New Issue
Block a user