Files
group_fqcd_jr/docs/07-测试问题修复记录.md
T
lzf_0626 36c7a9d8d2 文档:审查报告入库 + 全量校对补注
## 新入库(`docs/演示用/`)

- `代码库全面审查报告-2026-09-14.md`
- `代码修改方案-2026-09-14.md`
- `记忆系统排查报告-2026-09-14.md`
- `记忆系统修复文档-2026-09-14.md`
- `文档一致性审计报告-2026-09-14.md`
- `多Worker接入方案-2026-09-14.md`

## 全量校对(32 个既有文档 + `AGENTS.md`)

跨 39 个文件、**1125 insertions / 148 deletions**。

⚠️ **这批改动同样不是本次会话写的**。我抽样核对过性质:是**实质内容补充**而不是
格式/换行转换。例如 `docs/44-演示流程.md` 新增两条"2026-09-14 补注":

- `启动金融Agent平台.bat` 只在**桌面**上,仓库里只有 `启动平台.bat` 这一份
  (两份由同一个 `tools/make_launcher_bat.py` 产出,改完 `start.ps1` 重跑它一起更新);
- `advisor_t`(9020) 与 `offsite_t`(9006) **不在 `tools/seed_test_rbac.py` 的演示用户里**
  (那里只有 `cust_t`/`risk_t`/`admin_t`/`review_t` 四个),由 `grant_*.py` 系列创建,
  **重跑种子不会重建它们** —— 换机器时这两个账号登录失败,要先查 `sys_user` 有没有这两行,
  而不是查密码。

这两条都是对的地方,与我这一路踩到的现象一致(我确实用到了 `advisor_t`/`offsite_t`)。

**我没有逐字审阅全部 39 个文件**,只抽样确认了改动性质与规模。若其中有需要复核的段落,
请指明文件,我逐处核对。
2026-09-14 20:36:00 +08:00

9.7 KiB
Raw Blame History

测试问题修复记录

⚠️ 多处已过期(2026-09-14 复核更新): ① 启动命令用 conda activate jr_py313,本机实际解释器是 .\.venv\Scripts\python.exe; ② 文中称「当前没有组员业务 Agent」——已不成立:app/service/agent/bootstrap.py 的 register_business_agents()(L407-447)现注册 7 个:fund_query_demo、 customer_service、risk、offsite_fund、promotion_material、platform_probe、advisor; ③ 「接口目录实为 50 项」——现为 21 个 router(新增场外、推广、访客令牌、入驻 onboarding、 投顾推荐等),见 docs/05-接口文档.md §19; ④ 「升级/降级往返验证通过,68 张表」——那是 2026-09-11 的实测;现为 90 张表 = 89 张业务表 + alembic_version(场内 51 + 场外/推广 17 + 投顾 21), 核验用 tools/audit_schema.py。

(此批注由 2026-09-11 只读审计加入、2026-09-14 扩充;原文未改动。详见 docs/superpowers/ARCHIVE-2026-09-11-文档清理归档.md §3)

P1-1:JWT sub 在认证阶段验证为 ASCII 正整数且不超过 BIGINT UNSIGNED 范围。 test_security.py 11 项通过,非法 sub 的真实 ASGI HTTP 请求在查询身份前返回 401。

⚠️ 一处已过期:下文 P0-3 的启动命令用 conda activate jr_py313,本机实际解释器是 .\.venv\Scripts\python.exe。(原文未改动,仅加此批注。)

背景变更(2026-09-11):本文原先的「问题来源」docs\06-底座代码测试报告.md 已随文档清理删除 —— 但本文已完整承载该报告的全部 P0/P1 结论(P0-1/2/3、P1-1/2/3/4/5 逐条在册), 因此本文现在是这些修复结论的唯一权威来源,不依赖已删文件。

问题来源(已删除,见上):06-底座代码测试报告.md(当时保留原报告、不覆盖原始证据)。 当前完成状态以 docs/superpowers/sdd/2026-09-10-客服Agent与RAG-qyqy版/phase1-acceptance-report.md 为准。

