|
|
1 month ago | |
|---|---|---|
| app | 1 month ago | |
| data | 1 month ago | |
| infra | 1 month ago | |
| scripts | 1 month ago | |
| tests | 1 month ago | |
| .env | 1 month ago | |
| .gitignore | 1 month ago | |
| README.md | 1 month ago | |
| pyproject.toml | 1 month ago | |
| requirements.txt | 1 month ago | |
| uv.lock | 1 month ago |
技术栈:Python 3.11、FastAPI、LangGraph、Milvus、SQLite、Tavily、DeepSeek。
项目默认使用确定性的 demo 决策引擎和 Hash Embedding。无需模型 Key 即可完成数据准备、意图路由、Milvus 检索、SQL 查询、证据评分和多源执行。
19530 端口uv venv --python 3.11
uv sync --extra test
项目直接读取根目录下的 .env。DeepSeek、Tavily、Milvus 和数据路径等运行配置统一在该文件中维护。
按需安装可选能力:
# DeepSeek 模型适配。
uv sync --extra test --extra deepseek
# Tavily 网页搜索。
uv sync --extra test --extra web
# 本地 Sentence Transformers。
uv sync --extra test --extra embeddings
仓库中使用相对路径,运行时根据 app/config.py 的位置解析为绝对路径,不依赖当前工作目录。
SQLITE_PATH=./data/shop.db
ORDERS_SOURCE_PATH=./data/source/orders.csv
DOCUMENTS_PATH=./data/documents
不要提交本机绝对路径和 .env。部署时如需使用仓库外的数据目录,可以在本地 .env 中配置绝对路径。
Ubuntu Milvus 的 19530 已映射到 Windows 时:
MILVUS_URI=http://localhost:19530
uv run python scripts/wait_for_milvus.py
Milvus 位于另一台主机时,将 localhost 替换为 Windows 可访问的 Ubuntu IP。
docker compose -f infra/milvus/docker-compose.yml config --quiet
docker compose -f infra/milvus/docker-compose.yml up -d
uv run python scripts/wait_for_milvus.py
本地数据分为两组:
data/source/orders.csv:脱敏订单,写入 SQLite orders 表。data/documents/*.md:退货、运费和价保政策,切分后写入 Milvus。uv run python scripts/prepare_data.py
准备脚本会重建课程数据库和 Milvus Collection。切换 Embedding 模型后也必须重新执行,不能混用不同模型或维度的向量。
| 类型 | 问题 | 预期工具 |
|---|---|---|
| 向量检索 | 耳机拆封后还能七日无理由退货吗? | Milvus |
| SQL 查询 | U1001 最近 30 天有几笔有效订单,实付总额是多少? | SQLite |
| 网页搜索 | 查询品牌官网关于 XPhone 15 Pro 的最新公告 | Tavily |
| 双源组合 | 统计 U1001 最近 30 天订单,并结合内部运费规则说明是否包邮 | Milvus + SQLite |
| 三源组合 | 统计 U1001 最近 30 天购买 XPhone 15 Pro 的订单金额,结合内部退货政策和品牌官网最新公告给出售后建议 | Milvus + SQLite + Tavily |
订单用户编号在课程数据中使用 U1001。生产系统必须从登录态或服务端认证上下文注入,不能信任模型从自然语言生成的用户编号。
使用 --debug 时,CLI 会先打印每个工具实际执行的 Query、参数和状态,再输出完整响应:
uv run python -m app.cli `
"统计 U1001 最近 30 天订单,并结合内部运费规则说明是否包邮" `
--debug
输出结构:
=== 实际执行的工具查询 ===
- milvus_search: query='...' arguments={'policy_type': 'shipping_policy', 'top_k': 4}
- sql_query: query='...' arguments={'user_id': 'U1001', 'days': 30, ...}
API 请求设置 debug=true 时,响应中的 executed_queries 返回相同信息。普通请求不返回内部查询轨迹。
uv run pytest
uv run python scripts/smoke_test.py
单元测试不访问真实模型或网页;Smoke Test 使用真实 Milvus 与 SQLite。
uv run uvicorn app.main:app --reload
$body = @{
query = "统计 U1001 最近 30 天订单,并结合内部运费规则说明是否包邮"
session_id = "demo"
debug = $true
} | ConvertTo-Json
Invoke-RestMethod `
-Method Post `
-Uri "http://127.0.0.1:8000/query" `
-ContentType "application/json" `
-Body $body
Swagger UI:http://127.0.0.1:8000/docs
在 .env 中配置 Tavily:
TAVILY_API_KEY=your-tavily-api-key
WEB_SEARCH_MAX_RESULTS=5
未配置 Key 时,Milvus 和 SQLite 路由不受影响;web_search 返回 WEB_SEARCH_NOT_CONFIGURED。
运行时代码只通过 Settings 读取 TAVILY_API_KEY,不会从代码常量、请求参数或 CLI 参数接收 Key。
LLM_PROVIDER=deepseek
DEEPSEEK_API_KEY=your-deepseek-api-key
DEEPSEEK_BASE_URL=https://api.deepseek.com
DEEPSEEK_MODEL_NAME=deepseek-v4-flash
DEEPSEEK_ANSWER_THINKING=true
DeepSeekDecisionEngine 使用 Function Calling 输出 RouteDecision 和 QualityGrade。这两个结构化调用固定关闭 Thinking,避免 Thinking 模式与强制 tool_choice 冲突;最终答案是否使用 Thinking 由 DEEPSEEK_ANSWER_THINKING 控制。
DEEPSEEK_API_KEY 只通过 Settings 读取。所有 Key 使用 SecretStr 保存,打印配置对象时只显示掩码,不输出真实值。
docker compose -f infra/milvus/docker-compose.yml down
数据目录位于 infra/milvus/volumes,已在 .gitignore 中排除。