Reads a tender document (招标文件) and automatically generates a structured bid-document outline (投标文件大纲) — the chapter tree a bidder must produce to respond, traced back to where each requirement came from in the tender.
Built as a demo on Azure OpenAI. Upload a tender (single file or a package of files), watch a 9-step pipeline run live, review the outline with per-node source traceability and a coverage report, then export to Word.
A 9-step pipeline (src/bid_copilot/understanding/pipeline.py) turns raw files into the outline:
- parse —
.docxparsed locally (native heading levels preserved);.doc/.pdfgo through Azure Content Understanding for structured markdown. - classify — label each file (tender body / scoring / tech spec / commercial …).
- segment — split documents into chapter blocks.
- locate — find the key sections (bid-format, scoring, tech-spec, commercial).
- extract_skeleton — pull the explicit bid-document skeleton the tender prescribes.
- extract_requirements — extract response requirements. Scoring/commercial items are extracted one-by-one; technical parameter-level indicators are aggregated into a single "technical parameter response" entry (a bidder answers them in one response table, not as separate outline chapters), while requirements that need their own narrative / supporting material / dedicated section stay individual.
- merge — normalize and de-duplicate the skeleton, then attach requirements to nodes.
ref_ids are back-filled by engineering code, not by the LLM. - supplement — place any requirement that wasn't attached during merge (the LLM only decides where;
ref_idback-fill is again engineering). - finalize — renumber the tree and compute the coverage report from the tree's
ref_ids.
The web UI streams step progress over SSE, renders the outline with clickable source badges, shows a coverage panel, and exports Word.
- Backend: FastAPI + Uvicorn, Python 3.14
- LLM: Azure OpenAI (Responses API) via the
openaiSDK; three model tiers (main/mini/nano) selectable per pipeline step - Document parsing:
python-docx,pdfplumber, Azure Content Understanding (for.doc/ scanned PDFs) - Frontend: Tailwind (CDN) + Alpine.js, no build step
- No database — run state lives in memory + the
runs/directory on disk
src/bid_copilot/
api/main.py FastAPI app: upload, run, SSE progress/events, tree, export, delete
config.py Settings from env (.env)
models.py Pydantic models (OutlineTree, RequirementItem, SourceRef, …)
llm/client.py Azure OpenAI Responses API wrapper
parsing/ docx/pdf extraction + Content Understanding client
understanding/ the 9-step pipeline + per-step modules
alignment/ merge + supplement (requirement → outline placement)
output/ tree numbering + Word export
auth/ optional local login gate
src/web/ index.html + app.js (Alpine) + login page
scripts/run_visible.py run the full pipeline on a file/folder from the CLI
tests/ pytest suite
Requires Python 3.14. Dependencies are imported via PYTHONPATH=src (no pip install -e).
# 1. create venv + install deps
python -m venv .venv
.venv/bin/pip install -r requirements.txt
# 2. configure
cp .env.example .env # then fill in your Azure keys/endpoints
# 3. run the web app
ENABLE_LOCAL_AUTH=false PYTHONPATH=src .venv/bin/uvicorn bid_copilot.api.main:app --port 8080
# open http://127.0.0.1:8080Run the pipeline headless on a file or folder (dumps each step's JSON under runs/<name>/):
PYTHONPATH=src .venv/bin/python scripts/run_visible.py path/to/tender.docxRun the tests:
PYTHONPATH=src .venv/bin/pytest -qAll settings come from environment variables (see .env.example):
| Variable | Purpose |
|---|---|
FOUNDRY_API_KEY_AOAI |
Azure OpenAI API key |
AOAI_BASE_URL |
Azure OpenAI v1 endpoint (ends with /openai/v1/) |
MODEL_MAIN / MODEL_MINI / MODEL_NANO |
the three model deployment names |
CU_ENDPOINT / CU_KEY |
Azure Content Understanding (optional; needed for .doc/scanned PDF) |
ENABLE_LOCAL_AUTH + LOCAL_AUTH_* |
optional local login gate |
MAX_CONCURRENCY |
parallelism cap for per-chapter extraction and merge batching |
EFFORT_<STEP> |
reasoning effort per step (low/medium/high) |
MODEL_<STEP> |
model tier per step (main/mini/nano) |
Per-step model tiers let you trade speed for accuracy. The accuracy-critical steps — extract_requirements, merge, supplement — should stay on main; lighter steps (classify, locate, skeleton) can drop to mini/nano.
The image is a plain Python service (no local .doc converter — .doc parsing relies entirely on Content Understanding).
Build the image in the cloud (no local Docker needed) and deploy:
# build into ACR
az acr build --registry <acr-name> --image bid-copilot:0.0.1 .
# deploy (set --target-port explicitly; the app listens on $PORT, default 8080)
az containerapp up \
--name bid-copilot \
--resource-group <rg> \
--image <acr-name>.azurecr.io/bid-copilot:0.0.1 \
--target-port 8080 --ingress externalThen set the environment variables (FOUNDRY_API_KEY_AOAI, AOAI_BASE_URL, CU_*, MODEL_*, …) on the container app; the image ships without an .env.