Nav apraksta

wangxi 7f4040aab5 接口参数修改+回调测试案例 3 nedēļas atpakaļ
models 2a06946699 1st pubulished 2 mēneši atpakaļ
scripts 7f4040aab5 接口参数修改+回调测试案例 3 nedēļas atpakaļ
src 7f4040aab5 接口参数修改+回调测试案例 3 nedēļas atpakaļ
.env.example 7f4040aab5 接口参数修改+回调测试案例 3 nedēļas atpakaļ
.gitattributes 89ea7a514b 1 3 nedēļas atpakaļ
.gitignore 8b4c289303 chore(deploy): 部署脚本修复与依赖配置(uv 自动安装/清华镜像/uv.lock 入库/PATH 修复) 3 nedēļas atpakaļ
.python-version f13dab1ed0 first published 3 nedēļas atpakaļ
README.md 7f4040aab5 接口参数修改+回调测试案例 3 nedēļas atpakaļ
deploy.sh 8b4c289303 chore(deploy): 部署脚本修复与依赖配置(uv 自动安装/清华镜像/uv.lock 入库/PATH 修复) 3 nedēļas atpakaļ
pyproject.toml 8b4c289303 chore(deploy): 部署脚本修复与依赖配置(uv 自动安装/清华镜像/uv.lock 入库/PATH 修复) 3 nedēļas atpakaļ
uv.lock 8b4c289303 chore(deploy): 部署脚本修复与依赖配置(uv 自动安装/清华镜像/uv.lock 入库/PATH 修复) 3 nedēļas atpakaļ

README.md

Proposa AI

AI 驱动的投标书生成服务:解析招标文件、采购需求、澄清公告、公司资料和历史参考投书, 经 Step1-6 工作流生成最终 DOCX 投标书。HTTP API 是主要对接入口,后台直接执行与人工 验证相同的 scripts/test_step1.pyscripts/test_step6.py

处理流程

  1. Step 1:文档解析及表/函/表格提取。
  2. Step 2:招标需求、评分项、废标项及响应项分析。
  3. Step 3:结合模板、评分项和参考投书生成大纲。
  4. Step 4:生成章节内容及章节 DOCX 中间产物。
  5. Step 5:覆盖性、废标风险和内容完整性审核与修复。
  6. Step 6:按模板装配 DOCX,生成最终投标文件。

项目结构

proposa-ai/
├── src/                         # 正式功能代码
│   ├── config.py                # .env 与全局配置
│   ├── models.py                # 数据模型
│   ├── chapter_policy.py        # 章节结构策略
│   ├── doc_reader/              # PDF/DOCX/Markdown 读取
│   ├── llm_client/              # LLM 调用与缓存
│   ├── pdf_table_to_docx/       # PDF 表格提取
│   ├── step1_parsing/ ... step6_exporting/
│   └── templates/               # 正式 DOCX 模板资源
├── scripts/                     # 阶段脚本与快速回归
│   ├── test_step1.py ... test_step6.py
│   └── tests/
├── data/                        # 项目输入数据
├── test_data/                   # 手工测试数据
├── output/                      # 生成产物与缓存
├── models/                      # 可选本地 NLP 模型资源
├── deploy.sh                    # Linux 部署脚本
├── .env                         # 本地配置,Git 忽略
├── .env.example                 # 环境变量示例
└── pyproject.toml               # uv 依赖与安装配置

环境配置

uv sync

