从零开发 AIDataReport 智能数据报表工具|工程脚手架
1. 本篇要解决什么
前两篇把「为什么做」和「MVP 做什么」说清楚了。从这篇开始进入开发。
工程上第一刀不要 直接写 Text2SQL,也不要先接大模型。先把骨架立住:
- 有清晰的包结构,后面模块往哪放一目了然
- 有统一的运行时配置(环境变量 /
.env) - 本地能一键拉起系统配置库 PostgreSQL
- FastAPI 能启动,并且
/health能探活
2. 技术选型(脚手架相关,一句带过)
和 MVP 文档对齐,脚手架阶段用到的选型如下:
| 项 | 选型 | 本篇用途 |
|---|---|---|
| 语言 | Python 3.12 | 运行时 |
| API | FastAPI + Uvicorn | HTTP 入口 |
| 系统库 | PostgreSQL 16(Docker Compose) | 存租户/项目/配置/审计(后续篇) |
| 配置 | pydantic-settings + .env |
统一读环境变量 |
| 包管理 | pyproject.toml + requirements.txt |
可编辑安装与依赖锁定习惯 |
| CLI | Typer(入口先挂上) | 后续 init-db / config / query |
记住一个关键区分:
- 系统配置库:平台自己的库,存 Tenant / Project / Agent / 配置发布 / 审计
- 业务只读库:实施对接的销售库等,由配置里的 DataSource 描述,账号必须只读
3. 仓库目录怎么规划
从第一天就按「能力边界」分目录,而不是按「先写一堆 scripts」。当前目标结构如下:
text
ai-data-report/
├── configs/demo/sales/ # 销售试点 Demo 配置包(后续篇)
├── docs/ # 系列文章
├── src/bi_agent/ # 主包(src layout)
│ ├── domain/ # 配置契约、问数请求、ExecutionPlan
│ ├── db/ # 系统库 ORM、会话、建表
│ ├── config_service/ # 校验、YAML、发布仓储
│ ├── compiler/ # 配置驱动 SQL 编译
│ ├── reviewer/ # SQL 安全审核
│ ├── executor/ # 只读执行 Report Service
│ ├── formatter/ # JSON / Excel
│ ├── audit/ # 审计写入
│ ├── agent/ # 百炼 NL(阶段 4)
│ ├── api/routes/ # FastAPI 路由
│ ├── pipeline.py # 端到端编排(API/CLI 共用)
│ ├── cli.py # Typer CLI
│ ├── settings.py # 运行时配置
│ └── main.py # ASGI 入口
├── tests/
├── docker-compose.yml
├── .env.example
├── pyproject.toml
├── requirements.txt
└── README.md
为什么用 src/ 布局:
- 强制「安装后再导入」,避免在仓库根目录误 import 到半成品路径
- 和
pytest的pythonpath = ["src"]、可编辑安装pip install -e .配合自然
4. 声明项目与依赖
4.1 pyproject.toml(项目元数据 + 入口)
核心点:
- 包名:
bi-agent(CLI 命令同名) - 代码在
src/ - 控制台脚本:
bi-agent = bi_agent.cli:app - pytest 直接认
src
精简示意:
toml
[project]
name = "bi-agent"
version = "0.1.0"
description = "Configurable enterprise BI Agent platform (MVP)"
requires-python = ">=3.10"
dependencies = [
"fastapi>=0.115.0,<1.0.0",
"uvicorn[standard]>=0.32.0,<1.0.0",
"sqlalchemy>=2.0.36,<3.0.0",
"psycopg[binary]>=3.2.0,<4.0.0",
"pydantic>=2.9.0,<3.0.0",
"pydantic-settings>=2.6.0,<3.0.0",
"pyyaml>=6.0.2,<7.0.0",
"sqlglot>=25.0.0,<30.0.0",
"openpyxl>=3.1.5,<4.0.0",
"typer>=0.12.0,<1.0.0",
"httpx>=0.27.0,<1.0.0",
"python-dotenv>=1.0.1,<2.0.0",
]
[project.scripts]
bi-agent = "bi_agent.cli:app"
[tool.setuptools.package-dir]
"" = "src"
[tool.setuptools.packages.find]
where = ["src"]
[tool.pytest.ini_options]
pythonpath = ["src"]
testpaths = ["tests"]
4.2 requirements.txt
开发时也可以直接:
bash
pip install -r requirements.txt
pip install -e .
若可编辑安装失败,临时方案:
bash
# Windows PowerShell
$env:PYTHONPATH="src"
5. 运行时配置:settings.py + .env
把「连哪套系统库、LIMIT 上限、超时」集中到一处,后续 Reviewer / Executor 都从这里读阈值,避免魔法数散落。
python
# src/bi_agent/settings.py(核心结构)
from functools import lru_cache
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
extra="ignore",
)
# 系统配置库(不是业务只读库)
database_url: str = "postgresql+psycopg://bi_agent:bi_agent@localhost:5432/bi_agent"
app_env: str = "dev"
log_level: str = "INFO"
# 查询安全阈值
default_query_limit: int = 1000
max_query_limit: int = 10000
query_timeout_seconds: int = 30
max_sql_length: int = 20000
# 百炼:阶段 4 再填,MVP 可留空
bailian_api_key: str = ""
bailian_base_url: str = "https://dashscope.aliyuncs.com/compatible-mode/v1"
bailian_model: str = "qwen-plus"
@lru_cache
def get_settings() -> Settings:
return Settings()
配套 .env.example(复制为 .env 即可本地开发):
env
DATABASE_URL=postgresql+psycopg://bi_agent:bi_agent@localhost:5432/bi_agent
APP_ENV=dev
LOG_LEVEL=INFO
DEFAULT_QUERY_LIMIT=1000
MAX_QUERY_LIMIT=10000
QUERY_TIMEOUT_SECONDS=30
MAX_SQL_LENGTH=20000
BAILIAN_API_KEY=
BAILIAN_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
BAILIAN_MODEL=qwen-plus
6. FastAPI 空壳:main + /health
6.1 应用工厂
用 create_app() 而不是只写一个全局 app,测试里可以反复创建、互不污染:
python
# src/bi_agent/main.py
from fastapi import FastAPI
from bi_agent import __version__
from bi_agent.api.routes import config, health, query
def create_app() -> FastAPI:
app = FastAPI(
title="BI Agent",
description="可配置的企业报表 Agent 平台(MVP)",
version=__version__,
)
# 探活挂根路径;业务 API 统一 /api/v1
app.include_router(health.router)
app.include_router(config.router, prefix="/api/v1")
app.include_router(query.router, prefix="/api/v1")
return app
app = create_app()
6.2 健康检查
python
# src/bi_agent/api/routes/health.py
from fastapi import APIRouter
from bi_agent import __version__
router = APIRouter(tags=["health"])
@router.get("/health")
def health() -> dict[str, str]:
return {"status": "ok", "version": __version__}
包版本集中在一处:
python
# src/bi_agent/__init__.py
__version__ = "0.1.0"
6.3 第一个测试
探活是最便宜的回归保护:
python
# tests/test_health.py
from fastapi.testclient import TestClient
from bi_agent.main import create_app
def test_health() -> None:
client = TestClient(create_app())
resp = client.get("/health")
assert resp.status_code == 200
assert resp.json()["status"] == "ok"
7. 动手验证(本篇验收)
环境要求:Python 3.12+(推荐)。
bash
# 1) 虚拟环境
python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS / Linux
# source .venv/bin/activate
python --version # 建议 3.12.x+
# 2) 依赖
pip install -r requirements.txt
pip install -e .
# 3) 环境变量
copy .env.example .env # Windows
# cp .env.example .env # macOS / Linux
# 4) 启动 API
uvicorn bi_agent.main:app --reload --app-dir src
另开终端:
bash
curl http://127.0.0.1:8000/health
期望类似:
json
{"status":"ok","version":"0.1.0"}
跑测试:
bash
pytest tests/test_health.py -q
8. 小结与下篇预告
本篇完成了 AIDataReport(BI Agent)开发阶段的第一步:
- 用
src/bi_agent按能力拆模块 - 用 pydantic-settings 统一运行时配置
- 用 Docker Compose 拉起系统 PostgreSQL
- 用 FastAPI 应用工厂 +
/health证明进程活着
下一篇 《语义配置模型与系统库落库》 会回答:
- 租户 → 项目 → Agent 如何建模
- DataSource / Dataset / Join / Metric / Dimension 的配置怎么写
- 领域模型(Pydantic)和 ORM 表如何分离
bi-agent init-db如何把系统表建出来
发帖纯粹个人记录与技术交流,欢迎各位同行大佬围观、指点问题,也欢迎同好一起交流探讨,一起学习进步