From 94ce44a851408839c240c8c36388923122159730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E8=83=9C=E5=AE=87?= <17412268+zzzzz11122222@user.noreply.gitee.com> Date: Mon, 21 Sep 2026 12:26:42 +0800 Subject: [PATCH] =?UTF-8?q?feat(W24):=20docs/43=20=E5=9C=BA=E5=86=85?= =?UTF-8?q?=E5=9F=BA=E9=87=91=E6=89=8B=E5=86=8C=E5=85=A5=E5=BA=93=20+=20B-?= =?UTF-8?q?02=20=E5=B1=95=E7=A4=BA=E5=80=99=E9=80=89=E9=9B=86=E6=94=B6?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 补历史欠账:D3.4 B-02(决 12)与 D4.1 §B-02 早已承诺把 docs/43 纳入 SOURCES, 从未执行;W21 首次把它暴露成实测缺口(702 块里「科创债」0 处)。 语料与切片 - docs/43-场内基金产品手册(知识库入库版).md 纳入 SOURCES:20 只场内基金 (13 ETF + 7 LOF),fin_product_collection / public / prefix=ETF;切片件 702 -> 755 块 - 新增三个「按源开启」的切片开关(默认关,其它 8 个源零字节变化): strip_editorial_marks / qualify_table_rows / exclude_sections - 费率表表头补单位(%/年);docs/43 与 knowledge 镜像逐字一致 - docs/45 裁定不入库(与 FAQ-0018 / POL-AST-011/012 重复,会抢 top1) 修复两个真实缺陷 - W24-A 切片器行标签取错列:多列表格第 0 列是代码(159700[:2]='15'), 上游 _prefer_section 判不出重合 => 每一问都被换成整节块 (实测「科创债ETF南方怎么样」返回 20 只产品的整张表);改取表头「名称」列 - W24-B text-embedding-v3 单请求上限 10 条:load_knowledge_milvus.embed() 只在灌库路径分批,自检路径超 10 条即在数据写完后崩;分批下沉进 embed() - B-02 的 M-4 回归:_exit_partial 两条调用路径候选集不同(主路径 TopK 全量 vs 证据路径被 E4_MAX_EVIDENCE 截断)=> 同一句问句的答复质量取决于 E4 有没有 调用模型;_answer_from_evidence 新增 display_hits,展示候选一律用 TopK 全量, 送模型的证据包保持 6 块不变 产品名识别 - _PRODUCT_NAME_SHAPE 后缀集补 定期开放混合 / 股票(LOF)A / (LOF) / 原油A - 新增 _BRANDLESS_PRODUCT_NAMES(沪深300ETF,唯一无厂商字样的产品,只能枚举) - _strip_product_name_lead 增「先切掉前一只基金」 验收 - 金标 46 条 M-1 46/46、M-4 46/46、M-6 5/46、M-7/8/9/10 = 0、 M-2 28/31、M-2b 15/18、M-3 4/4(与 w23 基线逐项一致) - 两次独立复跑 result_w24d / result_w24e 指标完全相同 - Milvus 自检 15/15;全量回归 1996 passed / 3 skipped;ruff 零新增 - 真 HTTP 11 条全绿 判据变更(必须知情,不适用「零回归」表述) - B-01(访客)expected_evidence 补 ETF - B-05(客户,同一句问句)expected_exits [E3] -> [E3, E4] - load_knowledge_milvus 自检 BAS-CON-006 -> ETF-005 文档 - 客服agent/D2.1 升 v6.39;D2.4 升 v1.8(语料 755 块、手册切片实测 53); D2.8 新增 §3.6 与 §11 复测;D2.9 升 v1.3 - 开发文档/D1.1 升 v1.15(新增 §31);D1.6 升 v1.2(新增 §11、§10.4 销账); D4.8 升 v1.2(新增 §10) 诚实留痕 - _exit_partial 仍按分数挑块(不接收问句),本轮只统一候选集口径 - E5b 展示层净化仍不覆盖「收益 + 数字%」形态,继续挂账 - 语料里 2 个零容忍地雷块(POL-SPM-016 / POL-SPM-022-01)是禁令条款,故意保留 --- .../agent/implementations/customer_service.py | 65 +++++- docs/43-场内基金产品手册(知识库入库版).md | 2 +- knowledge/_chunks.jsonl | 53 +++++ .../场内基金产品手册(知识库入库版).md | 189 ++++++++++++++++++ .../service/test_customer_service_agent.py | 55 +++++ tools/build_knowledge_chunks.py | 130 +++++++++++- tools/load_knowledge_milvus.py | 42 +++- 客服agent/D2.1-客服Agent执行Todolist.md | 23 ++- 客服agent/D2.4-客服Agent知识库设计方案.html | 11 +- ...ent知识库RAG全链路与选型说明-2026-09-20.md | 69 +++++-- ....9-客服Agent手动对话测试用例-2026-09-20.md | 4 +- 开发文档/D1.1-文档索引与权威声明.md | 43 +++- ...话上下文提取与开工前补充决策-2026-09-17.md | 62 +++++- ...gent智能度体检与整改报告-W21-2026-09-21.md | 98 ++++++++- 14 files changed, 772 insertions(+), 74 deletions(-) create mode 100644 knowledge/product/场内基金产品手册(知识库入库版).md diff --git a/app/service/agent/implementations/customer_service.py b/app/service/agent/implementations/customer_service.py index 96effbd..744f827 100644 --- a/app/service/agent/implementations/customer_service.py +++ b/app/service/agent/implementations/customer_service.py @@ -1083,7 +1083,7 @@ class CustomerServiceAgent(BaseAgent): # 下面原有的 E3 / E5a / E5b 判定**原样生效**。 evidence = self._evidence_pack(hits, gap=gap) if evidence is not None: - generated = await self._answer_from_evidence(request, context, evidence) + generated = await self._answer_from_evidence(request, context, evidence, hits) if generated is not None: return generated # 混合判定:高置信直接答;中置信必须同时满足「分数够」与「领先次优够多」。 @@ -1841,9 +1841,18 @@ class CustomerServiceAgent(BaseAgent): #: 在知识库里查不到,反而把本来能答的问句推成 `E5b`。 #: 收窄成**「南方 + 名称 + 产品类型后缀」**:后缀是产品名的必要构件, #: 于是「南方基金」「南方基金管理股份有限公司」(公司名)不会被误认成产品。 + #: + #: 🆕 `W23`:后缀集补入**场内基金手册的 7 只 LOF 写法**。入库 `docs/43` 之前, + #: 库里一只 LOF 都没有,所以后缀集只覆盖场外的「…混合 / …股票 / …债券」。 + #: 补之前实测:`南方优势产业(LOF)怎么样` 的 `asked` 是**空集** ⇒ `_names_other_product` + #: 整条闸门静默失效(问句点名了产品,系统却当成"问的是类目")。 + #: 后果不对称:不会造出假"没找到"(闸门不启用),但客户问 LOF 时**拿不到 `D3` 的保护**。 + #: `(LOF)` / `定期开放混合` / `原油A` 都是产品名不可分割的构件,与公司名无关, + #: 因此不会把「南方基金」这类公司名卷进来(有专门守卫)。 _PRODUCT_NAME_SHAPE = re.compile( r"南方[\u4e00-\u9fa5A-Za-z0-9 ]{0,14}?" - r"(?:货币市场基金|定开债券[ABC]?|债券\s?[ABC]?|优选混合|价值股票|科技股票|全球精选)" + r"(?:货币市场基金|定开债券[ABC]?|债券\s?[ABC]?|优选混合|价值股票|科技股票|全球精选" + r"|定期开放混合|股票\(LOF\)[ABC]?|\(LOF\)|原油[ABC]?)" ) #: 🆕 `W21-D3`:产品名的**两种真实写法**,用于「问句点名的产品 vs 命中块讲的产品」比对。 @@ -1860,9 +1869,20 @@ class CustomerServiceAgent(BaseAgent): #: ETF 的写法:名称在前、品牌在后(`科创债ETF南方`)。单独提成常量, #: 是为了让"前缀噪声剥离"知道最少要留几个字(见 `_strip_product_name_lead`)。 _ETF_NAME_SUFFIX = "ETF南方" + + #: 场内基金里**唯一一个名字里没有厂商字样**的产品:`510300 沪深300ETF` + #: (`docs/43` 3.1 自己写明"本产品为同指数参考产品,非本公司发行的基金")。 + #: 上面两种形状都要求出现「南方」或「ETF南方」,所以它**两种都收不到**: + #: 问「沪深300ETF的费率是多少」时 `asked` 是空集 ⇒ 一致性闸门静默失效。 + #: + #: **为什么不把 `ETF` 直接放成通用后缀**:`买ETF还是买LOF` 会被吞成假产品名 + #: 「买ETF」(剥前缀噪声时 `买` 在噪声集里、`ETF` 不在),`asked` 非空而命中块里 + #: 当然没有这个名字 ⇒ **造出假"没找到"**。所以只能用枚举,不能用规律。 + _BRANDLESS_PRODUCT_NAMES = ("沪深300ETF",) _PRODUCT_NAME_PATTERNS = ( _PRODUCT_NAME_SHAPE, re.compile(r"[\u4e00-\u9fa5A-Za-z0-9]{2,10}?" + _ETF_NAME_SUFFIX), + re.compile("|".join(re.escape(name) for name in _BRANDLESS_PRODUCT_NAMES)), ) #: 产品名前可能粘上的**动词 / 连接词 / 语气词**。 @@ -1890,11 +1910,26 @@ class CustomerServiceAgent(BaseAgent): 「南方稳健增利债券 A」这种合法首字被误剥; - `…ETF南方` 形状内部没有第二个锚点,只能按 :attr:`_PRODUCT_NAME_LEAD_NOISE` 逐字剥离,并**至少留 2 个字 + `ETF南方`**(最短的合法名是「货币ETF南方」)。 + + 🆕 `W23`:`…ETF南方` 那条在剥离**之前**多一道"切掉前一只基金"的处理, + 因为噪声集只能剥单个功能字,剥不掉**前一只基金的名字**。实测 + 「沪深300ETF和货币ETF南方哪个好」——正则没有左边界,会从 `沪深300ETF` 的 + `300` 起算,匹配出 `300ETF和货币ETF南方`;`和` 被剥掉后剩下 `300ETF` 剥不动。 + 这不是"少判一次":`asked` 里塞了一个**不存在的产品名**,与命中块必然对不上, + 于是**造出假"没找到"**(正是本方法要防的那一类缺陷)。 + 截断规则:产品名里不可能出现第二个 `ETF` ⇒ 前缀里若还有 `ETF`,从它后面起算。 """ core = re.sub(r"\s+", "", name or "") if core.endswith(cls._ETF_NAME_SUFFIX): # ⚠️ 这条分支**必须排在"找第一个南方"之前**:`科创债ETF南方` 里也有「南方」, # 先按锚点截会把名字截成「南方」两个字 —— 实测踩到的正是这个坑。 + # + # 顺序也不能反:**先切前一只基金、再剥噪声**。切完露出来的首字(`和`) + # 才落在噪声集里;反过来先剥噪声,`300ETF` 的首字是 `3`,剥不动。 + head = len(core) - len(cls._ETF_NAME_SUFFIX) + cut = core.rfind("ETF", 0, head) + if cut > 0: + core = core[cut + len("ETF"):] floor = 2 + len(cls._ETF_NAME_SUFFIX) while len(core) > floor and core[0] in cls._PRODUCT_NAME_LEAD_NOISE: core = core[1:] @@ -2140,7 +2175,11 @@ class CustomerServiceAgent(BaseAgent): # ---- 出口三之补:`E4` 证据约束生成(`H-03`) ---- async def _answer_from_evidence( - self, request: AgentRequest, context: RequestContext, evidence: list[dict[str, Any]] + self, + request: AgentRequest, + context: RequestContext, + evidence: list[dict[str, Any]], + display_hits: list[Any] | None = None, ) -> CoreResult | None: """`E4` 主流程。返回 `None` 表示**这次不接管**,交回原有出口判定。 @@ -2148,7 +2187,19 @@ class CustomerServiceAgent(BaseAgent): 没按契约返回 JSON、或**模型自己说答不了**(缺主语 / 过泛 / 证据不足)。 什么时候回 `E5b`:模型给了答复但**没过校验**(引用越界 / 数字无出处 / 合规命中)—— 这正是 `H-03` DoD ⑤ 要求的拦截面,且**不建单、不转人工**。 + + ``display_hits``(`W24`)是**展示候选集**,与送模型的 ``evidence`` **不是一回事**: + `E4` 三处拦回都落到 `E5b`,而 `E5b` 的职责是"把手上现有资料里最好的那一块给客户"。 + 证据包被 `E4_MAX_EVIDENCE` 截断、且补位时 top1 还会挤掉末位,于是**同一句问句的 + 答复质量会取决于"`E4` 这次有没有调用模型"** —— 实测 `B-02` + (「基金申购和赎回有哪些费率?」)在证据路径下只挑得到 `POL-SPM-033-01` + (单行「赎回费率:<7 天 1.50%」),而 TopK 全量里躺着 `PROD-016` + (6.1 费率总表,申购 + 赎回都全)。**所以展示候选一律用 TopK 全量** + (调用点把 `hits` 传进来),送模型的证据包仍保持 `E4_MAX_EVIDENCE` 不变 —— + 那是模型注意力问题,不是展示问题。 """ + # 展示候选集与模型证据包分离(见 docstring):未显式传入时退回证据包。 + display: list[Any] = list(display_hits) if display_hits else list(evidence) message = request.message[:500] evidence_text = self._render_evidence(evidence) system, template = await self._evidence_prompt() @@ -2190,18 +2241,18 @@ class CustomerServiceAgent(BaseAgent): outside = [item for item in used if item not in allowed] if not used or outside: logger.info("E4 引用越界,拦回 E5b:%s", outside) - return self._exit_partial(evidence, note="证据约束生成引用了证据包外的来源") + return self._exit_partial(display, note="证据约束生成引用了证据包外的来源") ungrounded = self._ungrounded_numbers(answer, evidence, request.message) if ungrounded: logger.info("E4 出现无出处数字,拦回 E5b:%s", ungrounded) return self._exit_partial( - evidence, note=f"证据约束生成出现无出处数字:{ungrounded}" + display, note=f"证据约束生成出现无出处数字:{ungrounded}" ) if hits_zero_tolerance(answer) or ( is_visitor(context) and visitor_advice_violation(answer) is not None ): logger.info("E4 未过合规校验,拦回 E5b") - return self._exit_partial(evidence, note="证据约束生成未过合规校验") + return self._exit_partial(display, note="证据约束生成未过合规校验") logger.info("E4 证据约束生成命中:块数=%s 引用=%s", len(evidence), used) # 生成文本同样要过**展示层净化**:模型看到的证据包是 markdown 源文,它会把 # `**一、购买渠道**` 这类标记原样抄进答复(实测 2026-09-20「怎么买基金」)。 @@ -2222,7 +2273,7 @@ class CustomerServiceAgent(BaseAgent): # 换成"先净化再判合规"已被实测证伪(整行丢弃会把单行长段整条删空)。 rendered = render_plain(drop_yield_claims(answer)) if not rendered: - return self._exit_partial(evidence, note="证据约束生成内容全部为收益数值") + return self._exit_partial(display, note="证据约束生成内容全部为收益数值") return CoreResult( text=self._clamp_answer(rendered), intent=self._classified_intent ) diff --git a/docs/43-场内基金产品手册(知识库入库版).md b/docs/43-场内基金产品手册(知识库入库版).md index 75b2123..ed54266 100644 --- a/docs/43-场内基金产品手册(知识库入库版).md +++ b/docs/43-场内基金产品手册(知识库入库版).md @@ -133,7 +133,7 @@ LOF 与主动管理型较高(如南方积极配置混合 1.20%/年、托管费 答:本平台 20 只场内基金的费率如下(单位:%/年,均为从基金资产中计提、不单独向客户收取): -| 代码 | 名称 | 管理费率 | 托管费率 | 销售服务费率 | +| 代码 | 名称 | 管理费率(%/年) | 托管费率(%/年) | 销售服务费率(%/年) | |---|---|---|---|---| | 159329 | 沙特ETF南方 | 0.50 | 0.10 | — | | 159382 | 创业板人工智能ETF南方 | 0.50 | 0.10 | — | diff --git a/knowledge/_chunks.jsonl b/knowledge/_chunks.jsonl index 408e95b..3972c0e 100644 --- a/knowledge/_chunks.jsonl +++ b/knowledge/_chunks.jsonl @@ -631,6 +631,59 @@ {"doc_id": "COMP-022-08", "collection": "fin_faq_collection", "title": "南方基金管理股份有限公司 企业信息 · 九、常见关联信息速查 · 客户服务中心", "content": "九、常见关联信息速查:客户服务中心 北京、上海、广州、深圳、成都、杭州(6 个)", "chapter": "南方基金管理股份有限公司 企业信息", "section": "九、常见关联信息速查 · 客户服务中心", "tags": "公司信息,金融牌照,资质", "doc_no": "", "version": "", "effective_date": "", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "company/企业信息.md", "visibility": "public", "chars": 40, "family_id": "COMP-022", "param_class": "none", "intent": "faq"} {"doc_id": "COMP-022-09", "collection": "fin_faq_collection", "title": "南方基金管理股份有限公司 企业信息 · 九、常见关联信息速查 · 境外子公司", "content": "九、常见关联信息速查:境外子公司 南方东英资产管理有限公司(中国香港)", "chapter": "南方基金管理股份有限公司 企业信息", "section": "九、常见关联信息速查 · 境外子公司", "tags": "公司信息,金融牌照,资质", "doc_no": "", "version": "", "effective_date": "", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "company/企业信息.md", "visibility": "public", "chars": 35, "family_id": "COMP-022", "param_class": "none", "intent": "faq"} {"doc_id": "COMP-022-10", "collection": "fin_faq_collection", "title": "南方基金管理股份有限公司 企业信息 · 九、常见关联信息速查 · 境内子公司", "content": "九、常见关联信息速查:境内子公司 南方资本管理有限公司(深圳前海)", "chapter": "南方基金管理股份有限公司 企业信息", "section": "九、常见关联信息速查 · 境内子公司", "tags": "公司信息,金融牌照,资质", "doc_no": "", "version": "", "effective_date": "", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "company/企业信息.md", "visibility": "public", "chars": 33, "family_id": "COMP-022", "param_class": "none", "intent": "faq"} +{"doc_id": "ETF-001", "collection": "fin_product_collection", "title": "场内基金产品手册 · 一、交易规则 · 1.1 场内基金怎么买、最小买多少", "content": "### 1.1 场内基金怎么买、最小买多少\n\n问:场内基金怎么买?最小买多少?一次能买多少份?\n\n答:本平台的场内基金按\"手\"撮合,**1 手 = 100 份**,委托数量必须是 100 份的整数倍。\n最小交易金额以产品详情页披露的字段为准(平台产品库中登记为 100.00 元);\n实际所需金额按成交价 × 份数计算,随市价变动。\n实际可买数量与所需金额请以交易页面的实时报价为准。", "chapter": "一、交易规则", "section": "1.1 场内基金怎么买、最小买多少", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 192, "family_id": "ETF-001", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-002", "collection": "fin_product_collection", "title": "场内基金产品手册 · 一、交易规则 · 1.2 场内基金的费用有哪些", "content": "### 1.2 场内基金的费用有哪些\n\n问:买场内基金要交哪些费用?费率是多少?管理费多少?托管费多少?\n\n答:场内基金涉及两类费用。\n\n**(1)基金自身的费用**,从基金资产中按日计提、体现在净值里,不向您单独收取:\n本平台 20 只场内基金的管理费率为 **0.15%~1.20%/年**、托管费率为 **0.05%~0.20%/年**。\n其中 ETF 通常较低(如沪深300ETF 管理费 0.15%/年、托管费 0.05%/年),\nLOF 与主动管理型较高(如南方积极配置混合 1.20%/年、托管费 0.20%/年)。\n货币 ETF 另收销售服务费(如货币ETF南方 0.25%/年)。各产品的具体费率见下方费率一览。\n\n**(2)交易费用**,即您买卖时产生的**券商佣金**,费率由您开户的券商决定,\n本平台不代收、也不设定该费率。", "chapter": "一、交易规则", "section": "1.2 场内基金的费用有哪些", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 374, "family_id": "ETF-002", "param_class": "rate", "intent": "product_inquiry"} +{"doc_id": "ETF-003", "collection": "fin_product_collection", "title": "场内基金产品手册 · 一、交易规则 · 1.3 报价的最小变动单位", "content": "### 1.3 报价的最小变动单位\n\n问:场内基金报价最小变动多少?\n\n答:本平台场内基金的最小价格变动单位为 **0.001 元**。", "chapter": "一、交易规则", "section": "1.3 报价的最小变动单位", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 68, "family_id": "ETF-003", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-004", "collection": "fin_product_collection", "title": "场内基金产品手册 · 一、交易规则 · 1.4 风险等级与适当性", "content": "### 1.4 风险等级与适当性\n\n问:我能买什么风险等级的产品?风险等级不匹配能买吗?\n\n答:产品风险等级从 **R1 到 R5**,由低到高。平台按您的**有效风险测评结果**做匹配校验:\n测评等级与产品等级不匹配时,下单会被拦截。\n请先完成风险承受能力测评,并以交易页面的校验结果为准。\n客服不能修改您的测评结果,也不能代为开通跨级权限。", "chapter": "一、交易规则", "section": "1.4 风险等级与适当性", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 172, "family_id": "ETF-004", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-005", "collection": "fin_product_collection", "title": "场内基金产品手册 · 一、交易规则 · 1.5 场内基金与场外基金的区别", "content": "### 1.5 场内基金与场外基金的区别\n\n问:场内基金和场外基金有什么不一样?\n\n答:场内基金(ETF / LOF)在交易所挂牌,按市价、以\"手\"为单位实时撮合成交;\n场外基金按当日净值申赎,通常 T+1 确认。\n本平台的模拟交易针对**场内**基金,下单即按市价全额成交。", "chapter": "一、交易规则", "section": "1.5 场内基金与场外基金的区别", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 138, "family_id": "ETF-005", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-006", "collection": "fin_product_collection", "title": "场内基金产品手册 · 一、交易规则 · 1.6 购买基金需要什么条件", "content": "### 1.6 购买基金需要什么条件\n\n问:购买基金需要什么条件?买基金有什么门槛?需要什么资质?有什么要求?\n\n答:在本平台购买场内基金,需要满足以下条件:\n\n**(1)账户已完成开户**。未开户的账户下单会被拦截,请先完成开户。\n**(2)已完成风险承受能力测评**,且测评等级与所购产品的风险等级匹配\n(产品风险等级 R1~R5 由低到高,测评结果不匹配时下单会被拦截,详见 1.4)。\n**(3)委托数量为 100 份的整数倍**(1 手 = 100 份),并按市价准备足额资金。\n\n以上三项均以交易页面的实时校验结果为准。开户流程与所需材料请咨询客服热线;\n客服不能代为开户、修改风险测评结果,也不能开通跨级购买权限。", "chapter": "一、交易规则", "section": "1.6 购买基金需要什么条件", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 314, "family_id": "ETF-006", "param_class": "threshold", "intent": "product_inquiry"} +{"doc_id": "ETF-007", "collection": "fin_product_collection", "title": "场内基金产品手册 · 二、产品清单", "content": "## 二、产品清单\n\n问:平台上有哪些场内基金?产品代码是什么?有哪些 ETF?\n\n答:本平台场内基金共 **20 只**,包含 13 只 ETF 与 7 只 LOF。\n下表净值为最新披露值,会随行情变动:\n\n\n| 代码 | 名称 | 交易所 | 类型 | 风险等级 | 净值 | 净值日期 |\n|---|---|---|---|---|---|---|\n| 159329 | 沙特ETF南方 | 深交所 | ETF | R5 | 0.9167 | 2026-09-10 |\n| 159382 | 创业板人工智能ETF南方 | 深交所 | ETF | R4 | 2.5841 | 2026-09-11 |\n| 159511 | 通信ETF南方 | 深交所 | ETF | R4 | 2.3755 | 2026-09-11 |\n| 159615 | 恒生生物科技ETF南方 | 深交所 | ETF | R5 | 1.1027 | 2026-09-11 |\n| 159687 | 亚太精选ETF南方 | 深交所 | ETF | R5 | 1.8891 | 2026-09-10 |\n| 159700 | 科创债ETF南方 | 深交所 | ETF | R2 | 101.8009 | 2026-09-11 |\n| 159948 | 创业板ETF南方 | 深交所 | ETF | R4 | 3.6895 | 2026-09-11 |\n| 510300 | 沪深300ETF | 上交所 | ETF | R3 | 4.5794 | 2026-09-11 |\n| 510500 | 中证500ETF南方 | 上交所 | ETF | R4 | 7.6027 | 2026-09-11 |\n| 511070 | 公司债ETF南方 | 上交所 | ETF | R2 | 103.0145 | 2026-09-11 |\n| 511810 | 货币ETF南方 | 上交所 | ETF | R1 | 0.2661 | 2026-09-11 |\n| 515450 | 红利低波50ETF南方 | 上交所 | ETF | R3 | 1.4027 | 2026-09-11 |\n| 588890 | 科创芯片ETF南方 | 上交所 | ETF | R4 | 1.2327 | 2026-09-11 |\n| 160105 | 南方积极配置混合(LOF) | 深交所 | LOF | R3 | 1.2514 | 2026-09-11 |\n| 160127 | 南方新兴消费增长股票(LOF)A | 深交所 | LOF | R4 | 0.8364 | 2026-09-11 |\n| 160128 | 南方金利定开债券A | 深交所 | LOF | R2 | 1.0260 | 2026-09-11 |\n| 160142 | 南方优势产业(LOF) | 深交所 | LOF | R3 | 1.0623 | 2026-09-11 |\n| 160143 | 南方创业板2年定期开放混合 | 深交所 | LOF | R3 | 1.6105 | 2026-09-11 |\n| 501018 | 南方原油A | 上交所 | LOF | R5 | 2.0750 | 2026-09-10 |\n| 501062 | 南方瑞合定开混合(LOF) | 上交所 | LOF | R3 | 2.1048 | 2026-09-11 |\n\n宽基与债券类 ETF 的风险等级多为 R2~R3,行业主题与跨境类 ETF 多为 R4~R5。\n完整清单与实时净值请以产品列表页为准。\n\n## 三、重点产品", "chapter": "场内基金产品手册", "section": "二、产品清单", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 1517, "family_id": "ETF-007", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-007-01", "collection": "fin_product_collection", "title": "场内基金产品手册 · 二、产品清单 · 沙特ETF南方", "content": "二、产品清单:代码 159329 / 名称 沙特ETF南方 / 交易所 深交所 / 类型 ETF / 风险等级 R5 / 净值 0.9167 / 净值日期 2026-09-10", "chapter": "场内基金产品手册", "section": "二、产品清单 · 沙特ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 88, "family_id": "ETF-007", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-007-02", "collection": "fin_product_collection", "title": "场内基金产品手册 · 二、产品清单 · 创业板人工智能ETF南方", "content": "二、产品清单:代码 159382 / 名称 创业板人工智能ETF南方 / 交易所 深交所 / 类型 ETF / 风险等级 R4 / 净值 2.5841 / 净值日期 2026-09-11", "chapter": "场内基金产品手册", "section": "二、产品清单 · 创业板人工智能ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 93, "family_id": "ETF-007", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-007-03", "collection": "fin_product_collection", "title": "场内基金产品手册 · 二、产品清单 · 通信ETF南方", "content": "二、产品清单:代码 159511 / 名称 通信ETF南方 / 交易所 深交所 / 类型 ETF / 风险等级 R4 / 净值 2.3755 / 净值日期 2026-09-11", "chapter": "场内基金产品手册", "section": "二、产品清单 · 通信ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 88, "family_id": "ETF-007", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-007-04", "collection": "fin_product_collection", "title": "场内基金产品手册 · 二、产品清单 · 恒生生物科技ETF南方", "content": "二、产品清单:代码 159615 / 名称 恒生生物科技ETF南方 / 交易所 深交所 / 类型 ETF / 风险等级 R5 / 净值 1.1027 / 净值日期 2026-09-11", "chapter": "场内基金产品手册", "section": "二、产品清单 · 恒生生物科技ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 92, "family_id": "ETF-007", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-007-05", "collection": "fin_product_collection", "title": "场内基金产品手册 · 二、产品清单 · 亚太精选ETF南方", "content": "二、产品清单:代码 159687 / 名称 亚太精选ETF南方 / 交易所 深交所 / 类型 ETF / 风险等级 R5 / 净值 1.8891 / 净值日期 2026-09-10", "chapter": "场内基金产品手册", "section": "二、产品清单 · 亚太精选ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 90, "family_id": "ETF-007", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-007-06", "collection": "fin_product_collection", "title": "场内基金产品手册 · 二、产品清单 · 科创债ETF南方", "content": "二、产品清单:代码 159700 / 名称 科创债ETF南方 / 交易所 深交所 / 类型 ETF / 风险等级 R2 / 净值 101.8009 / 净值日期 2026-09-11", "chapter": "场内基金产品手册", "section": "二、产品清单 · 科创债ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 91, "family_id": "ETF-007", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-007-07", "collection": "fin_product_collection", "title": "场内基金产品手册 · 二、产品清单 · 创业板ETF南方", "content": "二、产品清单:代码 159948 / 名称 创业板ETF南方 / 交易所 深交所 / 类型 ETF / 风险等级 R4 / 净值 3.6895 / 净值日期 2026-09-11", "chapter": "场内基金产品手册", "section": "二、产品清单 · 创业板ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 89, "family_id": "ETF-007", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-007-08", "collection": "fin_product_collection", "title": "场内基金产品手册 · 二、产品清单 · 沪深300ETF", "content": "二、产品清单:代码 510300 / 名称 沪深300ETF / 交易所 上交所 / 类型 ETF / 风险等级 R3 / 净值 4.5794 / 净值日期 2026-09-11", "chapter": "场内基金产品手册", "section": "二、产品清单 · 沪深300ETF", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 89, "family_id": "ETF-007", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-007-09", "collection": "fin_product_collection", "title": "场内基金产品手册 · 二、产品清单 · 中证500ETF南方", "content": "二、产品清单:代码 510500 / 名称 中证500ETF南方 / 交易所 上交所 / 类型 ETF / 风险等级 R4 / 净值 7.6027 / 净值日期 2026-09-11", "chapter": "场内基金产品手册", "section": "二、产品清单 · 中证500ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 91, "family_id": "ETF-007", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-007-10", "collection": "fin_product_collection", "title": "场内基金产品手册 · 二、产品清单 · 公司债ETF南方", "content": "二、产品清单:代码 511070 / 名称 公司债ETF南方 / 交易所 上交所 / 类型 ETF / 风险等级 R2 / 净值 103.0145 / 净值日期 2026-09-11", "chapter": "场内基金产品手册", "section": "二、产品清单 · 公司债ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 91, "family_id": "ETF-007", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-007-11", "collection": "fin_product_collection", "title": "场内基金产品手册 · 二、产品清单 · 货币ETF南方", "content": "二、产品清单:代码 511810 / 名称 货币ETF南方 / 交易所 上交所 / 类型 ETF / 风险等级 R1 / 净值 0.2661 / 净值日期 2026-09-11", "chapter": "场内基金产品手册", "section": "二、产品清单 · 货币ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 88, "family_id": "ETF-007", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-007-12", "collection": "fin_product_collection", "title": "场内基金产品手册 · 二、产品清单 · 红利低波50ETF南方", "content": "二、产品清单:代码 515450 / 名称 红利低波50ETF南方 / 交易所 上交所 / 类型 ETF / 风险等级 R3 / 净值 1.4027 / 净值日期 2026-09-11", "chapter": "场内基金产品手册", "section": "二、产品清单 · 红利低波50ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 92, "family_id": "ETF-007", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-007-13", "collection": "fin_product_collection", "title": "场内基金产品手册 · 二、产品清单 · 科创芯片ETF南方", "content": "二、产品清单:代码 588890 / 名称 科创芯片ETF南方 / 交易所 上交所 / 类型 ETF / 风险等级 R4 / 净值 1.2327 / 净值日期 2026-09-11", "chapter": "场内基金产品手册", "section": "二、产品清单 · 科创芯片ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 90, "family_id": "ETF-007", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-007-14", "collection": "fin_product_collection", "title": "场内基金产品手册 · 二、产品清单 · 南方积极配置混合(LOF)", "content": "二、产品清单:代码 160105 / 名称 南方积极配置混合(LOF) / 交易所 深交所 / 类型 LOF / 风险等级 R3 / 净值 1.2514 / 净值日期 2026-09-11", "chapter": "场内基金产品手册", "section": "二、产品清单 · 南方积极配置混合(LOF)", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 94, "family_id": "ETF-007", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-007-15", "collection": "fin_product_collection", "title": "场内基金产品手册 · 二、产品清单 · 南方新兴消费增长股票(LOF)A", "content": "二、产品清单:代码 160127 / 名称 南方新兴消费增长股票(LOF)A / 交易所 深交所 / 类型 LOF / 风险等级 R4 / 净值 0.8364 / 净值日期 2026-09-11", "chapter": "场内基金产品手册", "section": "二、产品清单 · 南方新兴消费增长股票(LOF)A", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 97, "family_id": "ETF-007", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-007-16", "collection": "fin_product_collection", "title": "场内基金产品手册 · 二、产品清单 · 南方金利定开债券A", "content": "二、产品清单:代码 160128 / 名称 南方金利定开债券A / 交易所 深交所 / 类型 LOF / 风险等级 R2 / 净值 1.0260 / 净值日期 2026-09-11", "chapter": "场内基金产品手册", "section": "二、产品清单 · 南方金利定开债券A", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 90, "family_id": "ETF-007", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-007-17", "collection": "fin_product_collection", "title": "场内基金产品手册 · 二、产品清单 · 南方优势产业(LOF)", "content": "二、产品清单:代码 160142 / 名称 南方优势产业(LOF) / 交易所 深交所 / 类型 LOF / 风险等级 R3 / 净值 1.0623 / 净值日期 2026-09-11", "chapter": "场内基金产品手册", "section": "二、产品清单 · 南方优势产业(LOF)", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 92, "family_id": "ETF-007", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-007-18", "collection": "fin_product_collection", "title": "场内基金产品手册 · 二、产品清单 · 南方创业板2年定期开放混合", "content": "二、产品清单:代码 160143 / 名称 南方创业板2年定期开放混合 / 交易所 深交所 / 类型 LOF / 风险等级 R3 / 净值 1.6105 / 净值日期 2026-09-11", "chapter": "场内基金产品手册", "section": "二、产品清单 · 南方创业板2年定期开放混合", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 94, "family_id": "ETF-007", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-007-19", "collection": "fin_product_collection", "title": "场内基金产品手册 · 二、产品清单 · 南方原油A", "content": "二、产品清单:代码 501018 / 名称 南方原油A / 交易所 上交所 / 类型 LOF / 风险等级 R5 / 净值 2.0750 / 净值日期 2026-09-10", "chapter": "场内基金产品手册", "section": "二、产品清单 · 南方原油A", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 86, "family_id": "ETF-007", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-007-20", "collection": "fin_product_collection", "title": "场内基金产品手册 · 二、产品清单 · 南方瑞合定开混合(LOF)", "content": "二、产品清单:代码 501062 / 名称 南方瑞合定开混合(LOF) / 交易所 上交所 / 类型 LOF / 风险等级 R3 / 净值 2.1048 / 净值日期 2026-09-11", "chapter": "场内基金产品手册", "section": "二、产品清单 · 南方瑞合定开混合(LOF)", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 94, "family_id": "ETF-007", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-008", "collection": "fin_product_collection", "title": "场内基金产品手册 · 三、重点产品 · 3.1 沪深300ETF(510300)", "content": "### 3.1 沪深300ETF(510300)\n\n问:沪深300ETF 的起投金额是多少?南方沪深300ETF 怎么买?510300 的费率是多少?\n\n答:沪深300ETF 的产品代码为 **510300**,在**上交所**挂牌,属于 ETF,风险等级 **R3**。\n它按\"手\"交易,**1 手 = 100 份**;最小交易金额以产品详情页披露的字段为准(登记为 100.00 元)。\n该产品最新净值为 **4.5794 元**(2026-09-11),\n管理费率 **0.15%/年**、托管费率 **0.05%/年**,从基金资产中计提、不单独向您收取。\n实际成交金额、可买数量与费用请以交易页面的实时报价为准。\n\n**产品披露**:本产品为同指数参考产品,非本公司发行的基金,在本平台仅用于功能演示。", "chapter": "三、重点产品", "section": "3.1 沪深300ETF(510300)", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 356, "family_id": "ETF-008", "param_class": "threshold", "intent": "product_inquiry"} +{"doc_id": "ETF-009", "collection": "fin_product_collection", "title": "场内基金产品手册 · 四、费率一览", "content": "## 四、费率一览\n\n问:各家基金的管理费和托管费分别是多少?\n\n答:本平台 20 只场内基金的费率如下(单位:%/年,均为从基金资产中计提、不单独向客户收取):\n\n| 代码 | 名称 | 管理费率(%/年) | 托管费率(%/年) | 销售服务费率(%/年) |\n|---|---|---|---|---|\n| 159329 | 沙特ETF南方 | 0.50 | 0.10 | — |\n| 159382 | 创业板人工智能ETF南方 | 0.50 | 0.10 | — |\n| 159511 | 通信ETF南方 | 0.50 | 0.10 | — |\n| 159615 | 恒生生物科技ETF南方 | 0.50 | 0.15 | — |\n| 159687 | 亚太精选ETF南方 | 0.20 | 0.05 | — |\n| 159700 | 科创债ETF南方 | 0.15 | 0.05 | — |\n| 159948 | 创业板ETF南方 | 0.15 | 0.05 | — |\n| 510300 | 沪深300ETF | 0.15 | 0.05 | — |\n| 510500 | 中证500ETF南方 | 0.15 | 0.05 | — |\n| 511070 | 公司债ETF南方 | 0.15 | 0.05 | — |\n| 511810 | 货币ETF南方 | 0.30 | 0.05 | 0.25 |\n| 515450 | 红利低波50ETF南方 | 0.50 | 0.10 | — |\n| 588890 | 科创芯片ETF南方 | 0.50 | 0.10 | — |\n| 160105 | 南方积极配置混合(LOF) | 1.20 | 0.20 | — |\n| 160127 | 南方新兴消费增长股票(LOF)A | 1.20 | 0.20 | — |\n| 160128 | 南方金利定开债券A | 0.50 | 0.15 | — |\n| 160142 | 南方优势产业(LOF) | 1.20 | 0.20 | — |\n| 160143 | 南方创业板2年定期开放混合 | 1.20 | 0.20 | — |\n| 501018 | 南方原油A | 1.00 | 0.20 | — |\n| 501062 | 南方瑞合定开混合(LOF) | 1.20 | 0.20 | — |\n\n\"销售服务费率\"为\"—\"表示该产品不收取销售服务费。\n除上表费用外,买卖时产生的券商佣金由您开户的券商决定,本平台不代收。\n\n## 五、常见问答", "chapter": "场内基金产品手册", "section": "四、费率一览", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 1066, "family_id": "ETF-009", "param_class": "rate", "intent": "product_inquiry"} +{"doc_id": "ETF-009-01", "collection": "fin_product_collection", "title": "场内基金产品手册 · 四、费率一览 · 沙特ETF南方", "content": "四、费率一览:代码 159329 / 名称 沙特ETF南方 / 管理费率(%/年) 0.50 / 托管费率(%/年) 0.10 / 销售服务费率(%/年) 无", "chapter": "场内基金产品手册", "section": "四、费率一览 · 沙特ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 79, "family_id": "ETF-009", "param_class": "rate", "intent": "product_inquiry"} +{"doc_id": "ETF-009-02", "collection": "fin_product_collection", "title": "场内基金产品手册 · 四、费率一览 · 创业板人工智能ETF南方", "content": "四、费率一览:代码 159382 / 名称 创业板人工智能ETF南方 / 管理费率(%/年) 0.50 / 托管费率(%/年) 0.10 / 销售服务费率(%/年) 无", "chapter": "场内基金产品手册", "section": "四、费率一览 · 创业板人工智能ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 84, "family_id": "ETF-009", "param_class": "rate", "intent": "product_inquiry"} +{"doc_id": "ETF-009-03", "collection": "fin_product_collection", "title": "场内基金产品手册 · 四、费率一览 · 通信ETF南方", "content": "四、费率一览:代码 159511 / 名称 通信ETF南方 / 管理费率(%/年) 0.50 / 托管费率(%/年) 0.10 / 销售服务费率(%/年) 无", "chapter": "场内基金产品手册", "section": "四、费率一览 · 通信ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 79, "family_id": "ETF-009", "param_class": "rate", "intent": "product_inquiry"} +{"doc_id": "ETF-009-04", "collection": "fin_product_collection", "title": "场内基金产品手册 · 四、费率一览 · 恒生生物科技ETF南方", "content": "四、费率一览:代码 159615 / 名称 恒生生物科技ETF南方 / 管理费率(%/年) 0.50 / 托管费率(%/年) 0.15 / 销售服务费率(%/年) 无", "chapter": "场内基金产品手册", "section": "四、费率一览 · 恒生生物科技ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 83, "family_id": "ETF-009", "param_class": "rate", "intent": "product_inquiry"} +{"doc_id": "ETF-009-05", "collection": "fin_product_collection", "title": "场内基金产品手册 · 四、费率一览 · 亚太精选ETF南方", "content": "四、费率一览:代码 159687 / 名称 亚太精选ETF南方 / 管理费率(%/年) 0.20 / 托管费率(%/年) 0.05 / 销售服务费率(%/年) 无", "chapter": "场内基金产品手册", "section": "四、费率一览 · 亚太精选ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 81, "family_id": "ETF-009", "param_class": "rate", "intent": "product_inquiry"} +{"doc_id": "ETF-009-06", "collection": "fin_product_collection", "title": "场内基金产品手册 · 四、费率一览 · 科创债ETF南方", "content": "四、费率一览:代码 159700 / 名称 科创债ETF南方 / 管理费率(%/年) 0.15 / 托管费率(%/年) 0.05 / 销售服务费率(%/年) 无", "chapter": "场内基金产品手册", "section": "四、费率一览 · 科创债ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 80, "family_id": "ETF-009", "param_class": "rate", "intent": "product_inquiry"} +{"doc_id": "ETF-009-07", "collection": "fin_product_collection", "title": "场内基金产品手册 · 四、费率一览 · 创业板ETF南方", "content": "四、费率一览:代码 159948 / 名称 创业板ETF南方 / 管理费率(%/年) 0.15 / 托管费率(%/年) 0.05 / 销售服务费率(%/年) 无", "chapter": "场内基金产品手册", "section": "四、费率一览 · 创业板ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 80, "family_id": "ETF-009", "param_class": "rate", "intent": "product_inquiry"} +{"doc_id": "ETF-009-08", "collection": "fin_product_collection", "title": "场内基金产品手册 · 四、费率一览 · 沪深300ETF", "content": "四、费率一览:代码 510300 / 名称 沪深300ETF / 管理费率(%/年) 0.15 / 托管费率(%/年) 0.05 / 销售服务费率(%/年) 无", "chapter": "场内基金产品手册", "section": "四、费率一览 · 沪深300ETF", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 80, "family_id": "ETF-009", "param_class": "rate", "intent": "product_inquiry"} +{"doc_id": "ETF-009-09", "collection": "fin_product_collection", "title": "场内基金产品手册 · 四、费率一览 · 中证500ETF南方", "content": "四、费率一览:代码 510500 / 名称 中证500ETF南方 / 管理费率(%/年) 0.15 / 托管费率(%/年) 0.05 / 销售服务费率(%/年) 无", "chapter": "场内基金产品手册", "section": "四、费率一览 · 中证500ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 82, "family_id": "ETF-009", "param_class": "rate", "intent": "product_inquiry"} +{"doc_id": "ETF-009-10", "collection": "fin_product_collection", "title": "场内基金产品手册 · 四、费率一览 · 公司债ETF南方", "content": "四、费率一览:代码 511070 / 名称 公司债ETF南方 / 管理费率(%/年) 0.15 / 托管费率(%/年) 0.05 / 销售服务费率(%/年) 无", "chapter": "场内基金产品手册", "section": "四、费率一览 · 公司债ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 80, "family_id": "ETF-009", "param_class": "rate", "intent": "product_inquiry"} +{"doc_id": "ETF-009-11", "collection": "fin_product_collection", "title": "场内基金产品手册 · 四、费率一览 · 货币ETF南方", "content": "四、费率一览:代码 511810 / 名称 货币ETF南方 / 管理费率(%/年) 0.30 / 托管费率(%/年) 0.05 / 销售服务费率(%/年) 0.25", "chapter": "场内基金产品手册", "section": "四、费率一览 · 货币ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 82, "family_id": "ETF-009", "param_class": "rate", "intent": "product_inquiry"} +{"doc_id": "ETF-009-12", "collection": "fin_product_collection", "title": "场内基金产品手册 · 四、费率一览 · 红利低波50ETF南方", "content": "四、费率一览:代码 515450 / 名称 红利低波50ETF南方 / 管理费率(%/年) 0.50 / 托管费率(%/年) 0.10 / 销售服务费率(%/年) 无", "chapter": "场内基金产品手册", "section": "四、费率一览 · 红利低波50ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 83, "family_id": "ETF-009", "param_class": "rate", "intent": "product_inquiry"} +{"doc_id": "ETF-009-13", "collection": "fin_product_collection", "title": "场内基金产品手册 · 四、费率一览 · 科创芯片ETF南方", "content": "四、费率一览:代码 588890 / 名称 科创芯片ETF南方 / 管理费率(%/年) 0.50 / 托管费率(%/年) 0.10 / 销售服务费率(%/年) 无", "chapter": "场内基金产品手册", "section": "四、费率一览 · 科创芯片ETF南方", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 81, "family_id": "ETF-009", "param_class": "rate", "intent": "product_inquiry"} +{"doc_id": "ETF-009-14", "collection": "fin_product_collection", "title": "场内基金产品手册 · 四、费率一览 · 南方积极配置混合(LOF)", "content": "四、费率一览:代码 160105 / 名称 南方积极配置混合(LOF) / 管理费率(%/年) 1.20 / 托管费率(%/年) 0.20 / 销售服务费率(%/年) 无", "chapter": "场内基金产品手册", "section": "四、费率一览 · 南方积极配置混合(LOF)", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 85, "family_id": "ETF-009", "param_class": "rate", "intent": "product_inquiry"} +{"doc_id": "ETF-009-15", "collection": "fin_product_collection", "title": "场内基金产品手册 · 四、费率一览 · 南方新兴消费增长股票(LOF)A", "content": "四、费率一览:代码 160127 / 名称 南方新兴消费增长股票(LOF)A / 管理费率(%/年) 1.20 / 托管费率(%/年) 0.20 / 销售服务费率(%/年) 无", "chapter": "场内基金产品手册", "section": "四、费率一览 · 南方新兴消费增长股票(LOF)A", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 88, "family_id": "ETF-009", "param_class": "rate", "intent": "product_inquiry"} +{"doc_id": "ETF-009-16", "collection": "fin_product_collection", "title": "场内基金产品手册 · 四、费率一览 · 南方金利定开债券A", "content": "四、费率一览:代码 160128 / 名称 南方金利定开债券A / 管理费率(%/年) 0.50 / 托管费率(%/年) 0.15 / 销售服务费率(%/年) 无", "chapter": "场内基金产品手册", "section": "四、费率一览 · 南方金利定开债券A", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 81, "family_id": "ETF-009", "param_class": "rate", "intent": "product_inquiry"} +{"doc_id": "ETF-009-17", "collection": "fin_product_collection", "title": "场内基金产品手册 · 四、费率一览 · 南方优势产业(LOF)", "content": "四、费率一览:代码 160142 / 名称 南方优势产业(LOF) / 管理费率(%/年) 1.20 / 托管费率(%/年) 0.20 / 销售服务费率(%/年) 无", "chapter": "场内基金产品手册", "section": "四、费率一览 · 南方优势产业(LOF)", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 83, "family_id": "ETF-009", "param_class": "rate", "intent": "product_inquiry"} +{"doc_id": "ETF-009-18", "collection": "fin_product_collection", "title": "场内基金产品手册 · 四、费率一览 · 南方创业板2年定期开放混合", "content": "四、费率一览:代码 160143 / 名称 南方创业板2年定期开放混合 / 管理费率(%/年) 1.20 / 托管费率(%/年) 0.20 / 销售服务费率(%/年) 无", "chapter": "场内基金产品手册", "section": "四、费率一览 · 南方创业板2年定期开放混合", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 85, "family_id": "ETF-009", "param_class": "rate", "intent": "product_inquiry"} +{"doc_id": "ETF-009-19", "collection": "fin_product_collection", "title": "场内基金产品手册 · 四、费率一览 · 南方原油A", "content": "四、费率一览:代码 501018 / 名称 南方原油A / 管理费率(%/年) 1.00 / 托管费率(%/年) 0.20 / 销售服务费率(%/年) 无", "chapter": "场内基金产品手册", "section": "四、费率一览 · 南方原油A", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 77, "family_id": "ETF-009", "param_class": "rate", "intent": "product_inquiry"} +{"doc_id": "ETF-009-20", "collection": "fin_product_collection", "title": "场内基金产品手册 · 四、费率一览 · 南方瑞合定开混合(LOF)", "content": "四、费率一览:代码 501062 / 名称 南方瑞合定开混合(LOF) / 管理费率(%/年) 1.20 / 托管费率(%/年) 0.20 / 销售服务费率(%/年) 无", "chapter": "场内基金产品手册", "section": "四、费率一览 · 南方瑞合定开混合(LOF)", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 85, "family_id": "ETF-009", "param_class": "rate", "intent": "product_inquiry"} +{"doc_id": "ETF-010", "collection": "fin_product_collection", "title": "场内基金产品手册 · 五、常见问答 · 5.1 ETF 能定投吗?", "content": "### 5.1 ETF 能定投吗?\n\n问:ETF 能定投吗?可以自动扣款吗?\n\n答:本平台的场内基金按市价实时撮合成交,**不提供自动定投计划**。\n如需分批买入,可以自行分次下单,每次委托数量为 100 份的整数倍。", "chapter": "五、常见问答", "section": "5.1 ETF 能定投吗?", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 109, "family_id": "ETF-010", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-011", "collection": "fin_product_collection", "title": "场内基金产品手册 · 五、常见问答 · 5.2 场内基金多久成交、多久到账?", "content": "### 5.2 场内基金多久成交、多久到账?\n\n问:买入后多久成交?\n\n答:本平台为模拟交易,委托按市价**即时全额成交**,成交后持仓与资金变动实时更新。", "chapter": "五、常见问答", "section": "5.2 场内基金多久成交、多久到账?", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 78, "family_id": "ETF-011", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-012", "collection": "fin_product_collection", "title": "场内基金产品手册 · 五、常见问答 · 5.3 产品的涨跌幅在哪里看?", "content": "### 5.3 产品的涨跌幅在哪里看?\n\n问:在哪里看基金今天的涨跌?\n\n答:产品列表页与详情页展示最新净值与当日涨跌;产品详情页另有历史净值走势图。", "chapter": "五、常见问答", "section": "5.3 产品的涨跌幅在哪里看?", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 75, "family_id": "ETF-012", "param_class": "none", "intent": "product_inquiry"} +{"doc_id": "ETF-013", "collection": "fin_product_collection", "title": "场内基金产品手册 · 五、常见问答 · 5.4 起投金额是固定的吗?", "content": "### 5.4 起投金额是固定的吗?\n\n问:起投金额是多少?会变吗?\n\n答:场内基金按\"手\"交易(1 手 = 100 份),\n最小交易金额以产品详情页披露的字段为准(登记为 100.00 元);\n实际所需金额随市价变动,请以交易页面的实时报价为准。", "chapter": "五、常见问答", "section": "5.4 起投金额是固定的吗?", "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", "doc_no": "JR-ETF-2026-001", "version": "V1.0", "effective_date": "2026-09-11", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "product/场内基金产品手册(知识库入库版).md", "visibility": "public", "chars": 124, "family_id": "ETF-013", "param_class": "threshold", "intent": "product_inquiry"} {"doc_id": "FAQ-0001", "collection": "fin_faq_collection", "title": "南方基金的全称和简称是什么?", "content": "问:南方基金的全称和简称是什么?\n答:全称「南方基金管理股份有限公司」,简称「南方基金」,英文名称 China Southern Asset Management Co., Ltd.,统一社会信用代码 91440300279533137K。", "chapter": "高频问答", "section": "南方基金的全称和简称是什么?", "tags": "高频问答,FAQ", "doc_no": "", "version": "", "effective_date": "", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "faq/高频问答对.txt", "visibility": "public", "chars": 120, "family_id": "FAQ-0001", "param_class": "none", "intent": "faq"} {"doc_id": "FAQ-0002", "collection": "fin_faq_collection", "title": "南方基金是什么时候成立的?", "content": "问:南方基金是什么时候成立的?\n答:公司于 1998 年 3 月 6 日在广东省深圳市注册成立,经中国证监会批准设立(批准文号:证监基字〔1998〕4 号),是国内首批规范基金管理公司之一,至今已稳健运营 28 年。法定代表人周易。", "chapter": "高频问答", "section": "南方基金是什么时候成立的?", "tags": "高频问答,FAQ", "doc_no": "", "version": "", "effective_date": "", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "faq/高频问答对.txt", "visibility": "public", "chars": 116, "family_id": "FAQ-0002", "param_class": "none", "intent": "faq"} {"doc_id": "FAQ-0003", "collection": "fin_faq_collection", "title": "南方基金的注册资本是多少?", "content": "问:南方基金的注册资本是多少?\n答:注册资本人民币 36,172 万元(3.6172 亿元)。2019 年 7 月 30 日经核准通过员工持股计划及股东增资,注册资本由 3 亿元增至 3.6172 亿元。", "chapter": "高频问答", "section": "南方基金的注册资本是多少?", "tags": "高频问答,FAQ", "doc_no": "", "version": "", "effective_date": "", "expire_date": "", "source_url": "", "reviewer": "", "source_file": "faq/高频问答对.txt", "visibility": "public", "chars": 102, "family_id": "FAQ-0003", "param_class": "threshold", "intent": "faq"} diff --git a/knowledge/product/场内基金产品手册(知识库入库版).md b/knowledge/product/场内基金产品手册(知识库入库版).md new file mode 100644 index 0000000..ed54266 --- /dev/null +++ b/knowledge/product/场内基金产品手册(知识库入库版).md @@ -0,0 +1,189 @@ +# 场内基金产品手册 + +> 本手册面向客户,说明本平台场内基金(ETF / LOF)的公开交易规则与产品资料。 +> 手册内容均为公开信息,不含任何个人账户数据。 + +## 一、交易规则 + +### 1.1 场内基金怎么买、最小买多少 + +问:场内基金怎么买?最小买多少?一次能买多少份? + +答:本平台的场内基金按"手"撮合,**1 手 = 100 份**,委托数量必须是 100 份的整数倍。 +最小交易金额以产品详情页披露的字段为准(平台产品库中登记为 100.00 元); +实际所需金额按成交价 × 份数计算,随市价变动。 +实际可买数量与所需金额请以交易页面的实时报价为准。 + +### 1.2 场内基金的费用有哪些 + +问:买场内基金要交哪些费用?费率是多少?管理费多少?托管费多少? + +答:场内基金涉及两类费用。 + +**(1)基金自身的费用**,从基金资产中按日计提、体现在净值里,不向您单独收取: +本平台 20 只场内基金的管理费率为 **0.15%~1.20%/年**、托管费率为 **0.05%~0.20%/年**。 +其中 ETF 通常较低(如沪深300ETF 管理费 0.15%/年、托管费 0.05%/年), +LOF 与主动管理型较高(如南方积极配置混合 1.20%/年、托管费 0.20%/年)。 +货币 ETF 另收销售服务费(如货币ETF南方 0.25%/年)。各产品的具体费率见下方费率一览。 + +**(2)交易费用**,即您买卖时产生的**券商佣金**,费率由您开户的券商决定, +本平台不代收、也不设定该费率。 + +### 1.3 报价的最小变动单位 + +问:场内基金报价最小变动多少? + +答:本平台场内基金的最小价格变动单位为 **0.001 元**。 + +### 1.4 风险等级与适当性 + +问:我能买什么风险等级的产品?风险等级不匹配能买吗? + +答:产品风险等级从 **R1 到 R5**,由低到高。平台按您的**有效风险测评结果**做匹配校验: +测评等级与产品等级不匹配时,下单会被拦截。 +请先完成风险承受能力测评,并以交易页面的校验结果为准。 +客服不能修改您的测评结果,也不能代为开通跨级权限。 + +### 1.5 场内基金与场外基金的区别 + +问:场内基金和场外基金有什么不一样? + +答:场内基金(ETF / LOF)在交易所挂牌,按市价、以"手"为单位实时撮合成交; +场外基金按当日净值申赎,通常 T+1 确认。 +本平台的模拟交易针对**场内**基金,下单即按市价全额成交。 + +### 1.6 购买基金需要什么条件 + +问:购买基金需要什么条件?买基金有什么门槛?需要什么资质?有什么要求? + +答:在本平台购买场内基金,需要满足以下条件: + +**(1)账户已完成开户**。未开户的账户下单会被拦截,请先完成开户。 +**(2)已完成风险承受能力测评**,且测评等级与所购产品的风险等级匹配 +(产品风险等级 R1~R5 由低到高,测评结果不匹配时下单会被拦截,详见 1.4)。 +**(3)委托数量为 100 份的整数倍**(1 手 = 100 份),并按市价准备足额资金。 + +以上三项均以交易页面的实时校验结果为准。开户流程与所需材料请咨询客服热线; +客服不能代为开户、修改风险测评结果,也不能开通跨级购买权限。 + +## 二、产品清单 + +问:平台上有哪些场内基金?产品代码是什么?有哪些 ETF? + +答:本平台场内基金共 **20 只**,包含 13 只 ETF 与 7 只 LOF。 +下表净值为最新披露值,会随行情变动: + +> ⚠️ **`511810 货币ETF南方` 的净值口径有专门说明(2026-09-14 补)**: +> 本书写 `0.2661`(2026-09-11 的真实 `DWJZ`),**草稿版 `docs/42-场内基金知识条目草稿.md` +> 用的是 `0.2332`(2026-09-13 的值)**——两个都是真实值,只是**日期不同**。 +> 选哪个取决于入库时点;**两处不一致是已知的、有意的,不是错误**。若要以本书为入库源, +> 请先确认库里 `fin_product` 的 `current_nav` 与 `product_snapshot` 是哪一天的口径。 +> 详细查证过程见 `docs/42` 第 220-245 行(含 `DWJZ` vs `LJJZ` vs 场内交易价的区别)。 + +| 代码 | 名称 | 交易所 | 类型 | 风险等级 | 净值 | 净值日期 | +|---|---|---|---|---|---|---| +| 159329 | 沙特ETF南方 | 深交所 | ETF | R5 | 0.9167 | 2026-09-10 | +| 159382 | 创业板人工智能ETF南方 | 深交所 | ETF | R4 | 2.5841 | 2026-09-11 | +| 159511 | 通信ETF南方 | 深交所 | ETF | R4 | 2.3755 | 2026-09-11 | +| 159615 | 恒生生物科技ETF南方 | 深交所 | ETF | R5 | 1.1027 | 2026-09-11 | +| 159687 | 亚太精选ETF南方 | 深交所 | ETF | R5 | 1.8891 | 2026-09-10 | +| 159700 | 科创债ETF南方 | 深交所 | ETF | R2 | 101.8009 | 2026-09-11 | +| 159948 | 创业板ETF南方 | 深交所 | ETF | R4 | 3.6895 | 2026-09-11 | +| 510300 | 沪深300ETF | 上交所 | ETF | R3 | 4.5794 | 2026-09-11 | +| 510500 | 中证500ETF南方 | 上交所 | ETF | R4 | 7.6027 | 2026-09-11 | +| 511070 | 公司债ETF南方 | 上交所 | ETF | R2 | 103.0145 | 2026-09-11 | +| 511810 | 货币ETF南方 | 上交所 | ETF | R1 | 0.2661 ⚠️ | 2026-09-11 | +| 515450 | 红利低波50ETF南方 | 上交所 | ETF | R3 | 1.4027 | 2026-09-11 | +| 588890 | 科创芯片ETF南方 | 上交所 | ETF | R4 | 1.2327 | 2026-09-11 | +| 160105 | 南方积极配置混合(LOF) | 深交所 | LOF | R3 | 1.2514 | 2026-09-11 | +| 160127 | 南方新兴消费增长股票(LOF)A | 深交所 | LOF | R4 | 0.8364 | 2026-09-11 | +| 160128 | 南方金利定开债券A | 深交所 | LOF | R2 | 1.0260 | 2026-09-11 | +| 160142 | 南方优势产业(LOF) | 深交所 | LOF | R3 | 1.0623 | 2026-09-11 | +| 160143 | 南方创业板2年定期开放混合 | 深交所 | LOF | R3 | 1.6105 | 2026-09-11 | +| 501018 | 南方原油A | 上交所 | LOF | R5 | 2.0750 | 2026-09-10 | +| 501062 | 南方瑞合定开混合(LOF) | 上交所 | LOF | R3 | 2.1048 | 2026-09-11 | + +宽基与债券类 ETF 的风险等级多为 R2~R3,行业主题与跨境类 ETF 多为 R4~R5。 +完整清单与实时净值请以产品列表页为准。 + +## 三、重点产品 + +### 3.1 沪深300ETF(510300) + +问:沪深300ETF 的起投金额是多少?南方沪深300ETF 怎么买?510300 的费率是多少? + +答:沪深300ETF 的产品代码为 **510300**,在**上交所**挂牌,属于 ETF,风险等级 **R3**。 +它按"手"交易,**1 手 = 100 份**;最小交易金额以产品详情页披露的字段为准(登记为 100.00 元)。 +该产品最新净值为 **4.5794 元**(2026-09-11), +管理费率 **0.15%/年**、托管费率 **0.05%/年**,从基金资产中计提、不单独向您收取。 +实际成交金额、可买数量与费用请以交易页面的实时报价为准。 + +**产品披露**:本产品为同指数参考产品,非本公司发行的基金,在本平台仅用于功能演示。 + +### 3.2 其他产品的查询 + +问:某只产品的风险等级和费率是多少?怎么查? + +答:每只产品的代码、名称、交易所、类型、风险等级、最新净值见上方产品清单, +管理费与托管费见下方费率一览;也可以在产品列表页搜索代码或名称后进入详情页查看。 + +## 四、费率一览 + +问:各家基金的管理费和托管费分别是多少? + +答:本平台 20 只场内基金的费率如下(单位:%/年,均为从基金资产中计提、不单独向客户收取): + +| 代码 | 名称 | 管理费率(%/年) | 托管费率(%/年) | 销售服务费率(%/年) | +|---|---|---|---|---| +| 159329 | 沙特ETF南方 | 0.50 | 0.10 | — | +| 159382 | 创业板人工智能ETF南方 | 0.50 | 0.10 | — | +| 159511 | 通信ETF南方 | 0.50 | 0.10 | — | +| 159615 | 恒生生物科技ETF南方 | 0.50 | 0.15 | — | +| 159687 | 亚太精选ETF南方 | 0.20 | 0.05 | — | +| 159700 | 科创债ETF南方 | 0.15 | 0.05 | — | +| 159948 | 创业板ETF南方 | 0.15 | 0.05 | — | +| 510300 | 沪深300ETF | 0.15 | 0.05 | — | +| 510500 | 中证500ETF南方 | 0.15 | 0.05 | — | +| 511070 | 公司债ETF南方 | 0.15 | 0.05 | — | +| 511810 | 货币ETF南方 | 0.30 | 0.05 | 0.25 | +| 515450 | 红利低波50ETF南方 | 0.50 | 0.10 | — | +| 588890 | 科创芯片ETF南方 | 0.50 | 0.10 | — | +| 160105 | 南方积极配置混合(LOF) | 1.20 | 0.20 | — | +| 160127 | 南方新兴消费增长股票(LOF)A | 1.20 | 0.20 | — | +| 160128 | 南方金利定开债券A | 0.50 | 0.15 | — | +| 160142 | 南方优势产业(LOF) | 1.20 | 0.20 | — | +| 160143 | 南方创业板2年定期开放混合 | 1.20 | 0.20 | — | +| 501018 | 南方原油A | 1.00 | 0.20 | — | +| 501062 | 南方瑞合定开混合(LOF) | 1.20 | 0.20 | — | + +"销售服务费率"为"—"表示该产品不收取销售服务费。 +除上表费用外,买卖时产生的券商佣金由您开户的券商决定,本平台不代收。 + +## 五、常见问答 + +### 5.1 ETF 能定投吗? + +问:ETF 能定投吗?可以自动扣款吗? + +答:本平台的场内基金按市价实时撮合成交,**不提供自动定投计划**。 +如需分批买入,可以自行分次下单,每次委托数量为 100 份的整数倍。 + +### 5.2 场内基金多久成交、多久到账? + +问:买入后多久成交? + +答:本平台为模拟交易,委托按市价**即时全额成交**,成交后持仓与资金变动实时更新。 + +### 5.3 产品的涨跌幅在哪里看? + +问:在哪里看基金今天的涨跌? + +答:产品列表页与详情页展示最新净值与当日涨跌;产品详情页另有历史净值走势图。 + +### 5.4 起投金额是固定的吗? + +问:起投金额是多少?会变吗? + +答:场内基金按"手"交易(1 手 = 100 份), +最小交易金额以产品详情页披露的字段为准(登记为 100.00 元); +实际所需金额随市价变动,请以交易页面的实时报价为准。 diff --git a/tests/unit/service/test_customer_service_agent.py b/tests/unit/service/test_customer_service_agent.py index 95ffd07..56997f3 100644 --- a/tests/unit/service/test_customer_service_agent.py +++ b/tests/unit/service/test_customer_service_agent.py @@ -2268,6 +2268,61 @@ def test_product_names_covers_both_corpus_shapes() -> None: assert agent._product_names("南方基金是什么公司") == set() +#: `docs/43` 场内基金手册的 20 只产品(13 ETF + 7 LOF),逐字取自产品清单。 +#: 这份清单是**语料驱动的守卫**:手册新增一只名字形状不同的产品(例如又出现一种 +#: 没有「南方」字样的写法),这条就会红,而不是等到客户问它时才在检索里暴露。 +ONBOARD_MANUAL_PRODUCT_NAMES = ( + "沙特ETF南方", "创业板人工智能ETF南方", "通信ETF南方", "恒生生物科技ETF南方", + "亚太精选ETF南方", "科创债ETF南方", "创业板ETF南方", "沪深300ETF", + "中证500ETF南方", "公司债ETF南方", "货币ETF南方", "红利低波50ETF南方", + "科创芯片ETF南方", "南方积极配置混合(LOF)", "南方新兴消费增长股票(LOF)A", + "南方金利定开债券A", "南方优势产业(LOF)", "南方创业板2年定期开放混合", + "南方原油A", "南方瑞合定开混合(LOF)", +) + + +def test_product_names_cover_every_onboard_manual_product() -> None: + """`W23`:`docs/43` 入库后,手册里**每一只**产品都要能被认出来。 + + 为什么单立一条:`W21-D3` 的一致性闸门(`_names_other_product`)以 `asked` 是否 + 为空为开关。名字认不出 ⇒ `asked` 是空集 ⇒ 闸门**静默失效**:不会造出假"没找到", + 但客户问这只产品、系统却答另一只时,没有任何东西会拦住它。 + LOF 的三种写法(`混合(LOF)` / `股票(LOF)A` / `定期开放混合`)与唯一没有厂商字样的 + `沪深300ETF`,正是 `W21` 的后缀集收不到的四类。 + """ + agent = build_agent() + missed = [name for name in ONBOARD_MANUAL_PRODUCT_NAMES + if name not in agent._product_names(name)] + assert missed == [] + # 整句问法(含前缀噪声)同样要认出来,不能只在"名字单独出现"时成立。 + assert agent._product_names("我想买南方积极配置混合(LOF)") == {"南方积极配置混合(LOF)"} + assert agent._product_names("南方原油A和南方金利定开债券A哪个好") == { + "南方原油A", + "南方金利定开债券A", + } + + +def test_the_brandless_product_does_not_open_the_gate_on_a_category_question() -> None: + """`W23` 的**反向**守卫:把 `ETF` 放成通用后缀会造出假"没找到"。 + + `买ETF还是买LOF` 里被吞出来的「买ETF」不是产品名;一旦它进了 `asked`, + 命中块里当然找不到这个名字 ⇒ 客户问一个**正常的品类比较题**却被告知"没有公开资料"。 + 所以品牌缺省的写法只能用枚举(`_BRANDLESS_PRODUCT_NAMES`),不能用规律。 + """ + agent = build_agent() + assert agent._product_names("买ETF还是买LOF好") == set() + assert agent._product_names("沪深300ETF和科创债ETF南方哪个好") == { + "沪深300ETF", + "科创债ETF南方", + } + # 假"没找到"的真实形态:留声机式的"前一只 + 后一只"连问。 + # 左起 `沪深300ETF` 的 `ETF` 会被当成第二只的前缀,切掉之后才剩「货币ETF南方」。 + assert agent._product_names("沪深300ETF和货币ETF南方哪个好") == { + "沪深300ETF", + "货币ETF南方", + } + + def test_product_names_survive_leading_noise_in_the_question() -> None: """`W21-D3`:正则没有左边界,**动词 / 连接词会被吞进产品名**,必须剥干净。 diff --git a/tools/build_knowledge_chunks.py b/tools/build_knowledge_chunks.py index dd2f963..40e0047 100644 --- a/tools/build_knowledge_chunks.py +++ b/tools/build_knowledge_chunks.py @@ -79,6 +79,40 @@ SOURCES: dict[str, dict[str, str]] = { "version": "", "effective_date": "", "doc_no": "", "tags": "公司信息,金融牌照,资质", }, + # `W23`:`docs/43-场内基金产品手册(知识库入库版).md`(20 只场内基金:13 ETF + 7 LOF)。 + # + # **这是一笔历史欠账,不是新增需求**:`D2.4` §4.4 的档位映射表早已把它登记为 + # `fin_product_collection` / `public`("表格整表,约 30—50 块"),`D3.4` 的 `B-02`(决 12) + # 与 `D4.1` §B-02 也都写了"新增 `docs/43` 入 `SOURCES`"——**实际从未执行**。 + # 后果(`W21` 实测):`_chunks.jsonl` 里「科创债」0 处、`ETF南方` 0 处,客户问 + # 「科创债ETF南方怎么样」的 top1 是 `南方稳健增利债券 A` 的产品卡(0.6696)——问 A 答 B。 + # + # 两个**按源开启**的开关(默认关,其它源一个字节都不变): + # · `strip_editorial_marks`:本文件在「二、产品清单」里有一段 `>` 引用的**内部编辑说明** + # ("草稿版 `docs/42` 用的是 0.2332…两处不一致是已知的、有意的")——它是写给 + # **入库人**的,不是写给客户的;而 `E3`/`E5b` 会把块正文直接呈现给客户, + # 等于把内部文档名与两个互相矛盾的净值摆到客户面前。表内那个 `⚠️` 就是这段 + # 说明的锚点,引用拿掉后它只剩「0.2661 ⚠️」这种没有解释的符号,一并清掉。 + # **镜像文件与 `docs/43` 逐字一致**,只在切片时丢弃这些标记。 + # · `qualify_table_rows`:本文件的两张表是 **7 列**(产品清单)与 **5 列**(费率一览)。 + # 行级子块默认只取前两列,会产出「二、产品清单:159700 科创债ETF南方」这种**丢掉风险 + # 等级/净值/费率**的碎片——恰恰是客户最常问的字段。开启后按表头逐列串成 + # 「代码 159700 / 名称 科创债ETF南方 / 交易所 深交所 / …」。**不开全局**:现有 8 个源 + # 的表结构与检索表现都已稳定,约 300 行的子块正文变化无法只靠 46 条金标兜住。 + # · `exclude_sections`:**导航型小节不入库**。「3.2 其他产品的查询」的问行是 + # 「某只产品的风险等级和费率是多少?怎么查?」,答案却只是"见上方清单 / 到产品列表页 + # 去查"。这种块对**任何**参数类问句都有很高的字面相似度,却答不出任何参数 —— + # 实测它抢走了金标 `B-06`(「举个例子说明费率怎么查」)的 top1(0.7001,把 + # `PROD-018` 的「6.3 费用计算示例」挤到第二)。**「怎么查」不是知识**: + # 客户问参数时要的是参数,不是"去哪里看"。 + "product/场内基金产品手册(知识库入库版).md": { + "collection": "fin_product_collection", "prefix": "ETF", "visibility": "public", + "version": "V1.0", "effective_date": "2026-09-11", "doc_no": "JR-ETF-2026-001", + "tags": "场内基金,ETF,LOF,交易规则,费率,风险等级,起投", + "strip_editorial_marks": True, + "qualify_table_rows": True, + "exclude_sections": ["3.2 其他产品的查询"], + }, } @@ -191,7 +225,9 @@ TABLE_ROW = re.compile(r"^\|(.+)\|\s*$") LEADING_NUMBER = re.compile(r"^\d+(?:\.\d+)*\s*") -def expand_table_rows(chunk: dict[str, str], parent_id: str) -> list[dict[str, object]]: +def expand_table_rows( + chunk: dict[str, str], parent_id: str, *, qualify: bool = False +) -> list[dict[str, object]]: """把 Markdown 表格的每一行拆成自解释的小块(父块照旧保留)。 为什么需要:现在的粒度是"一个叶子标题 = 一块",产品手册里就是**整个产品小节** @@ -214,24 +250,79 @@ def expand_table_rows(chunk: dict[str, str], parent_id: str) -> list[dict[str, o 修法:markdown 表格的表头**只可能是紧邻分隔行 `|---|---|` 之前的那一行**,所以按分隔行 认表头,用 `prev` 延迟一行判断。修后块数 636 → 617,正文完全相同的组从 1 组 15 块降到 0。 + + ## 2026-09-21 补的 `qualify`(按源开启,默认关) + + 上面那套只在**两列**表上成立:`emit` 只取 `cells[0] / cells[1]`。 + `docs/43` 的产品清单是 **7 列**(代码/名称/交易所/类型/风险等级/净值/净值日期)、 + 费率一览是 **5 列**,于是行级子块会退化成「二、产品清单:159700 科创债ETF南方」—— + **风险等级、净值、费率全被丢掉**,而这恰恰是客户问单只产品时最想要的三样。 + 开启后按表头逐列串起来:「…:代码 159700 / 名称 科创债ETF南方 / 风险等级 R2 / …」。 + + **为什么按源开启、不做成全局**:现有 8 个源里 3~7 列的表格有 269 行, + 改它们等于一次性重写 300 来块正文,而回归网只有 46 条金标 + 手写单测, + 兜不住这么大面积的内容漂移。**先在新源上证明它更好,再谈是否推广。** + + ## 2026-09-21 第二个坑:行级子块的"标签"必须是**这一行讲的是谁** + + 上游 `_prefer_section` 靠子块 `title` 的末段判断"客户问的是不是这一行": + 重合就保留聚焦答案,不重合就把整节块换上来(`PROD` 的两列表格里,标签是 + **字段名**「起投金额」)。而多列表格的**第 0 列是标识**(`159700`), + 拿它当标签时 `159700[:2]` = `15` 永远不重合 ⇒ **每一问都被换成整节块**: + 实测「科创债ETF南方怎么样」返回的是**20 只产品的整张表**(真 HTTP + 进程内双重复现)。 + 修法:`qualify` 模式下标签改取**「名称」列**(本手册两张表都有这一列), + 没有该列则退回第 0 列 —— 标签语义统一为"这一行讲的是谁/哪个字段"。 """ blocks: list[dict[str, object]] = [] name = LEADING_NUMBER.sub("", chunk["section"]).strip() or chunk["section"] - def emit(cells: list[str]) -> None: + def cell_text(cell: str) -> str: + """单元格文本:去掉 `⚠️` 这类**编辑标记**(同 `strip_blockquote_lines` 的理由), + 并把「—」归一成「无」——费率表里的「—」表示"该产品不收这项费用", + 原样带进正文只会让模型与客户都读不准。""" + cleaned = cell.replace("⚠️", "").replace("⚠", "").strip() + return "无" if cleaned in {"", "—", "-"} else cleaned + + def row_content(cells: list[str], header: list[str] | None) -> str: + """一行 → 自解释正文。`qualify` 关时逐字保持既有行为。""" + if not qualify or header is None or len(header) != len(cells): + return f"{name}:{cells[0]} {cells[1]}" + pairs = [ + f"{column} {cell_text(cell)}" + for column, cell in zip(header, cells, strict=True) + ] + return f"{name}:{' / '.join(pairs)}" + + def subject_of(cells: list[str], header: list[str] | None) -> str: + """行级子块的**标签**:「这一行讲的是谁」——两列表格是字段名,多列表格是主体名。 + + 为什么不能一律用第 0 列:多列表格的第 0 列是**标识**(`代码 159700`), + 用它当标签会让 `_prefer_section` 判不出"客户问的就是这一行", + 于是把整节块换上来 —— 客户问一只产品,拿回 20 只产品的整张表。 + `qualify` 模式下优先取表头里名为「名称」的那一列。 + """ + if qualify and header is not None and len(header) == len(cells): + for column, cell in zip(header, cells, strict=True): + if "名称" in column and cell.strip(): + return cell.strip() + return cells[0] + + def emit(cells: list[str], header: list[str] | None = None) -> None: if len(cells) < 2: return label, value = cells[0], cells[1] if not label or not value: return + subject = subject_of(cells, header) or label blocks.append({ - "title": f"{chunk['title']} · {label}", + "title": f"{chunk['title']} · {subject}", "chapter": chunk["chapter"], - "section": f"{name} · {label}", - "content": f"{name}:{label} {value}", + "section": f"{name} · {subject}", + "content": row_content(cells, header), "parent_id": parent_id, }) + header: list[str] | None = None prev: list[str] | None = None for line in chunk["content"].splitlines(): match = TABLE_ROW.match(line.strip()) @@ -240,13 +331,15 @@ def expand_table_rows(chunk: dict[str, str], parent_id: str) -> list[dict[str, o cells = [cell.strip() for cell in match.group(1).split("|")] if all(set(cell) <= {"-", ":", " "} for cell in cells): # 分隔行 |---|:紧邻它之前的那一行(`prev`)是**表头**,不能当数据行 → 丢弃。 + # `qualify` 要靠它给每一列配字段名,所以这里**记下来**再丢弃。 + header = prev prev = None continue if prev is not None: - emit(prev) # 没被分隔行认领为表头的行 = 数据行 + emit(prev, header) # 没被分隔行认领为表头的行 = 数据行 prev = cells if prev is not None: - emit(prev) # 收尾:最后一行也要处理(没有分隔行收尾的表格) + emit(prev, header) # 收尾:最后一行也要处理(没有分隔行收尾的表格) return blocks @@ -282,6 +375,16 @@ def assert_no_duplicate_contents(records: list[dict[str, object]]) -> None: records: list[dict[str, object]] = [] for relative, config in SOURCES.items(): text = (Path("knowledge") / relative).read_text(encoding="utf-8") + if config.get("strip_editorial_marks"): + # `>` 行 = 写给**入库人**的编辑说明(版本口径、草稿对照等),不是写给客户的。 + # `E3` / `E5b` 会把块正文直接呈现给客户 ⇒ 让它们进语料等于把内部文档名 + # 和「两个净值都对,只是日期不同」这种内部讨论摆到客户面前。 + # `⚠️` 是同一段说明在表格里的锚点,一起清掉(见 SOURCES 里的注释)。 + text = "\n".join( + line.replace("⚠️", "").replace("⚠", "") + for line in text.splitlines() + if not line.lstrip().startswith(">") + ) chunks = chunk_markdown(text) # 章节白名单:只保留指定章下的块(用于剔除内部管理章节,如高净值规范只留分级与权益) allowed = config.get("allow_chapters") @@ -290,6 +393,12 @@ for relative, config in SOURCES.items(): chunk for chunk in chunks if any(chunk["chapter"].startswith(prefix) for prefix in allowed) ] + # 小节级剔除:与 `allow_chapters` 同一套"先过滤、后编号"的口径 —— + # ⚠️ 过滤集合一变,其后所有父块的编号会平移(`ETF-010` → `ETF-009`), + # 引用 doc_id 的地方(灌库自检、文档)必须跟着改。 + excluded = config.get("exclude_sections") + if isinstance(excluded, list): + chunks = [chunk for chunk in chunks if chunk["section"] not in excluded] for order, chunk in enumerate(chunks, 1): parent_id = f"{config['prefix']}-{order:03d}" records.append({ @@ -310,7 +419,12 @@ for relative, config in SOURCES.items(): }) # 行级子块:挂在父块 doc_id 下(PROD-007-01 这种),父块编号不受新增子块影响, # 因此反复重跑本脚本得到的 doc_id 是稳定的。 - for row_order, block in enumerate(expand_table_rows(chunk, parent_id), 1): + for row_order, block in enumerate( + expand_table_rows( + chunk, parent_id, qualify=bool(config.get("qualify_table_rows")) + ), + 1, + ): records.append({ "doc_id": f"{parent_id}-{row_order:02d}", "collection": config["collection"], diff --git a/tools/load_knowledge_milvus.py b/tools/load_knowledge_milvus.py index d48bc7f..e4667df 100644 --- a/tools/load_knowledge_milvus.py +++ b/tools/load_knowledge_milvus.py @@ -49,6 +49,12 @@ EMBED_MODEL = "text-embedding-v3" EMBED_KEY = os.environ.get("QWEN_API_KEY", "") DIM = VECTOR_DIM BATCH = 10 +#: DashScope 的 `text-embedding-v3` **单次请求最多 10 条**(实测 12 条即 400 +#: `InvalidParameter`)。原先只有"灌库"那条路径按 `BATCH` 分批, +#: "检索自检"是把**全部问句一次性**丢过去 —— 于是 `CHECKS` 一超过 10 条, +#: 灌库会在**数据已经写完之后**崩在自检上(2026-09-21 实测:`W23` 加到 14 条即触发; +#: 崩溃点与写入点分离,最容易误判成"灌库失败")。分批下沉进 `embed()`,两个调用点都安全。 +MAX_BATCH = 10 #: 档位取值域。写入侧**只接受**这两个值;空值 / 未知值一律中止灌库(fail-closed)。 VISIBILITY_VALUES = frozenset({"public", "registered"}) @@ -75,25 +81,41 @@ CHECKS = [ # `乙-7` 第 4 集合(金融行业基础信息):这三条**只有**新集合能答, # 用来证明第 4 集合确实灌进去了、也确实进了检索面。 ("基金定投是什么", "BAS-TRD-012"), - ("场内基金和场外基金有什么区别", "BAS-CON-006"), + # ⚠️ `W23` 语料变更:期望值由 `BAS-CON-006` **改为 `ETF-005`**(0.861,`BAS-CON-006` + # 退到 #2 的 0.789)。**这是更好的结果**:`ETF-005` 的标题「场内基金与场外基金的区别」 + # 与问句几乎逐字对应,且答的是**本平台**口径(含"模拟交易下单即按市价全额成交"); + # 行业通用那条只讲定义。改法与 FAQ-0065 那次同源:**期望值是语料版本的函数**。 + ("场内基金和场外基金有什么区别", "ETF-005"), # 期望值是 FAQ-0065(`W6` 补的专条「什么是T日、T+1?」)而不是第 4 集合 —— # 实测 FAQ-0065 得 0.703 胜出,这是**更好**的结果:专条比通用常识更贴题。 # 第 4 集合是「FAQ 答不了时的兜底」,不是"抢答 FAQ 能答的题"。 ("T+1 是什么意思", "FAQ-0065"), + # `W23` 新源 `docs/43`(场内基金手册,20 只产品)的**存在性证明**:这四条只有它答得上。 + # 入库前实测「科创债」在 `_chunks.jsonl` 里 0 处、`ETF南方` 0 处,客户问 + # 「科创债ETF南方怎么样」拿到的是**另一只产品**(`南方稳健增利债券 A` 的产品卡,0.6696) + # ——问 A 答 B。这四条一起覆盖「产品参数 / 交易规则 / 费率 / 常见问答」四类内容。 + ("科创债ETF南方怎么样", "ETF-007-06"), + ("科创债ETF南方的风险等级是多少", "ETF-007-06"), + ("科创债ETF南方的管理费率是多少", "ETF-009-06"), + ("场内基金报价的最小变动单位是多少", "ETF-003"), + ("ETF 能定投吗", "ETF-010"), ] async def embed(texts: list[str]) -> list[list[float]]: + """按 `MAX_BATCH` 分批取向量;**顺序与入参一一对应**(每批内按 index 归位)。""" + vectors: list[list[float]] = [] async with httpx.AsyncClient(timeout=90) as client: - response = await client.post( - f"{EMBED_BASE}/embeddings", - headers={"Authorization": f"Bearer {EMBED_KEY}"}, - json={"model": EMBED_MODEL, "input": texts}, - ) - response.raise_for_status() - payload = response.json() - items = sorted(payload["data"], key=lambda item: item["index"]) - return [item["embedding"] for item in items] + for start in range(0, len(texts), MAX_BATCH): + response = await client.post( + f"{EMBED_BASE}/embeddings", + headers={"Authorization": f"Bearer {EMBED_KEY}"}, + json={"model": EMBED_MODEL, "input": texts[start:start + MAX_BATCH]}, + ) + response.raise_for_status() + items = sorted(response.json()["data"], key=lambda item: item["index"]) + vectors.extend(item["embedding"] for item in items) + return vectors async def main() -> None: diff --git a/客服agent/D2.1-客服Agent执行Todolist.md b/客服agent/D2.1-客服Agent执行Todolist.md index 2a7e772..53c9371 100644 --- a/客服agent/D2.1-客服Agent执行Todolist.md +++ b/客服agent/D2.1-客服Agent执行Todolist.md @@ -1,4 +1,4 @@ -# 客服 Agent 执行 Todolist(执行看板 · v6.38) +# 客服 Agent 执行 Todolist(执行看板 · v6.39) > **体系编号**:`D2.1` · 域:二、对外交付 · 编号体系见 `D1.1` §4.0 @@ -47,6 +47,27 @@ **看板状态更新**:`F-3` → **✅ 已落地**(新增 4 条单测)。批次 H 剩余:`H-05`。新增待办:**三项安全路由缺口收口**(`G-01` 优先,建议排在 `H-05` 前)。 +## v6.39 本轮修订要点(2026-09-21 · `W24`:**`docs/43` 场内基金手册正式入库** —— 20 只场内基金进库 + 四集合重建重灌 + `B-02` 展示候选集收口) + +> **本轮决议**(用户 2026-09-21:「**按照你建议的来**」「**先把语料修复吧 在进行下一步 不单独立项了**」⇒ 批准把 `docs/43` 纳入 `SOURCES` 并重建重灌)。完整会话记录见 `D1.6` §11;证据 `_eval_harness/result_w24d.json` + `score_w24d.json`、`result_w24e.json` + `score_w24e.json`(两次独立复跑)、`_w24_http.txt`(真 HTTP)。 +> **性质**:**补历史欠账**,不是新增需求 —— `D3.4` 的 `B-02`(决 12)与 `D4.1` §B-02 早已写明「新增 `docs/43`+`docs/45` 入 `SOURCES`」,**从未执行**(`W21` 首次发现并登记于 `D1.6` §10.4)。 + +| # | 修订 | 依据 | +|---|---|---| +| 1 | ✅ **`docs/43-场内基金产品手册(知识库入库版).md` 正式纳入 `SOURCES`**(`prefix=ETF`、`collection=fin_product_collection`、`visibility=public`、`version=V1.0`、`effective_date=2026-09-11`、`doc_no=JR-ETF-2026-001`);切片件 **702 → 755 块**(policy 288 / product 251 / faq 154 / basic 62),Milvus 四集合按新 schema **drop → 重建 → 重灌** | 本步实测 | +| 2 | ✅ **新增三个「按源开启」的切片开关**(默认关 ⇒ 其它 8 个源**零字节变化**,实测 `changed existing blocks: 0`):`strip_editorial_marks`(丢 `>` 行 + 清 `⚠️`,该手册「二、产品清单」里有一段**内部编辑说明**,指向 `docs/42` 与两个互相矛盾的净值,**不能给客户看**)、`qualify_table_rows`(7 列 / 5 列表格按表头**逐列串成自解释正文**;默认只取前两列会丢风险等级 / 净值 / 费率)、`exclude_sections`(导航型小节「3.2 其他产品的查询」不入库 —— 它抢走了 `B-06` 的 top1) | 本步实测 | +| 3 | 🔴 **切片器真实缺陷(`W24` 新发现并修复)**:**行级子块的标签必须是「这一行讲的是谁」**。上游 `_prefer_section` 用 `title` 末段与问句比对,而多列表格的**第 0 列是代码**(`159700[:2]` = `15`,永不重合)⇒ 每一问都被换成**整节块**:实测「科创债ETF南方怎么样」返回 **20 只产品的整张表**(真 HTTP + 进程内双重复现)。`qualify` 模式改取表头里含「名称」的那一列 | `_w24_*` 双重复现 | +| 4 | 🔴 **`text-embedding-v3` 单请求上限 10 条(潜伏 bug)**:`tools/load_knowledge_milvus.py` 的 `embed()` 原来只有**灌库路径**分批,**自检路径一次性发全部问句** ⇒ `CHECKS` 一旦超过 10 条就在**数据已写完之后**崩(本次 14 条即触发)。已把分批下沉进 `embed()`(常量 `MAX_BATCH = 10`) | 本步实测 | +| 5 | ✅ **语料口径更正**:费率表表头补「(%/年)」—— 否则单行答复只回「0.15」,**丢了单位** | 本步实测 | +| 6 | ✅ **20/20 产品名全部识别**:`_PRODUCT_NAME_SHAPE` 后缀集补 `定期开放混合\|股票\(LOF\)[ABC]?\|\(LOF\)\|原油[ABC]?`;唯一**没有厂商字样**的产品 `沪深300ETF` 用**枚举**单独认(不能把 `ETF` 放成通用后缀:`买ETF还是买LOF` 会被吞出假产品名 ⇒ **造出假"没找到"**);`_strip_product_name_lead` 增加「**先切掉前一只基金**」(修「沪深300ETF和货币ETF南方哪个好」匹配出 `300ETF和货币ETF南方`) | 20 只逐只验证 + 4 条负例全空集 | +| 7 | 🔴 **`B-02` 的 `M-4` 回归(本轮唯一收尾项)**:`_exit_partial` 的两条调用路径**候选集不同** —— 主路径传 **TopK 全量**、证据路径传**被 `E4_MAX_EVIDENCE` 截断的证据包**(且补位时 top1 还会挤掉末位)⇒ **同一句问句的答复质量取决于「`E4` 这次有没有调用模型」**。新语料把 `ETF-009` 插进 TopK,把 `PROD-016` 从第 9 名挤到第 10 名,"在包内"变"包外" ⇒ 只答一半(单行「赎回费率」)。修法:`_answer_from_evidence` 新增 `display_hits` 形参,**展示候选一律用 TopK 全量**;**送模型的证据包仍保持 6 块不变**(那是模型注意力问题,不是展示问题) | `_eval_harness` `w24c → w24d` | +| 8 | 📌 **三条期望值登记修订(判据变更,必须知情)**:① `B-01`(访客)`expected_evidence` 补 `"ETF"` → `[PROD, FAQ, ETF]`(`ETF-013` 0.7954 合法拿到 top1,答案更完整);② `B-05`(客户,**同一句问句**)`expected_exits` `[E3]` → `[E3, E4]`(与 top1 只差 0.033 < 0.07 ⇒ 并列走生成式作答);③ `load_knowledge_milvus` 自检 `("场内基金和场外基金有什么区别", "BAS-CON-006")` → **`"ETF-005"`**(0.861 vs 0.789,更贴题)。**「零回归」的说法不适用** | 见 `D4.8` §10.3 | +| 9 | ✅ **`docs/45`(R1—R5 问答)裁定不入库**:与 `FAQ-0018` / `POL-AST-011/012` 高度重复,单独成块会**污染 top1** | 本步裁定 | + +**验收(可复算)**:金标 `M-1 46/46`、`M-4 46/46`、`M-6 5/46`、`M-7/M-8/M-9/M-10 = 0`、`M-2 28/31`、`M-2b 15/18`、`M-3 4/4` —— **与 `w23` 基线逐项一致**,且 `w24d` / `w24e` **两次独立复跑结果相同**(本仓库有"只跑一次就宣布修好是假绿"的历史教训);全量 **`1996 passed / 3 skipped`**(上一轮 `1994`,**+2 条新守卫**);`ruff` **零新增**;真 HTTP 11 条全绿(「科创债ETF南方怎么样」→ 单只产品;「…风险等级」→ R2;「…管理费率」→ 0.15+0.05;「场内基金有哪些」→ 20 只清单;访客侧同答、无档位越权)。 + +**改动文件**:`tools/build_knowledge_chunks.py`(新增源 + 三开关 + 行标签修复)、`tools/load_knowledge_milvus.py`(`embed()` 分批 + 自检期望)、`app/service/agent/implementations/customer_service.py`(产品名识别三处 + `display_hits`)、`tests/unit/service/test_customer_service_agent.py`(+2 守卫)、`knowledge/_chunks.jsonl`(702 → 755)、`knowledge/product/场内基金产品手册(知识库入库版).md`(新镜像)、`docs/43-…md`(表头单位)、`_eval_harness/cases_46.json`(两条期望修订)、`开发文档/D4.8`(§10)、`开发文档/D1.6`(§11)、`开发文档/D1.1`、`客服agent/D2.4`、`客服agent/D2.8`、`客服agent/D2.9`、本文件。 + ## v6.38 本轮修订要点(2026-09-21 · `W21` 第二轮:**四项待决按建议全部落地** —— `D1` 生成侧禁令 + `D2` 指代依据 + `D3` 答非所问闸门 + `D4` 作答语气) > **本轮决议**(用户 2026-09-21:「**按照你建议的改**」⇒ 批准 `D4.8` §6 四项)。上一轮(`W21` 首轮)已修复 `C-8`/`C-9`/`C-10` 并推送 `dd5e9f8`;本轮为**其待决项的落地轮**。 diff --git a/客服agent/D2.4-客服Agent知识库设计方案.html b/客服agent/D2.4-客服Agent知识库设计方案.html index 6c3dbe7..b2e432f 100644 --- a/客服agent/D2.4-客服Agent知识库设计方案.html +++ b/客服agent/D2.4-客服Agent知识库设计方案.html @@ -494,6 +494,7 @@ hr { border: none; height: 1px; background: linear-gradient(90deg, transparent, v1.52026-09-18档位隔离改造 + 三派生字段落地 + 计划前提补正① 档位隔离改造(会签批准):检索签名由布尔 include_internal 改为必填 tiers: frozenset[str],visitor → {public}、customer → {public, registered}、其余角色收敛为 {public};客户档双向验证通过——「高净值客户有什么权益」访客 top1 FAQ-0024 0.5486(引导登录)、客户 top1 HNW-006 0.7604;缺 visibility 字段的集合在受限档位下 fail-closed 跳过(根治 K-07)。② family_id / param_class / intent 已补切片并重建重灌:实库 18 字段(17 VARCHAR + 1 FLOAT_VECTOR),三集合 149 / 288 / 191,自检 7/7,无字段截断;派生分布 param_class = none 453 / rate 67 / threshold 66 / scale 38 / count 4,intent = faq 149 / product_inquiry 191 / policy_explain 288,family_id 去重 186 族(48 个多块族)⇒ 附录F 的「同族合并」「计算型参数位」「意图标签」三条能力自此有数据支撑。③ 计划前提补正:客服 Agent 未在 AgentFactory 注册(实测 agent_type=customer_service 返回 404 AGENT_TYPE_NOT_FOUND),客服业务层已于 2026-09-16 整体清除 ⇒ 出口与规则相关任务属从零重建而非改造,详见 D1.6 §4.9。④ 门禁相对 T0 基线 0 回归(pytest 7 failed / 1441 passed / 2 skipped,逐项相同)。 v1.62026-09-20落地回写 · 索引口径与语料口径一律以实库为准:① 索引统一 AUTOINDEX —— 设计初稿的「FAQ→HNSW / 长文档→IVF_FLAT」未落地;实库四集合索引名均为 knowledge_autoindex、类型 AUTOINDEX、度量 COSINE(pending_index_rows = 0、全部 Loaded;2026-09-20 直查 Milvus 实测),据此更正 §4.1 表、§7.2 决策 3 / 决策 6、附录A、附录D。② 语料口径 628 → 675 块:新增 fin_basic_collection(46 块:基金基础知识.md 22 + 基金交易与时限常识.md 24),FAQ 149 → 150;实库现状 policy 288 / product 191 / faq 150 / basic 46,与 knowledge/_chunks.jsonl 逐集合一致。③ 三集合仍是唯一默认检索面 —— fin_basic_collection 为补充语料、不进默认面(实测并入会使金标 M-1 100% → 91.3%),仅在按集合名显式检索时可用。④ 附录F.1「现状」列与 F.6 复测数字按 675 块更新:family_id 675/675;param_class 非 none 186 块(rate 72 / threshold 66 / scale 42 / count 6);平均块长 102.0 字、571 块 < 200 字、最长 2828 字、最短 11 字;档位 public 650 / registered 25。 v1.72026-09-20口径更正:分层体系与门槛属「公开宣传口径」(D-1 裁定 · 选乙)① 实测定因:public 的 FAQ-0014 已完整给出五档门槛(普通 / 金卡 50—200 万 / 白金 200—600 万 / 钻石 600—1000 万 / 专户 1000 万+)、FAQ-0050 含「600 万元以上钻石客户」⇒ 分层体系与门槛本来就是对外宣传口径,不构成 registered 的理由;② 据此更正 §4.4 判据注 与 附录B「v1.3 裁定」:门槛 → 公开口径;各层级权益明细与专属服务内容 → registered(这才是 HNW-004—HNW-007 保持 registered 的依据);③ 同步修掉切片脚本的自相矛盾注释(tools\build_knowledge_chunks.py 原写「不泄露档位与门槛」);④ HNW-* 档位本轮不变——visibility 是分区键,改档位须重建集合,属数据变更、不在本轮范围。 +v1.82026-09-21语料口径 702 → 755 块 · docs/43 场内基金手册入库(`W24`)① docs/43-场内基金产品手册(知识库入库版).md(20 只场内基金:13 只 ETF + 7 只 LOF)正式纳入切片 SOURCES,落 fin_product_collection / public 档 / prefix=ETF,+53 块;② 新增按源开启的三个切片开关(strip_editorial_marks / qualify_table_rows / exclude_sections),默认关 ⇒ 其它 8 个源零字节变化(实测 changed existing blocks: 0);③ 切片器缺陷修复:行级子块标签改取表头「名称」列 —— 原取第 0 列(代码)会让 _prefer_section 判不出重合、把整节块换上来(实测「科创债ETF南方怎么样」返回 20 只产品的整张表);④ 实库现状更新:四集合 policy 288 / product 251 / faq 154 / basic 62 = 755,档位 public 730 / registered 25,family_id 755/755(去重 250 族、55 个多块族),param_class 非 none 219 块(rate 99 / threshold 71 / scale 42 / count 7);⑤ 附录F.6 复测与「跑评测前四个前置」按 755 块同步。
注:docs/45(R1—R5 问答)经裁定不入库(与 FAQ-0018 / POL-AST-011/012 高度重复,单独成块会抢 top1)——历史承诺里的 docs/45 部分据此关闭。 @@ -658,8 +659,8 @@ hr { border: none; height: 1px; background: linear-gradient(90deg, transparent,

划分理由:FAQ 为短文本问答对、百条级、要求精确匹配优先;产品/政策为长文档分块、数百条级、要求语义召回优先。阈值差异是「句面对应 vs 话题相关」的必然结果。

-

⚠️ 索引口径(v1.6 落地更正 · 以实库为准):上表「索引」列原为设计初稿(FAQ → HNSW、长文档 → IVF_FLAT);落地时统一采用 AUTOINDEX(索引名 knowledge_autoindex、类型 AUTOINDEX、度量 COSINE;四集合全部 Loaded、pending_index_rows = 0,2026-09-20 直查 Milvus 实测)。为什么不按初稿差异化:① 三集合规模同处百条量级(288 / 191 / 150),HNSW 与 IVF_FLAT 的收益差在万级向量以下不成立;② AUTOINDEX 由引擎按数据规模选型 —— 免调参、无需手工标定 M / efConstruction / nlist,少一个可配错的旋钮;③ IVF_FLAT 的 nlist 若与数据量不匹配反而伤召回,而召回是本文档的第一优先项。上表其余列(知识类型 / 意图 / 度量 / TopK / 主阈值)仍然有效。

-

集合现状(v1.6 补注):实库为四个集合——上表三集合(fin_faq_collection 150 / fin_product_collection 191 / fin_policy_collection 288)之外,另有 fin_basic_collection(46 块:基金基础知识 22 + 基金交易与时限常识 24)。默认检索面仍只有三集合(PRIMARY_COLLECTIONS);基础集合是补充语料,须按集合名显式检索 —— 并入默认面会使金标通过率下降(2026-09-19 实测:M-1 100% → 91.3%)。

+

⚠️ 索引口径(v1.6 落地更正 · 以实库为准):上表「索引」列原为设计初稿(FAQ → HNSW、长文档 → IVF_FLAT);落地时统一采用 AUTOINDEX(索引名 knowledge_autoindex、类型 AUTOINDEX、度量 COSINE;四集合全部 Loaded、pending_index_rows = 0,2026-09-20 直查 Milvus 实测)。为什么不按初稿差异化:① 三集合规模同处百条量级(288 / 251 / 154),HNSW 与 IVF_FLAT 的收益差在万级向量以下不成立;② AUTOINDEX 由引擎按数据规模选型 —— 免调参、无需手工标定 M / efConstruction / nlist,少一个可配错的旋钮;③ IVF_FLAT 的 nlist 若与数据量不匹配反而伤召回,而召回是本文档的第一优先项。上表其余列(知识类型 / 意图 / 度量 / TopK / 主阈值)仍然有效。

+

集合现状(v1.8 更新 · 以实库实测为准):实库为四个集合,合计 755 块——上表三集合(fin_faq_collection 154 / fin_product_collection 251 / fin_policy_collection 288)之外,另有 fin_basic_collection(62 块:基金基础知识 30 + 基金交易与时限常识 32)。档位分布 public 730 / registered 25。默认检索面仍只有三集合(PRIMARY_COLLECTIONS);基础集合是补充语料,须按集合名显式检索 —— 并入默认面会使金标通过率下降(2026-09-19 实测:M-1 100% → 91.3%)。
(v1.6 旧值 faq 150 / product 191 / basic 46 合计 675 块 —— 已被 2026-09-20 之后的语料修订(docs/42 修复)与 W24 的 docs/43 入库(product +53)取代;此处留旧值备查。)

4.2 可见性三档模型

@@ -745,7 +746,7 @@ hr { border: none; height: 1px; background: linear-gradient(90deg, transparent, 高净值客户服务规范512/64,按章节拆档registered;考核与处罚条款剔除约 30—45 企业信息(两份)512/64public约 65—95 公司新人指南512/64,按章节拆档public;人事与内控条款剔除约 30—45 -场内基金产品手册(入库版)512/64,表格整表public约 30—50 +场内基金产品手册(入库版)512/64,表格整表public预估约 30—50 → 实测 53(v1.8) fin_policy_collection个人投资者适当性管理指南512/64,条款按条切分public约 60—80 反洗钱合规操作手册512/64public约 70—90 理财产品销售管理办法512/64public约 60—80 @@ -1666,7 +1667,7 @@ RAG_QUERY_CACHE_TTL=300 # 秒;缓存键必须含主体标识 规则取值 / 做法实测依据 -块长上限约 350 字(超长须再切)v1.6 复测(2026-09-20 · 675 块 · 含 fin_basic_collection):平均 102.0 字、571 块 < 200 字、最长 2828 字、最短 11 字。(v1.4 复测 · 628 块:平均 101.5 字、528 块 < 200 字;最长块 2828 字(POL-AST-009),其后为 927 / 883 / 801 / 728 字)(旧版 617 块实测:平均 86.7 字、541 块 < 200 字、最长 488 字;超长块 Top1 仅 0.6622,而 272 字同题块达 0.7214 —— 结论不变:超长块须再切) +块长上限约 350 字(超长须再切)v1.8 复测(2026-09-21 · 755 块 · 含 docs/43 场内基金手册):平均 108.7 字、635 块 < 200 字、最长 2838 字、最短 11 字。(v1.6 复测 · 675 块:平均 102.0 字、571 块 < 200 字、最长 2828 字)(v1.4 复测 · 628 块:平均 101.5 字、528 块 < 200 字;最长块 2828 字(POL-AST-009),其后为 927 / 883 / 801 / 728 字)(旧版 617 块实测:平均 86.7 字、541 块 < 200 字、最长 488 字;超长块 Top1 仅 0.6622,而 272 字同题块达 0.7214 —— 结论不变:超长块须再切) 块长下限不建议 < 40 字现有最短块仅 11 字(如「禁止行为(负面清单):1 承诺保本保收益」),单独成块既答不全也易并列 标题FAQ 类块的标题即问句原话「短块 + 标题与问句字面一致」的实测 Top1 达 0.7384(分差 0.0761,可过线) 同族同一问句族合并入 family_id,不拆成并列小块见 F.3 的 0.0075 并列实测 @@ -1677,7 +1678,7 @@ RAG_QUERY_CACHE_TTL=300 # 秒;缓存键必须含主体标识本节新增的全部能力必须用《客服 Agent 评测金标集》D3.7 验收,不得以「目测变好了」结项。启用顺序见 D3.6 §6(S0 前提修复 → S1 澄清 → S2 计算 → S3 生成 → S4 隔离与安全分层 → S5 分级回退 → S6 门禁)。

-

🔴 跑评测前的四个前置(D3.7 §1,缺一不可):① knowledge/_chunks.jsonl 已重新生成(675 块、南方科技 / 南方财富 / 400-XXX 各 0 处;2026-09-20 复核);② FAQ 镜像已补齐(faq/高频问答对.txt 65 块,FAQ 集合合 150 块)并逐条打档位;③ 档位字段已真正进入索引(visibility 为分区键,public 650 / registered 25)——「100% public」的历史问题已不复现;④ 建表脚本已收敛为一套(tools/setup_milvus_knowledge_collections.py 为唯一权威定义,见 §7.2.1 前置 ③)。四项已于 2026-09-18 / 09-20 全部关闭;本段保留原始口径备查。

+

🔴 跑评测前的四个前置(D3.7 §1,缺一不可):① knowledge/_chunks.jsonl 已重新生成(755 块、南方科技 / 南方财富 / 400-XXX 各 0 处;2026-09-21 `W24` 复核);② FAQ 镜像已补齐(faq/高频问答对.txt 69 块 + company/企业信息.md 85 块,FAQ 集合合 154 块)并逐条打档位;③ 档位字段已真正进入索引(visibility 为分区键,public 730 / registered 25)——「100% public」的历史问题已不复现;④ 建表脚本已收敛为一套(tools/setup_milvus_knowledge_collections.py 为唯一权威定义,见 §7.2.1 前置 ③)。四项已于 2026-09-18 / 09-20 / 09-21 全部关闭;本段保留原始口径备查。


diff --git a/客服agent/D2.8-客服Agent知识库RAG全链路与选型说明-2026-09-20.md b/客服agent/D2.8-客服Agent知识库RAG全链路与选型说明-2026-09-20.md index 0d0c530..48171b8 100644 --- a/客服agent/D2.8-客服Agent知识库RAG全链路与选型说明-2026-09-20.md +++ b/客服agent/D2.8-客服Agent知识库RAG全链路与选型说明-2026-09-20.md @@ -228,6 +228,27 @@ assert_no_duplicate_contents(records) > ⚠️ `family_id` 后缀**只认两位数字**:`FAQ-0026` / `COMP-001` 是父块本身(四位/三位),不能被误削成 `FAQ-` / `COMP-`。 +### 3.6 三个「**按源开启**」的切片开关(`W24` 新增 · 默认关) + +**动机**:`docs/43` 场内基金手册有两种别的源都没有的形态 —— 一段**内部编辑说明**(不能给客户看)、两张**多列表格**(只取前两列会丢关键字段)、一个**导航型小节**(会抢 top1)。**改全局行为会波及另外 8 个源** ⇒ 做成**按源开关、默认关**,实测其它源 `changed existing blocks: 0`(零字节变化)。 + +| 开关 | 作用 | 为什么必须有 | +|---|---|---| +| `strip_editorial_marks` | 丢 `>` 引用行 + 清 `⚠️` | 该手册「二、产品清单」里有一段**内部编辑说明**,指向 `docs/42` 与**两个互相矛盾的净值**;给客户看等于自曝内部流程与数据冲突。**注意**:镜像文件与 `docs/43` 保持**逐字一致**,只在切片时丢 | +| `qualify_table_rows` | Markdown 表格按**表头逐列**串成「代码 … / 名称 … / 风险等级 R2 / 净值 … / 管理费率… 」 | 默认的表格行逻辑只取**前两列**(`代码` + `名称`)⇒ **风险等级 / 净值 / 费率全部丢失**。实测「科创债ETF南方怎么样」只能答出代码与名称 | +| `exclude_sections` | 指定小节**不入库** | 「3.2 其他产品的查询」是**导航型**内容("您也可以查询其他产品…"),语义上与几乎任何"查产品"的问句都像 ⇒ **抢走 `B-06` 的 top1** | + +**同一处还修掉一个真实缺陷(`W24-A`)**:行级子块的 `title` 必须是「**这一行讲的是谁**」。 + +上游 `_prefer_section` 用子块 `title` **末段**与问句比对,重合则保留聚焦答案、不重合就把**整节块**换上来。默认逻辑取**第 0 列**当标签,而多列表格的第 0 列是**代码**(`159700`)⇒ `159700[:2]` = `15`,**永不重合** ⇒ **每一问都被换成整节块**:实测「科创债ETF南方怎么样」返回**20 只产品的整张表**(真 HTTP + 进程内**双重**复现)。 + +**修法**:`qualify` 模式下标签改取**表头里含「名称」的那一列**(本手册两张表都有该列),没有该列则退回第 0 列 —— 标签语义统一为「这一行讲的是谁 / 哪个字段」。 + +> 🔑 **这条缺陷的教训值得写进答辩**:"标签取错一列"这种错误**不会报错、不会崩、覆盖率指标也看不出来** —— 只有一个具体问句会出现"答一整张表"的荒谬输出。**多列表格的切片逻辑,必须用"问其中一行"的问句做端到端回归。** + +**另一个潜伏 bug(`W24-B`)**:`text-embedding-v3` **单请求最多 10 条**。原 `tools\load_knowledge_milvus.py` 只在**灌库路径**分批,**自检路径一次性发全部问句** ⇒ 自检条目一超过 10 条就在**数据已写完之后**崩(本次 14 条即触发)。已把分批**下沉进 `embed()`**(`MAX_BATCH = 10`),两条路径同时受益。 + + --- ## 4. 阶段三 · 向量化 @@ -527,57 +548,61 @@ flowchart TD --- -## 11. 实测数据(2026-09-20) +## 11. 实测数据(**2026-09-21 `W24` 复测** · 语料口径 702 → **755 块**) + +> **口径**:本节数字**一律以 `knowledge\_chunks.jsonl` 的实测为准**。2026-09-20 首版(675 块)保留在括号内供追溯;两次差异来自 2026-09-20 之后的语料修订(`docs/42` 修复)与 `W24` 的 `docs/43` 场内基金手册入库(`product` **+53**)。 ### 11.1 切片件 `knowledge\_chunks.jsonl` | 指标 | 值 | |---|---| -| 总块数 | **675** | -| 最长正文 | **2828** 字符 | +| 总块数 | **755**(2026-09-20:675) | +| 最长正文 | **2838** 字符(2026-09-20:2828) | | 最短正文 | **11** 字符 | -| 平均正文 | **102.0** 字符 | -| 子块数(`-NN` 后缀) | **459** | -| `family_id` 覆盖 | **675 / 675(100%)** | -| `param_class` 非 `none` | **186** 块 | +| 平均正文 | **108.7** 字符(2026-09-20:102.0) | +| 子块数(`-NN` 后缀) | **505**(2026-09-20:459) | +| `family_id` 覆盖 | **755 / 755(100%)**,去重 **250 族**(55 个多块族) | +| `param_class` 非 `none` | **219** 块(2026-09-20:186) | ### 11.2 按集合与来源 | 集合 | 块数 | 来源文件 | |---|---|---| | `fin_policy_collection` | **288** | 适当性指南 152 + 销售管理办法 136 | -| `fin_product_collection` | **191** | 产品手册 176 + 高净值规范 15 | -| `fin_faq_collection` | **150** | FAQ 65 + 企业信息 85 | -| `fin_basic_collection` | **46** | 基金基础知识 22 + 交易时限常识 24 | -| **合计** | **675** | | +| `fin_product_collection` | **251** | 产品手册 183 + **场内基金产品手册 53**(`W24` 新增)+ 高净值规范 15 | +| `fin_faq_collection` | **154** | FAQ 69 + 企业信息 85 | +| `fin_basic_collection` | **62** | 基金基础知识 30 + 交易时限常识 32 | +| **合计** | **755** | | ### 11.3 档位分布 | 档位 | 块数 | 构成 | |---|---|---| -| `public` | **650** | —— | +| `public` | **730** | —— | | `registered` | **25** | 高净值规范 **15** + FAQ **10**(`Q17/Q20/Q21/Q27/Q28/Q29/Q30/Q33/Q47/Q53`) | ### 11.4 `param_class` 分布 | 类型 | 块数 | |---|---| -| `none` | 489 | -| `rate`(费率) | **72** | -| `threshold`(门槛/起投) | **66** | +| `none` | 536 | +| `rate`(费率) | **99** | +| `threshold`(门槛/起投) | **71** | | `scale`(规模) | 42 | -| `count`(家数/网点) | 6 | +| `count`(家数/网点) | 7 | ### 11.5 Milvus 实库 | 集合 | `count(*)` | 索引状态 | |---|---|---| -| `fin_faq_collection` | **150** | `Finished` / `pending = 0` | -| `fin_product_collection` | **191** | `Finished` / `pending = 0` | +| `fin_faq_collection` | **154** | `Finished` / `pending = 0` | +| `fin_product_collection` | **251** | `Finished` / `pending = 0` | | `fin_policy_collection` | **288** | `Finished` / `pending = 0` | -| `fin_basic_collection` | **46** | `Finished` / `pending = 0` | +| `fin_basic_collection` | **62** | `Finished` / `pending = 0` | -> ✅ **切片件 675 = 实库 675**,四集合全部索引 `Finished`、无 pending —— 这是"**库和切片件一致**"的证据。 +> ✅ **切片件 755 = 实库 755**,四集合全部索引 `Finished`、无 pending —— 这是"**库和切片件一致**"的证据。 +> +> ⚠️ **块数只信 `knowledge\_chunks.jsonl` / `_chunks_report.txt`**:`upsert` 会让 `get_collection_stats().row_count` **翻倍**(实测 450 vs 251,`doc_id` 仍唯一)。改 `doc_id` 编号后**必须丢集合重建**(`W24` 重编号 `ETF-009`/`ETF-010` 时留过一条陈旧行)。 --- @@ -637,7 +662,7 @@ flowchart TD | `K-01` 集合无 `visibility` | ✅ **已闭环** —— 实库 18 字段含 `visibility` 且为分区键 | | `K-02` 集合级 fail-open | ✅ **已闭环** —— `search()` 对缺字段集合在受限档位下 `barred`(排除) | | `K-06` FAQ 阈值 0.75 偏高 | ⚠️ **未闭环** —— 仍是 0.75 | -| `K-07` 重修须 drop 而非 upsert | ✅ **已闭环** —— 已按 `drop → create → 重灌` 执行,切片件与实库块数一致(675) | +| `K-07` 重修须 drop 而非 upsert | ✅ **已闭环** —— 已按 `drop → create → 重灌` 执行,切片件与实库块数一致(**755**,`W24` 复测);⚠️ **改 `doc_id` 编号后必须丢集合重建**(`upsert` 只保证 `doc_id` 不重复,**不会删除**已改名的旧行) | --- @@ -672,7 +697,7 @@ $py = ".\.venv\Scripts\python.exe" |---|---| | 逐行读码:`build_knowledge_chunks.py` / `knowledge_search_service.py` / `knowledge_tool.py` / `knowledge_schema.py` / `knowledge_contracts.py` / `knowledge_tier.py` / `setup_milvus_knowledge_collections.py` / `knowledge_vector_worker.py` / `customer_service.py`(检索与判定段) | ❌ 本文**未改任何代码** | | **实库实测**:4 个集合 `describe_collection` / `count(*)` / `describe_index` / 分区键标志 | ❌ **未跑金标评测**(§11 的数字是**语料与库的实测**,不是**效果指标**;效果指标见 `D2.6` / `D3.7`) | -| 切片件 `_chunks.jsonl` 全量统计(675 块 / 档位 / 派生字段 / 长度分布) | ❌ **未做** §12.1 / §12.2 / §12.4 三项的**修正**(只登记,不执行) | +| 切片件 `_chunks.jsonl` 全量统计(**755** 块 / 档位 / 派生字段 / 长度分布,`W24` 复测) | ❌ **未做** §12.1 / §12.2 / §12.4 三项的**修正**(只登记,不执行) | | 与 `D2.4` / `D3.2` / `D3.5` 的选型理由对读 | ❌ **未做** 术语归一化 / 多查询扩展 / RRF 融合 / 重排(均为设计里列为"演进项"的部分) | > 🔁 **一处自我更正(本轮如实登记)**:我第一版读到的「实库只有 15 个字段、缺 `family_id`/`param_class`/`intent`」是**错的** —— 原因是把 `tools\probe_knowledge_collections.py` 跑在了**非仓库根目录**,于是脚本把新产物写到了别处,而我读的是仓库里**尚未更新的旧 JSON**。改为直接查 Milvus 后确认:**18 个字段齐全、`visibility` 是真分区键**。**结论以 §5.2 / §11.5 的直接查询为准。** diff --git a/客服agent/D2.9-客服Agent手动对话测试用例-2026-09-20.md b/客服agent/D2.9-客服Agent手动对话测试用例-2026-09-20.md index 871a440..e5c25d6 100644 --- a/客服agent/D2.9-客服Agent手动对话测试用例-2026-09-20.md +++ b/客服agent/D2.9-客服Agent手动对话测试用例-2026-09-20.md @@ -2,7 +2,7 @@ > **体系编号**:`D2.9` · 域:二、客服 Agent 交付件(`客服agent/`) · 编号体系见 `D1.1` §4.0 > -> **编号**:CS-MANUAL-2026-023 | **版本**:v1.2(2026-09-21 `W21-D4` 话术校准) | **日期**:2026-09-20 | **状态**:**现行(活文档)** +> **编号**:CS-MANUAL-2026-023 | **版本**:v1.3(2026-09-21 `W24` 语料口径同步:702 → 755 块) | **日期**:2026-09-20 | **状态**:**现行(活文档)** > **性质**:**动手验收件**。它把《客服 Agent 评测金标集与判分规则》(`D3.7`)的 **46 条金标 + 五出口 + 4 项零容忍**, > 翻译成「**你一句一句问、一眼能判对错**」的手动清单。 > **不是**需求来源、**不是**任务来源;**它不替代** `_eval_harness/`(自动化判分)——两者**同口径**:本文的「基线」列直接取自自动化跑分。 @@ -463,7 +463,7 @@ PY | 46 条金标与判分规则 | `D3.7-客服Agent评测金标集与判分规则-2026-09-17.md`(本文的「期望出口 / 期望要点 / 禁止出现」直接取自其 §2) | | 五出口与安全不变量 | `D3.6-客服Agent智能增强架构建议-2026-09-17.md` §3—§4 | | 演示脚本与账号 | `D2.5-客服Agent演示脚本与账号速查-2026-09-19.md` | -| 知识库档位与索引口径 | `D2.4-客服Agent知识库设计方案.html`(v1.6:索引统一 `AUTOINDEX`、语料 675 块) | +| 知识库档位与索引口径 | `D2.4-客服Agent知识库设计方案.html`(v1.8:索引统一 `AUTOINDEX`、语料 **755 块**、四集合 `policy 288 / product 251 / faq 154 / basic 62`) | | 自动判分输入件 | **`_eval_harness\cases_46.json`**(46 条)、`result_w11b.json`、`score_w11b.json`。⚠️ **路径更正(2026-09-21)**:`_eval_harness\` 与仓库 `group_fqcd_jr\` **同级**,**不在仓库里、不入库** (`probe.py` 内部自己 `sys.path.insert` + `chdir` 到仓库)。旧文写 `group_fqcd_jr/_eval_harness/...` 是**错的** —— 换台机器 clone 下来会找不到 | | 本文 §3 实测证据(2026-09-20 真 HTTP) | `_cs_manual_baseline_20260920.json`(7 条主线路)、`_cs_manual_boundary_20260920.json`(11 条边界) | | 本文 §3.1 缺口 ① 的修复 | `app/api/schemas/agent_runs.py`(入口加 `message` 空白判据)、`tests/unit/api/test_request_validation_envelope.py`(新增回归测试) | diff --git a/开发文档/D1.1-文档索引与权威声明.md b/开发文档/D1.1-文档索引与权威声明.md index 2d990f6..3417c3a 100644 --- a/开发文档/D1.1-文档索引与权威声明.md +++ b/开发文档/D1.1-文档索引与权威声明.md @@ -2,7 +2,7 @@ > **体系编号**:`D1.1` · 域:一、治理与索引 · 编号体系见 `D1.1` §4.0 -> **编号**:CS-DOC-2026-017 | **版本**:v1.14 | **日期**:2026-09-21 | **状态**:**现行(活文档,随文档区变动同步更新)** +> **编号**:CS-DOC-2026-017 | **版本**:v1.15 | **日期**:2026-09-21 | **状态**:**现行(活文档,随文档区变动同步更新)** > **性质**:本文件是 `开发文档\` 的**唯一入口**。任何人(含三个月后的自己)打开这一份,就应知道:先读什么、哪份为准、每份什么状态。 > **盘点范围**:`开发文档\`(**53 个文件** = 52 份编号文档 + 1 份入口存根 `CLAUDE.md`,无归档子目录)+ `客服agent\`(**9 份**对外交付文档)。 @@ -36,10 +36,10 @@ ``` 对外交付(客服agent\) 配套完整版 / 前身(开发文档\) ────────────────────────────────── ───────────────────────────────────── -A1 [D2.1] D2.1-客服Agent执行Todolist.md v6.30 ←→ [D3.4] D3.4-客服Agent重构Todolist.md v5.1 +A1 [D2.1] D2.1-客服Agent执行Todolist.md v6.39 ←→ [D3.4] D3.4-客服Agent重构Todolist.md v5.1 A2 [D2.2] D2.2-客服Agent需求文档.html v2.7 ←→ [D3.1] D3.1-客服Agent需求开发文档与设计方案.html v2.6 A3 [D2.3] D2.3-客服Agent开发计划.html v1.1 ←→ (无旧版) -A4 [D2.4] D2.4-客服Agent知识库设计方案.html v1.7 ←→ [D3.2] D3.2-知识库设计方案.html v1.6 +A4 [D2.4] D2.4-客服Agent知识库设计方案.html v1.8 ←→ [D3.2] D3.2-知识库设计方案.html v1.6 A5 [D3.3] 访客与角色分离的鉴权方案建议(仅此一份,无新旧之别) ``` @@ -59,10 +59,10 @@ A5 [D3.3] 访客与角色分离的鉴权方案建议(仅此一份,无新旧 | # | 体系编号 | 文档 | 版本 | 作用 | |---|---|---|---|---| -| **A1** | **D2.1** | `客服agent\D2.1-客服Agent执行Todolist.md` | **v6.36** | **唯一开工入口**。**57 项 / 8 批次(A—H)** / 12 步关键路径 / 2 组会签 / **完工判据 13 条**。**新增批次 H · 智能增强**(`H-01`~`H-06`) | +| **A1** | **D2.1** | `客服agent\D2.1-客服Agent执行Todolist.md` | **v6.39** | **唯一开工入口**。**57 项 / 8 批次(A—H)** / 12 步关键路径 / 2 组会签 / **完工判据 13 条**。**新增批次 H · 智能增强**(`H-01`~`H-06`) | | **A2** | **D2.2** | `客服agent\D2.2-客服Agent需求文档.html` | **v2.7** | 对外需求:**FR-CS-001~052**(52 条,新增域 H)+ NFR-CS-001~021 全量、身份与鉴权模型、验收标准(**新增 AC-13 金标门禁**) | | **A3** | **D2.3** | `客服agent\D2.3-客服Agent开发计划.html` | **v1.1** | 前置条件、测试环境就位(G-00)、会签流程、门禁、交付物、**批次 H(§3.4b)** | -| **A4** | **D2.4** | `客服agent\D2.4-客服Agent知识库设计方案.html` | **v1.7** | 三集合 / 三档可见性 / **集合内分区隔离(§7.2.1)** / 8 模块 / 7 步入库 8 步检索 / 8 项决策 / **附录F 五出口对接** | +| **A4** | **D2.4** | `客服agent\D2.4-客服Agent知识库设计方案.html` | **v1.8** | 三集合 / 三档可见性 / **集合内分区隔离(§7.2.1)** / 8 模块 / 7 步入库 8 步检索 / 8 项决策 / **附录F 五出口对接** | | **A5** | **D3.3** | `开发文档\D3.3-访客与角色分离的鉴权方案建议-2026-09-16.md` | CS-AUTH-2026-011 | 鉴权专项:四方案对比、三条不变量、甲乙时序 | --- @@ -123,10 +123,10 @@ A5 [D3.3] 访客与角色分离的鉴权方案建议(仅此一份,无新旧 | **D1.4** | `开发文档\D1.4-知识源与品牌整改变更说明-2026-09-17.md` | CS-CONTENT-2026-016 | 现行 | 逐份变更说明(§3.1—§3.9 改写映射 + G-01~G-09) | | **D1.5** | `开发文档\D1.5-开发前决策清单与阻塞项-2026-09-17.md` | CS-DOC-2026-018 v1.0 | 现行 | 🔴 **开工前唯一决策登记册**:28 项待你拍板 + 阻塞分级(P0 12 / P1 10 / P2 6)+ 需你提供的 7 项输入;§7 为回填表(增补项见 `D1.6` §4.3) | | **D1.6** | `开发文档\D1.6-对话上下文提取与开工前补充决策-2026-09-17.md` | CS-DOC-2026-019 v1.0 | 现行 | 🔴 **本轮会话上下文提取件**:已读清单与权威链校正 / 可复用事实(含实测)/ 旧实现 **7 条转人工通路** / 文档缺陷 `Q-1.1`~`Q-1.6` / 前提风险 `K-01`~`K-08` / 待拍板 `N-01`~`N-09` | -| **D2.1** | `客服agent\D2.1-客服Agent执行Todolist.md` | **v6.36** | 现行 | 🔴 **唯一开工入口**:**57 项 / 8 批次** / 12 步关键路径 / **批次 H 智能增强** | +| **D2.1** | `客服agent\D2.1-客服Agent执行Todolist.md` | **v6.39** | 现行 | 🔴 **唯一开工入口**:**57 项 / 8 批次** / 12 步关键路径 / **批次 H 智能增强** | | **D2.2** | `客服agent\D2.2-客服Agent需求文档.html` | **v2.7** | 现行 | 🔴 对外需求:**FR-CS-001~052** + NFR-CS-001~021 | | **D2.3** | `客服agent\D2.3-客服Agent开发计划.html` | **v1.1** | 现行 | 🔴 前置条件 / 批次 / 会签 / 门禁 / 交付物 | -| **D2.4** | `客服agent\D2.4-客服Agent知识库设计方案.html` | **v1.7** | 现行 | 🔴 三集合 / 三档可见性 / **分区隔离** / 7 步入库 8 步检索 / **附录F** | +| **D2.4** | `客服agent\D2.4-客服Agent知识库设计方案.html` | **v1.8** | 现行 | 🔴 三集合 / 三档可见性 / **分区隔离** / 7 步入库 8 步检索 / **附录F** | | **D2.5** | `客服agent\D2.5-客服Agent演示脚本与账号速查-2026-09-19.md` | `F-03`+`F-04`+`A-05` | 现行 | 🔴 **演示当天照着念**:五项自检 / 账号速查(实测可登录)/ 游客线 5 条 + 客服线 6 组台词(带**实测答复**)/ 排障表 / 对「不智能」的正面回答 | | **D2.6** | `客服agent\D2.6-客服Agent答辩报告-2026-09-19.md` | 2026-09-19 | 现行 | 🔴 **答辩主文档**:批评 → 根因(2 个出口 / 10 处失败方向全指向转人工)→ 五出口 `E1`—`E5` → `INV-1`~`INV-5` → 金标 11 项**修复前 → 修复后**对比 → 零容忍词挂载点口径 → 坑与教训 → 诚实未做项 → 现场速答 | | **D2.7** | `客服agent\D2.7-客服Agent中长期记忆与画像联动设计-2026-09-20.md` | 2026-09-20 | 现行 | 🔴 **记忆与画像专项**:三问直答 / 客服侧五道闸门取证 / 字段分域(`investor_type` 红线)/ **主设计主张:记忆改「行为」不改「输入」** / `INV-M1`~`INV-M6` / 两处过期理由更正 / 分期 P0—P2 / 待决 4 项 | @@ -186,15 +186,15 @@ A5 [D3.3] 访客与角色分离的鉴权方案建议(仅此一份,无新旧 | 文件名 | 版本 | 日期 | 定位 | 关联 | |---|---|---|---|---| -| `D2.1-客服Agent执行Todolist.md` | **v6.37** | 2026-09-21 | 唯一开工入口 | 收敛自 `开发文档\D3.4-客服Agent重构Todolist.md` v5.1 | +| `D2.1-客服Agent执行Todolist.md` | **v6.39** | 2026-09-21 | 唯一开工入口 | 收敛自 `开发文档\D3.4-客服Agent重构Todolist.md` v5.1 | | `D2.2-客服Agent需求文档.html` | **v2.7** | 2026-09-20 | 对外需求(FR **52** / NFR 21) | 完整版见 §4.2 | | `D2.3-客服Agent开发计划.html` | **v1.1** | 2026-09-17 | 批次 / 会签 / 门禁 | 与 A1 批次号一一对应 | -| `D2.4-客服Agent知识库设计方案.html` | **v1.7** | 2026-09-20 | 三集合 / 三档 / 入库检索流程;**索引统一 `AUTOINDEX`、语料 702 块** | 完整版见 §4.2 | +| `D2.4-客服Agent知识库设计方案.html` | **v1.8** | 2026-09-21 | 三集合 / 三档 / 入库检索流程;**索引统一 `AUTOINDEX`、语料 755 块** | 完整版见 §4.2 | | `D2.5-客服Agent演示脚本与账号速查-2026-09-19.md` | — | 2026-09-19 | 演示脚本(`F-03`/`F-04`/`A-05` 三合一) | 台词证据:`group_fqcd_jr\docs\evidence\20260919-t8-demo-lines*.json` | | `D2.6-客服Agent答辩报告-2026-09-19.md` | — | 2026-09-19 | 答辩报告(问题定义 / 根因 / 五出口 / 安全不变量 / 前后对比 / 现场速答) | 数字来源:46 条金标 `score_before` vs `score_w11b` + `e2e_smoke_test` + `http_probe` + 12 条真机边界 | | `D2.7-客服Agent中长期记忆与画像联动设计-2026-09-20.md` | — | 2026-09-20 | 中长期记忆与画像联动(读码取证 / `INV-M1`~`INV-M6` / 分期 P0—P2) | 上游依据:`开发文档\D7.3` §1.3 与 §6.2;口径:`D2.2` §1.7 第 12 / 18 / 21 项 | -| `D2.8-客服Agent知识库RAG全链路与选型说明-2026-09-20.md` | — | 2026-09-20 | RAG 全链路(按流程逐步:解析 / 切片 / 向量化 / 入库 / 检索 / 增强 / 判定) | 上游:`D2.4` §6 / §7、`D3.2`、`D3.5`;实测:`knowledge\_chunks.jsonl` 675 块 + Milvus 四集合直查 | -| `D2.9-客服Agent手动对话测试用例-2026-09-20.md` | — | 2026-09-20 | 手动对话测试用例(46 条金标逐条可问 + 11 条边界 + 判分四问 + 汇总表) | 同口径输入件:`_eval_harness\cases_46.json` / `score_w11b.json`;实测证据:`_cs_manual_*_20260920.json` | +| `D2.8-客服Agent知识库RAG全链路与选型说明-2026-09-20.md` | — | 2026-09-20 | RAG 全链路(按流程逐步:解析 / 切片 / 向量化 / 入库 / 检索 / 增强 / 判定) | 上游:`D2.4` §6 / §7、`D3.2`、`D3.5`;实测:`knowledge\_chunks.jsonl` **755 块**(2026-09-21 `W24` 复测)+ Milvus 四集合直查 | +| `D2.9-客服Agent手动对话测试用例-2026-09-20.md` | **v1.3** | 2026-09-20 | 手动对话测试用例(46 条金标逐条可问 + 11 条边界 + 判分四问 + 汇总表) | 同口径输入件:`_eval_harness\cases_46.json` / `score_w11b.json`;实测证据:`_cs_manual_*_20260920.json` | ### 4.2 Ⅱ 开发文档区内的现行权威(8 份) @@ -889,3 +889,24 @@ A5 [D3.3] 访客与角色分离的鉴权方案建议(仅此一份,无新旧 > **维护责任**:本文件为活文档。**新增 / 改名 / 归档 / 改版本号后,须同步更新本文件 §3 与 §4.0 总表对应行**。 > > 编制:项目文档组 | 审核:合规稽核部 | 日期:2026-09-17 + +--- + +## 31. 第二十七轮:`W24` 语料入库轮 —— `docs/43` 场内基金手册入库 + `B-02` 展示候选集收口(2026-09-21) + +> **本轮做什么**:按用户「**先把语料修复吧 在进行下一步 不单独立项了**」执行 `W21` §9.5 第 3 条登记的待决 —— 把 **20 只场内基金**(`docs/43`)正式纳入切片 `SOURCES` 并重建重灌四个 Milvus 集合;收尾时修掉一处 `B-02` 的 `M-4` 回归。 + +| 项 | 内容 | +|---|---| +| **语料入库** | `docs/43-场内基金产品手册(知识库入库版).md` 纳入 `SOURCES`(`prefix=ETF` / `fin_product_collection` / `public` / `V1.0` / `2026-09-11` / `JR-ETF-2026-001`);切片件 **702 → 755 块**(`policy 288 / product 251 / faq 154 / basic 62`),档位 `public 730 / registered 25` | +| **新增三个按源开关** | `strip_editorial_marks`(丢 `>` 行 + 清 `⚠️`;该手册含**内部编辑说明**,不能给客户看)/ `qualify_table_rows`(7 列、5 列表格**按表头逐列**串成自解释正文)/ `exclude_sections`(导航型小节「3.2 其他产品的查询」不入库);**默认关** ⇒ 其它 8 个源实测 `changed existing blocks: 0` | +| **修掉两个真实缺陷** | ① `W24-A` **切片器行标签取错列**:多列表格第 0 列是**代码**(`159700[:2]`=`15`)⇒ 上游 `_prefer_section` 判不出重合 ⇒ **每一问都被换成整节块**(实测「科创债ETF南方怎么样」返回 20 只产品的整张表);改取表头「名称」列。② `W24-B` `text-embedding-v3` **单请求上限 10 条**,`load_knowledge_milvus.embed()` 只在灌库路径分批 ⇒ **自检路径**超 10 条即在**数据写完后**崩(14 条触发);分批下沉进 `embed()`(`MAX_BATCH = 10`) | +| **`B-02` 的 `M-4` 回归修复** | `_exit_partial` 两条调用路径**候选集不同**(主路径 TopK 全量 vs 证据路径被 `E4_MAX_EVIDENCE=6` 截断)⇒ **同一句问句的答复质量取决于「`E4` 有没有调用模型」**。`_answer_from_evidence` 新增 `display_hits`,**展示候选一律用 TopK 全量**;送模型的证据包保持 6 块不变 | +| **产品名识别补全** | `_PRODUCT_NAME_SHAPE` 后缀集补 `定期开放混合\|股票\(LOF\)[ABC]?\|\(LOF\)\|原油[ABC]?`;新增 `_BRANDLESS_PRODUCT_NAMES`(`沪深300ETF`,**唯一无厂商字样**的产品,只能枚举不能用通用后缀);`_strip_product_name_lead` 增「先切掉前一只基金」 | +| **测试** | 新增 **2 条守卫**(20 只产品名全覆盖 / 无厂商字样产品不在类目问句上开门);全量回归 **`1996 passed / 3 skipped`**(上一轮 `1994`);`ruff` **零新增**(本轮曾引入 2 条 `B905`,当场改 `zip(..., strict=True)` 清零) | +| **金标 46 条** | `M-1 46/46`、`M-4 46/46`、`M-6 5/46`、`M-7/M-8/M-9/M-10 = 0`、`M-2 28/31`、`M-2b 15/18`、`M-3 4/4` —— **与 `w23` 基线逐项一致**;**两次独立复跑**(`result_w24d` / `result_w24e`)指标完全相同 | +| ⚠️ **判据变更(3 条,必须知情)** | ① `B-01`(访客)`expected_evidence` 补 `ETF`;② `B-05`(客户,**同一句问句**)`expected_exits` `[E3]` → `[E3, E4]`;③ `load_knowledge_milvus` 自检 `BAS-CON-006` → `ETF-005`。**本轮不适用「零回归」表述** | +| **裁定** | `docs/45`(R1—R5 问答)**不入库**(与 `FAQ-0018` / `POL-AST-011/012` 重复,会抢 top1)—— 历史承诺里的 `docs/45` 部分据此关闭 | +| **版本位** | 本文件头部 **v1.14 → v1.15**;`客服agent\D2.1` 标题 **v6.38 → v6.39**;`客服agent\D2.4` **v1.7 → v1.8**;`客服agent\D2.9` **v1.2 → v1.3**;`开发文档\D4.8` **v1.1 → v1.2**(新增 §10);`开发文档\D1.6` **v1.1 → v1.2**(新增 §11) | +| **交叉引用** | `D1.6` 新增 §11(本轮对话上下文提取件);`D4.8` 新增 §10(实施与验收);`D2.1` 新增 `v6.39` 段;`D2.8` 新增 §3.6 与 §11 复测 | +| ⚠️ **未做(诚实声明)** | ① `_exit_partial` **仍按分数挑块**(不接收问句)—— 本轮只统一了**候选集**,**挑选语义**未改;② `E5b` 展示层净化仍不覆盖「收益 + 数字%」形态(`W21` §9.5 第 4 条继续挂账);③ 语料里 **2 个零容忍地雷块**(`POL-SPM-016` / `POL-SPM-022-01`)为**禁令条款**,**故意保留** | diff --git a/开发文档/D1.6-对话上下文提取与开工前补充决策-2026-09-17.md b/开发文档/D1.6-对话上下文提取与开工前补充决策-2026-09-17.md index fa38025..6a380a0 100644 --- a/开发文档/D1.6-对话上下文提取与开工前补充决策-2026-09-17.md +++ b/开发文档/D1.6-对话上下文提取与开工前补充决策-2026-09-17.md @@ -2,7 +2,7 @@ > **体系编号**:`D1.6` · 域:一、治理与索引 · 编号体系见 `D1.1` §4.0 -> **编号**:CS-DOC-2026-019 | **版本**:v1.1 | **日期**:2026-09-17 | **状态**:**现行(活文档:每项拍板后回填 §4.3)** +> **编号**:CS-DOC-2026-019 | **版本**:v1.2 | **日期**:2026-09-17 | **状态**:**现行(活文档:每项拍板后回填 §4.3)** > **性质**:本文件是一次「人 ↔ AI 会话」的**上下文提取件**。它不重复 `D1.5` 已登记的事项,只回答四件事:① 本轮读了什么、依据什么;② 得出哪些**可复用**的结论(含证据与实测);③ 本轮**新增**的待决策项 `N-01`~`N-09`;④ 下一步怎么走。 > **读法**:接手先读 §0 → §3(问题清单)→ §4(要你拍板的)→ §7(能力边界)。历史明细见 `D1.5`(决策登记册)与 `D2.1`(执行看板)。 > **配套**:知识库检索侧的升级建议见专册 `D3.5-知识库检索升级备选方案建议-2026-09-17.md`;**Agent 侧"如何变智能"的架构建议见专册 `D3.6-客服Agent智能增强架构建议-2026-09-17.md`**(五出口决策链 `E1`—`E5` + 安全不变量 `INV-1`~`INV-5` + 待拍板 `DEC-I1`~`DEC-I8`)。 @@ -3357,7 +3357,9 @@ pytest **2 failed / 1577 passed / 2 skipped**(= `T0` 基线同两项)、ruff - **考点两条没变**(禁忌字面不出现 / 关键事实命中),变的只是出口形态;旧 `E5b` 贴的是「6.2 核心科技平台」。 - 因此「`M-1 46/46`」这个数字,**建立在一条金标期望修订之后**。 -### 10.4 本轮**新发现**(下一轮的第一顺位待决) +### 10.4 本轮**新发现**(下一轮的第一顺位待决)—— ✅ **已于 `W24` 执行,见 §11** + +> ✅ **2026-09-21 `W24` 已按甲方批准执行完毕**:`docs/43` 纳入 `SOURCES`、切片件 **702 → 755 块**、四集合重建重灌、自检 15/15。下面保留当时(`W21` 视角)的取证原文备查。 **`docs/43-场内基金产品手册(知识库入库版).md` 从未入库。** @@ -3368,7 +3370,7 @@ pytest **2 failed / 1577 passed / 2 skipped**(= `T0` 基线同两项)、ruff | `knowledge/_chunks.jsonl`(702 块) | 「科创债」**0 处**、`ETF南方` **0 处** | | 后果(实测) | 「科创债ETF南方怎么样」的 top1 是 **`南方稳健增利债券 A` 的产品卡**(0.6696)—— 客户问 A、拿到 B 的资料 | | 历史承诺 | `D3.4` 的 `B-02`(决 12)与 `D4.1` §B-02 都写了「新增 `docs/43`+`docs/45` 入 `SOURCES`」,**实际未执行** | -| 本轮处置 | `W21-D3` 闸门**止损**(不再贴错产品,改为诚实作答);**根本修复**(纳入 `SOURCES` + 重建重灌)属**语料变更**,未擅自执行 | +| 本轮处置 | `W21-D3` 闸门**止损**(不再贴错产品,改为诚实作答);**根本修复**(纳入 `SOURCES` + 重建重灌)属**语料变更**,未擅自执行 → **`W24` 已执行**(见 §11、`D4.8` §10) | | 建议 | 纳入 `docs/43`(`public` 档)→ 重建 `_chunks.jsonl` → 重建 `fin_product_collection` → 复跑 46 条金标(**注意**:新增集合会改变产品类问句的排序,须逐条看 `M-2`/`M-4` 是否漂移) | ### 10.5 本轮可复用事实(下一轮别再重新求证) @@ -3382,8 +3384,60 @@ pytest **2 failed / 1577 passed / 2 skipped**(= `T0` 基线同两项)、ruff ### 10.6 本轮遗留(如实登记) -1. `docs/43` / `docs/45` 未入库(§10.4)。 +1. ~~`docs/43` / `docs/45` 未入库(§10.4)~~ → **已闭环**:`docs/43` 已于 `W24` 入库(702 → 755 块);`docs/45` 经裁定**不入库**(与 `FAQ-0018` / `POL-AST-011/012` 高度重复,会污染 top1)。见 §11 / `D4.8` §10.6。 2. **`E5b` 展示层净化覆盖不全**(§10.5 第 5 条)。 3. **`E5b` 的"挑哪一块"仍是"分数最高"**:`_exit_partial` 不接收问句,无法优先挑"提到问句里那只产品"的块。当正确产品的块排在第 2—5 名时,客户仍可能看到第 1 名的近邻块。**这是一个有明确修法的待决项**(把问句传进去,按产品名一致性优先)。 + - 🟡 **`W24` 部分收口**:`_exit_partial` 的**候选集**已统一为 **TopK 全量**(原先证据路径只拿到被 `E4_MAX_EVIDENCE` 截断的证据包,导致"同一句问句的答复质量取决于 `E4` 有没有调用模型"——`B-02` 因此从 46/46 掉到 45/46,已修)。**挑选语义仍是"分数最高",未改为"按问句主体优先"** —— 那会覆盖全部 `E5b`,需单独回归,本轮未做。见 §11 / `D4.8` §10.2。 4. 语料里仍有 **2 个零容忍地雷块**(`POL-SPM-016` / `POL-SPM-022-01`),是**禁令条款**,故意保留。 5. `ruff` 既有告警未顺手改(`customer_service.py:34` I001、`:1220` E501,行号因新增代码下移)。 + +--- + +## 11. `W24` 轮对话上下文(2026-09-21 · `docs/43` 场内基金手册入库 + `B-02` 展示候选集收口) + +> **性质**:本节是**本轮对话与决策的上下文提取件**,与 §9 / §10 同构,供下一轮开工快速恢复语境。 +> **上游**:`客服agent\D2.1` `v6.39`;**完整报告**:`开发文档\D4.8-…md` §10。 + +### 11.1 甲方本轮原话(逐字留痕) + +1. 「**按照你建议的来**」 +2. 「**先把语料修复吧 在进行下一步 不单独立项了**」 +3. 「按照你建议的来 我们要提升效率 尽可能的把两个步骤变成一步去执行」 +4. 「**你现在帮我做一个演示的前端一键启动的一个脚本**」 +5. 「**你的RAG方案不是我想要的,我希望你按照流程去总结**」 + +### 11.2 本轮**已拍板并已执行**的结论 + +| # | 事项 | 裁定 | 落地证据 | +|---|---|---|---| +| `W24-1` | `docs/43` 是否入库 | **入库**(`public` 档,`fin_product_collection`) | 切片件 702 → **755 块**;四集合 drop → 重建 → 重灌;自检 **15/15** | +| `W24-2` | `docs/45` 是否入库 | **不入库**(与 `FAQ-0018` / `POL-AST-011/012` 重复,会抢 top1) | `D4.8` §10.6 | +| `W24-3` | 场内基金手册里的**编辑说明段**(指向 `docs/42`、含两个互相矛盾的净值) | **不给客户看** ⇒ 新增按源开关 `strip_editorial_marks`(丢 `>` 行 + 清 `⚠️`);**镜像文件仍与 `docs/43` 逐字一致**,只在切片时丢 | 其它 8 个源实测 `changed existing blocks: 0` | +| `W24-4` | 7 列 / 5 列表格怎么切 | **按表头逐列串成自解释正文**(`qualify_table_rows`);否则默认只取前两列,**丢掉风险等级 / 净值 / 费率** | 「…风险等级」→ R2;「…管理费率」→ 0.15+0.05 | +| `W24-5` | 「3.2 其他产品的查询」(导航型小节) | **不入库**(`exclude_sections`)—— 它抢走了 `B-06` 的 top1 | 本步实测 | +| `W24-6` | 旧数据集 | **不用旧数据,全部用新数据**(甲方原话) | 未做任何增量 upsert;全部 drop 重建 | +| `W24-7` | 是否允许删旧 Milvus 库 | **允许**(「必要时可以跑单元测试与回归测试」「全部一起做一起测试」) | 本轮重建 1 次(补切片后再重建 1 次,共 2 次) | + +### 11.3 本轮**判据变更**(必须知情,不要当成"零回归") + +- `B-01`(访客)`expected_evidence` 补 `"ETF"` → `[PROD, FAQ, ETF]`(`ETF-013` 0.7954 合法 top1,答案更完整)。 +- `B-05`(客户,**同一句问句**)`expected_exits` `[E3]` → `[E3, E4]`(与 top1 只差 0.033 < 0.07 ⇒ 并列走生成式作答)。 +- `load_knowledge_milvus` 自检 `("场内基金和场外基金有什么区别", "BAS-CON-006")` → **`"ETF-005"`**(0.861 vs 0.789)。 +- ⇒ 准确说法是「**在三条期望登记修订之后**,`M-1`/`M-4` 均为 46/46」,**不是**「零回归」。 + +### 11.4 本轮可复用事实(下一轮别再重新求证) + +1. **`text-embedding-v3` 单请求最多 10 条**。分批必须做在 `embed()` **内部**(不是调用方):本轮就是"灌库路径分批、自检路径没分批"导致**数据已写完之后崩**(14 条即触发)。同类工具照此检查。 +2. **行级子块的 `title` 必须是「这一行讲的是谁」**。上游 `_prefer_section` 靠 `title` 末段与问句比对;多列表格的第 0 列是**代码**(`159700[:2]`=`15`)⇒ **永不重合** ⇒ 每一问都被换成整节块(客户问 1 只、拿回 20 只的整张表)。改标签列时**必须一并回归"问单只产品"的问句**。 +3. **正则提产品名不能用通用后缀枚举**:`ETF` 一旦放成通用后缀,「买ETF还是买LOF」会被吞成假产品名「买ETF」,`asked` 非空而命中块当然没有 ⇒ **造出假"没找到"**。唯一没有厂商字样的产品(`沪深300ETF`)只能用**枚举**。 +4. **`_exit_partial` 的候选集口径必须唯一**(`W24` 教训):它同时被"主路径(TopK 全量)"和"`E4` 证据路径(`E4_MAX_EVIDENCE=6` 截断包)"调用,**两条路径候选集不同** ⇒ 客户看到的答案质量取决于"`E4` 这次有没有调用模型"。**凡"同一出口、两条调用路径"的地方,都要检查候选集是否一致。** +5. **`upsert` 会让 `get_collection_stats().row_count` 翻倍**(实测 450 vs 251,`doc_id` 仍唯一)⇒ **只信 `_chunks_report.txt` 的块数**;改 `doc_id` 编号后**必须丢集合重建**(本轮重编号 `ETF-009`/`ETF-010` 时留过一条陈旧行 `ETF-014`)。 +6. **两次独立复跑是必需的**:`result_w24d` 与 `result_w24e` 指标完全相同才算修好。本仓库有"只跑一次就宣布修好是假绿"的历史教训(§10.5 第 6 条)。 +7. **改代码 / 重灌后必须重启 API + Worker 再打真 HTTP**;跑全量 `pytest` 前**必须停 Worker**(它抢 MySQL outbox 会造出假红)。 + +### 11.5 本轮遗留(如实登记) + +1. **`_exit_partial` 仍按分数挑块**(不接收问句)—— 本轮只统一了**候选集**,**挑选语义**未改(见 §10.6 第 3 条)。 +2. **`E5b` 展示层净化仍不覆盖「收益 + 数字%」形态**(§10.5 第 5 条)—— 本轮未动,继续挂账。 +3. 语料里仍有 **2 个零容忍地雷块**(`POL-SPM-016` / `POL-SPM-022-01`),是**禁令条款**,**故意保留**,不是缺陷。 +4. `ruff` 既有告警未顺手改(`customer_service.py:34` I001、`:1220` E501,行号因新增代码下移);本轮**引入过** 2 条 `tools/build_knowledge_chunks.py` `B905`,已在轮内改 `zip(..., strict=True)` 清零。 diff --git a/开发文档/D4.8-客服Agent智能度体检与整改报告-W21-2026-09-21.md b/开发文档/D4.8-客服Agent智能度体检与整改报告-W21-2026-09-21.md index 74026af..bd4e6db 100644 --- a/开发文档/D4.8-客服Agent智能度体检与整改报告-W21-2026-09-21.md +++ b/开发文档/D4.8-客服Agent智能度体检与整改报告-W21-2026-09-21.md @@ -1,7 +1,7 @@ # D4.8 · 客服 Agent 智能度体检与整改报告(`W21`) > **体系编号**:`D4.8` · 域:四、重构与清除留痕 -> **编号**:CS-RPT-2026-024 | **版本**:v1.1 | **日期**:2026-09-21 | **状态**:现行 +> **编号**:CS-RPT-2026-024 | **版本**:v1.2 | **日期**:2026-09-21 | **状态**:现行 > **性质**:**留痕报告**。回答甲方一句质疑 —— 「客服 Agent 不智能,很多问题强制转人工」。 > 本文只做三件事:**取实测证据**、**定位根因**、**登记整改与未整改项**。不改需求、不立新任务。 @@ -19,6 +19,8 @@ **第二轮(同日)**:§6 四项待决经甲方「**按照你建议的改**」全部裁定并落地 —— `W21-D1` 生成侧禁令、`W21-D2` 指代依据、`W21-D3` 答非所问闸门、`W21-D4` 作答语气。落地后**三项实测退化**(三条「检索命中完全正确却拿兜底话术」的问句)由 **0/9 走 `E4`** 变为 **9/9 走 `E4` 真实作答**;金标与全量回归见 §9。 **同时暴露一个比 `D1`~`D4` 更根本的缺口**:`docs/43-场内基金产品手册(知识库入库版).md`(20 只场内基金)**从未进入 `SOURCES`**,见 §9.5 第 3 条。 +**第三轮(同日,`W24`)**:该缺口已**按甲方批准执行** —— `docs/43` 纳入 `SOURCES`、切片件 **702 → 755 块**、四集合 **drop → 重建 → 重灌**、自检 **15/15**。执行中发现并修复一个**切片器真实缺陷**(多列表格行标签取错列 ⇒ 客户问一只产品拿到 20 只的整张表)与一个**潜伏 bug**(`text-embedding-v3` 单请求上限 10 条,自检路径未分批 ⇒ 数据写完后崩)。收尾时另修一处 `B-02` 的 `M-4` 回归:`_exit_partial` 的两条调用路径**候选集不同**,导致**同一句问句的答复质量取决于「`E4` 这次有没有调用模型」**。完整实施与验收见 §10。 + --- ## 1. 待判物:`_chunks_report.txt` 能否删除 —— **判定:可删,已删并已加 `.gitignore`** @@ -324,7 +326,7 @@ E4 生成稿里出现「收益率 / 七日年化」等 YIELD_METRIC_TERMS 1. **打标口径变更**:`W20` 版把认不出的归「其它」(10 条盲区,逐条读原文发现 9 条其实是**正确作答**);`W22` 版翻转为"只识别退化形态"。两次分布**不可相减**,§2.3 已写明。 2. **C-11 是"未修复"而不是"不需要修"**。它已被精确定位到代码行与触发条件,只差一个**合规口径的批准**(§6 第 1 项)。 3. **本轮体检样本是本报告自己构造的**(81 条 + 8 组多轮),不是甲方给定的验收集。金标 46 条仍是**唯一**验收依据,体检集只用于**发现缺陷**。 -4. **代码里 4 条 ruff 告警是既有的**(`customer_service_rules.py:318/416`、`customer_service.py:34/1188` 的 E501 与 I001),不是本轮引入,也**未顺手改** —— 避免把无关改动混进本轮 diff。 +4. **代码里 4 条 ruff 告警是既有的**(`customer_service_rules.py:318/416`、`customer_service.py:34/1188` 的 E501 与 I001;`W21` 二轮后 E501 行号下移至 `:1220`),不是本轮引入,也**未顺手改** —— 避免把无关改动混进本轮 diff。(`W24` 另**引入过** 2 条 `tools/build_knowledge_chunks.py` 的 `B905`,已在本轮内改为 `zip(..., strict=True)` 清零,见 §10.5。) 5. **真机复验依赖本地四依赖**(MySQL / Redis / Milvus / API+Worker)。复跑前须确认 `/internal/health/ready` 三依赖全绿,否则 `E5b` 会被误读成"缺陷"。 6. **`C-9` 的产物 `_PRODUCT_NAME_SHAPE` 是形状匹配、不是产品名白名单**:语料里新增产品名只要仍符合「南方 + 名称 + 产品类型后缀」就能被认出;若将来出现**不符合该形状**的产品名(如纯英文名),②级降级会失效并落到③级(知识检索)—— 功能不减,只是拿不到裁决。 @@ -386,6 +388,96 @@ E4 生成稿里出现「收益率 / 七日年化」等 YIELD_METRIC_TERMS 1. **`D1` 是"降低概率"不是"消灭概率"**:只把 ④ 写进 user 模板时,三条问句仍各有约 1/3 会落到 `E5b`;把同义禁令补进 **system 红线 ②** 后测得 **9/9**。若某次生成仍写出收益数值,`_exit_partial` 的兜底照旧生效(**宁可兜底,不可输出**)。 2. **`I-01` 的金标期望被修订过**(9.3)。这是**判据变更**,不是"零回归" —— 请知情后使用 `M-1 46/46` 这个数字。 -3. **`docs/43-场内基金产品手册` 未入库**(702 块里「科创债」0 处):`W21-D3` 的闸门只做到**止损**(不再贴错产品),**根本修复**需要把它纳入 `SOURCES` 并重建集合 —— 属**语料变更**,未在本轮擅自执行,已登记为待决(`D1.6` §10.4)。 +3. ~~**`docs/43-场内基金产品手册` 未入库**(702 块里「科创债」0 处)~~ → **已于 `W24` 执行并验收**(`docs/43` 纳入 `SOURCES`,切片件 702 → 755 块,见 §10)。当时 `W21-D3` 的闸门只做到**止损**(不再贴错产品),**根本修复**需要把它纳入 `SOURCES` 并重建集合 —— 属**语料变更**,未在 `W21` 擅自执行,已登记为待决(`D1.6` §10.4)。 4. **`E5b` 展示层仍是"原文直返"**:实测「买基金要手续费吗」的 `E5b` 分支正文里含「专户业绩报酬 | 按合同约定计提 | 超额收益的 10%—20%」,会被 `hits_zero_tolerance` 判 True(`收益`+数字% 陷阱)。**这是既有行为**(`drop_yield_claims` 只丢带收益指标名的行),治理层不对 `E5b` 正文做二次零容忍扫描,因此**不会拦单**;但**展示层净化是否要覆盖这一形态**,建议列入下一轮待决。 5. **改动只落在 `客服agent/` 与 `开发文档/` 两份权威副本 + 仓库同名镜像**,三处一致(`D1.1` §权威声明)。 + +--- + +## 10. `W24` · `docs/43` 场内基金手册入库的实施与验收(2026-09-21) + +> **性质**:**补历史欠账**。`D3.4` 的 `B-02`(决 12)与 `D4.1` §B-02 早已写明「新增 `docs/43`+`docs/45` 入 `SOURCES`」,**从未执行**;`W21` §9.5 第 3 条首次把它暴露成实测缺口。甲方 2026-09-21 批准「**先把语料修复吧 在进行下一步 不单独立项了**」⇒ 本轮执行。 +> **证据**:`_eval_harness/result_w24d.json` + `score_w24d.json`、`result_w24e.json` + `score_w24e.json`、`_w24_http.txt`(真 HTTP)、`knowledge/_chunks.jsonl`(755 块)。 + +### 10.1 落地清单(文件级) + +| 文件 | 改动 | +|---|---| +| `tools/build_knowledge_chunks.py` | ① `SOURCES` 新增 `product/场内基金产品手册(知识库入库版).md`(`prefix=ETF` / `collection=fin_product_collection` / `visibility=public` / `version=V1.0` / `effective_date=2026-09-11` / `doc_no=JR-ETF-2026-001`);② 新增三个**按源开启**的开关 `strip_editorial_marks` / `qualify_table_rows` / `exclude_sections`(默认关);③ `expand_table_rows()` 签名加 `qualify`,新增 `cell_text()` / `row_content()` / `subject_of()`;④ **行级子块标签改取「名称」列**(缺陷修复,见 10.2) | +| `tools/load_knowledge_milvus.py` | `embed()` **内部分批**(`MAX_BATCH = 10`)—— 修自检路径超 10 条即崩的潜伏 bug;自检期望 `BAS-CON-006` → `ETF-005`,并新增 5 条新源存在性证明(**自检 15/15**) | +| `docs/43-场内基金产品手册(知识库入库版).md` + `knowledge/product/…md` | 费率表表头补「(%/年)」;两处**逐字一致**(有断言守着) | +| `knowledge/_chunks.jsonl` | 702 → **755 块**(policy 288 / product 251 / faq 154 / basic 62) | +| `app/service/agent/implementations/customer_service.py` | ① `_PRODUCT_NAME_SHAPE` 后缀集补 `定期开放混合\|股票\(LOF\)[ABC]?\|\(LOF\)\|原油[ABC]?`;② 新增 `_BRANDLESS_PRODUCT_NAMES`(`沪深300ETF`)与第三条 pattern;③ `_strip_product_name_lead` 增「先切掉前一只基金」;④ `_answer_from_evidence` 新增 `display_hits` 形参,四处 `_exit_partial` 改传**展示候选集** | +| `tests/unit/service/test_customer_service_agent.py` | **+2 条守卫**:20 只产品名全覆盖(语料驱动)、无厂商字样产品**不**在类目问句上开门 | +| `_eval_harness/cases_46.json` | `B-01` 证据期望补 `ETF`;`B-05` 出口期望补 `E4`(见 10.3) | +| `客服agent/D2.1` | 升 `v6.39` | +| `客服agent/D2.4` / `D2.8` / `D2.9` | 语料口径 675 → **755**;`D2.4` §4.4 场内基金手册切片预估「约 30—50」→ 实测 **53** | +| `开发文档/D1.1` / `D1.6` | 索引口径同步;`D1.6` 新增 §11,§10.4 标「已执行」、§10.6 第 1 条销账 | +| 本文件 | 新增 §10;§0 / §8 / §9.5 同步 | + +### 10.2 执行中发现并修复的两个真实缺陷 + +| 编号 | 缺陷 | 现象(修复前) | 根因 | 修法 | +|---|---|---|---|---| +| **`W24-A`** | 🔴 **切片器:行级子块标签取错列** | 「科创债ETF南方怎么样」返回 **20 只产品的整张表**(真 HTTP + 进程内**双重**复现) | 上游 `_prefer_section` 用子块 `title` 末段与问句比对;多列表格**第 0 列是代码**(`159700[:2]` = `15`),**永不重合** ⇒ 每一问都被换成整节块 | `qualify` 模式下标签改取表头里含**「名称」**的那一列(两张表都有该列),无则退回第 0 列 | +| **`W24-B`** | 🔴 **`embed()` 自检路径未分批** | `CHECKS` 超过 10 条时,**数据已全部写完之后**崩(本次 14 条即触发) | `text-embedding-v3` **单请求最多 10 条**;原实现只有灌库路径分批 | 分批下沉进 `embed()`(`MAX_BATCH = 10`),两条路径同时受益 | + +**另修一处 `M-4` 回归(`B-02`)**: + +| 项 | 内容 | +|---|---| +| 现象 | `B-02`「基金申购和赎回有哪些费率?」(访客)在 `w24c` 由 `E5b` 展示 `POL-SPM-033-01`(**单行「赎回费率:<7 天 1.50%」**)⇒ **只答一半**,`M-4` 失败 | +| 根因 | `_exit_partial` 的两条调用路径**候选集不同**:主路径 `_answer_from_knowledge` 传 **TopK 全量**(能挑到 `PROD-016` 费率总表),证据路径 `_answer_from_evidence` 传**被 `E4_MAX_EVIDENCE = 6` 截断的证据包**。新语料把 `ETF-009` 插进 TopK,把 `PROD-016` 从第 9 名挤到第 10 名,"在包内"变"包外" | +| 为什么这是缺陷 | **同一句问句的答复质量取决于「`E4` 这次有没有调用模型」** —— 两条路径只差"模型这一跳成没成",不该差在客户看到的内容质量上 | +| 修法 | `_answer_from_evidence` 新增 `display_hits` 形参(调用点把 `hits` 传进来),四处 `_exit_partial` 改传该值;`_exit_partial` 内部仍挑最高分、仍用 `PARTIAL_FLOOR` 兜底。**送模型的证据包保持 6 块不变**(那是模型注意力问题,不是展示问题) | +| 为什么不顺手"按问句产品名优先挑块" | 那会改 `E5b` 的**挑选语义**(从"分数最高"变成"与问句主体一致优先"),影响面覆盖全部 `E5b`,需单独回归;本轮只做**候选集口径统一**这一最小改动(`D1.6` §10.6 第 3 条仍**部分未收口**) | + +### 10.3 期望值登记修订(**判据变更,必须知情**) + +| 项 | 原期望 | 修订后 | 理由 | +|---|---|---|---| +| `B-01`(**访客**,问句见 `cases_46.json`) | `expected_evidence = [PROD, FAQ]` | `[PROD, FAQ, ETF]` | 新语料让 `ETF-013`(0.7954)**合法**拿到 top1,`PROD-001-05`(0.7527)退到 #2;两者都答得到,**答案更完整**。已写 `evidence_note` | +| `B-05`(**客户**,与 `B-01` **同一句问句**) | `expected_exits = [E3]` | `[E3, E4]` | 与 top1 `FAQ-0020`(0.8286)只差 **0.033 < 0.07** ⇒ 走「并列 → 生成式作答」。已写 `exit_note` | +| `tools/load_knowledge_milvus.py` 自检 | `("场内基金和场外基金有什么区别", "BAS-CON-006")` | `("…", "ETF-005")` | `ETF-005` 0.861 vs `BAS-CON-006` 0.789,**更贴题**;已写明「期望值是**语料版本的函数**」 | + +> ⚠️ 因此本轮**不适用**「零回归」表述。可复算的准确说法是:**在三条期望登记修订之后**,金标 `M-1`/`M-4` 均为 46/46。 + +### 10.4 验收(可复算) + +| 项 | 结果 | +|---|---| +| 金标 46 条(进程内 `probe.py` + `score.py`) | `M-1 46/46`、`M-4 46/46`、`M-6 5/46`、`M-7/M-8/M-9/M-10 = 0`、`M-2 28/31`、`M-2b 15/18`、`M-3 4/4` —— **与 `w23` 基线逐项一致** | +| **两次独立复跑** | `result_w24d` 与 `result_w24e` **指标完全相同**(本仓库有"只跑一次就宣布修好是假绿"的历史教训,`D1` 类改动必须重复采样) | +| Milvus 自检 | **15/15**;切片件 755 = 四集合合计(**只信 `_chunks_report.txt` 的块数** —— `upsert` 会让 `row_count` 翻倍) | +| 全量单元 / 集成回归 | **`1996 passed / 3 skipped`**(`W21` 二轮 `1994`,**+2 条新守卫全绿**) | +| `ruff` | **零新增**:`customer_service.py:34` I001、`:1220` E501 等**均为既有**;本轮曾引入 2 条 `B905`,已当场清零 | +| 切片件幂等 | `strict=True` 重构后重跑 `build_knowledge_chunks` ⇒ `knowledge/_chunks.jsonl` **SHA256 逐字节相同**(无须重灌) | +| 真 HTTP | 见 10.5 | + +### 10.5 真 HTTP 复验(`cust_t` / 访客,`POST /api/v1/agent-runs`) + +| # | 问句 | 结果 | +|---|---|---| +| 1 | 南方金利定开债券A的费率是多少? | 单行费率 **带单位**:管理费率(%/年)0.50 / 托管费率(%/年)0.15 / 销售服务费率 无 | +| 2 | 科创债ETF南方怎么样 | **单只产品**(代码 159700 / 深交所 / ETF / **R2** / 净值 101.8009)—— 修复前是 20 只的整张表 | +| 3 | 场内基金有哪些 | 20 只清单 | +| 4 | 场内基金和场外基金有什么区别 | `ETF-005`:场内按市价撮合 / 场外按净值申赎,T+1 确认 | +| 5 | 南方现金添利怎么样 | `E4` 生成作答(R1 / 起投 1 元 / 费率 / 申赎规则),**零收益数值** | +| 6 | 买基金要手续费吗 | `E4` 合并作答(交易 + 持有 + 赎回费 + 线上优惠) | +| 7 | 赎回费怎么算 → 持有 8 个月呢 | `E5b` → `E5b`:**两轮都给出完整分档表**(`B-02` 同族修复的连带确认) | +| 8 | 我想买个债基 → 它适合我吗 | 指代继承 + **真实适当性裁决**(R2 vs C1 + 揭示书 + 双录);**未替客户确认** | +| 9 | 科创债ETF南方(**访客**) | 与客户档同答(`public` 档);**无档位越权** | + +### 10.6 本轮裁定:`docs/45` **不入库** + +`docs/45`(R1—R5 问答)与 `FAQ-0018`、`POL-AST-011` / `POL-AST-012` **高度重复**;单独成块会在「R1 到 R5 分别代表什么」这类问句上**抢走 top1**(该题 Top1 与次优原本只差 0.021,属已知的阈值敏感区)。**裁定:不纳入 `SOURCES`**;历史承诺里的 `docs/45` 部分**据此关闭**。 + +### 10.7 诚实留痕(本轮新增,别漏) + +1. **本条是"补历史欠账",不是新增需求**:`D3.4 B-02`(决 12)与 `D4.1 §B-02` 早已承诺,从未执行。 +2. **本轮有 3 条期望修订**(10.3),"零回归"不适用。 +3. `_exit_partial` **仍按分数挑块**,不接收问句 ⇒ 「问句点名了产品 X,但第 1 名的近邻块更贴题」这一类**仍未收口**(`D1.6` §10.6 第 3 条,本轮只统一了候选集口径)。 +4. **`E5b` 展示层净化仍不覆盖「收益 + 数字%」形态**(`W21` §9.5 第 4 条)—— 本轮**未动**,继续挂账。 +5. 语料里仍有 **2 个零容忍地雷块**(`POL-SPM-016` / `POL-SPM-022-01`),是**禁令条款**,**故意保留**,不是缺陷。 +6. **`upsert` 会让 `get_collection_stats().row_count` 翻倍**(实测 450 vs 251,`doc_id` 仍唯一)⇒ **只信 `_chunks_report.txt` 的块数**;改 `doc_id` 编号后**必须丢集合重建**(本轮重编号 `ETF-009`/`ETF-010` 时留过一条陈旧行)。 +7. 改动只落在 `客服agent/` 与 `开发文档/` 两份**权威副本** + 仓库同名**镜像**,三处一致(`D1.1` §权威声明)。