项目已在 pyproject.toml[tool.uv] 中配置清华 PyPI 镜像 (https://pypi.tuna.tsinghua.edu.cn/simple)加速依赖下载;需要更换镜像时, 设置环境变量 UV_INDEX_URL 覆盖即可。

复制 .env.example.env

Copy-Item .env.example .env

至少填写:

DEEPSEEK_API_KEY=你的密钥

操作系统环境变量的优先级高于 .env

环境变量说明

参数 必填 默认值 说明
DEEPSEEK_API_KEY DeepSeek API 密钥。仅保存在本地 .env,不得提交到 Git。
DEEPSEEK_BASE_URL https://api.deepseek.com DeepSeek 兼容 API 的基础地址。
DEEPSEEK_MODEL deepseek-v4-flash 分析、生成和审核使用的模型名称。
BID_OUTPUT_DIR output 工作流默认输出目录;相对路径以仓库根目录为基准。
BID_WRITER_CONCURRENCY 5 Step4 并发撰写章节的最大线程数。
BID_ANALYZER_CONCURRENCY 5 Step2 并发分析文档分块的最大线程数。
BID_HEADING_CONCURRENCY 5 Step6 标题层级分析的最大 LLM 并发数。
PROPOSA_API_HOST 0.0.0.0 HTTP API 监听地址;默认监听所有网卡。
PROPOSA_API_PORT 8000 HTTP API 监听端口。
PROPOSA_WORK_DIR output/api_jobs HTTP API 和 test_step1-6 默认工作目录;每个请求的内容放在 PROPOSA_WORK_DIR/<request_id>/ 下,中间文件在 work/,最终 DOCX 在请求目录根。
REFERENCE_DIR /data/shenqin/ JSON 模式下参考投标文件相对路径的全局目录。
TPC_DIR JSON 模式下招标文件、采购需求和澄清公告相对路径的拼接前缀。
PROPOSA_API_MAX_UPLOAD_MB 512 单个上传文件的大小上限,单位 MB。
PROPOSA_API_TIMEOUT_SECONDS 28800 Step1-6 后台子进程超时秒数,默认 8 小时。
PROPOSA_API_CONCURRENCY 1 同时执行的后台生成任务数;大型 DOCX 场景建议保持 1。
PROPOSA_CALLBACK_TIMEOUT_SECONDS 30 单次回调 HTTP 请求的超时秒数。
PROPOSA_CALLBACK_MAX_RETRIES 3 回调失败时的最大尝试次数,包含首次请求。

部署(Linux 服务器)

仓库提供一键部署脚本 deploy.sh,按顺序完成:

  1. 检测到服务器未安装 uv 时自动安装(默认开启,AUTO_INSTALL_UV=0 可关闭);
  2. 使用 ModelScope 将 gpt2-chinese-cluecorpussmall-onnx 下载到 models/ 目录(模型文件已存在时跳过);
  3. 使用 uv sync --frozen 安装锁定依赖;
  4. 后台启动 proposa-api,写入 PID/日志文件并做 /health 健康检查。

部署前准备

cp .env.example .env

编辑 .env 填写真实配置,至少设置 DEEPSEEK_API_KEYPROPOSA_API_PORTPROPOSA_WORK_DIRREFERENCE_DIR 等可按需调整。

执行部署

sh deploy.sh            # 完整部署并后台启动(可重复执行)
sh deploy.sh status     # 查看运行状态与健康检查
sh deploy.sh stop       # 停止后台服务
sh deploy.sh restart    # 停止后重新后台启动

日志位于 output/api.log,PID 位于 output/api.pid

可用环境变量:MODEL_REPO(默认 Maiteka/gpt2-chinese-cluecorpussmall-onnx)、MODEL_DIR(默认 models/gpt2-chinese-cluecorpussmall-onnx)、UV_CACHE_DIR(默认 <仓库根>/.uv-cache)、UV_INDEX_URL(默认清华 PyPI 镜像 https://pypi.tuna.tsinghua.edu.cn/simple)、PROPOSA_API_PORT(默认读取 .env,缺省 8000)、 RESTART=1(服务已运行时强制重启)、AUTO_INSTALL_UV=0(uv 缺失时只提示、 不自动安装)。

HTTP API(对接主文档)

启动服务

uv run proposa-api

接口总览

接口 方法 说明
/health GET 健康检查。
/api/v1/final-review POST 提交生成任务,返回 HTTP 202。
/api/v1/jobs/{request_id} GET 查询任务状态。
/api/v1/jobs/{request_id}/progress GET 查询 Step1-6 进度。
/api/v1/jobs/{request_id}/file GET 下载最终 DOCX。

请求格式

接口同时支持 multipart/form-data 文件上传和 application/json 服务端本地路径。

multipart/form-data

curl.exe -X POST "http://127.0.0.1:8000/api/v1/final-review" `
  -F "CALLBACK_URL=http://121.43.55.7:10026/shenqin/tender/callback" `
  -F "txbId=投续表的DMS-ID" `
  -F "TENDER_FILE=@招标文件.pdf" `
  -F "PROCUREMENT_FILE=@采购需求.docx" `
  -F "REFERENCE_BID=@参考投标文件.docx" `
  -F "CLA_FILE=@澄清公告.docx" `
  -F "CLEAN_INTERMEDIATE=false" `
  -F "REFERENCE_IS_ABSOLUTE=false"

application/json

curl.exe -X POST "http://127.0.0.1:8000/api/v1/final-review" `
  -H "Content-Type: application/json" `
  --data '{
    "CALLBACK_URL": "http://121.43.55.7:10026/shenqin/tender/callback",
    "txbId": "投续表的DMS-ID",
    "TENDER_FILE": "E:/CODE/proposa-ai/test_data/171-上海群众艺术馆/上海市群众艺术馆物业管理服务采购项目招标文件.pdf",
    "PROCUREMENT_FILE": "E:/CODE/proposa-ai/test_data/171-上海群众艺术馆/采购需求.docx",
    "TPC_IS_ABSOLUTE": true,
    "REFERENCE_BID": "E:/CODE/proposa-ai/test_data/171-上海群众艺术馆/参考投标文件/物业管理费项目投标文件.docx",
    "REFERENCE_IS_ABSOLUTE": true,
    "CLA_FILE": "",
    "CLEAN_INTERMEDIATE": false
  }'

请求参数

参数 必填 说明
CALLBACK_URL 处理成功或失败后接收 JSON 的回调地址。
txbId 投续表的 DMS ID,回调时原样返回。
TENDER_FILE 招标文件;multipart 上传 PDF,JSON 传服务端本地路径。
PROCUREMENT_FILE 采购需求;multipart 上传 DOCX,JSON 传服务端本地路径。
REFERENCE_BID 参考投标文件;multipart 上传 DOCX,JSON 传服务端本地路径。
CLA_FILE 澄清公告文件;当前仅接收并记录,不参与生成。
TPC_IS_ABSOLUTE JSON 是 仅 JSON 路径模式生效,必须传布尔值;false 时 TENDER_FILE、PROCUREMENT_FILE、非空 CLA_FILE 与 TPC_DIR 拼接,true 时原样读取。接口不会根据路径文本自行判断。
CLEAN_INTERMEDIATE 默认 true;设为 false 时保留中间文件并返回 intermediateDir
REFERENCE_IS_ABSOLUTE JSON 是 仅 JSON 路径模式生效,必须传布尔值;falseREFERENCE_BIDREFERENCE_DIR 拼接,true 时原样读取。接口不会根据路径文本自行判断。

REFERENCE_BID 路径处理

JSON 模式下:

  • REFERENCE_BID 为绝对路径时直接使用。
  • REFERENCE_BID 为相对路径时,设置 "REFERENCE_IS_ABSOLUTE": false,接口按 REFERENCE_DIR + REFERENCE_BID 拼接为绝对路径,再复制到隔离任务目录。
  • 接口只按 REFERENCE_IS_ABSOLUTE 的布尔值处理,不根据路径是否以 / 开头自行判断; 相对模式下会先去掉文件值开头的路径分隔符再与配置目录拼接。

multipart 模式下 REFERENCE_BID 是上传文件本身,REFERENCE_IS_ABSOLUTE 仅兼容接收, 不参与路径拼接。

上传校验规则

  • TENDER_FILE 必填且不能为空,只接受能正常打开并提取到文字的 PDF;纯扫描或损坏的 PDF 会返回 HTTP 400。
  • PROCUREMENT_FILEREFERENCE_BID 只接受 DOCX;服务验证 ZIP 容器、必需 OOXML 部件和正文 XML。
  • CLA_FILE 可选;当前仅保存并在回调中记录原始文件信息,不参与生成或校验。
  • JSON 模式路径不存在或扩展名不符合要求时,会在启动 worker 前返回 400。
  • Windows 路径建议使用正斜杠,或正确转义反斜杠。

处理流程与文件路径

提交成功后,后台按顺序执行 test_step1.pytest_step6.py

每个请求的内容都放在:

PROPOSA_WORK_DIR/<request_id>/
├── work/                          # Step1-6 中间文件与缓存
└── <招标文件名>.docx              # 最终 DOCX

最终文件名规则:招标 PDF 原文件名去掉 .pdf 后追加 .docx

CLEAN_INTERMEDIATE=true(默认)时,处理结束后只清理 work/,最终 DOCX 保留; falsework/ 也保留。

提交响应(HTTP 202)

{
  "request_id": "501c42526b5b4bb6ad68f0d736ef5884",
  "txbId": "1",
  "status": "processing",
  "message": "三份文件校验通过,已开始处理",
  "resultPath": "E:/CODE/proposa-ai/api_output_v1/501c42526b5b4bb6ad68f0d736ef5884/招标文件.docx",
  "output_path": "E:/CODE/proposa-ai/api_output_v1/501c42526b5b4bb6ad68f0d736ef5884/招标文件.docx",
  "progress_url": "http://127.0.0.1:8000/api/v1/jobs/501c42526b5b4bb6ad68f0d736ef5884/progress"
}
字段 说明
request_id 任务唯一 ID,用于查询进度、状态和下载最终文件。
txbId 请求传入的投续表 DMS ID,原样回传。
status 当前任务状态;202 响应中为 processing
message 处理提示信息。
resultPath 最终 DOCX 的绝对路径,位于 PROPOSA_WORK_DIR/<request_id>/ 下。
output_path resultPath 相同的兼容字段。
progress_url 轮询步骤进度的 URL。
intermediate_dir 可选;仅当 CLEAN_INTERMEDIATE=false 时返回,指向 work/ 目录。

status 任务状态

状态值 含义
queued 任务已创建,等待后台 worker 开始处理。
processing 正在执行 Step1-6,可通过 progress_url 查看当前步骤。
completed 处理成功,resultPath 指向最终 DOCX,可下载。
failed 处理失败,resultPatherror: 具体错误信息

进度查询

curl.exe "http://127.0.0.1:8000/api/v1/jobs/任务ID/progress"
{
  "updated_at": "2026-08-26T14:29:00+00:00",
  "current_step": 3,
  "total_steps": 6,
  "current_step_name": "Step 3 投书大纲生成",
  "status": "running",
  "message": "Step 3 投书大纲生成 正在处理",
  "completed_steps": [1, 2]
}

进度接口中的 status

状态值 含义
running 当前步骤正在执行。
completed 当前步骤已完成;当 current_step 为 6 且状态为 completed 时,表示全部步骤完成。

任务状态查询与下载

curl.exe "http://127.0.0.1:8000/api/v1/jobs/任务ID"
curl.exe "http://127.0.0.1:8000/api/v1/jobs/任务ID/file" --output 投标文件.docx

GET /api/v1/jobs/{request_id}/file 仅在任务 completed 且文件存在时返回 DOCX。

回调

回调只在任务成功或失败后发送,不在提交时发送。

成功回调:

{
  "request_id": "任务ID",
  "txbId": "投续表的DMS-ID",
  "status": "completed",
  "resultPath": "output/api_jobs/<request_id>/招标文件.docx",
  "files": {
    "TENDER_FILE": {"filename": "招标文件.pdf", "size": 123, "sha256": "..."},
    "PROCUREMENT_FILE": {"filename": "采购需求.docx", "size": 456, "sha256": "..."},
    "REFERENCE_BID": {"filename": "参考投书.docx", "size": 789, "sha256": "..."}
  },
  "final_review_url": "http://API地址/api/v1/jobs/任务ID/file"
}

失败回调:

{
  "request_id": "任务ID",
  "txbId": "投续表的DMS-ID",
  "status": "failed",
  "resultPath": "error: Step1-6 处理进程异常退出(退出码 7)",
  "error": "Step1-6 处理进程异常退出(退出码 7)"
}

回调说明:

  • 使用 POST JSON,不发送 token。
  • 默认最多重试 3 次;重试仍失败时,任务状态记录 callback_delivered=falsecallback_error
  • 请求中上传了 CLA_FILE 时,files.CLA_FILE 会包含 filename/size/sha256。
  • CLEAN_INTERMEDIATE=false 时,成功和失败回调都会额外返回 intermediateDir

常见 HTTP 状态码

状态码 含义
202 校验通过,任务已异步开始。
400 文件、路径、参数或回调地址校验失败。
413 上传文件超过大小限制。
415 不支持的 Content-Type。
422 缺少必填字段。
500 后台任务提交失败。

测试与局部验证

# 快速回归
uv run python -m unittest discover -s scripts/tests -v

# 按阶段手工测试(会使用 test_data 并可能调用 LLM)
uv run python scripts/test_step3.py
uv run python scripts/test_step6.py

生成结构保护规则

  • 一级章节原则上以模板为准;只有评分项大类无法匹配任何模板标题时,才允许在“项目经理” 一级章之前新增一级章,并统一重排编号。
  • “需求理解”章完整保留模板原文,只允许增补属于需求理解的具体小评分项作为直接下一级标题。
  • 评分项按“评分大类 → 具体小评分项 → 响应正文”组织;复用时必须能识别小评分项核心主题, 禁止重复创建同名/近义标题;新增标题不带分值,不使用 SC-xx 等内部编号。
  • 废标项统一归入第一章要求承诺函,不散落、不重复。
  • 最终真实一级章统一使用 第{中文序号}章 和 Heading 1,并映射到最终大纲。
  • 模板中显式 Heading 1-4 只用于标题层级语义,不复制其视觉格式。
  • Step5 补充正文必须写入 Step3 绑定的最深层评分标题节点。
  • 开标一览表服务内容/服务要求/服务期限按表内填写说明统一填“响应”。
  • 只有 %%表名%% 占位符允许创建或整表替换;Markdown 表格不能绕过授权生成 DOCX 表格。
  • 表格、图片、图注、前后说明、签名照片/签署人/日期等原生内容块必须保持完整。