P1-3 / P1-4:SSE 与分层

SSE 首次等待完成后输出 delta,终态重连用 replace;6 项测试覆盖 Unicode 分块和恢复。 Controller 数据查询移至 Repository,权限/事务/状态判断移至 Service;SSE 轮询由 RunQueryService 读取数据库快照,连接期间不持有数据库 Session。12 项相关回归通过。 AST 架构测试禁止 Controller 依赖 Model/Repository 和直接操作 Session。

接口目录实为 50 项(4 运行 + 7 会话 + 2 记忆 + 1 知识 + 33 管理 + 3 运维)。 (⚠️ 2026-09-14:该 50 项为当时的快照;现共 21 个 router,含场外/推广/访客令牌/onboarding/投顾推荐,见 docs/05 §19。) 这些是底座范围;不能把未实现的管理接口当作组员业务范围排除。报告的 46 项计数修正。

P1-2:治理钩子接线及当前支持范围

工厂向每个新 Agent 注入 AgentGovernance;BaseAgent 调用 resolve/recall/review。 默认实现直读生效 ConfigRelease、按意图收窄工具集合、读取已审核禁止表达规则, 并通过 MemoryService 读取当前客户有效记忆。handle 可读取 config/memories。 输出校验使用进入 handle 之前捕获的快照;业务代码改写实例属性不改变审查输入。 没有工厂注入的 Agent 不可执行;禁止覆盖全部治理钩子。

合规覆盖:固定禁止表达、已审核 contains/exact 规则、号码脱敏、已授权记忆引用校验。 敏感记录、工具调用和其他类型引用在公共执行器尚未接入时拒绝,不允许 Agent 自报成功。 正则规则因 Python re 无执行超时暂时失败关闭。适当性产品比较、真实模型意图分类和 公共工具执行器尚未完成;本项只证明报告所列三个空钩子已接线,不宣称完整合规体系。 配置工具上限按意图保存,不使用 Agent 级并集。没有 active 发布使用 code:version, 默认不给工具权限。已补 conversation_message 原有 intent/confidence/source_references ORM 映射和结果持久化;没有数据库结构改动。

验证:9 项治理单测及 1 项真实 MySQL 配置读取、客户记忆和已审核禁止表达替换测试。

P0-1 / P0-2:统一授权与注册校验

JWT 验签后每次读取 MySQL 的 sys_user、有效 sys_user_role/sys_role、权限及客户归属。 账号状态只接受基线的“正常”,角色状态采用 active;不从 user_type/employee_role 或客户端 roles/portal 推导权限。权限数据暂不缓存,撤权在下次请求立即生效。 REST 服务端入口为 api,工厂要求 definition.allowed_roles/allowed_portals 及 agent:run。 受理在写入消息/幂等/run/Outbox 前校验;拒绝写独立审计。 未知 Agent 按 05 权威规范返回 404 AGENT_TYPE_NOT_FOUND,而非报告建议的 400。

验证:41 项单元测试;真实 MySQL 身份权限测试;4 项注册拒绝及受理回归。

P0-3:独立 Worker 与持久化闭环

启动(两个终端,均先激活 conda 环境 jr_py313):

python -m uvicorn app.main:app --host 127.0.0.1 --port 8099
python -m app.worker

Worker 的轮询周期、租约和重试次数配置在 .env 的 WORKER_POLL_SECONDS、 WORKER_LEASE_SECONDS、WORKER_RETRY_LIMIT。python -m app.worker --once 只处理一轮。 启动命令会消费本库待处理任务;测试使用唯一 run_id 隔离,不消费开发者其他历史运行。

2026-09-11:自审口径、测试基座与错误码对齐

配置发布允许创建人自审(用户确认口径):撤销 config_release 的 chk_config_release_separation 约束(迁移 20260911_drop_review_separation), ConfigReleaseService.approve 与 AdminService 驳回分支均如实写入 reviewer_id。 升级/降级往返验证通过,68 张表字段、索引、外键指纹不变。 (⚠️ 2026-09-14:68 张为当时口径;现 90 张 = 89 业务 + alembic_version。)

