测试日志:理财师线(顾问 Agent)端到端缺口补测
1. 文档元数据
| 项 |
值 |
| 测试记录编号 |
TEST-LOG-2026-09-13-AD-001 |
| 缺陷/变更标题 |
理财师线端到端覆盖缺口补测(AD7 KYC 闭环 · AD8 接缝取证 · AD9 无消费方端点 · AD10 代客下单)+ Part B/C |
| 文档版本 |
v1.0 |
| 创建日期 |
2026-09-13 |
| 关联分支 |
merger |
| 关联拍板/TODO |
本轮七项口径:产出边界「盘点 + 直接开跑补齐」· 缺陷「只报告不修」· 数据「快照+还原」· 覆盖「三条线全补」· 报告包「一线一包」 |
| 风险等级 |
LOW(本线无 P0/P1;唯一实质缺陷在孤儿代码内) |
| 缺陷类型 |
接缝绑定错误(潜在)· 可观测性/契约观察 |
| 发现阶段 |
端到端覆盖缺口补测轮 · Part A/B/C |
2. 组织与责任
| 项 |
值 |
| 所属模块 |
理财师线(顾问 Agent):app/api/advisor_agent.py · app/service/agent_graph.py · app/service/agent_tools.py |
| 模块负责人 |
— |
| 发现人 |
本轮补测 |
| 修改人 |
无(本轮只报告不修) |
| 测试执行人 |
本轮补测 |
| 评审人 |
— |
3. 缺陷描述
ADV 缺陷-3 —— build_advisor_tools 三处工具绑定到不存在的 service 方法
| 项 |
值 |
| 缺陷编号 |
ADV-3 |
| 严重等级 |
P3(潜在;整支为孤儿代码,无生产调用方) |
| 复现步骤 |
python scripts/dev/advisor_gap_e2e_smoke.py --only AD8 |
| 期望 |
工具应调用真实存在的 service 方法与签名 |
| 实际 |
4 条用例稳定 FAIL:AttributeError: no attribute 'chat' / TypeError: missing keyword-only 'auth' / AttributeError: no attribute 'search' / AttributeError: no attribute 'check' |
| 根因 |
见下「根因表」——工具内写的方法名与真实 service 不符(含签名形态差异) |
| 修复方案 |
本轮不修。 修复轮方向:改工具内调用为真实名 + 同步改 tests/test_step11_agent_graph.py 的替身(改真实 service 或 spec= 严格 Mock),否则修完仍被 Mock() 遮蔽 |
根因表(逐条定位到行号):
| 用例 |
工具 |
工具内写法 |
真实 API |
异常 |
| AD8-03 |
manage_kyc_session(action="chat") |
kyc_service.chat(...) |
app/service/kyc_session_service.py:140 chat_session |
AttributeError |
| AD8-04 |
manage_kyc_session(action="get") |
kyc_service.get_session(session_id=) |
app/service/kyc_session_service.py:133 get_session(session_id, *, auth) |
TypeError |
| AD8-05 |
search_templates |
template_service.search(query=, limit=) |
app/service/template_service.py 仅 reload/try_render/list_published_prompts;真搜索在 script_template_service.py |
AttributeError |
| AD8-06 |
compliance_check |
compliance_service.check(text=, scene=) |
app/service/compliance_check_service.py:38 check_text(payload, *, trace_id, advisor_id) |
AttributeError |
排除"探针写错"的证据: 同一探针路径、同一 invoke 方法、同样注入真实 service,对照组 AD8-01(持仓)/ AD8-02(净值)稳定 PASS。只有接错的三个失败 ⇒ 失败来自接缝本身,不是探针。
遮蔽成因(AD8-07 实测): tests/test_step11_agent_graph.py:113 是全仓唯一调用方,其 :114-117 为四个 service 全注入 Mock()。Mock() 对任意属性访问返回可调用对象,故三者"全部通过"。实测同一调用在 Mock 下返回 ok → <Mock name='mock.chat()' id=...>。
ADV 缺陷-4 —— 两条端点可用但全仓无消费方
| 项 |
值 |
| 缺陷编号 |
ADV-4 |
| 严重等级 |
P3(观察项,非故障) |
| 复现步骤 |
python scripts/dev/advisor_gap_e2e_smoke.py --only AD9 |
| 期望 |
—(记录事实) |
| 实际 |
GET /api/advisor-agent/dashboard/ping 与 GET /api/advisor-agent/allocation/ping 实测 HTTP 200 且回显正确,但全仓无任何前端/脚本引用;guard/check 与 copy/track 有调用方 |
| 根因 |
未被接入(设计遗留) |
| 修复方案 |
记录以备清理或接入。本轮不修 |
4. 变更清单
| 类型 |
路径 |
说明 |
| 新建 |
scripts/dev/advisor_gap_e2e_smoke.py |
AD7–AD10 缺口补测脚本(真实 HTTP) |
| 收复 |
scripts/e2e/09-advisor-pages.mjs |
自仓库外 C:/Users/Windows/e2e-jinrong/ 收回,改指向仓库内 scripts/e2e/harness.mjs(F-05) |
| 未改 |
scripts/dev/advisor_e2e_smoke.py |
回归基线,一个字未动(拍了口径:只新增分组会污染退出码对照,故另起脚本) |
| 未改 |
app/** · web/src/** |
本轮边界:不改业务代码 |
5. 测试执行记录
| 序号 |
类型 |
用例/命令 |
执行人 |
结果 |
证据 |
| 1 |
Part A |
python scripts/dev/advisor_gap_e2e_smoke.py --report …/ad-gap.md |
本轮 |
31 PASS / 4 FAIL / 0 SKIP / 10 INFO(退出码 1,FAIL 全为 AD8 取证) |
_raw/ad-gap.md |
| 2 |
Part B |
node 09-advisor-pages.mjs |
本轮 |
17 PASS / 0 FAIL / 0 SKIP(退出码 0) |
_raw/results-09-advisor.json · _raw/09-advisor-pages.stdout.txt · _raw/shots/ |
| 3 |
Part C |
python scripts/dev/advisor_e2e_smoke.py --report …/_partc-advisor.md |
本轮 |
63 PASS / 0 FAIL / 0 SKIP(退出码 0,基线未退步) |
_raw/_partc-advisor.md · _raw/_partc-advisor.stdout.txt |
分组明细(Part A):
| 组 |
覆盖对象 |
PASS |
FAIL |
SKIP |
INFO |
| AD7 |
KYC 全链路复现(建会话→读视图→chat→complete→状态机负例→越权/404) |
13 |
0 |
0 |
2 |
| AD8 |
接缝取证(真实 service + 2 对照组 + Mock 遮蔽面) |
2 |
4 |
0 |
2 |
| AD9 |
无前端调用方的端点(含注入拦截负例) |
9 |
0 |
0 |
5 |
| AD10 |
代客下单链路(归属·越权·冒用通道·缺头) |
7 |
0 |
0 |
1 |
|
合计 |
31 |
4 |
0 |
10 |
分母纪律: AD8 的 4 条 FAIL 按 R3 单列,不计入通过率分母。有效通过率视为 31 PASS / 0 FAIL(口径外)。
关键正面证据(逐条):
| 用例 |
断言 |
实测 |
| AD7-02 |
GET /sessions/{id}(v1.0 时 500)→ 200 |
HTTP 200 |
| AD7-04 |
POST /sessions/{id}/chat(v1.0 时 500)→ 200 |
HTTP 200 |
| AD7-04a |
返回 parsed_fields/collected_fields/missing_fields 三件套 |
parsed=dict collected=dict missing=list |
| AD7-05 |
agent_message.created_at 无 NULL(v1.0 500 根因面) |
2 行,NULL 的 seq_no=[] |
| AD7-06 |
非归属顾问读同一会话 → 拒 |
HTTP 403 / 40302 |
| AD7-07 |
不存在的 session → 404 |
HTTP 404 / 40401 |
| AD7-09 / 7-10 |
completed 后再 chat / 再 complete → 409 |
HTTP 409 / 40901 ×2 |
| AD9-03a |
安全文本 action 枚举 |
action='passed'(拦截值为 'blocked') |
| AD9-03b |
放行时未命中任何 GUARD_* 规则 |
guard_type=None |
| AD9-04 |
提示词注入被拦 |
HTTP 400 / 40002 |
| AD10-02a |
名下客户回显一致 |
customer_id='CUST-1001' |
| AD10-03 |
非名下客户越权 → 拒 |
HTTP 403 / AUTH_403_NOT_ASSIGNED |
| AD10-04 |
缺 X-Agent-Type → 拒 |
HTTP 401 / AUTH_401_MISSING_AGENT_TYPE |
| AD10-05 |
不存在的客户 → 拒(非静默放行) |
HTTP 403 / AUTH_403_NOT_ASSIGNED |
| AD10-06 |
customer 型 token 冒用 advisor 通道 → 拒 |
HTTP 403 / AUTH_403_AGENT_MISMATCH |
本轮我方订正(1 处,非产品缺陷): AD9-03a 首跑期望写 action='pass' → FAIL。查源码确认真实枚举为 'passed'(app/service/input_guard_service.py:22),拦截为 'blocked'(app/service/input_guard.py:111)。是我的期望错,不是产品错,已订正并补 9-03b 把「放行」与「未命中规则」拆成两条。
6. 数据准备
| 数据集 |
是否重灌 |
操作 |
jinrong_core 交易表 |
否 |
本线 Part A 不写 Core 交易表(AD10 只到受理与归属,未提交代客交易) |
jinrong_agent KYC 会话 |
否 |
AD7 新建 1 个 KYC 会话(kyc_sess_c2f2b6f33129487d9c7187bd056590f3),追加型,按语义不回滚 |
jinrong_agent copy/track |
否 |
AD9-05 留痕 1 条(track_dc908deaf5904aab96a7a64efd200ed1),追加型留痕 |
jinrong_core 5 写表 |
— |
Part C 的 advisor_e2e_smoke.py 会写入;已在 Phase 6 依 snapshot.sql 还原并逐表 CHECKSUM 校验通过(见横切包 XC-001 §6) |
7. 结论
| 项 |
结论 |
| 需求覆盖 |
理财师线 AD7 KYC 全链路 · AD8 接缝 · AD9 无消费方端点 · AD10 代客下单 四组补齐;/api/advisor-agent/* 写操作由既有基线 63 条覆盖 |
| 回归 |
✅ 未退步:Part C 基线 advisor_e2e_smoke.py 63 PASS / 0 FAIL,退出码 0;Part B 17 PASS / 0 FAIL |
| 剩余风险 |
① AD8 修法未实施验证(须同步改测试替身);② 代客下单未走写入侧;③ 两条 ping 端点无消费方未决 |
| 建议人工再验 |
① 打开 /app/advisor/kyc 与 /app/advisor/customers 目视确认;② 若决定启用 agent_graph.py,必须先在真实 service 下跑通 AD8 组再接入 |
8. 签核 / 修订历史
| 版本 |
日期 |
修订人 |
说明 |
| v1.0 |
2026-09-13 |
本轮补测 |
首次建立:AD7–AD10 + Part B/C 证据归档 |