unis_crm/MCP优化方案.md

159 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# MCP 优化方案
> 基于当前 MCPLLM Agent 工具实现现状整理。日期2026-09-16。
> 前提:完整测试 **132 个用例全部通过**(含数据权限守卫 `everyMcpMapperMethodShouldBeProtectedOrExplicitlyTenantSafe`、SQL 可重写守卫等),服务可正常编译运行。
---
## 一、现状总览
### 1.1 MCP 工具清单24 个)
| # | 工具名 | 归属类 | 能力 |
|---|---|---|---|
| 1 | `crm_universal_search` | CrmUniversalSearchToolProvider | 跨实体关键词通用搜索(客户/商机/拓客/日报/签到/待办/活动/跟进) |
| 2 | `crm_user_profile` | UserProfileToolProvider | 当前用户画像(账号/姓名/角色/部门/租户) |
| 3 | `crm_entity_detail` | CrmEntityDetailToolProvider | 按实体类型+ID 查详情可带相关明细customer/opportunity/daily_report/checkin/sales_expansion/channel_expansion/crm_expansion/todo |
| 4 | `crm_work_report_search` | WorkReportSearchToolProvider | 日报分页检索 |
| 5 | `crm_opportunity_search` | OpportunitySearchToolProvider | 商机分页检索(阶段/金额/排序/字段投影) |
| 6 | `crm_customer_search` | CrmDetailSearchToolProvider | 客户分页检索 |
| 7 | `crm_checkin_search` | CrmCheckinSearchToolProvider | 外勤打卡分页检索 |
| 8 | `crm_expansion_search` | CrmExpansionSearchToolProvider | 销售/渠道拓客分页检索 |
| 9 | `crm_crm_expansion_search` | CrmCrmExpansionSearchToolProvider | CRM 拓客crm_crm_expansion检索 |
| 10 | `crm_followup_search` | CrmFollowupSearchToolProvider | 商机/拓客跟进检索 |
| 11 | `crm_todo_search` | CrmTodoSearchToolProvider | 待办检索 |
| 12 | `crm_work_today_status` | CrmWorkTodayStatusToolProvider | 今日工作状态 |
| 13 | `crm_org_user_search` | CrmOrgUserSearchToolProvider | 组织用户检索 |
| 14 | `crm_report_query` | CrmReportQueryToolProvider | 报表/统计查询(见 1.2 |
| 15 | `crm_report_catalog` | CrmReportCatalogToolProvider | 报表与查询能力目录 |
| 16 | `crm_metadata_catalog` | CrmMetadataToolProvider | 模块/字段/工具能力目录 |
| 17 | `crm_dict_options` | CrmDictOptionsToolProvider | 系统字典类型和选项 |
| 18 | `crm_field_comment` | CrmFieldCommentToolProvider | MCP 返回字段的中文注释(读 DB COMMENT |
> 注:上表已覆盖全部 ToolProvider 类。`crm_search`/`crm_detail` 等仅表示内部方法,非独立注册工具。
### 1.2 `crm_report_query` 支持的 reportType10 种)
`dashboard_summary`、`sales_performance`、`opportunity_funnel`、`opportunity_trend`、`daily_report_completion`、`checkin_summary`、`expansion_summary`、`customer_summary`、`todo_summary`、`channel_analytics`(下含 `channelTierDistribution`、`channelIntentDistribution`、`channelCoverageSummary`、`channelRevenueTier`、`channelContactWecomDistribution` 5 个子维度)。
### 1.3 Mapper 规模LlmMcpMapper.xml83 个 select
- **列表/搜索**35 个(各实体 search + count + universalSearch
- **Dashboard/汇总**27 个dashboard* 、*Summary、funnel/trend/completion、channel* 聚合)
- **详情**15 个select*Detail + 相关明细 select*Followups/Contacts/Coverages/Comments/Messages/Opportunities
- **基础数据**selectUserProfile/Roles/Orgs、searchOrgUsers、searchOrganizations、searchRoles、selectDictTypes/Options、selectColumnComments
### 1.4 数据权限模型
McpDataPermissionInterceptor 通过 SQL 重写注入 owner 过滤。策略见 `buildPolicies()`[McpDataPermissionInterceptor.java](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/llm/security/McpDataPermissionInterceptor.java#L402-L445)
| RESOURCE 常量 | owner 列 | 关键受管方法 |
|---|---|---|
| RESOURCE_DAILY_REPORT | r.user_id | searchWorkReports、详情、comments、messages、日报完成率 |
| RESOURCE_CHECKIN | c.user_id | 签到系列 |
| RESOURCE_CUSTOMER | c.owner_user_id | 客户系列 |
| RESOURCE_EXPANSION | s.owner_user_id / c.owner_user_id / ce.owner_user_id | 销售/渠道/CRM 拓客系列 + channel* 聚合 |
| RESOURCE_WORK | t.user_id / l.operator_user_id / u.user_id | 待办、活动、今日状态 |
| RESOURCE_OPPORTUNITY | o.owner_user_id+项目归属地投影,预售后台可见) | 商机系列、跟进、漏斗 |
| RESOURCE_ALL | u.user_id | searchOrgUsers |
受两类守卫测试保护:每个 mapper 方法要么受策略保护、要么显式 tenant-safe受保护语句必须能被重写器正确注入过滤条件。
---
## 二、可优化点
按「优先级 P0(高) / P1(中) / P2(低)」与「收益」分组,每条含:现状、优化建议、影响。
### P0-1 `actual_signed_summary` 报表「已声明但不可用」(实际缺陷)
- **现状**[CrmReportQueryToolProvider.java](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/llm/tools/CrmReportQueryToolProvider.java)
- reportType 参数枚举里含 `"actual_signed_summary"`L64`crm_report_catalog` 也声明了该 reportTypeL61
-`query()` 的分发 switchL87-98**没有 `case "actual_signed_summary"`**,会落到 `default``BusinessException("不支持的 reportType")`
- 而 L110 的 `if ("actual_signed_summary".equals(reportType)) { signedSummary(...) }` 分支位于 switch **之后**,因 switch 先抛异常而**永远不可达**。
- 底层 `signedSummaryRows / signedSummaryQuality` mapper 已就绪XML 2508/2548且已注册数据权限只是没被 switch 接上。
- **建议(核心修复,最小改动)**:在 switch 增加 `case "actual_signed_summary" -> signedSummaryRows/...` 或在 `query()` 入口先处理该 reportType使声明与实现一致。建议顺带新增一个「枚举声明的 reportType 与 switch 分支一一对应、且 catalog 同步」的守卫测试,防止再次漂移。
- **影响**Agent 按 catalog 调用该报表当前会直接报错 → 属功能性缺陷,建议优先修复。
### P0-2 商机筛选/权限口径的「预售后台」与 SDK 差异(一致性风险中)
- **现状**[McpDataPermissionInterceptor.java](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/llm/security/McpDataPermissionInterceptor.java#L436-L442) 中商机策略 `preSalesVisible=true` 并带 `o.project_ownership_location` 投影,而日报/签到/客户/拓客等 `preSalesVisible=false`。这个差异是**有意的**(对应商机模块的预售后台可见规则),但**没有注释说明为什么商机特殊**。
- **建议**:在策略处补注释说明「商机为何独享 preSalesVisible 与项目归属地投影」,避免后续维护误改。纯注释改动,零回归。
- **收益**:提升可维护性,防止未来误删权限差异导致商机数据越权或越收窄。
### P0-3 用户相关查询的敏感字段需审校(安全加固)
- **现状**:用户画像已暴露工号/职位/入职日期等。需核验 `selectUserProfile` / `searchOrgUsers` **是否为显式列**(而非 `*`),确认无 password/salt/token/密钥字段带出。
- **建议**:通读 `selectUserProfile`、`searchOrgUsers` 两个查询,确认全部为**显式列**,且不含密码/盐/token/密钥类字段。若有 `*`,改为显式列。同时补一个**黑名单断言测试**:任何 MCP 返回的字段名不得出现 `password`/`secret`/`token`/`key` 等(类似现有权限守卫)。
- **收益**:杜绝敏感信息经 MCP 泄漏的隐患,测试固化。
### P0-4 数据权限守卫测试存在,但缺乏「跨实体 join 越权」用例
- **现状**:现有测试验证每个受保护语句可被重写,但未覆盖**关联明细查询**(如 `selectChannelExpansionOpportunities`、`selectCustomerOpportunities`、`selectSalesExpansionOpportunities`)在注入 owner 条件后,相联合的 on 条件是否真正约束到目标行。
- **建议**:增加针对性测试:构造「主实体可见但关联实体不可见」的数据,断言关联明细不会越权返回超此 owner 范围的行。
- **收益**:堵住「通过详情联查旁路越权」的潜在漏洞。
### P1-1 报表 `channel_analytics` 的 groupBy 口径无文档校验
- **现状**`channel_analytics` 有参数 `groupBy`[CrmReportQueryToolProvider.java](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/llm/tools/CrmReportQueryToolProvider.java#L53)但内部各聚合方法tier/intent/coverage/revenue/wecom`groupBy` 的支持程度看不到统一校验。
- **建议**:明确每个子维度「是否接受 groupBy、接受哪些取值」对不支持的值在入口做显式校验并报错而非静默忽略避免返回误导性聚合。补充文档化说明到 `crm_report_catalog`
- **收益**:提升统计可信度、避免 Agent 拿到「以为分组了其实没有」的数据。
### P1-2 `crm_report_catalog` 的工具清单可能未随新工具同步
- **现状**[CrmReportCatalogToolProvider.java](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/llm/tools/CrmReportCatalogToolProvider.java#L36-L50) 内联维护了一个工具清单(含 `crm_universal_search`、`crm_work_report_search` 等),但**未见 `crm_user_profile`/`crm_work_today_status`/`crm_crm_expansion_search`/`crm_report_query` 之外全部 24 个工具是否都在清单内**,且是手工维护,易遗漏。
- **建议**:改为**从注册表动态生成**工具清单(遍历已注册 ToolProvider 的名称与描述),消除手工同步漂移;或至少补齐缺失工具并加「清单与注册工具数量一致」的守卫测试。
- **收益**Agent 发现工具的能力与真实能力一致,避免「目录找不到但实际能调」。
### P1-3 `selectColumnComments`crm_field_comment的表白名单是硬编码
- **现状**[FieldCommentRegistry.java](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/llm/tools/support/FieldCommentRegistry.java) 以硬编码 `tables()` / `TABLE_COLUMNS` 维护「别名→物理列」映射,新增表/字段需人工双处登记(列表+注释),易漏。
- **建议**:为映射的完整性加守卫测试:`TABLE_COLUMNS` 键集合与 `tables()` 返回的说明集合一致;并可核对每个映射的物理列是否在本批次已暴露的查询字段内。
- **收益**:固化「新增字段必登记注释」这一约定,避免字段有值但无注释。
### P1-4 `limit` 上限保护已较好,但部分列表可细化
- **现状**:商机等已统一 `pageSize/limit` 默认 10、最大 50[OpportunitySearchToolProvider.java](file:///Users/kangwenjing/Downloads/crm/unis_crm/backend/src/main/java/com/unis/crm/llm/tools/OpportunitySearchToolProvider.java#L56-L57),含 offset 计算)。这是好实践。
- **建议**:核验所有其余 search 工具(日报/客户/打卡/拓客/跟进/待办/CRM拓客是否都用了同一套 `MAX_LIMIT``getOffset` 兜底,避免个别工具直接透传 limit 造成大结果集。可抽公共参数抽取器统一。
- **收益**:统一防爆量,避免个别查询成为性能/响应放大点。
### P2-1 列表 keyword 搜索未纳入 coverage/联系人等子表字段
- **现状**:销售/渠道列表已新增 `coverageSummary` 聚合列(本轮),但 **keyword 匹配仍只针对主表字段**,不会命中覆盖省/市或渠道联系人姓名。
- **建议**:如需「按覆盖地市、按联系人搜索」,在 keyword 的 LIKE 条件里补充对 coverage 子表(省/市)与 contact 表的关联匹配。
- **收益**:搜索召回更贴合业务;成本是 SQL 变复杂,需先确认是否为业务目标。
### P2-2 覆盖地市 / 日报消息只存在于「详情联查」,无独立搜索工具
- **现状**`coverage`、`work_report_message` 已通过 `crm_entity_detail` 的相关明细暴露,但无独立搜索入口。
- **建议**:若 Agent 需要「按覆盖省/市全局统计」或「全局检索某条日报消息」,可新增 `crm_coverage_search` / `crm_report_message_search` 独立工具;否则保持现状(避免工具爆炸)。
- **收益**按需增强过度新增会导致工具面过大、Agent 选择成本上升。
### P2-3 未处理缺口(已知合理保留)
- `sys_org.parent_id/org_code/sort_order`:框架表无建表脚本,列名未核实,暂不补真实值。
- `business_calendar_day`、`report_reminder_*`、`speech_recognition_config`、`dashboard_analytics_card_config`:配置类表,未暴露属合理(报告鉴权用白名单、配置不属业务数据)。
- `crm_oms_dict_mapping`OMS 后调用映射表,**按需求明确不暴露**。
---
## 三、推荐落地顺序(不含重构)
| 序号 | 项 | 工作量 | 回归风险 |
|---|---|---|---|
| 1 | **P0-1 修复 actual_signed_summary 不可用缺陷**(核心) | 小 | 低 |
| 2 | P0-3 敏感字段审校 + 黑名单测试 | 小 | 低 |
| 3 | **P0-4 关联明细越权测试** | 中 | 零(纯加测试) |
| 4 | P1-1 channel_analytics groupBy 校验 | 小 | 低 |
| 5 | P1-2 report_catalog 动态化/补全 + 守卫 | 中 | 低 |
| 6 | P1-3 field_comment 注册一致性守卫 | 小 | 零(纯加测试) |
| 7 | P1-4 limit 兜底统一 | 小-中 | 低 |
| 8 | P2-1/P2-2 按需 | 视需求 | 视改动 |
---
## 四、当前状态结论
- **功能完整性**:核心业务主表 + 详情 + 报表 + 数据权限均已覆盖132 用例全绿,服务可启动。**不存在阻断性缺陷**。
- **最值得优先做**P0-2敏感字段黑名单与 P0-3关联明细越权测试——属于「安全加固」性质风险低、收益高建议放到下一批次。
- 其余 P1/P2 多为可维护性与体验增强,可按迭代排期,不必一次性做完。