测试基座:tests/conftest.py 在导入 app 前把测试期引擎换成 NullPool,消除多事件循环 下的连接复用(集成失败 30 → 2);单元测试显式给定外部识别开关,不再读取本机 .env (注意 app/core/config.py 的 load_dotenv 会让 _env_file=None 失效,必须用构造参数)。

错误码以 docs/05-接口文档.md §3.5/§3.6 为唯一权威:删除第二套配置发布入口 (app/api/controllers/config_releases.py 及其专属 ConfigApplicationService); app/main.py 新增 RequestValidationError 统一信封;docs/09 错误码表同步对齐。

跑集成测试前必须停掉 python -m app.worker:worker 会认领任意 queued/running/cancel_requested 运行并消费 Outbox 事件,与测试共用同一数据库时会造成 偶发失败(实测:测试造的 queued 运行 1 秒内被外部 worker 领走;取消用例两次响应仅 status 从 cancel_requested 变为 cancelled)。长期方案是让集成测试指向独立测试库。

记忆抽取事件按策略门控(MemoryService.should_extract_memory):只有显式持久事实 (用户偏好/约束/身份/目标信号、成功工具结果、业务事件)才写 memory.extraction_requested。 断言该事件的用例必须使用带信号的消息。

集成测试统一走独立测试库,避免与开发用的 worker 争抢同一份数据:

python -m tools.run_tests_on_test_db                    # 默认 tests/integration
python -m tools.run_tests_on_test_db tests/unit tests/contract tests/integration

该工具只在本进程内把 MYSQL_DSN 换成 jr_agent_test(.env 不改),空库时先导入 alembic/baseline_generated.sql(33 张历史基线表不在迁移链里),再 alembic upgrade head 并灌入 9001/9002/9003 测试账号。首次使用需由有权限的账号建库并授权:

CREATE DATABASE IF NOT EXISTS jr_agent_test CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
GRANT ALL PRIVILEGES ON jr_agent_test.* TO 'jr_app'@'localhost';

HTTP 与 Worker 共用 app/service/agent/bootstrap.py 注册入口。当前没有组员业务 Agent; (⚠️ 2026-09-14:现有 7 个组员业务 Agent,见文件头批注与 bootstrap.py L407-447。) 未注册业务类型明确拒绝。集成测试显式注入 TestAgent,不在生产注册假业务实现。

Outbox 消费确认的是 MySQL 中已存在的持久运行;进程在确认后退出,下一进程仍可扫描 queued 或过期 running。模型执行期间不持有数据库长事务;续租使用独立短事务。 每次领取生成新的 worker_id;complete_run 行锁、终态幂等和所有权校验阻止迟到提交。 执行前重新加载用户权限。恢复性错误有上限重试;终止错误同步幂等状态及审计。

验证:test_worker_runtime_mysql.py 三项真实 MySQL 测试,覆盖 HTTP 受理、Outbox 消费、并发执行、结果查询、记忆事件提交、重复执行、执行前撤权和迟到 Worker 提交。 这是底座执行闭环验收,不代表真实模型供应商或业务 Agent 流程已通过验收。

P1-5:底座接口和事务幂等

补充统一动态资源管理路由,覆盖配置发布、平台配置、模型端点、路由、Prompt、意图、回复模板、 禁止表达和审计;公共会话/取消/记忆/知识/健康/指标入口已注册。写操作通过新增 api_request_receipt 保存用户、作用域、幂等键、请求摘要和结果,结果与业务写入及审计同一事务。 配置只能编辑草稿,审核需要双人,激活/停用/回滚校验状态;模型路由禁止重复备用端点, 端点必须 active;HTTP 不暴露密钥或旧 fallback_endpoint_ids。OpenAPI 以 FastAPI 运行时生成, 具体路径使用资源注册表,避免 Controller 复制业务字段。