同步 docs/24 的过期状态;登录端点不改(docs/05 明确平台不实现)
docs/24 是客服阶段的总结,正文保留为当时的事实,本次做两件事: 1. 刷新"当前状态"表的数字:mypy 113→137 文件、unit+contract 497→664、 integration 29→33、active 发布 181(5 项)→201(10 项:9 条 agent_tools + 1 条客服 闲聊提示词)。数字由 tools/probe_release_state.py 只读查库得到,证据留档 docs/evidence/release-state.json。 2. 追加第七节"后续更新",记录阶段总结写完之后的变化:两件待决策事项已落地(闲聊 提示词已按继承语义发布、适当性两侧口径复核通过)、风控合并后的处理要点、以及 三个仍然开着的口子(热线占位符、like %% 全表扫、offset 游标并发跳行)。 登录端点:**不改**。docs/05 §11 明确"JWT 签发、刷新、注销由统一身份认证模块负责, Agent 平台不重复实现",本平台只做校验(app/core/security.py)。第五节第 8 条据此改写, 同时把第 7 条"风控尚未启动"标记为已完成。 顺带修正"已知缺口"里的一条:列表接口的 next_cursor/has_more 风控侧已补齐,客服侧仍待办。
This commit is contained in:
@@ -108,10 +108,10 @@
|
||||
|
||||
| 项 | 值 |
|
||||
|---|---|
|
||||
| 分支 | `qyqy_develop_1`,领先 `origin` **13 个提交**(含本文,尚未推送) |
|
||||
| 门禁 | `ruff` 干净 / `mypy` 113 文件无问题 / **497** unit+contract / 29 integration |
|
||||
| 分支 | `qyqy_develop_1`(本表数字已按 2026-09-11 刷新,见文末"后续更新") |
|
||||
| 门禁 | `ruff` 干净 / `mypy` **137** 文件无问题 / **664** unit+contract / **33** integration |
|
||||
| 知识库 | 636 块(faq / product / policy 三个集合) |
|
||||
| 发布配置 | 版本 **181** active,5 条配置项 |
|
||||
| 发布配置 | 版本 **201** active(`cs-prompt-d918ca507fc6`),**10** 条配置项:9 条 `agent_tools` + 1 条客服闲聊提示词 |
|
||||
| 新增测试 | 5 个文件:`test_knowledge_keyword_recall`、`test_knowledge_granularity`、`test_customer_service_search_query`、`test_customer_service_suitability`、`test_customer_service_topic_matrix` |
|
||||
| 新增工具 | `tools/chat_console.py` |
|
||||
|
||||
@@ -152,14 +152,14 @@ a6c09fa 检索问句只在客户这一句说不清楚时才带上文
|
||||
### 后续功能(按依赖顺序)
|
||||
|
||||
6. **投顾 Agent**:画像(`fin_customer_profile` + `profile_snapshots`)、图投影(Neo4j)、适当性裁决**都已就绪**,可以开始。
|
||||
7. **风控 Agent**:需要先有规则引擎,尚未启动。
|
||||
8. **登录接口**:`docs/19` 里列为未解决项、当时有意推迟。前端要变成"能用"的产品级界面,这一步绕不过去。
|
||||
7. ~~**风控 Agent**:需要先有规则引擎,尚未启动。~~ **已完成**:规则引擎、扫描、研判、日报、通知和 15 个 REST 端点均已合并并通过门禁;代码评审与逐条处理结果见 `docs/25`。
|
||||
8. **登录接口**:**平台不实现**。`docs/05` §11 明确"JWT 签发、刷新、注销由统一身份认证模块负责,Agent 平台不重复实现",本平台只做校验(`app/core/security.py`)。前端要变"能用",正确做法是接统一认证模块,而不是在这里加一个登录端点。
|
||||
|
||||
### 已知但暂不处理的缺口
|
||||
|
||||
- `projection_reconciliation_service` 的事件层 outbox(`MemorySyncOutbox`)**没有生产者**。
|
||||
- `RUN_NOT_CANCELLABLE` 这个错误码被复用在了状态冲突场景(语义不贴切)。
|
||||
- `docs/05` §9.5 缺 `agent-intent-configs` 的 GET 说明;列表类接口普遍没有 `next_cursor`/`has_more`。
|
||||
- `docs/05` §9.5 缺 `agent-intent-configs` 的 GET 说明;~~列表类接口普遍没有 `next_cursor`/`has_more`~~ —— **风控侧已补齐**(信封对齐 §3.3、游标进 `meta`),客服侧仍待办。
|
||||
- 客服热线仍是占位符 `400-XXX-XXXX`(`customer_service.py` 的 `HOTLINE`),真号码确定后应改为读配置项。
|
||||
|
||||
---
|
||||
@@ -169,3 +169,44 @@ a6c09fa 检索问句只在客户这一句说不清楚时才带上文
|
||||
如果你要在这套底座上加新 Agent,先读 `docs/19`(接入实操 + 11 个真实坑)和 `docs/23`(记忆分层与画像)。
|
||||
|
||||
**本阶段最值得抄走的一条经验**:新接一个能力之后,**别只看代码在不在,要跑一遍确认数据真的流过**。这个项目里"接口留好了、线没接上"出现过至少五次,而它们的表现都是**静默降级**——不报错,只是回答变得又慢又差,或者一直转人工。
|
||||
|
||||
---
|
||||
|
||||
## 七、后续更新(2026-09-11)
|
||||
|
||||
本节只记录上面那份阶段总结**写完之后**发生的变化,原文保留为当时的事实。
|
||||
|
||||
### 7.1 本文第五节里那两件"要你先决策的事",都已落地
|
||||
|
||||
1. **客服闲聊提示词已落库**:发布版本 **201**(`cs-prompt-d918ca507fc6`)active,包含
|
||||
9 条 `agent_tools` + 1 条客服闲聊提示词,共 10 条配置项。发布脚本
|
||||
`tools/publish_chitchat_prompt.py` 按当时的要求做了**继承**:读当前 active 版本的
|
||||
配置项、映射字段、写入新版本,`config_release` 的整版本替换语义不会清空工具白名单。
|
||||
> 顺带记一条教训:`config_release` 是**整版本替换**,新增一项配置必须把老项带上,
|
||||
> 否则激活的一瞬间老配置全部消失。这条已写进 `docs/05` 与评审报告。
|
||||
2. **风险测评记录**:`fin_risk_assessment` 的补数属于演示数据准备,不是代码问题;
|
||||
风控合并后适当性链路两侧(客服 `check_suitability` / 风控 RW-007)已按同一口径
|
||||
`C ≥ R` 复核通过。
|
||||
|
||||
### 7.2 风控模块合并后的处理(详见 `docs/25`)
|
||||
|
||||
组员的风控模块(规则引擎 + 扫描 + 研判 + 日报 + 通知 + 15 个 REST 端点)已合并。
|
||||
评审报告 `docs/25` 逐条核实后的结果:**真缺陷已修,误报已更正,根因在别处的已改到别处**。
|
||||
重点几条:
|
||||
|
||||
- 时区口径统一到**北京时间**(库内仍是 UTC naive,转换只在 `app/core/timeutil.py`);
|
||||
- 列表信封对齐 `docs/05` §3.3、游标绑定用户与查询条件(§3.8);
|
||||
- 6 个写接口接入平台幂等(§5.1),实现了 `ApiTransactionService.execute_in` 以免
|
||||
"内层提交外层事务";
|
||||
- 通知失败不再静默、扫描调度持久化、跨进程扫描锁、全量查询封顶并在响应里标注截断;
|
||||
- `trigger_rule_codes` 补 JSON 多值索引(迁移 `20260911_risk_rule_index`),
|
||||
`EXPLAIN` 由 `type=ALL` 变为 `type=range`;
|
||||
- 第十五条豁免额度(C3→R4 ≤ 20%、C4→R5 ≤ 10%)落地到扫描与研判两侧。
|
||||
|
||||
### 7.3 三个仍然开着的口子
|
||||
|
||||
1. **客服热线仍是占位符** `400-XXX-XXXX`(`customer_service.py` 的 `HOTLINE`)。
|
||||
2. **`like(f"%{keyword}%")` 全表扫**:前后通配符在 B-tree 上无解,根治要全文索引 +
|
||||
中文分词组件,属部署依赖,本轮不做。
|
||||
3. **offset 游标 + 可变排序键**在真实并发下仍可能跳行/重复,根治要改 keyset 分页,
|
||||
属于分页协议级重做,未单方面改动。
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"recent_releases": [
|
||||
{
|
||||
"id": 201,
|
||||
"release_no": "cs-prompt-d918ca507fc6",
|
||||
"title": "客服闲聊提示词",
|
||||
"status": "active",
|
||||
"created_at": "2026-09-11 05:25:44.185697",
|
||||
"activated_at": "2026-09-11 05:25:44.742980"
|
||||
},
|
||||
{
|
||||
"id": 198,
|
||||
"release_no": "probe-e68780d3f3",
|
||||
"title": "探针验证 恢复",
|
||||
"status": "superseded",
|
||||
"created_at": "2026-09-11 05:12:33.626078",
|
||||
"activated_at": "2026-09-11 05:12:34.229356"
|
||||
},
|
||||
{
|
||||
"id": 197,
|
||||
"release_no": "probe-ee4036b9f8",
|
||||
"title": "探针验证 分支4",
|
||||
"status": "superseded",
|
||||
"created_at": "2026-09-11 05:12:29.813587",
|
||||
"activated_at": "2026-09-11 05:12:30.409705"
|
||||
},
|
||||
{
|
||||
"id": 196,
|
||||
"release_no": "probe-aa84e8efd5",
|
||||
"title": "探针验证 恢复",
|
||||
"status": "superseded",
|
||||
"created_at": "2026-09-11 05:12:11.881449",
|
||||
"activated_at": "2026-09-11 05:12:12.528369"
|
||||
},
|
||||
{
|
||||
"id": 195,
|
||||
"release_no": "probe-5eb538bf50",
|
||||
"title": "探针验证 分支4",
|
||||
"status": "superseded",
|
||||
"created_at": "2026-09-11 05:12:08.861544",
|
||||
"activated_at": "2026-09-11 05:12:09.497513"
|
||||
}
|
||||
],
|
||||
"active_items": [
|
||||
{
|
||||
"namespace": "agent_tools",
|
||||
"items": 9
|
||||
}
|
||||
],
|
||||
"active_prompt_versions": 1
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
"""只读探查:当前 active 配置发布与配置项数量(用于同步 docs/24 的"当前状态"表)。
|
||||
|
||||
只做 SELECT,结果写 `docs/evidence/release-state.json`:
|
||||
|
||||
python tools/probe_release_state.py
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import text
|
||||
|
||||
from app.infrastructure.db import SessionFactory
|
||||
|
||||
OUTPUT = Path("docs/evidence/release-state.json")
|
||||
|
||||
|
||||
async def collect() -> dict[str, Any]:
|
||||
report: dict[str, Any] = {}
|
||||
async with SessionFactory() as session:
|
||||
report["recent_releases"] = [
|
||||
dict(row)
|
||||
for row in (
|
||||
(
|
||||
await session.execute(
|
||||
text(
|
||||
"""
|
||||
SELECT id, release_no, title, status, created_at, activated_at
|
||||
FROM config_release
|
||||
ORDER BY id DESC
|
||||
LIMIT 5
|
||||
"""
|
||||
)
|
||||
)
|
||||
)
|
||||
.mappings()
|
||||
.all()
|
||||
)
|
||||
]
|
||||
report["active_items"] = [
|
||||
dict(row)
|
||||
for row in (
|
||||
(
|
||||
await session.execute(
|
||||
text(
|
||||
"""
|
||||
SELECT i.namespace, COUNT(*) AS items
|
||||
FROM platform_config_item i
|
||||
JOIN config_release r ON r.id = i.release_id
|
||||
WHERE r.status = 'active'
|
||||
GROUP BY i.namespace
|
||||
ORDER BY i.namespace
|
||||
"""
|
||||
)
|
||||
)
|
||||
)
|
||||
.mappings()
|
||||
.all()
|
||||
)
|
||||
]
|
||||
report["active_prompt_versions"] = (
|
||||
await session.execute(
|
||||
text(
|
||||
"""
|
||||
SELECT COUNT(*) FROM prompt_template_version p
|
||||
JOIN config_release r ON r.id = p.release_id
|
||||
WHERE r.status = 'active'
|
||||
"""
|
||||
)
|
||||
)
|
||||
).scalar_one()
|
||||
return report
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
report = await collect()
|
||||
OUTPUT.parent.mkdir(parents=True, exist_ok=True)
|
||||
OUTPUT.write_text(
|
||||
json.dumps(report, ensure_ascii=False, indent=2, default=str),
|
||||
encoding="utf-8",
|
||||
)
|
||||
print(f"wrote {OUTPUT}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user