[U-thread] P7.U-autotune-multi-target
[部门] Python sidecar(pysidecar) · ADR-AIOS-11 §4.1 fork 6 多目标曲线 + 偏差/平滑/聚类数学层 · skill: (无 · 通用 Python numpy/scipy/FastAPI)
[Worker CWD] d:/work/25_claude/workspace/AlgoDepartment/04_development/
[Occupies] P7.K-endpoints(write · 新增 /auto_tune/deviation + /auto_tune/simulate_apply · 扩 /auto_tune/geq_optimize)+ P7.K-models(write · 新增 AnalyzeRequest/Response models)+ P7.K-targets(write · 新增 pysidecar/auto_tune/targets/ 资源目录 + 3 条曲线 JSON)
[优先级] P1 · 1.0d · 跨栈独立 · ClaudeB 先做(短工作量优先 · 解锁 P5 fork 5)· 与 P5.U-autotune-batch-pipeline 同 ClaudeB 串行
[ADR] d:/work/25_claude/workspace/AlgoDepartment/06_docs/site-build/docs/08-implementation/40-aios/ADR/ADR-AIOS-11-xitune-autotune.md (§1.3.1 + §1.3.2 + §1.3.3 + §2.1.2 + §2.1.3 + §4.1 fork 6)
[参考文档]
- 上述 ADR 主文(accepted v1.1)· 重点读 §1.3.1 Harman 4-Phase 流程 + §1.3.2 GEQ-LSQ MVP 锁定 + §1.3.3 内置 3 条目标曲线 + §4.1 fork 6 实施清单
- 上游 ADR:d:/work/25_claude/workspace/AlgoDepartment/06_docs/site-build/docs/08-implementation/40-aios/ADR/ADR-AIOS-07-p3-p4-overlap.md(§1.3.4 三层分工铁律 · P7 = 数学层)
- 同部门标本(强制 read · 4 维度对齐):d:/work/25_claude/workspace/AlgoDepartment/06_docs/site-build/docs/08-implementation/40-aios/prompts/done/P5.U-meter-source-tap--4adda88.md(虽然部门是后端 · 但同 ClaudeB · 同三层分工 · 格式严守 11 字段 frontmatter + 顶部表格 + 中文方括号代码块 + 末尾解锁链/风险/历史)
- 同期派 P5 fork(读以同步 schema):d:/work/25_claude/workspace/AlgoDepartment/06_docs/site-build/docs/08-implementation/40-aios/prompts/active/P5.U-autotune-batch-pipeline.md(P5 调本任务新端点的 schema 期望 · 你的 response model 必须能映射到 P5 的 DeviationResult / GeqOptimizeResult / PredictedResponse)
- pysidecar/main.py(已实装 19 端点 · 你新增 2 端点 + 扩 1 端点)· 关键既有端点:
· POST /auto_tune/realtime_frame(已实装 · FFT + 群延迟 + 相位 · 不动)
· POST /auto_tune/calculate(已实装 · 直接差值法 · 不动 · 前端 c039075 在用)
· POST /auto_tune/geq_optimize(已实装 · 最小二乘 · **本任务扩 target_curve 参数**)
- pysidecar/models.py(Pydantic models · 你新增 AnalyzeRequest/Response 类型)
- pysidecar/auto_tune/(若已存在则在此扩展 · 否则按需新建子目录 + __init__.py)
- 不要参考 frontend/backend 代码(本任务 100% Python · 跨语言不复用)
【背景】
ADR-AIOS-11 已 accepted(2026-05-29 13:46 用户原话 "accept ADR-AIOS-11 · 顺便 start fork 5+6")· 本任务承担 P5 fork 5 编排所需的全部数学算子 + 资源 · ClaudeB 先做(1.0d 短)→ 后做 P5 fork 5(1.5d)· 总 2.5d 串行。
**架构契约 ADR-07 §1.3.4 三层分工铁律(本任务核心地位)**:
- **P7(本任务)= 数学分析层** · numpy/scipy 全栈 · 偏差曲线 / 共振峰识别 / 模拟应用 / 1/3 oct 平滑 / 多点平均 / GEQ 求解
- P5(同期派 fork 5 · ClaudeB 后做)= I/O 编排 · 调本任务端点 · 不做数学
- 前端(待派 fork 1-4)= UI · 通过 P5 调本任务 · 不做数学
**业务流程**(本任务负责的数学环节):
- 偏差曲线计算:`deviation_curve_db = current_response_db - target_curve_db`(逐频段)
- 共振峰识别:scipy.signal.find_peaks(deviation 上 +6dB / -6dB 阈值)+ Q 估算 + 增益估算
- 1/3 oct 平滑:对数频段 1/3 倍频程平均(31 中心频点 ISO 标准)
- 多点平均:多个测量点 magnitude_db 几何平均(对数域)
- 模拟应用:GEQ filters 参数下发到模拟 IIR cascade · 计算"应用后预测响应"
【内置 3 条目标曲线 schema】(§1.3.3 锁定)
JSON 文件:pysidecar/auto_tune/targets/{flat,harman_2017,bk_1974}.json
schema:
{
"id": "flat | harman_2017 | bk_1974",
"name": "Flat | Harman 2017 | B&K 1974",
"description": "...",
"freq_hz": [20, 25, 31.5, 40, 50, ..., 16000, 20000], // ISO 1/3 oct 31 频点
"magnitude_db": [...] // 对应 31 个增益值(平直全 0 · Harman 2017 低频 +6 / 高频 -1dB/oct · B&K 1974 偏明亮)
}
- flat:全 31 频点 0dB
- harman_2017:见 Olive et al. 2017 论文 · 20Hz +6dB · 200Hz +3dB · 1kHz 0dB · 10kHz -2dB · 20kHz -8dB(线性插值 31 频点)
- bk_1974:经典 HiFi · 高频偏明亮 · 5-10kHz +1~+2dB · 其他平直
- 工程师导入自定义曲线**留远景 §11.6 不做**(本任务仅 3 条内置)
【新增 2 端点 + 扩 1 端点 schema】
### POST /auto_tune/deviation(新增)
请求:
{
"freq_hz": [...], // 测量频点
"magnitude_db": [...], // 测量幅度
"phase_deg": [...], // 测量相位
"target_id": "flat | harman_2017 | bk_1974",
"smoothing": "none | one_third_oct" // 默认 one_third_oct
}
响应:
{
"deviation_curve_db": [...], // current - target(对齐目标曲线频点)
"resonance_peaks": [{"freq_hz": float, "q": float, "gain_db": float, "type": "peak | dip"}],
"rms_error_db": float, // 整体 RMS 偏差(关键指标 · P5 iterate 收敛判定用)
"suggestions": [{"freq_hz": float, "gain_db": float, "q": float, "reason": str}] // 建议调整清单(Phase 2 UI 用)
}
### POST /auto_tune/simulate_apply(新增)
请求:
{
"current_response": {"freq_hz": [...], "magnitude_db": [...], "phase_deg": [...]},
"filters": [{"type": "peak | low_shelf | high_shelf", "freq_hz": float, "q": float, "gain_db": float}]
}
响应:
{
"predicted_response": {"freq_hz": [...], "magnitude_db": [...], "phase_deg": [...]}
}
- 实施:对每个 filter 计算 IIR biquad 系数(scipy.signal.iirpeak / iirnotch · 或手写 RBJ cookbook)→ scipy.signal.freqz 计算频响 → 与 current_response 频域相乘(对数域相加)→ 返回 predicted
### POST /auto_tune/geq_optimize(扩 target_curve 参数)
当前已实装(c039075 锚点)· 本任务**扩展 target_curve 接受 string id**(原可能仅接受数组)· 兼容旧调用(若旧接口收数组继续保留)· 新接口接受:
{
...既有字段...,
"target_id": "flat | harman_2017 | bk_1974", // 新增 · 与 target_curve 数组二选一
"constraints": { // 新增 · 可选
"phase_continuity": bool, // 默认 false
"group_delay_min": bool, // 默认 false
"gain_bounds_db": [-12, 12] // 默认 [-12, 12]
}
}
- 内部加载 targets/{target_id}.json 替代数组 · 其他逻辑不动
【新增 2 数学算子】
### one_third_oct_smoothing(magnitude_db: array, freq_hz: array) -> array
- ISO 1/3 oct 31 中心频点(20Hz~20kHz)
- 对每个中心频点 fc · 取 [fc/2^(1/6), fc*2^(1/6)] 区间内的所有原始 magnitude_db 做几何平均(对数域算术平均)
- 返回 31 长度 smoothed_db 数组
### multi_point_averaging(measurements: List[{freq_hz, magnitude_db, phase_deg}]) -> {freq_hz, magnitude_db, phase_deg}
- 输入:N 个测量点的 freq/magnitude/phase
- 输出:幅度对数域几何平均 · 相位向量平均(complex exp 后取均值再 angle)
- 频点已对齐(测量点应在相同 freq_hz 网格)· 不在本算子做插值
【执行步骤】
1. 自查 + 同步:
git status # 工作区干净
git branch --show-current # = xistudio
git pull origin xistudio --no-rebase # 拿最新 main + 看是否有冲突
ls pysidecar/ # 看现有结构
cat pysidecar/main.py | head -50 # 确认 FastAPI 入口 + 既有路由
cat pysidecar/models.py | head -30 # 确认 Pydantic models 风格
2. 新增 pysidecar/auto_tune/targets/(目录新建):
- flat.json(31 频点全 0dB)
- harman_2017.json(Olive 论文曲线插值 31 频点)
- bk_1974.json(经典 HiFi · 高频 +1~+2dB)
- __init__.py(可选 · 若需要 import 加)
3. 新增 pysidecar/auto_tune/target_loader.py(资源加载):
- def load_target(target_id: str) -> dict:加载 targets/{target_id}.json · 校验 schema · 返回 dict
- def list_targets() -> List[dict]:返回 3 条曲线 metadata({id, name, description})· Phase 1 UI 选择器用
- 缓存(LRU)+ 文件不存在抛 ValueError
4. 新增 pysidecar/auto_tune/smoothing.py(数学算子):
- def one_third_oct_smoothing(magnitude_db: np.ndarray, freq_hz: np.ndarray) -> np.ndarray
- ISO 1/3 oct 31 中心频点 · 对每个 fc 取区间几何平均 · 见上述 schema 段定义
- 单测覆盖:输入 100 频点平直 → 输出 31 频点全相同 · 输入有局部峰 → 输出局部 +N dB
5. 新增 pysidecar/auto_tune/averaging.py(数学算子):
- def multi_point_averaging(measurements: List[dict]) -> dict
- 幅度对数域算术平均(等价几何平均)· 相位 complex exp 平均后 angle
- 单测覆盖:N=3 同源测量 → 输出 = 输入(任一)· N=3 不同测量 → 输出在中位附近
6. 新增 pysidecar/auto_tune/deviation.py(核心数学):
- def compute_deviation(meas: dict, target: dict, smoothing: str = "one_third_oct") -> dict
- 逻辑:
· 若 smoothing="one_third_oct"·先用 smoothing.one_third_oct_smoothing
· deviation_curve_db = meas_smoothed - target_db(对齐 31 频点)
· resonance_peaks = scipy.signal.find_peaks(deviation, height=6dB) + scipy.signal.find_peaks(-deviation, height=6dB)
· 估算 Q(用峰宽 -3dB 点)+ gain_db(峰值)· type="peak" if positive else "dip"
· rms_error_db = np.sqrt(np.mean(deviation**2))
· suggestions:取 top-5 峰 · {freq, gain=-deviation_at_peak, q, reason}
- 单测覆盖:平直输入 vs 平直目标 → deviation 全 0 · rms_error_db=0 · peaks 空 · 输入有 +6dB @ 1kHz → suggestions 含 1kHz -6dB 项
7. 新增 pysidecar/auto_tune/simulate.py(核心数学):
- def simulate_apply(current_response: dict, filters: List[dict]) -> dict
- 对每个 filter 用 RBJ biquad cookbook 计算系数(peak/low_shelf/high_shelf)· 或用 scipy.signal.iirpeak/iirnotch
- scipy.signal.freqz 计算频响 H(jw)
- predicted_magnitude_db = current_magnitude_db + 20*log10(|H|)(对数域相加)
- predicted_phase_deg = current_phase_deg + np.angle(H, deg=True)
- 返回 predicted_response dict
- 单测覆盖:current 全 0dB · filter peak +6dB Q=1.0 @1kHz → predicted 在 1kHz 处 ≈ +6dB 其他基本不变
8. 新增 pysidecar/models_autotune.py(或扩 models.py):
- Pydantic class DeviationRequest / DeviationResponse
- Pydantic class SimulateApplyRequest / SimulateApplyResponse
- Pydantic class TargetCurveInfo(用于 list_targets 返回)
9. 修改 pysidecar/main.py(新增 2 端点 + 扩 1 端点):
- @app.post("/auto_tune/deviation") · 调 deviation.compute_deviation · 返回 DeviationResponse
- @app.post("/auto_tune/simulate_apply") · 调 simulate.simulate_apply · 返回 SimulateApplyResponse
- 既有 @app.post("/auto_tune/geq_optimize"):扩 target_id 参数 · 内部 target_loader.load_target(req.target_id) · 兼容既有 target_curve 数组(if-else)
- @app.get("/auto_tune/targets")(新增 · 可选 · Phase 1 UI 拉曲线列表):调 target_loader.list_targets
10. 单测 + 验收:
cd pysidecar
# 新增 tests/auto_tune/test_smoothing.py(>= 3 case)
# 新增 tests/auto_tune/test_averaging.py(>= 3 case)
# 新增 tests/auto_tune/test_deviation.py(>= 4 case · 平直/带峰/收敛/边界)
# 新增 tests/auto_tune/test_simulate.py(>= 3 case · peak/shelf 各 1)
# 新增 tests/auto_tune/test_endpoints.py(>= 3 case · TestClient 调 3 端点)
# 至少 16 个新 case 总计
pytest # 全绿 · 既有不破坏
pytest tests/auto_tune/ -v # 详细看新增覆盖
11. 端到端验证(若 pysidecar 可启动):
uvicorn main:app --port 8001 &
curl -X POST http://localhost:8001/auto_tune/deviation -H "Content-Type: application/json" -d '{"freq_hz":[20,...],"magnitude_db":[...],"phase_deg":[...],"target_id":"flat","smoothing":"one_third_oct"}'
curl -X GET http://localhost:8001/auto_tune/targets # 返回 3 条曲线 metadata
12. Commit + push:
cd .. # 回到 04_development/
git add pysidecar/auto_tune/ \
pysidecar/models_autotune.py \
pysidecar/main.py \
pysidecar/tests/auto_tune/
git commit -m "..."(见下)
git push origin xistudio
【验收】
- 11-13 文件落地(targets/×3 JSON + target_loader/smoothing/averaging/deviation/simulate ×5 py + models_autotune.py + main.py 修改 + tests/auto_tune/×5 + 可选 __init__.py)
- pytest 全绿 · 新增 >= 16 用例
- 3 端点可达:POST /auto_tune/deviation · POST /auto_tune/simulate_apply · GET /auto_tune/targets · /auto_tune/geq_optimize 扩展兼容 + 新参数生效
- 内置 3 条目标曲线 JSON 格式正确(31 频点 ISO 标准)· `cat targets/flat.json | python -m json.tool` 无错
- 三元组 commit hash 7 位完整
- 端到端验证(可选):curl POST /auto_tune/deviation 返回字段齐全 · GET /auto_tune/targets 返回 3 条
【commit】
commit subject:`feat(P7): add /auto_tune/{deviation,simulate_apply,targets} + 3 built-in target curves (Flat/Harman 2017/B&K 1974) [ADR-AIOS-11 §4.1 fork 6]`
trailer(必须精确):
[step=1/1] [pid=P7] [uid=U-autotune-multi-target] [occupies=P7.K-endpoints+P7.K-models+P7.K-targets]
[files=pysidecar/auto_tune/targets/**, pysidecar/auto_tune/target_loader.py, pysidecar/auto_tune/smoothing.py, pysidecar/auto_tune/averaging.py, pysidecar/auto_tune/deviation.py, pysidecar/auto_tune/simulate.py, pysidecar/models_autotune.py, pysidecar/main.py, pysidecar/tests/auto_tune/**]
[ipc=auto_tune/deviation, auto_tune/simulate_apply, auto_tune/targets, auto_tune/geq_optimize(extended)]
回告格式:① 新增端点列表 ② 3 条目标曲线 freq/db 关键值 ③ 单测新增 case 数 ④ pytest 全绿截图(可选) ⑤ 同步 push 后 P5 fork 5 可立即拉取联调。
【禁止】
- ❌ 不许修改既有 19 端点的现有逻辑(/auto_tune/realtime_frame / /auto_tune/calculate / 18 个 /analyze/* 全部不动)· 仅扩 /auto_tune/geq_optimize target_id 参数(向后兼容 · 不删既有数组接口)
- ❌ 不许修改 contracts/protocol-v1.md(已 frozen)· 本任务走 dev-api 桥接 · 走 P5 fork 5 转发
- ❌ 不许改 frontend / backend_csharp / dsp_algo 代码(本任务 100% pysidecar 内)
- ❌ 不许引入新依赖(numpy + scipy + FastAPI + pydantic 已存量 · 不许 import 其他)· 若必须新增需先 ask
- ❌ 不许把数学逻辑写到 main.py 路由层(必须在 auto_tune/*.py 模块内 · main.py 仅做路由 + 调用)
- ❌ 不许在 P7 上做硬件 I/O(WASAPI / 麦克风 / 文件读写超出 targets/ 范围)· 数学纯函数为主
- ❌ 不许跳验收 · pytest 红必须修到全绿 · 不许 skip 测试
- ❌ 不许省略三元组 trailer
- ❌ 不许自改本 prompt 文件