模块 1 · 路由地图
v0.1 路由:
customers / products / advisors / compliance
代销平台 API 是「读 Core 模拟库」的
REST
门面。路由按业务域切分,不用 /api/platform/* 前缀。
和 Agent 对话线(/api/chat)是两条平行轨道——指挥 AI 加「查持仓」时,先确认走哪条。
四块 canonical 域(以本文为准)
/api/customers/*
客户 L0 档案、持仓、流水。平台客户 REST 薄路由(customers.py) → platform/customer_service → core_ro。
/api/products/*
产品列表、净值曲线。理财师/客户看产品详情都走这里,不另开 Agent Tool HTTP。
/api/advisors/*
理财师名下客户归属。advisor 只能查本人 roster,风控/分析员可全量只读。
/api/compliance/*
适当性判定等合规读接口。canonical:Agent 合并期改调同一 Service,不保留第二套路径。
app/service/platform/,旧路径仅过渡。
三层分工(别让 AI 在路由里写 SQL)
点击「下一步」看一层层往下走
"""代销平台 · 客户 L0 与资产读 API(canonical · 不要求 X-Agent-Type)。"""
@router.get("/{customer_id}/holdings")
def list_holdings(
customer_id: str,
auth: AuthContext = Depends(get_platform_auth_context),
):
assert_platform_customer_access(auth, customer_id, ...)
return ok(platform_service.list_holdings(customer_id))
文件头就写明:这是平台 canonical 读接口,不要 X-Agent-Type。
URL 用复数 customers + 嵌套 holdings,参数名和域 ID 一致。
鉴权用平台专用函数,不是 chat 那条 get_auth_context。
先断言「你有没有权看这个 customer_id」,再调 Service,路由里不出现 SQL。
理财师 Agent 也要查客户持仓,应该新建 /api/agent/holdings 吗?
模块 2 · 平台鉴权
get_platform_auth_context:
只要 JWT,不要 X-Agent-Type
对话线(/api/chat)必须带
X-Agent-Type;
平台读 API 故意不要——App 和仪表盘只证明「你是谁」,不声明「你在敲哪扇 Agent 窗」。
和 chat 鉴权的对照表
| 场景 | 函数 | X-Agent-Type | 归属断言 |
|---|---|---|---|
| 平台 REST | get_platform_auth_context | 不要 | assert_platform_customer_access |
| 对话 / 风控 REST | get_auth_context | 必须 | assert_customer_access + 准入矩阵 |
apiFetch 读持仓只带 Bearer;若误加 X-Agent-Type: customer,平台路由会忽略,但混进 chat 路由就会多一层校验甚至 401。
群聊:前端问「张三持仓多少」
理财师工作台发 GET 持仓——只走平台三层,不进 LangGraph。
def get_platform_auth_context(request: Request) -> AuthContext:
"""平台 API 鉴权:JWT 通道 **不要求** X-Agent-Type。"""
auth_header = request.headers.get("Authorization", "")
if auth_header[:7].lower() == "bearer ":
claims = verify_token(token)
return _bind_state(request, _claims_to_auth(claims))
# dev:X-Debug-Role / X-Debug-Actor 兜底
函数名就写死:平台鉴权,不读 X-Agent-Type。
有 Bearer 就验 JWT,解析出 actor_id 和 roles。
本地开发没 JWT 时,可用 debug 头冒充身份(仅 development)。
和 get_auth_context 不同:后者在 JWT 通道会强制校验 X-Agent-Type + 准入矩阵。
分析员「问数工作台」调 /api/analyst/chat,要带 X-Agent-Type: analyst 吗?
模块 3 · 出参脱敏
PLATFORM_RESPONSE_DESENSITIZE:
模拟库原样,真库再遮
环境变量
PLATFORM_RESPONSE_DESENSITIZE
控制平台 API 出参是否打码姓名/手机/证件。
v0.1 默认关——本地 Core 模拟库联调看原值;接真 Core 再开,在
Platform Service
统一出口处理。
开关行为一览
| 开关 | 模拟库联调 | 生产 / 真 Core |
|---|---|---|
false(默认) | L0 字段原样返回 | — |
true | — | 姓名/手机/证件/银行卡打码;customer_id 不脱敏 |
app/service/platform/common.py)的 maybe_desensitize_row → prepare_row。REST 与 Agent 适配层都应走这里,不要在路由里手写 mask。
def maybe_desensitize_row(row: dict | None) -> dict | None:
if row is None or not settings.platform_response_desensitize:
return row
for key, masker in (
("display_name", d.mask_name),
("mobile_phone", d.mask_phone),
("id_card_no", d.mask_id_card),
...
): ...
def prepare_row(row):
return to_jsonable(maybe_desensitize_row(row) or row)
开关关着 → 数据库行原样返回,方便本地对账。
开关开着 → 按字段名找 mask 函数,逐个打码。
prepare_row 是统一出口:先脱敏,再把 Decimal/日期转成 JSON 能序列化的类型。
指挥 AI 加新字段时,若含 PII,记得在这里登记 masker。
自检:你能否指挥 AI 正确切换?
准备接真 Core,要让出参打码手机号,最省事的做法是?
脱敏开启后,customer_id 会被打码吗?
模块 4 · 接缝与草案
适当性只准一条 canonical
还有空壳与行情草案
平台 API 是 v0.1 的「对外标准」,但仓库里已有重复能力、空壳路由、Phase B 草案。 指挥 AI 加第三条适当性 URL 或前端直连行情源,都会踩接缝。
路由补全:staff 与空壳
已实现 · v0.1 四域 + staff
/api/customers · products · advisors · compliance · staff
空壳 · 一期不做
知识库空壳 API(knowledge.py) · 管理空壳 API(admin.py) — T-21 拍板:脚本入库,无上传/重建 HTTP。
适当性:两条 URL,别再开第三条
POST /api/compliance/suitability-check
→ get_platform_auth_context
→ app/service/platform/
POST /api/risk/suitability/check — 风控 REST + X-Agent-Type: risk。
客服 suitability_check Tool 走 core_ro.check_suitability,是第三条对话内路径,但底层同一判定内核。
新增 HTTP 路由前先查契约迁移表,避免四个入口四个口径。
行情 Phase B(草案 · 未落地)
v0.2 草案:nav-snapshot · sync_market_nav。C-05 硬规则:禁止爬虫、禁止前端直连第三方行情——净值须走后端同步管道。