Windows
|
2fe7d0c506
|
feat(投顾): 客户主动申报投顾方案 + 投顾受理自动生成方案草稿
客户在自己主页提交申报 → 投顾工作台受理 → 自动跑既有推荐逻辑生成一份待审核草稿 →
投顾再走既有的「审核通过 → 发送给客户」。补上原先「客户只能被动等方案」的缺口。
后端
- 新增表 advisor_service_request(本轮新建,带 AUTO_INCREMENT)+ 迁移
20260916_advisor_service_request(幂等:先查表再建,兼容本库 alembic 指针滞后)
- 新增 AdvisorServiceRequestService:create / list_mine / queue / review
· 申报前置:风险测评必须存在且未失效(FM-03,12 个月),服务端判
· 队列复用 ProductRecommendationService._visible_customer_ids(本人 + 归属),待受理排前
· 受理即调用推荐逻辑生成草稿并把 content_id 回填;生成前置失败给出人话原因并保持待受理
· 「已发送客户」不落库,由关联方案 published_at 推导,避免两处状态各写各的
- 权限码 9071-9074(客户 write/read:self、投顾 read/review),已进种子与授权工具
前端
- 投顾工作台新增「客户申报」面板(受理 / 驳回,驳回理由客户可见)
- 客户页新增申报表单(金额/期限/风险偏好/备注)与「我的申报」列表
客户自助路由刻意不挂投顾灰度闸门:那是投顾业务的灰度,客户提交自己的申请不该被它拦下。
|
2026-09-16 18:17:46 +08:00 |
|
lzf_0626
|
076d786bc6
|
补齐客服转人工工单流:从"只能看"到"能推进"(基线状态机,不自行发明)
## 问题
`svc_handover_ticket` 的 DDL 与状态机在 `docs/02` §7.2 早就定好了
(pending → assigned → processing → resolved → closed,未解决可 cancelled),
但平台**只有 handover:read(只读队列)**:没有任何入口能改状态、assigned_to /
accepted_at / resolved_at / closed_at / resolution 五列**全库 0 非空**,
于是 40 张工单永远停在 pending —— 用户看到的就是"工单全都长一样"。
## 改了什么
后端:
- 新增 `app/service/customer_service_handover_action_service.py`:五个动作
(分配/接单/解决/关闭/取消),`SELECT ... FOR UPDATE` 锁单后判状态;
接单允许从 pending 自助接管(同时记受理人);取消不写 closed_at(该列属 closed 状态);
每次流转写一条 interaction_audit(handover.assigned/accepted/resolved/closed/cancelled);
非法流转 409、坐席不存在 422、工单不存在 404;回包不含 customer_id/session_id。
- 只读服务保持只读(读侧与写侧是两条边界,单测守着"读侧不许长出写方法"),
但列表支持 `?status=` 六态筛选、详情补上受理人与流转时间(坐席侧路由信息,非客户数据)。
- `app/api/controllers/admin.py`:五个 action 端点 A049–A053
(assignments / acceptances / resolutions / closures / cancellations),
走 `ApiTransactionService.execute_in` —— 幂等记录与业务写入同事务、重复键回放。
- 权限:新增 `handover:write`(9069,只授 admin),已并进种子
`tools/seed_test_rbac.py`;配套幂等脚本 `tools/grant_handover_write_permission.py`。
前端(管理员工作台 · 转人工工单页):
- 按状态给按钮(待处理→分配/直接接单、已分配→接单、处理中→解决、已解决→关闭、
未解决都可取消),加了状态筛选与"刷新";摘要弹窗补上受理人与四个时间点、处置结论。
- api-client 注册五个端点;workspace.js 的 api-client 引用与页面自身的 ?v= 一并升版,
避免浏览器拿旧缓存(旧缓存里没有这些端点)。
冒烟与测试:
- `tools/e2e_smoke_test.py`:B 段建的测试工单由 F 段走完 分配→接单→解决→关闭 收尾
—— 既不再把测试件堆在 pending 队列里(此前每次冒烟攒一张),又让每次冒烟都覆盖一遍状态机。
总数 40 → 44 项,实测 44/44 全绿。
- 新增单测 24 条(状态机合法/非法路径、越权、坐席不存在、审计、视图不泄漏客户标识)
与一条真机集成用例(HTTP 十步 + 数据库侧审计证据 + 自动清理)。
- 读侧那条"详情不得返回 assigned_to"的旧断言按新口径更新,并写清为什么。
## 验证
- `pytest tests/unit tests/contract` → 1489 passed, 2 skipped, 0 failed
- 新增集成用例通过;`tests/integration` 全量跑时
`test_memory_extraction` / `test_run_cancellation_mysql` 两条偶发红 —— 单独跑都通过,
是 AGENTS.md 已登记的"常驻 Worker 抢队列"(跑验收前须先停 Worker)
- `tools/portal_api_check.py` → 41 项通过 39、失败 0
- `tools/e2e_smoke_test.py` → 44/44 全通过
- `python tools/check_rbac_seed_consistency.py` → 通过(种子 63 条权限)
- 真机 HTTP 实测:分配→接单→解决→关闭四步 200 且时间戳齐全;取消路径 200 且 closed_at 为空;
同键重发回放不二次推进;对已关闭工单再分配 409;风控账号处置 403
## 文档
`docs/44-演示流程.md`(场景 4/8 + 命令 + 44 项)、`docs/演示用/后端接口文档`(新增 §11.4b 与
A049–A053)、`docs/演示用/全功能流程-大白话版.md`(工单页签改"读写"+ 已知偏差)、
`AGENTS.md`(9066-9069 号段演进 + 冒烟 44 项)
|
2026-09-15 00:41:59 +08:00 |
|
lzf_0626
|
5da2fb5790
|
投顾代客三项的权限码缺失:整片 403(与 9057-9059 同一个坑的第二批)
## 现象
投顾工作台(`advisor_t`,9020)一选客户就整片失败,页面只显示
「请求失败,请检查权限或稍后重试」。API 访问日志给出真相:
```
POST /api/v1/advisor/recommendations 403
POST /api/v1/advisor/asset-allocation 403
POST /api/v1/advisor/portfolio-analysis 403
GET /api/v1/advisor/recommendations/published 200 ← 只有它不需要代客权限
```
## 根因:三个 `*:customer` 权限码在库里根本不存在
三个服务在"代客"(`customer_id != context.user_id`)时要求的是**动态拼出来的
`:customer` 变体**:
- `product_recommendation_service.py:71` → `product-recommendation:generate:customer`
- `asset_allocation_service.py:57` → `asset-allocation:generate:customer`
- `portfolio_analysis_service.py:46` → `portfolio-analysis:read:customer`
而 `sys_permission` 里 `:customer` 后缀**只有 4 个**(`memory:read:customer`、
`investment-goal:{read,write,confirm}:customer`)—— 这三个从来没登记过。
`AuthorizationService.require()` 第一步就查不到该码 ⇒ 直接 403。
这与种子里 **9057-9059 的注释是同一个坑**("动态拼出来的权限码,对账工具抓不到字面量,
于是投顾查/建/确认客户目标全部 403"),前人修了 investment-goal 那三个,这三个漏了。
## 修法(按仓库纪律:种子定义权限码 → grant 脚本绑定角色)
1. `tools/seed_test_rbac.py`:新增 **9066-9068** 三个码,`data_scope=own_customers`
(`require_customer_scope` 只放行 `all`,或 `own_customers` 且客户确在
`context.customer_ids` = 归属客户内 —— 这正是投顾该有的最小权限),
并写明三处调用点,避免后人再漏;
2. `tools/grant_advisor_role.py`:把三个码加入 `ADVISOR_GRANTED_CODES`
(admin 因 `ADMIN_PERMISSIONS = 全部种子权限` 自动获得)。
已执行:`seed_test_rbac.py` → `grant_advisor_role.py`
(advisor 新增 3 项,共 31 项;admin 62 项)。
## 验证(真实 HTTP,9020 身份)
| 客户 | 推荐方案 | 资产配置 | 组合分析 |
|---|---|---|---|
| 9101(演示客户) | 200 `profile_required` | 200 `profile_required` | 200 `no_positions` |
| **9001(真实客户)** | **200 已生成方案**(content_id=5, pending_review) | **200 ready**(含配置比例) | **200 ready**(7 个持仓,市值 10698.60) |
**403 全部消失**;对真实客户三项均返回真实结果。
## 同时补的归属数据
`sys_customer_assignment` 原有 9020→9001 一行;工作台把演示客户的 id
(9101-9104)当真实 `customer_id` 发给后端,而 `own_customers` 要求客户在投顾名下,
因此用 `tools/assign_customer_scope.py` 补了 4 行(9020 → 9101/9102/9103/9104),
回验 `customer_ids = ('9001','9101','9102','9103','9104')`。
## 仍未解决(属**数据**缺口,不是权限)
1. **9101-9104 在库里没有任何数据**:`advisor-config.js:126` 注释指向的
`_seed_demo_customers.py` 在仓库、git 历史与桌面上**都不存在**(从未提交),
所以这四位没有账号 / 风险测评 / 投资目标 / 持仓 —— 只能返回 `profile_required`。
2. **推荐候选恒为 0**:`AdvisorProductRepository.authoritative_tradable_products`
要求**已验证**的适当性证据与合同快照(`review_status='verified'` 且
`source_url` / `document_sha256` 非空,fail closed),而
`advisor_product_suitability_reference` / `advisor_product_contract_snapshot`
均为 0 行。这需要产品治理线提供可核查证据,**不应伪造**。
## 门禁
`tools/check_rbac_seed_consistency.py` 通过(种子内 id 唯一、各 grant 脚本与种子逐条一致);
`pytest tests/unit tests/contract` 全绿。
|
2026-09-14 22:56:42 +08:00 |
|
lzf_0626
|
0b82053ca2
|
fix(rbac): 交易权限的 data_scope 写成了动作名,导致下单/成交静默 403
`tools/seed_test_rbac.py` 的 `PERMISSIONS` 行形状是
`(id, 权限码, resource, action, data_scope)`,而 T 段那四条写成了
`(9061, "trade:order:create", "trade", "order", "create")` ——
「resource, action」多写了一段,于是 action 落进了 `data_scope` 的位置。
## 后果是静默失效,不是放宽
`IdentityRepository.load_context` 只收集 `data_scope ∈ {self, own_customers, all}`
的权限,其余**整条丢弃**:
if row["permission_code"] and row["data_scope"] in rank:
于是客户在库里**明明有**这四个权限,`POST /api/v1/users/me/orders`、
`GET /api/v1/users/me/orders`、`GET /api/v1/users/me/transactions` 等 T 段端点
却全部返回 `403 AGENT_PERMISSION_DENIED`「缺少操作权限」。
而同一批里 `account:read:self`、`holding:read:self` 的 scope 是 `self`,照常 200 ——
现象特别像"只有交易坏了",几乎不会有人去怀疑**权限行本身写错了字段**。
因为 seed 是 `DELETE FROM sys_permission WHERE id BETWEEN 9001 AND 9099` 的**重建**语义,
**每跑一次种子就重现一次**,而 `tools/seed_demo_data.py` 的第 1 步正是它。
## 修复与守卫
- 四条改为 `"self"`,并就地写清第 5 个字段是 `data_scope`、只能取三种值;
- `tools/check_rbac_seed_consistency.py` 增加**第 5 条检查**:每行字段数必须是 5,
且 `data_scope` 取值合法。已用模拟对象确认它能抓住事故写法
(非法 scope 报 2 条、字段数不足报 1 条、合法写法通过),
并由 `tests/unit/tools/test_rbac_seed_consistency.py` 纳入门禁。
修复后实测:库内非法 `data_scope` 归零(`self` 22 → 26);
`/users/me/{account/dashboard,holdings,transactions,orders}` 全部 200;
下单成交价 4.579(真实行情);e2e 冒烟 40/40;integration 106 passed。
|
2026-09-13 22:57:59 +08:00 |
|
lzf_0626
|
f4eaa2f631
|
fix(seed): 客户种子改为已开户;portal.py 标注为调试工具
- seed_test_rbac.py 把 fund_account_status 硬编码成 'closed',与 create_test_user.py
的 {customer: 已开户, employee: closed} 口径不一致,导致种子建的客户永远未开户
- 影响:GET /users/me/account/dashboard、/users/me/cash-ledger 恒返回 404,
正式前端 T001/T009 两个页面打不开(端点本身是好的)
- 改为按 user_type 取状态;配合 tools/seed_sim_account_demo.py 开虚拟资金账户后
T001/T006/T007/T009 全部 200
- tools/portal.py:正式前端已在 app/static/portal/,本工具降级为跨角色联调工具
|
2026-09-13 16:19:54 +08:00 |
|
张胜宇
|
ebc3fe4cbe
|
feat(§T): 账户看板 + 场内模拟交易 9 端点(用户自助首版)
新增 §T 用户自助段(docs/05 §19 新号段 7 个 = A×40/C×7/K×4/M×4/O×3/R×4/T×9):
- T001 GET /api/v1/users/me/account/dashboard — 账户/资金/持仓/盈亏汇总
- T002 POST /api/v1/users/me/orders — 委托提交(首版 market 立即全额成交)
- T003 / T004 / T005 委托列表/详情/撤单
- T006 GET /api/v1/users/me/holdings — 持仓列表(含市值/盈亏/当日盈亏)
- T007 / T008 成交记录列表/详情
- T009 GET /api/v1/users/me/cash-ledger — 资金账本
要点(与 docs/00 §6.6 一致):
- 首版市价委托立即全额成交,不实现撮合队列/部分成交;T005 撤单首版对任何在场委托返回 ORDER_NOT_CANCELLABLE (409)
- 价格来源复用 base FundQuoteService;service 层不二次封装(满足 AGENTS 第 2 条)
- 首版风控 3 条硬性:产品可交易、客户适当性、持仓比例上限(fin_market_price 缺失或过期 → 拒绝买入)
- 数据库零修改:10 张 fin_* 表全部 docs/00 既定,本批 PR 改列类型与可空性均 0;底座实际偏差(id 无 AUTO_INCREMENT、所谓'生成列'是普通 NOT NULL)由 service _next_id / 业务派生值补偿
注册 API:9 端点均注册进 app.main;user=9001(cust)'s id 写账
权限码(tools/seed_test_rbac.py 同步登记 + CUSTOMER 全量):
9047 account:read:self
9048 trade:order:create
9049 trade:order:read
9050 trade:order:cancel
9051 holding:read:self
9052 trade:txn:read
错误码(app/core/errors.py + docs/05 §3.6 + tests/unit/core/test_errors.py DOCUMENTED 三方同步):
404 ACCOUNT_NOT_FOUND / ORDER_NOT_FOUND
409 ORDER_NOT_CANCELLABLE
422 INSUFFICIENT_FUNDS / INSUFFICIENT_HOLDING / HOLDING_RATIO_EXCEEDED / SUITABILITY_MISMATCH / PRODUCT_NOT_TRADABLE
503 FUND_QUOTE_UNAVAILABLE(可重试)
新增:app/api/controllers/trading.py / app/api/schemas/trading.py / app/service/trade_service.py / tools/seed_sim_account_demo.py / tests/unit/service/test_trade_service.py(unit×8) / tests/contract/test_trading_endpoint_contract.py(contract×11)
修改:app/main.py(挂载 controller) / app/core/errors.py(10 新异常类) / tools/seed_test_rbac.py / docs/05-接口文档.md(§19 T001-T009 + §3.6 9 新码) / tests/unit/core/test_errors.py(DOCUMENTED 同步)
门禁:pytest tests/unit tests/contract 1313 passed (+19 新增) / ruff all clean / 三道守卫全过
|
2026-09-12 15:50:37 +08:00 |
|
lzf_0626
|
615032ab00
|
fix: 修 POST /api/v1/conversations 的 MissingGreenlet(会话建不出来导致转人工 404)
- public_platform_service: 创建 ConversationSession 时显式赋值四个 server_default 时间列,
否则 flush() 后需回读数据库生成值,在 async session 里以同步属性访问触发
MissingGreenlet,接口 500,连带转人工一直报会话不存在
- portal: 客服改用 C001 真实建会话;转人工改传 reason_code/reason_detail(原 reason 属额外字段 422)
- portal: 投顾查客户投资目标走 customers/{id} 变体
- seed/grant: 补 investment-goal:{read,write,confirm}:customer 三个动态拼出的权限码
(data_scope=own_customers,投顾只看名下客户)
|
2026-09-12 15:37:36 +08:00 |
|
lzf_0626
|
bfd622b592
|
feat: 统一登录门户(按角色分流五套工作台)+ 补齐权限覆盖缺口
- tools/portal.py:客户/风控/运营/管理员/投顾五个工作台,走真实登录与真实接口
- 新增 6 个此前从未建过的权限码(promotion:* 4 个 / financial:nl2sql:read / probe:read),
它们让推广材料与金融 NL2SQL 两条线对所有角色都是 403
- 投顾权限 10 → 25 项:补投资目标创建确认、agent:run、行情、知识检索、客户画像、推广材料
- 运营补 financial:nl2sql:read;create_test_user.py 不再硬编码角色 id(改按 role_code 查库)
- portal 的写请求补 Idempotency-Key 头(漏了会被 AGENT_INPUT_INVALID 拒)
- 新增 tools/check_permission_coverage.py 做权限对账
|
2026-09-12 15:21:34 +08:00 |
|
zhangshy
|
5634fdc023
|
完善风控登录权限和模型能力配置
|
2026-09-12 14:43:05 +08:00 |
|
lzf_0626
|
ffbcc229c2
|
fix: 删掉 NL 合并后残留的未使用变量 role_ids(ruff F841)
|
2026-09-12 14:17:42 +08:00 |
|
wangjianlong_0626
|
57677f6554
|
merge: 合并主干 qyqy_develop(PR #7 之后)并对齐两套投影实现
共同祖先 bbf623a;主干 54 个提交、118 个文件;本线 25 个文件;9 个冲突文件。
主干这次把 **ZSY 的整条投影实现合进来了(PR #7)**,而本线此前的提交正是
移植并修正同一套代码 —— 因此冲突的本质是"同一功能两份实现并存",取舍错了会把
已修好的缺陷又带回来。逐项取舍与理由见 `docs/39-主干合并对策记录.md`。
## 取舍(9 个冲突)
取本线:
- `app/infrastructure/milvus_profile_projection.py` —— 主干是 ZSY 原版,含两处必炸点:
① `customer_id` 要求 int 而本仓所有生产者都写 `str` ⇒ 每个事件必然失败;
② 不可投影的 `memory_key` 直接 raise ⇒ 一条 `constraint:` 记忆毒死整客户整批。
本线版已放宽为「接受纯数字字符串」与「跳过并留痕」。
- `memory_sync_outbox_worker.py` / `conversation_privacy.py` / `risk_questionnaire.py`
—— 代码逐行一致,仅注释与说明文字详略不同(`risk_questionnaire.py` 两边**独立做了
完全相同的修复**,都改成 re-export `app.model.profile`)。
- 两个投影测试文件 —— 本线是他那份的**超集**(4→10、4→5 例,包含他全部用例)。
两边合并:
- `app/worker/runtime.py`:`__init__` 两边各加一个参数,都要。
- `app/service/agent/implementations/customer_service.py`:import 取并集;
`COMPANY` 取主干的「奶龙基金责任有限公司」("奶龙"是本项目实际品牌名,主干多处出现),
`HOTLINE`/`SERVICE_HOURS` **取本线的修复**(主干仍是占位符 `400-XXX-XXXX`,
本线已改为引用 `customer_service_rules` 的唯一来源 —— 这是 A1 缺陷修复,
否则同一客服给客户两个不同号码)。
- `AGENTS.md`:表数/Agent 清单取主干(90/89、7 个 Agent),本线的
`-X utf8` 与两条 outbox 易错点保留,测试基线按合并后实测重算。
## 消费端只保留一套(本次最重要的一处)
合并后曾出现**两套消费者读同一个 `memory_sync_outbox`**:`__main__.py`(PR #7)
与 `runtime.consume_profile_projections()`(本线),而**两者的 neo4j handler 不同**
—— 前者用 ZSY 的 `Neo4jProfileProjection`(按客户各建私有节点),
后者用主干 `ProfileGraphProjectionService`(共享 tag 节点、只投影已确认事实)。
同一事件被谁领到结果不定,等于"同一事实在图里有两种说法",正是**方案 A 要避免的状态**。
现只保留 runtime 那一套(带 `memory_sources` 兜底、neo4j 复用主干服务),
删除 `__main__.py` 的重复接线;装配入口职责仍在该文件(注入 `relationships` /
`projection_cleaner`),Milvus 客户端由 `bootstrap` 工厂惰性构造、缺配置时显式降级。
副作用:`app/infrastructure/neo4j_profile_projection.py` 不再被生产代码引用,成为
**死代码**(本线未删,属架构师线,其单测仍在)—— 待架构师决定删或明确分工。
## 顺带修掉的 3 个继承缺陷(主干同样存在,PR #7 后未整套复跑故未发现)
1. `tools/seed_test_rbac.py` **少建 `review_t`(9004)账号** —— 两个集成测试都依赖它
("账号存在但无权限应返回 200 空集而非 404"、`PLACEHOLDER_ACCOUNTS`)。
同时把用户↔角色绑定从 `zip(..., strict=True)` 改为**显式配对表**:原写法隐含
"USERS 与 ROLES 一一对应",一加不绑角色的账号就 ValueError、整个种子跑不完
(commit 在最后,外部表现是"什么都没发生")。
2. `CustomerProfileCandidateService._write_profile_snapshot` **漏写 `current_customer_id`**
—— 该列不是生成列而是普通可空列 + 唯一键 `uk_profile_snapshot_current`,
不写则唯一键形同虚设(多个 NULL 不冲突),且旧当前版本也没清该列、补写就会撞键。
现旧值置 None、新值显式写入(与 `ProfileGenerationService._clear_current` 一致)。
3. 集成测试前置未记录 —— 13 个登录/RBAC 用例因 401 而红,实为"测试账号不存在",
跑 `seed_test_rbac.py` + `set_user_password.py` 后转绿;已在 `AGENTS.md` 记明,
避免被误判成代码缺陷。
## 文档
- 新增 `docs/39-主干合并对策记录.md`(逐文件取舍 + 理由 + 遗留)
- `docs/37` 订正一处过时说法:曾写 `current_customer_id` 无人使用且故意不映射,
实际 `app/model/profile.py` 已映射且有人使用(详见该文档 §6.2 的订正块)
- 文档编号:主干已占 29–36,本线两份文档让号至 `docs/37`、`docs/38`
## 验证(合并后实测)
- `pytest tests`(全量)→ `2 failed, 1396 passed, 2 skipped`
- `pytest tests/integration` → `102 passed, 1 skipped`(修上述 1、2 后从 15 failed 归零)
- `mypy app` → `Success: no issues found in 245 source files`
- `tools/audit_schema.py` → 89 张业务表无缺失/意外(未改动任何表结构)
- `tools/check_authoritative_docs.py` → 52 份文档无编号冲突
- `tools/check_rbac_seed_consistency.py` → 通过
那 2 个失败是既有环境项(`test_offsite_document_recognition_adapter.py` 断言请求体
中文原文而 httpx 序列化成 `\uXXXX`),与本次合并无关。
|
2026-09-12 13:14:57 +08:00 |
|
lzf_0626
|
4cc0ecdea9
|
fix: 种子改用 UPSERT 建用户且不覆盖密码,解除投顾 FK 导致的跑不通;记录 RBAC 对齐最终状态
|
2026-09-12 12:22:32 +08:00 |
|
lzf_0626
|
cdb2de3e45
|
chore: 权限号段根治——9041-9046 并进种子;修正 grant 脚本与种子 9020-9034 的 id 映射冲突
|
2026-09-12 12:09:55 +08:00 |
|
Windows
|
bbf623a464
|
merge: integrate advisor capabilities on latest qyqy base
|
2026-09-11 21:03:58 +08:00 |
|
Windows
|
b6429e0e9c
|
feat: add advisory product comparison tool
|
2026-09-11 19:20:32 +08:00 |
|
Windows
|
95633188f1
|
test: extend advisor e2e fixtures
|
2026-09-11 17:59:47 +08:00 |
|
wangjianlong_0626
|
e4c4099aaa
|
wip: 客服Agent + RAG + 画像收尾(基于 6516ccb)
|
2026-09-11 14:46:40 +08:00 |
|
lzf_0626
|
6516ccb385
|
feat: 第二版——接口契约对齐 docs/05,修复静默故障与数据库基线
相对第一版 46fc976 的完整变更。组员迁移对照表见 docs/20。
一、对外契约对齐 docs/05(破坏性,共 4 处,组员需按 docs/20 调整)
1) 配置发布端点改为文档规定的复数资源名:submit→validations、
approve→reviews(需 body decision)、activate→activations、
rollback→rollbacks;第一版这 4 个动词式路径 docs/05 从未定义过。
2) 错误码由 8 个笼统码改为 15 个具体语义码(FORBIDDEN→AGENT_PERMISSION_DENIED、
UNAUTHORIZED→AUTHENTICATION_REQUIRED、CONFLICT→RESOURCE_VERSION_CONFLICT、
RESOURCE_NOT_FOUND→RUN_NOT_FOUND/SESSION_NOT_FOUND 等),
输入类错误状态码 400→422。
3) POST /api/v1/agent-runs 与 GET /api/v1/agent-runs/{run_id} 统一为
{data, meta} 信封(data 内字段名与语义未变)。
4) 错误响应体统一为 {error:{code,message,retryable,field_errors}, meta:{trace_id}},
不再返回 FastAPI 默认的 {"detail": ...}。
二、数据库基线与约束
新增 39 张表的基线迁移(链根)与联合唯一键纠偏(4 张表、删 8 增 4,幂等收敛);
撤下 config_release 的双人复核 CHECK(应用层已允许自审,审核节点保留,
自审如实写入 reviewer_id);记忆 active key 生成列与唯一键;
activate 开始记录 supersedes_release_id 使版本链可追溯。
docs/00 基线未修改,未重命名或删除任何表与字段。
三、修复会静默出错或无报错的缺陷
- 跑完集成测试后平台会静默失去生效配置:清理只删自己创建的版本,却没有恢复被它
顶成 superseded 的原生效版本,且审计一并删除因而完全无痕,表现为所有工具被拒
但没有任何报错。已修清理逻辑并加恢复。
- Worker 单轮异常导致进程退出;记忆抽取调用方的“事务已开始”异常;
召回缓存丢失 degraded 标记;连接时区未生效导致 created_at/updated_at 差 8 小时;
.env 与 os.getenv 密钥来源分裂导致“没有可用的已批准模型端点”。
- 记忆信号识别漏判与跨键误命中;SSE 未带 Accept 的协商行为。
四、功能补齐
记忆链路 P1/P2/P3(抽取、受控词表、召回与缓存、生命周期级联及投影事件)、
fin_* 场内交易只读 ORM 层、agent_intent_config 状态流转并在运行期真正生效、
限流(Redis 固定窗口、故障一律放行)、游标校验、trace_id 中间件、
示例业务 Agent fund_query_demo 与一键端到端验证脚本,以及审计/指纹/迁移状态工具。
五、文档与验证
新增 docs/19(业务 Agent 接入实操)、docs/20(第一版迁移指南)与 docs/evidence 证据;
docs/01/02/06/08/09/17 同步实现现状。
验证结果:ruff 通过、mypy 103 文件无错、unit+contract 447 passed、
integration 29 passed、acceptance_check --production 7 PASS、
demo_agent_e2e 9/9 PASS(含失败关闭反证)。
|
2026-09-10 15:55:54 +08:00 |
|
Codex
|
b1497fd2c6
|
chore: initialize project repository
|
2026-09-09 21:55:37 +08:00 |
|