lzf_0626
|
9dd2802d67
|
客户看板「今日盈亏」不再是硬编码 0:按行情/净值基准真实计算(含当日买卖)
- 口径:今日盈亏 = 今日市值 − 昨日持仓市值 − 今日买入金额 + 今日卖出金额(不含费用,与「持有盈亏」同口径)
- 昨日持仓数量由当日成交反推,不需要新表新字段
- 基准优先场内行情最近两个交易日收盘价(与同页 latest_price/market_value 同源、客户可核对),
行情只有一天时回退基金净值(15911/159991-159995 的行情本身就来自净值序列)
- 只认 transaction_type ∈ {买入,卖出}:演示库里混进的风控场外申购/赎回(RISKDEMO-*)
会把「昨日持仓数量」抬到 76 万份、今日盈亏从 -21.22 变成 -1612.72
- 接口字段与前端一行未改:HoldingItem.today_profit_loss / PortfolioSummary 两个字段数值变真
- 新增 tools/check_today_profit_loss.py:纯 SQL 独立复算并与接口逐只比对(实测 9001 = -132.70 / -0.2528%)
- 新增 6 条单测;pytest tests/unit tests/contract → 1466 passed, 0 failed
|
2026-09-15 00:03:23 +08:00 |
|
lzf_0626
|
84bf51c0d0
|
NL2SQL:5 位产品代码被丢弃导致"查询成功但数据错"(演示产品 15911 正是 5 位)
## 现象
通用自然语言查询里问「查询15911的净值」,返回的是**别的产品**的数据:
```
问:查询15911的净值 → 返回 511810 货币ETF南方 的净值
问:查询基金代码15911最近30天净值 → 同上
```
而且 `status = success`、`message = 查询成功` —— **不报错,只是数据是错的**。
## 根因
`financial_nl2sql_service._filters()` 只认 6 位数字:
```python
re.findall(r"(?<!\d)\d{6}(?!\d)", question) # 15911 只有 5 位 ⇒ 被静默丢弃
```
过滤条件为空 ⇒ 生成的 SQL **没有产品过滤**(`WHERE 1=1 ... GROUP BY ... LIMIT 50`)
⇒ 返回一批无关于问题的产品净值。现库实测:**6 位代码 25 个、5 位代码 1 个**
(后者就是演示产品 `15911`)—— 所以这个缺陷精确地打在了演示产品上。
`tests/unit/service/test_financial_nl2sql_golden_cases.py` 里 30 条黄金用例**全是 6 位代码**
(159511/588890/159948),因此从未暴露。
## 修法
`app/service/financial_nl2sql_service.py`:
- 产品代码模式放宽为 **5–6 位**(`(?<!\d)\d{5,6}(?!\d)`);
- 5 位数字更容易与"金额/数量/天数"撞车(6 位不会),因此加单位守卫:
紧跟 `元/万元/万/亿/份/股/手/天/日/周/个月/年/次/笔` 的一律**不当**产品代码
(例:「申购金额50000元」里的 50000);
- 抽成 `_product_codes()` 便于单测。
## 验证
- `query("查询15911的净值")` → SQL 含 `p.product_code = :filter_0`、参数 `15911`、
返回行 `{"product_code": "15911", "product_name": "15911 自定义产品", "nav": "1.074596"}`;
三种问法("查询15911的净值"/"查询基金代码 15911 最近 30 天的净值"/"15911 的最新净值是多少")全部命中 15911;
- 新增 5 条单测:5 位代码成为过滤条件、6 位仍正常、金额不当代码、
年份/天数不被误认、多代码并存;
- `pytest tests/unit tests/contract` → **1458 passed, 2 skipped, 0 failed**;
- `ruff` 干净。
## 说明(未改,供你决定)
问「最新净值」时返回的是该产品的 50 行净值明细(SQL 无 `ORDER BY`),
不是"最新的那一条"。要的话我可以补成按日期倒序、单点取值。
|
2026-09-14 22:32:26 +08:00 |
|
lzf_0626
|
1d6c32f6b3
|
场外核对:桥接"单据账户标识 → 平台交易账号";保存识别字段后自动重跑核对;运营角色补推介材料权限
## 一、根因:核对查不到不是"缺数据",是**账户口径没桥接**
实测(客户 10002 的单据):
```
生成的 SQL: JOIN fin_holding h ... WHERE h.trade_account = '10002' ← 单据上的账户标识
结果: 0 行 → 「申请前持有份额」= null → 规则判「无法判断」→ 单据状态 query_failed
```
而库里 `fin_holding.trade_account` 存的是**平台交易账号** `FSA{客户号:06d}`
(与 `fin_sim_account.account_no` 同值,见 `tools/seed_sim_account_demo.py:226`),
客户 10002 **一直持有 15911 共 1000 份**。所以页面上"单据核对与运营动作"是空的,
看起来像缺数据,实为**单据印的账户标识与平台账号是两个口径,中间少了一步换算**。
修法:新增 `OffsiteFundService._platform_account()`,在**单据字段落库的两个入口**
(`_create_document` 新建、`_apply_recognition_fields` 保存/重试)做换算,规则:
1. 已是库里的 `account_no` → 原样;
2. 纯数字且命中 `customer_id` → 用该客户的 `account_no`;
3. 其余原样返回并留 warning —— **失败关闭,不猜不造**(不凭空生成 `FSA099999`)。
一处修好,后端查询、NL2SQL 页面的自然语言、页面展示三处口径一致。
附件上的 OCR **原始值不受影响**(`extracted_fields` 仍是单据上印的 `10002`)。
端到端证据(同一张单据,只走服务层):
| 阶段 | document.account_identifier | 规则数据 | 单据状态 |
|---|---|---|---|
| 修复前 | `10002` | `{}` → 无法判断 | `query_failed` |
| 保存识别字段后 | **`FSA010002`** | — | `query_failed` |
| 触发核对后 | `FSA010002` | **申请前持有份额 1000.0000** → **正常** | **planned** |
## 二、前端:保存识别字段后自动重跑核对
`employee-operations/offsite/offsite.js`:原先 `save-ocr` 只保存 + 重载页面,
**不触发核对**,于是运营改完字段点保存,那两个区块要么停留在上一次核对的状态、
要么整块是空的,必须再手动点一次"重新核对并判定规则"——看起来像"保存没生效"。
现在:保存 == 运营已人工确认该单据内容,因此保存成功后**自动对该附件关联的单据**
执行「触发 NL2SQL → 拉取返回字段 → 重新判定规则」,并在提示里区分"已重新核对"
与"核对未全部成功"。
顺带把这段逻辑抽成 `recalculateDocument(taskId)`,与面板上的"重新核对并判定规则"
按钮**走同一条路径** —— 两处各写一份正是"保存后不刷新"这类不一致的来源。
## 三、运营(operator)角色权限
`tools/grant_operator_role.py` 的授权清单补齐:`promotion:write/read/review/deliver`
(`promotion_material_service.py` 的八处 `_require` 恰好只用这四个码,缺任一都会 403,
例如只给 write 会在查看详情 read 那一步被拒)+ `agent:run`。
已实际执行并**从身份侧验证**(`IdentityRepository.load_context`):
9005 / 9006 现在各 7 项权限,四个推介材料码齐全。
- NL2SQL 全库与它相关的权限码**只有 `financial:nl2sql:read`**(`offsite:nl2sql` 在
`sys_permission` 里并不存在,是 `offsite_fund_service` 里 any-of 校验的死值),
该码运营早已有,本次无需新增。
- 可持续性已核实:`sys_role_permission` / `sys_user_role` **没有外键**,
重跑 `seed_test_rbac.py`(DELETE 重建 9001-9099 号段权限)**不会**删掉运营的绑定;
且本工具按**权限码查 id**、不写死 id,天然抗号段变动。
## 四、10001 / 10002 的 15911 持仓
复核结论:**各 1000 份,且三处口径一致**(`fin_holding.market_value` = 数量 × 最新净值、
净值历史 120 条、`fin_product.current_nav` 与净值最新一条一致、账户可用资金正常)。
`fin_holding` 的唯一键是 `(customer_id, product_id)`,所以**不能**再插一行
`trade_account='10002'` 的"同一个持仓"—— 那会把持仓重复计数,是错的。
需要改数量就用 `python tools/seed_custom_holdings.py --quantity N`(默认就是这两个客户 + 15911)。
## 五、验证与回归
- 新增 `tests/unit/service/test_offsite_account_bridge.py`(5 条:账号原样 / 客户号换算 /
认不出原样返回 / 不凭空造账号 / 空值不查库);
- `pytest tests/unit/service -k offsite` → 34 passed;
- 场外集成测试 4 个文件 → 27 passed;
- `ruff` 干净;`mypy app` 仍只有组员新代码里那 3 个既有错(与本次无关);
- 前端 `node --check offsite.js` 通过。
|
2026-09-14 22:16:29 +08:00 |
|
lzf_0626
|
c4f4c33fd6
|
记忆召回补一道能力码闸门:归属关系不等于授权
上一版把员工召回改成"按 sys_customer_assignment 归属"时,只看了**归属关系**,
没校验**能力码** —— 这留下一个我自己带出来的口子:
- 平台读他人画像/记忆的正式路径(customer_profile_service、public_platform_service)
校验的是 `memory:read:customer`(sys_permission id=9010,data_scope='own_customers')
+ `customer_ids`;
- 而召回是**另一条**把客户记忆送进模型上下文的路径,只按归属放行,等于让
"有归属行但没有该权限"的账号(例如 operator:只有 financial:nl2sql:read
+ offsite:write)凭空获得读他人客户记忆的能力 —— 它上一版之前是读不到的。
现改为**两个条件都满足才放行**(缺一即失败关闭,并在日志里分开点名"缺能力码"
与"归属未维护",避免运维在错误的表上找问题):
① `memory:read:customer` 在 context.permissions 里;
② 客户落在 context.customer_ids(sys_customer_assignment 生效行)内。
演示账号不受影响:9002(risk_operator)、9020(advisor)、9003(admin) 三个角色都持有该权限码,
真实身份链路复验 9020 仍是"修复前 0 条 → 修复后 2 条"。
测试:tests/unit/core/test_memory_scope.py +3 条(缺能力码读不到/能力码与归属是"与"关系/
现有用例补上能力码),tests/unit/service/test_agent_governance.py 同步。
`pytest tests/unit tests/contract` → 1448 passed, 2 skipped, 1 failed
(唯一失败仍是组员正在改的投顾页面)。
|
2026-09-14 21:40:05 +08:00 |
|
lzf_0626
|
9eebf9627f
|
记忆召回:按 sys_customer_assignment 归属定范围,不再把员工号当客户号
## 修的是什么
`governance.recall()` 把 `int(context.user_id)` 当客户号用。后果有两个,
方向相反但都致命:
1. **员工身份(风控/投顾/运营/管理员/system)恒空** —— 员工不是客户,
那是个不存在的客户号;日志只说 "empty",看不出是"设计如此"还是"记忆坏了"。
2. **越权陷阱** —— 员工号与客户号同号段(演示数据里客户 9001-9020、
员工 9002/9020 并存)。`int(user_id)` 一旦与真实客户号重合,就会把
**陌生客户的长期记忆读进来并注入提示词**,且不报错、看起来正常。
同一个问题在代码里还有另外两处**各自判断**、口径互不一致:
`BaseAgent.recall_memory()` 要求"每条记忆 customer_id == context.user_id"
(否则抛"越过客户范围"),`review_output()` 的引用校验只认同一条件。
## 怎么修的
新增 `app/core/memory_scope.py` 作为**唯一判定口径**,三处共用:
- 客户身份(customer / authenticated_user):**只读自己**,分配表里有别行也不读别人;
- 员工身份:**只读 `sys_customer_assignment` 分配给自己**的客户
(`context.customer_ids`,由 `IdentityRepository.load_context()` 读入);
归属未维护 ⇒ **失败关闭**,并在日志里点名"归属未维护",与"库里确实没有记忆"区分开;
- 访客:无(上游已拦)。
细节约定:
- 归属客户按客户号**升序**召回、单次上限 `MAX_RECALL_CUSTOMERS=10`
—— 升序是为了确定性(同一身份每次取同一批,不随数据库返回顺序漂移),
上限是为了别把成百上千条他人记忆塞进一个提示词;
- 跨客户合并后按置信度降序、`(客户号, uuid)` 兜底排序,最多 10 条;
- 员工同时持有多个归属客户的记忆时,`memory_context_text()` **逐行标注客户号**
并把提示词改成"多个客户的长期事实" —— 否则模型会把 A 客户的事实当成 B 客户的。
单一客户时保持原格式(客户身份的提示词与改动前逐字相同);
- 引用校验与范围守卫都改用同一口径:员工引用**归属客户**的记忆不再被判成伪造引用;
引用**非归属客户**的记忆即便被塞进 memories 也照样拦下。
## 验证(真实身份链路 + 生产召回装配)
`IdentityRepository.load_context` → `PlatformGovernance.recall`(含 Milvus 语义通道):
- 身份展开:roles=('advisor',)、customer_ids=('9001',)(sys_customer_assignment
里唯一那行 9020→9001)、可读范围 (9001,);
- **修复前** `recall(int(user_id)=9020)` → **0 条**;
- **修复后** `recall(按归属)` → **2 条**(客户9001:进取型 / 约三年);
- 边界:客户身份 9001 可读范围 (9001,);无归属员工 9002 = ()(失败关闭,
且**没有**把 9002 当客户号);未分配时的 9020 = ()。
测试:`pytest tests/unit tests/contract` → **1445 passed, 2 skipped, 1 failed**
(1432 + 新增 13;唯一失败是组员正在改的投顾页面,与记忆链路无关)。
新增用例:`tests/unit/core/test_memory_scope.py`(8 条,含"员工号不得被当成客户号"
的反例断言)、`tests/unit/service/test_agent_governance.py`(+5 条:归属召回/
无归属失败关闭且不碰数据库/客户只读自己/引用校验/越界守卫)。
## 遗留(已在 AGENTS.md 与文档里写明,未自行实施)
风控扫描这条线**仍读不到记忆**:它是唯一消费召回内容的地方
(`risk_agent.py:224`),而扫描上下文是 user_id="0"/roles=("system",) 且无归属行。
根因是**顺序问题**:召回发生在 handle() 之前,上下文里没有"本次目标客户"这个概念。
出路有两条:① 给风控专员补 sys_customer_assignment 行(运维动作,立即可用);
② 在 RequestContext 加显式的 target_customer_id 并校验它落在归属集合内
(推荐,但属跨线协议改动,等确认)。
文档:docs/演示用/记忆召回恒空-根因与修复-2026-09-14.md 新增 §五(含 §5.4 遗留说明)、
AGENTS.md 新增"记忆可读范围只有一个判定口径"易错点,并按 2026-09-14 复测更新测试基线。
|
2026-09-14 21:33:16 +08:00 |
|
lzf_0626
|
0c642133d4
|
修复长期记忆向量链路:投影入队 + 集合名三侧同源 + 召回按客户过滤
语义召回"恒空"的根因分四层,本提交修掉投递层与读取层(另两层——重试计数
门禁、episode 不投 rebuild 事件——已在前两个提交修复)。
R2 投递层:dispatch_profile_rebuild 只做图投影,没有任何 Milvus 投递,
长期记忆的向量从未被写入过(实测客户 9001 在 memory_sync_outbox 里 0 行)。
新增 _enqueue_memory_vector_projection(),在画像重建后投
MemorySyncOutbox(target_store="milvus")。走事件而不是同步写,是为了拿
Outbox 的重试/退避/死信,且不把 embedding 的网络等待拖进事务。
⚠️ payload 必须带 version(正整数):适配器 _coerce_profile_version 缺它
直接抛 ValueError(实测踩到,事件立刻 failed)。
R4 读取层:写的集合与读的集合不是同一个 ——
写(MilvusProfileProjection)用 "user_long_term_memory_v1",
读(bootstrap.get_vector_memory_adapter)与删(projection_cleanup_service)
却用 settings.milvus_collection = "jr_memory",而该集合从未被创建。
⇒ 召回:MilvusClient 构造不校验集合存在,适配器"构造成功"但每次 search
抛异常被 VectorMemoryAdapter 吞成 degraded → 召回恒
degraded_reasons=('milvus_unavailable',)、向量命中恒 0 条;
⇒ 清理:jr_memory 不在集合列表 → 走 vector_collection_absent 分支 →
报清理成功但一个向量都没删,陈旧向量永久留存。
修法:PROFILE_COLLECTION 成为唯一常量,读/删两侧直接引用它;
并删除 Settings.milvus_collection 配置项、清掉 .env.example 的
MILVUS_COLLECTION —— 写侧从来没读过它,一个只在契约一侧生效的配置项
比没有配置项更危险(Settings 的 extra="ignore" 会让其他环境残留的该
变量被安全忽略)。
顺带:
- 语义检索把客户过滤下推到 Milvus(filter="customer_id == N")。此前不带
过滤,别家客户的命中会白占 limit 名额,稀释本客户的召回条数。
- upsert 在 sources 为空时先返回,不再无条件 load_collection ——
"本来就没有可写内容"不该被记成投递失败(10001/10002 那两条事件即如此
重试 5 次进死信)。
回归守卫:tests/unit/infrastructure/test_memory_vector_collection_consistency.py
断言读侧与删侧用的都是 PROFILE_COLLECTION,且被删掉的配置项不得回归。
这个缺陷能活下来,正是因为两侧单测全绿而接缝无人守。
验证(走生产装配、进程内调用,未重启你正在跑的 API 窗口):
读侧集合打印 user_long_term_memory_v1(修前为 jr_memory);
召回 degraded=False / reasons=() / sources 含 milvus,且排序随 query 语义
变化(投资期限→horizon 0.288 > risk_level 0.201;风险偏好→risk_level 0.287);
query="进取型" 双通道合并且 vector_score=0.9987;query=None 走 mysql 全量。
pytest tests/unit tests/contract → 1432 passed, 2 skipped, 1 failed
(唯一失败是同事正在改的投顾页面,与记忆链路无关)。
文档:docs/演示用/记忆召回恒空-根因与修复-2026-09-14.md(新增,四层根因+证据)、
docs/37-记忆投影链路实现说明.md(补集合名三侧契约)。
|
2026-09-14 21:26:03 +08:00 |
|
lzf_0626
|
e4cd336afa
|
修复权限拒绝变 500、访客令牌无限流,并让两处测试跟上代码
## 1. 权限不足从 500 修回 403(14 个测试失败里的 11 个)
`app/service/authorization_service.py` 的 `_deny` 构造 `InteractionAudit(...)` 时
**漏了 `created_at`**(该列 NOT NULL 且无默认值),而 `raise ForbiddenAgentError`
写在 `session.commit()` **之后** —— commit 必抛 IntegrityError
(1048: Column 'created_at' cannot be null),于是**永远走不到 raise**:
预期 403,实际 500:Internal Server Error
**⇒ 任何「权限不足」的请求都变成 500**,破坏 docs/05 §3.6 的错误码契约。
全仓 100+ 处 `InteractionAudit(...)` 都跟着 `created_at=now`,只有这一处没有
(由 2026-09-14 的 `857c106`「投顾工作台:三接口支持按客户出方案」引入)。
修两处:
- 补 `created_at`;
- **并把审计写入失败与 403 解耦**(try/except + `logger.warning(exc_info=True)`):
安全判定不该依赖审计表是否可写 —— 拒绝就是拒绝。但也绝不静默,审计缺失是合规问题。
## 2. 访客令牌端点补限流(P0-4)
`app/api/controllers/visitor_tokens.py` 此前**零认证、零限流**,可以不限量铸造
有效 JWT;每个都能调 `/api/v1/agent-runs` 触发 LLM 调用,而 agent-runs 的限流
按 `user_id` 计、访客 `sub` 每次都是新随机值 ⇒ **限流被天然绕过**。
把 `rate_limit.py` 的登录闸门抽成通用的 `_enforce_ip_rate_limit(...)`,
新增 `enforce_visitor_token_rate_limit` 挂到该端点的 router 上。
⚠️ 刻意**用独立计数器前缀**(`visitor-token` vs `login`)而不是直接复用登录闸门:
共用会让两者互相挤占配额 —— 正常访客刷几次页面就把别人挡在登录外。
阈值 30 次/分钟(比登录的 10 次宽松,因为访客进站/刷新会正常签发)。
## 3. 测试跟上代码(2 个)
- `test_product_recommendation_service.py`:monkeypatch 打在 `current_for_agent` 上,
而 `generate()` 现在走 `current_for_customer(customer_id, context)` —— 补丁不生效,
真实方法被执行并命中鉴权抛 403。改为按新签名打补丁。
- `test_authorization_service.py` 等 3 个:随第 1 项一起恢复。
## 实测
- `pytest tests/unit tests/contract` -> **1427 passed, 2 skipped, 2 failed**
(修复前 **14 failed** / 1426 passed)
- `_deny`:权限不足恢复 **403**(原为 500)
- 访客限流:连发 40 次 -> `{201: 30, 429: 10}`,首次被拒返回
`RATE_LIMITED「访客令牌签发过于频繁:每 60 秒最多 30 次」`
(修复前:连发 15 次全部 201、无限流)
- `ruff check` -> All checks passed
## 剩余 2 个失败(需产品决策,未擅自处理)
1. `test_advisor_workspace_registers_documented_operation_endpoints` ——
`employee-advisor/dashboard/index.html` 已被**整个替换**为一个自包含静态页
(`data-page-node-id` 属性、内联全部 CSS/JS、硬编码 `API="http://127.0.0.1:8000"`、
自带「离线本地引擎」),**不引用 `api-client.js` / `app-shell.js`、不调 `mountShell`**。
测试断言的是旧页面措辞("组合分析"),新页面写的是"生成推荐方案"。
是接受替换后的页面(改测试断言),还是恢复挂平台壳的版本,属产品决策。
2. `test_docs_endpoint_ids` —— `docs/05` §19 表里被插入了分组标题行
(`**场外基金**`、`**账户与交易**`),而检查工具要求首列是端点编号。
|
2026-09-14 20:14:50 +08:00 |
|
yuancong_0626
|
14e027f781
|
合并yy与远程qyqy_develop并保留业务优先级
|
2026-09-14 18:16:33 +08:00 |
|
yuancong_0626
|
2548c39c6b
|
袁聪的最后一次完善更新
|
2026-09-14 18:13:10 +08:00 |
|
zhangshy
|
bfbaf823c2
|
同步预警队列后端分页上限为十条
|
2026-09-14 10:50:48 +08:00 |
|
张胜宇
|
f5d1b24618
|
merge qyqy_develop and retain risk review updates
|
2026-09-14 01:41:02 +08:00 |
|
yuancong_0626
|
abbfb6eddd
|
合并yy并同步远程qyqy_develop
|
2026-09-14 01:11:07 +08:00 |
|
yuancong_0626
|
7a49a1c2c8
|
袁聪的前端调修
|
2026-09-14 01:07:43 +08:00 |
|
张胜宇
|
e3c316aafb
|
Enforce trading authorization and suitability checks
|
2026-09-13 19:24:59 +08:00 |
|
张胜宇
|
e38ece32bf
|
前端提交
|
2026-09-13 15:56:54 +08:00 |
|
zhangshy
|
001ba065df
|
Merge remote-tracking branch 'origin/qyqy_develop' into RM2_develop
|
2026-09-12 16:12:02 +08:00 |
|
zhangshy
|
eb4e895e4e
|
修复高风险预警邮件通知并补充测试
|
2026-09-12 15:56:50 +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 |
|
wangjianlong_0626
|
58c28ef4a0
|
fix(memory): 记忆抽取容忍"单元素数组"形状,空结果不再被判为无效 JSON
## 现象(2026-09-12 跑真实 Worker 时发现)
`episode_worker` 反复报 `模型记忆抽取输出不是有效 JSON` 并重试到失败:
```
pydantic_core.ValidationError: Input should be a valid dictionary or instance of _ExtractionPayload
input_value=[{'memory_key': None, 'value': None, 'memory_type': None, 'confidence': 0}]
input_type=list
```
## 根因
契约是**对象** `{...}`,而模型在 episode 抽取路径会返回**单元素数组** `[{...}]`。
`_parse` 直接 `json.loads` 后交给 pydantic,数组自然过不了 `model_validate`,
于是被归入"输出不是有效 JSON"这一条 —— 但它其实是**合法的空结果**
(`_validate` 已能把"三字段为 null 且 confidence=0"正确识别为"无持久事实",返回 None)。
后果:这类 episode 白跑一遍模型调用、重试到 `retry_count` 上限后判失败,
**该片段的记忆永远抽不出来**。
## 修法
`_parse` 里只对"**恰好一个对象**的数组"做归一化:
- `[{...}]` → 取 `{...}`(空结果照常返回 None;有事实照常解析)
- 多元素数组、元素非对象、空数组 → **不猜**,仍交给校验失败关闭
(多元素时无法判断哪个是答案,猜错会把错误记忆写进库,比失败更糟)
## 测试
`tests/unit/service/test_memory_extraction_service.py` 追加 2 个用例:
- `test_single_element_array_is_normalized`:数组包空结果 → None;数组包有事实 → 正常解析
- `test_multi_element_or_non_dict_array_still_fails_closed`:多元素 / 非对象元素 / 空数组 → 仍失败
## 验证
- `pytest tests/unit/service/test_memory_extraction_service.py tests/unit/worker/test_memory_extraction_worker.py` → 28 passed
- `mypy app` → 0 错 / 245 文件
> 说明:`memory_extraction_service.py` 属主干线代码。此处是从**实际运行日志**里发现的
> 健壮性缺陷,改动限定在输出形状归一化,不改变抽取契约与校验规则。
|
2026-09-12 14:11:42 +08:00 |
|
wangjianlong_0626
|
f7b35006ff
|
Merge remote-tracking branch 'origin/qyqy_develop' into nl-merge-colleague
|
2026-09-12 14:06:18 +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 |
|
yuancong_0626
|
a501060e50
|
merge: merge yc into qyqy_develop
|
2026-09-12 12:50:14 +08:00 |
|
yuancong_0626
|
91f5373cfb
|
袁聪的第三次提交,API接口完善
|
2026-09-12 12:20:01 +08:00 |
|
张胜宇
|
9aaacc242f
|
chore: 清理违反底座规则的死代码并修正接口文档编号
- 删除生产死代码 app/service/knowledge_tool_service.py 与
app/infrastructure/milvus_knowledge_adapter.py:后者硬编码 Milvus 字段名,
违反 AGENTS.md §E,且仅被前者引用;生产检索链路实际走
knowledge_search_tool -> KnowledgeSearchService -> knowledge_schema 运行时探测。
- 删除上述两模块的单测,以及依赖 legacy 位置参数构造的
tests/unit/service/test_knowledge_retrieval.py。
- app/service/knowledge_retrieval_service.py 整文件回退底座版本,
移除 legacy 双构造与重复检索实现。
- docs/05-接口文档.md:客服画像候选改登记为 §8.5,恢复 §8.2 解析知识引用;
既有 §8.1-§8.4 编号全部保持,修复此前出现两个 8.3 的问题。
- app/model/profile.py:current_customer_id 改为普通可空列映射,与
alembic/baseline_generated.sql 及真实库一致;原 Computed 声明会让 ORM 把该列
从 INSERT 中排除,与「必须显式写入」的实际 schema 不符。
- 新增 docs/客服Agent接入底座扩展说明_v1.md,供集成分支评审逐项确认。
验证:pytest tests/unit tests/contract -> 1275 passed, 2 skipped, 0 failed;
ruff check app tests tools alembic 通过;mypy app 通过(244 个源文件)。
|
2026-09-12 11:15:24 +08:00 |
|
wangjianlong_0626
|
8a0cbab636
|
fix(memory-projection): 订正 outbox 取值口径并接通画像投影链路
背景:memory_sync_outbox 这条链此前**完全没有消费者**,且生产端照 docs/00 §6.4.6
写成大写 MILVUS/NEO4J + 中文「待处理」,而消费端按 target_store 的**值**分派 handler、
且只领 status in {pending, failed} —— 两个条件都不满足,事件任何消费者都领不到、
永久滞留且不报错(唯一键 (event_uuid, target_store) 对大小写无约束,MySQL 也不报错)。
根因是代码与测试都硬编码字面量,所以测试跟着一起错、谁也没拦住。
订正
- profile_generation_service:取值改为全仓一致的小写(milvus/neo4j/upsert/pending)
- 测试改为引用常量并断言消费端契约,不再硬编码(硬编码是本次跑偏的直接原因)
- 新增契约回归测试:断言大写值分派不到 handler、会进死信,谁改回大写立刻红
- 新增 tools/normalize_memory_sync_outbox.py:订正历史脏行(默认 dry-run、幂等)
接通投影链路(此前零消费者)
- 新增 Milvus 集合 user_long_term_memory_v1 及建集合工具(幂等、不覆盖已有集合)
- 新增 MilvusProfileProjection / MilvusProfileVectorClient,并修掉移植带来的两处必炸点:
customer_id 由「必须 int」放宽为接受数字字符串(本仓所有生产者都写 str,
不放宽则每个事件必然失败);不可投影的 memory_key 由「整批 raise」改为跳过留痕
(否则一条 constraint: 记忆毒死该客户整批,而受控词表 13 个键里有 7 个不满足前缀)
- 新增 MemorySyncOutboxWorker(领取/指数退避/死信骨架保留原样)并接入 WorkerRuntime
- milvus → 向量投影;neo4j → 复用主干 ProfileGraphProjectionService(方案 A,
不引入第二套投影,避免同一事实在图中两种说法、违反主干既有的只投影已确认事实的不变式)
- 生产端从 memory_unit(status=active) 组装 memory_sources,随事件带上确定快照
- 前置移植 conversation_privacy:写外部存储前脱敏手机号/证件号/银行卡等
验证
- 新增 17 个单测;全量 2 failed, 1307 passed, 2 skipped
(2 个失败为既有环境项:断言请求体中文原文而 httpx 序列化成 \uXXXX,非本次引入)
- mypy app → 0 错(227 文件);audit_schema → 89 张业务表无缺失/意外,未改动表结构
- 真机:真实 embedding(1024 维) + 真实 Milvus 写入并回读通过
- 整合链路(测试记忆 → 生产端组装 → outbox → 消费端投递 → Milvus 回读)通过,
且 MySQL 已回滚、Milvus 无残留
文档
- 新增 docs/32-记忆投影链路实现说明.md:真实口径、根因、契约与验证证据(供接手)
- AGENTS.md:新增该易错点;新增 Windows 中文输出乱码的正确命令(-X utf8);
校正测试基线与 mypy 文件数
未做:未改 docs/00 基线、未动数据库迁移、未改投顾线代码、未启动常驻 Worker。
遗留:投顾线两处生产者的 payload 缺 memory_sources,会被消费至死信,待架构师确认是否投影。
|
2026-09-12 10:45:40 +08:00 |
|
张胜宇
|
ef701c844c
|
merge: integrate ZSY customer service and profile capabilities
|
2026-09-11 22:31:51 +08:00 |
|
wangjianlong_0626
|
cc6ae87f26
|
Merge remote-tracking branch 'origin/qyqy_develop' into nl-merge-colleague
|
2026-09-11 21:18:30 +08:00 |
|
qyqy
|
a7e2d3eac0
|
fix(customer-service): 统一客服热线来源;补 transfer_required 出参(docs/05 §6.3 未兑现的一半)
两个都是"代码里存在但没接对"的缺陷,都不是新功能。
## A1 客服热线在代码里有两个值(一个出口给假号码)
- `app/core/customer_service_rules.py:35` `CONTACT_PHONE = "15936583816"` ← 真号码,安全路由 6 处在用
- `app/service/agent/implementations/customer_service.py:155` `HOTLINE = "400-XXX-XXXX"` ← 占位符,兜底出口在用
后果:**同一个客服给客户两个不同的电话号码**。问"风险等级怎么划分"被安全路由处理时给真号码;
问一个知识库答不了的问题走兜底时给 `400-XXX-XXXX` —— 客户按这个号码永远打不通。
修法:`HOTLINE` / `SERVICE_HOURS` 改为**转发** `customer_service_rules` 的两个常量
(不是"改成相同的值",而是引用同一对象,避免日后再次漂移);工作时间也随之从
"每日 7:00-22:00" 统一为 "工作日 09:00-18:00"(与安全路由出口一致)。
新增守卫测试用 `is` 断言对象同一性 —— 值相等挡不住"两边各写一份恰好相同"的漂移。
## A2 `transfer_required` 既没落库也没出参
`docs/05` §6.3 一直规定 `GET /agent-runs/{run_id}` 的 `result` 里有
`transfer_required` / `transfer_reason`,但实现里两个都没有:前端判断"这轮要不要转人工"
只能靠**猜正文里有没有兜底话术的开头**(`docs/24` 自己把这称为权宜之计)。
- 写入侧:`conversation_message` **没有** `transfer_required` 列,加列要迁移且规则 4 禁止改既有
字段定义 ⇒ 放进 `tool_calls` 这个现成 JSON 列,作为 `calls` 的兄弟键
(`{"calls": [...], "transfer_required": bool, "transfer_reason": str|None}`)
- 读取侧:`RunQueryService.get` 取出来放进 `result`;**兼容历史行**(`calls` 裸列表 / None →
按 False/None 处理,不抛异常、也不凭正文猜)
刻意**没做**的一半:`docs/05` §6.3 的 `result` 里还有 `degraded` / `degradation_reason`,
但 `CoreResult` 里根本没有这两个字段(降级信息目前只在工具出参里)—— 补它要改
`CoreResult` 并让各 Agent 传递降级状态,属另一个改动范围。**已在交付说明里注明这一半仍缺。**
## 真机验证
| 问题 | transfer_required | transfer_reason | 正文电话 |
|---|---|---|---|
「请介绍一下量子纠缠在基金估值中的应用」 | **True** | 置信度不足:score=0.571 gap=0.004 | 15936583816 ✅ |
「请帮我计算一下三体问题的数值解」 | **True** | 置信度不足:score=0.499 gap=0.011 | 15936583816 ✅ |
「基金申购后多久确认」(正常知识直返) | False | — | 无(正确) |
「你们公司明天会下雪吗」(闲聊出口) | False | — | 无(正确) |
(第一次我用"下雪"当兜底用例,结果它被闲聊出口正确接住了 —— 是我的期望值写错,不是代码问题。)
门禁:测试 1223 passed(新增 4 个用例)/ 3 failed(均为已知非代码缺陷)/ mypy 0 错。
|
2026-09-11 21:17:47 +08:00 |
|
Windows
|
bbf623a464
|
merge: integrate advisor capabilities on latest qyqy base
|
2026-09-11 21:03:58 +08:00 |
|
Windows
|
f5dd5b8ba2
|
feat: add advisor rollout gate and rollback playbook
|
2026-09-11 20:28:21 +08:00 |
|
lzf_0626
|
f09ea9e988
|
Merge origin/NL_develop:客服画像出口、知识管理三端点、合规语境与知识向量链路
NL 线(含其并入的袁聪场外/推广域)。唯一冲突是 .gitignore —— 双方都往同一区域加了
.workdir/,取对方版本(他的更完整,含 .tmp/ 与说明),顺带修掉我之前用
Add-Content -Encoding utf8 造成的编码混合(read 工具当时报 invalid UTF-8)。
合并后修的问题 —— 都不是"改别人业务逻辑",是让门禁能绿:
1. 缺运行依赖 python-docx。document_parser.py 解析 .docx 用它,但 requirements.txt 与
pyproject.toml 都没声明 —— 别人环境跑知识入库会直接
ModuleNotFoundError: No module named 'docx'。已补声明。
2. ruff 7 项:其中 tests/conftest.py 的 F821 Undefined name 'Path'(他的 tmp_path 修复
写了字符串注解 "Path" 却漏 import,运行时不求值所以没炸,但 mypy/ruff 会抓)、
tools/publish_customer_service_config.py 的 F841 inherited_keys 死变量(他改同 key
覆盖、换成 inherited_only 后忘删旧的)、3 处 E501,另 2 项 ruff --fix 自动修复。
3. 合规基线种子未跑:integration 的 test_compliance_seed_mysql 4 个用例要求
agent_negative_word 有 7 条 active 且已复核、agent_reply_template 覆盖 6 场景。
跑 tools/seed_compliance_baseline.py(11 条 active 规则 / 6 个场景模板)后 80 passed。
验证:ruff 干净 / mypy 180 文件 0 错 / unit+contract 1140 passed /
integration 80 passed / 表数 68(alembic 已在 20260911_merge_risk_heads)。
唯一失败 tests/unit/repository/test_fund_readonly_contract.py 是双方一致的既有缺陷:
它断言 Base.metadata 里的 fin_* 表集合,而实测为空集 —— 即该测试依赖别的测试先导入模型的
副作用,单独跑必失败。NL 方也明确"不修不报",此处照办,仅记录。
|
2026-09-11 20:22:59 +08:00 |
|
Windows
|
2376585898
|
feat: add dual-source quote health orchestration
|
2026-09-11 19:47:14 +08:00 |
|
zhangshy
|
beb67f0a26
|
优化奶龙字段语义理解
|
2026-09-11 19:37:26 +08:00 |
|
张胜宇
|
5777f0f86e
|
feat: include memory sources in profile sync events
|
2026-09-11 19:37:18 +08:00 |
|
Windows
|
b6429e0e9c
|
feat: add advisory product comparison tool
|
2026-09-11 19:20:32 +08:00 |
|
qyqy
|
7677aeaee1
|
merge: 并入同事的场外申购/推广/行情/NL2SQL 线(11 提交、334 文件)
冲突仅 3 个文件,全部取并集(双方都没有需要丢弃的改动):
- app/main.py:import 双方路由(我方 knowledge_management + 同事的 offsite_fund/
promotion_material);include_router 段本已自动合并
- app/service/agent/bootstrap.py:import 与工具注册均取并集
(query_customer_profile + query_financial_data 都注册)
- tests/integration/test_config_release_mysql.py:outbox 清理同时保留
架构师的 event_type 限定(防误删其它域 outbox 行)与同事新增的 peer_release_id
同事这轮带入:11 个 alembic 迁移(建 offsite_* / promotion_* 等表)、
场外申购与推广素材 Agent、financial NL2SQL 工具。
注意:本库尚无 offsite_*/promotion_* 表,跑相关测试前需要执行 alembic upgrade。
边界核对:同事的场外代码未写入场内交易表(fin_sim_order/fin_capital_flow/fin_cash_ledger),
符合 AGENTS.md 规则 8。
|
2026-09-11 18:56:26 +08:00 |
|
qyqy
|
928d0bcea3
|
chore: 按评审恢复 5 份文档、审计补 agent_type、修订环境口径
评审意见落地(§3.3 驳回删除 / §3.1 审计补充 / §1.4 解释器 / §2 编号):
- docs/04、06、10、13、99 全部恢复(评审:删除收益为零、保留成本同样为零);
AGENTS.md 改为"保留但仅作历史参考"并列入 D 类,ARCHIVE 归档说明加作废声明
- 治理审计补留痕:interaction_audit.detail 增加 agent_type 与 governance_rewrite
(治理层会改写对外输出,事后必须能追溯到是哪个 Agent 触发的;不改表结构,detail 是 JSON 列)
新增 tests/unit/service/test_agent_persistence_audit.py 锁住该契约
- AGENTS.md 修订环境口径:解释器各用本机可用的那个(.venv 被 gitignore、不进仓库);
config_release 与 Milvus schema 均属环境数据、不随代码合并,相关结论必须带环境限定;
测试基线 1034;mypy 数字双方不可比(本机未装 sqlalchemy2-stubs,报错集中在模型层)
- docs/26 JWT 文档:因 21 已被风控迁移清单占用而改名,PR 描述里会单独说明
|
2026-09-11 18:42:46 +08:00 |
|
qyqy
|
5f82ac5107
|
feat(knowledge): 检索字段名改为运行时探测,两套集合 schema 都能跑
评审意见 §1.3 要求的修法。背景(双方实测共同确认):同一批集合名在两个开发环境里是两套不同 schema:
我方:knowledge_id / snippet(无 visibility),行数 106/177/73
架构师:doc_id / content / chapter / section / doc_no / visibility,行数 125/297/214
上一轮我把字段名硬编码成我方那套,在架构师环境会让 Milvus 报 field doc_id not exist
→ 三集合全失败 → 客服一律转人工(反向亦然)。硬编码任一套都会打挂另一套。
改法(采纳评审建议):
- 新增 app/core/knowledge_schema.py:describe_collection → 逻辑名到物理名映射,按集合缓存;
缺必需字段的集合明确判为不可用并如实记 degraded,不静默零召回
- 检索服务改为逐集合探测:output_fields 只请求实际存在的字段;visibility 过滤有该字段才拼
- KnowledgeHit 对外形状不变,检索逻辑(字面召回/父子块/去重/置信判定)一行未改
测试:新增 17 个探测单测;架构师的关键词召回测试参数化为两套 schema 各跑一遍。
|
2026-09-11 18:42:42 +08:00 |
|
Windows
|
930dd59d67
|
feat: complete advisory market data refresh pipeline
|
2026-09-11 18:39:57 +08:00 |
|
张胜宇
|
27174b9ce2
|
feat: generate approved profile snapshots
|
2026-09-11 17:58:46 +08:00 |
|
张胜宇
|
a769130658
|
feat: add profile candidate review workflow
|
2026-09-11 17:47:06 +08:00 |
|
yuancong_0626
|
3f7c5ca74f
|
merge: 同步 origin/qyqy_develop(风控扫描、知识检索、客服 Agent、协商等)
冲突处理:均为双方各自新增,按并集保留——
- .gitignore:本地 data/logs 忽略项 + 远端 .dsh-drop/
- app/core/config.py:offsite/promotion 与 risk_scan 配置项并存
- app/service/agent/bootstrap.py:场外/推介/风控/NL2SQL/知识检索 工具与 Agent 全部注册
- app/worker/__main__.py:场外邮件 Worker 接线 + runtime 关系服务/投影清理注入并存
收尾:新增 20260911_merge_risk_heads 收敛迁移双 head;按 docs/21 生成 config/jwt/dev 开发密钥。
|
2026-09-11 17:32:47 +08:00 |
|
yuancong_0626
|
61861cdef6
|
袁聪merge:合并分支
|
2026-09-11 17:26:18 +08:00 |
|
张胜宇
|
3a21afa847
|
feat: add customer profile candidate flow
|
2026-09-11 17:16:43 +08:00 |
|
yuancong_0626
|
fd9598efdf
|
袁聪的第二次提交,项目已完整
|
2026-09-11 16:57:47 +08:00 |
|
Windows
|
852bafb374
|
feat: add profile drift governance workflow
|
2026-09-11 16:42:31 +08:00 |
|
lzf_0626
|
5bbf8481ef
|
Merge pull request '信封补齐、适当性矩阵修正与 Worker 失败原因落库' (#5) from qyqy_develop_1 into qyqy_develop
合并 qyqy_develop_1 第三轮工作:meta 信封补齐与公共化、适当性按第十二条匹配矩阵、Worker 失败原因区分固定文案与异常消息。
|
2026-09-11 16:42:29 +08:00 |
|
zhangshy
|
a122f7afba
|
修复风控预警记录查询与准备脚本
|
2026-09-11 16:40:15 +08:00 |
|
lzf_0626
|
fcd3eaadd6
|
Merge origin/qyqy_develop:风控时间口径补齐 + Agent 截断提示
组员的 4 个提交(相对 c11e56c):
- 4b0c148 把我这边的风控 P3 收尾与适当性豁免额度合并进 qyqy_develop(PR #4)
- 2ebe438 docs: 增加风控 Agent 工具白名单与意图配置
- 5102eaa Merge origin/qyqy_develop into RM2_develop
- d2cdbba 修复风控时间口径与 Agent 截断提示
自动合并零冲突,两侧的改动是互补而非重叠:
- 时区:我在 list_alerts 做了 from_local,他补的是 list_evidence 与通知列表;
- 截断:我加了 data_truncated / evidence_truncated,他在 risk_agent 的 system prompt
里加了第 10 条 —— 这些标记出现时不得按全量证据下结论。方向是一致的。
验证:ruff 干净 / mypy 138 文件 / 703 unit+contract(+6,组员新增的用例)/
33 integration,均在合并后的工作树上跑过。
|
2026-09-11 16:33:55 +08:00 |
|
Windows
|
4088c634de
|
feat: complete advisor conversation loop
|
2026-09-11 16:12:38 +08:00 |
|