Skip to main content
Glama
ayeyouok

a207-router-mcp

by ayeyouok
README.md
# a207-router-mcp(M13 · 意图路由与安全闸)

调度 Agent 独占的确定性判定层。安全红线不交给概率模型:P0 危急词、三条互斥硬规则
(MX-1/MX-2/MX-3)、权限矩阵,全部落成可单测的纯函数。

- 技术栈:FastMCP ≥2.0 + pydantic ≥2 + Python 3.13,stdio 传输
- 依据:`docs/01_多智能体与MCP总体需求梳理_v1.md` §5/§6/§7、`docs/02_Wave1_构建规格_v1.md` §2.1
- 零跨包引用:不 import 任何其他 `a207-*` 包,入参由调度 Agent 从 PCP 取值后显式传入

## 包结构

```
src/a207_router_mcp/
  core.py          安全闸 + 意图分类(纯逻辑,无 fastmcp)
  permissions.py   权限矩阵执行点 + 写工具识别(MX-3 落点)
  constants.py     角色、13 个 MCP 登记表、权限矩阵、写权策略
  lexicon.py       P0 危急词、分期词族、意图路由表、槽位词典
  slots.py         正则抽槽位
  models.py        pydantic 请求/响应模型
  server.py        FastMCP 实例,挂载四个工具
tests/test_tools.py  46 个断言,纯 python 可跑
```

## 四个工具

### check_safety_gate(intent, caller, target_mcp="")

判定顺序固定:**P0 危急词 → 入参登记校验 → MX-1 → MX-2 → MX-3 → 权限矩阵兜底**。

返回:`{allow, reason, applied_rule, gate, delegate_to, requires_confirmation, matched_keywords}`
- `applied_rule`:`MX-1 | MX-2 | MX-3 | none`
- `gate`:`P0_EMERGENCY | INPUT | MX | PERMISSION_MATRIX | PASS`,用于区分同为 `none` 的拒绝来源

| 规则 | 内容 |
|------|------|
| P0 | 命中危急词(抽搐/少尿/无尿/大量出血/意识不清等 30 余条)即断崖终止,不进入任何业务 MCP |
| MX-1 | 分期/eGFR/几期/肾小球滤过/PEW/生长障碍/血压百分位类问题,caller 为 nutritionist 或 parent_assistant 时拒绝并 `delegate_to=doctor_assistant`;`a207-ckd-clinical-calc-mcp` 对非 doctor_assistant 一律关闭 |
| MX-2 | `a207-gamification-mcp` 只接受 child_companion;child_companion 调 HIS/LIS/营养计算/临床计算/食谱/风险/报告/通知一律拒绝 |
| MX-3 | 写工具 → 允许 caller:`upsert_lab_result`→doctor_assistant;`upsert_food_diary`→parent_assistant/child_companion;`log_meal_checkin`、`award_badge`→child_companion;`push_to_emr`→doctor_assistant(`requires_confirmation=true`);`notify_*`/`trigger_warning_event`/`close_warning`→risk_warning。工具与 target_mcp 不匹配同样拒绝 |

`intent` 可传用户原文,也可直接传工具名;中文写入说法(写回病历、录入化验、打卡等)已登记别名,避免绕过 MX-3。

### classify_intent(text)

规则命中优先,未命中返回 `intent="unknown"` 且 `fallback="llm"`,由上层软分类后**再过一次安全闸**。

返回:`{intent, mcp, slots, confidence, allowed_callers, matched_keywords, candidates, fallback, note}`

| intent | 目标 MCP | 允许 caller |
|--------|----------|-------------|
| emergency_red_flag | a207-router-mcp | 全部(强制前置,`fallback=halt`) |
| patient_profile | a207-his-mcp | 调度/医生/营养师(RL)/家长(RL)/风险预警 |
| clinical_staging | a207-ckd-clinical-calc-mcp | 仅 doctor_assistant |
| lab_result | a207-lis-mcp | 医生/家长(受限视图)/风险预警 |
| nutrition_calc | a207-nutrition-calc-mcp | 医生/营养师/家长 |
| meal_plan | a207-meal-plan-mcp | 医生/营养师 |
| risk_assessment | a207-risk-rules-mcp | 医生/风险预警 |
| nutrition_assessment | a207-nutrition-assessment-mcp | 见权限矩阵,写入按 MX-3 |
| gamification | a207-gamification-mcp | 仅 child_companion |
| notify | a207-notify-mcp | 仅 risk_warning 可写 |
| report | a207-report-mcp | 医生/营养师/家长(RL)/风险预警 |
| knowledge | a207-knowledge-mcp | 全部,按角色切语料 |
| followup | a207-followup-mcp | 医生/营养师/风险预警 |

打分口径:命中关键词长度求和,同分按优先级(P0→P5)取胜;`confidence = min(0.95, 0.50 + 0.05 × 分数)`。
"排食谱/一周食谱/三餐怎么安排"路由到 M7;裸词"食谱"按构建规格 §2.1 归 M5,由调用方结合上下文决定是否转交。

### check_permission(caller, mcp, action="read")

需求文档 §7 权限矩阵的执行点。`action` 可传 `read`/`write`,也可直接传工具名。

返回:`{allow, access, resolved_action, reason, write_tool, requires_confirmation, limited_view}`
- `access` 为矩阵原始格子:`R` / `RL` / `R/W` / `-`;`limited_view=true` 表示只能给受限视图
- **精度说明**:命中已登记写工具时,MX-3 映射优先于矩阵格子(v1.2 已用 MX-3 收口写入路径),
  因此 `parent_assistant + a207-nutrition-assessment-mcp + upsert_food_diary` 放行,其余读操作仍按矩阵为 `-`。

### extract_slots(text)

正则 + 词典抽槽位,抽不到留空,绝不猜测。

返回:`{patient_id, food[], analyte[], date[], number[], age_years, weight_kg, height_cm, time_window_days, meal, measure[]}`
- `patient_id` 匹配 PCP 契约 `^P[0-9]{4,}$`
- `analyte` 归一到 PCP 字段名(potassium / phosphorus / scr / albumin / hemoglobin …),单位口径以
  `contracts/pcp-schema.json` 为准:Scr μmol/L,K/P/Ca mmol/L,能量 kcal,蛋白 g,身高 cm,体重 kg
- `number` 保留原始单位;斤自动折算 kg,月龄自动折算岁

## 验证

```
python -m py_compile src/a207_router_mcp/*.py
python tests/test_tools.py     # total=46 passed=46 failed=0
```

单测覆盖:MX-1/MX-2/MX-3 越权拒绝与正常放行、P0 断崖、未登记 caller/MCP、矩阵兜底、
意图路由 8 例、权限矩阵 6 组、槽位抽取 6 例,另含"无跨 a207 包引用 + 纯逻辑层不依赖 fastmcp"的静态自查。

启动(需先安装 fastmcp):`fastmcp dev src/a207_router_mcp/server.py` 或 `python -m a207_router_mcp.server`。

TDQS

B3.2/5.0

Scored across 4 tools

Disambiguation3/5

Tool purposes are mostly distinct, but check_permission and classify_intent both deal with caller permissions, and extract_slots overlaps with classify_intent's slot output, creating potential confusion about when to call each. Descriptions help clarify the different aspects (permission matrix vs routing vs safety gate vs slot extraction).

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern with lowercase snake_case: check_permission, check_safety_gate, extract_slots, classify_intent. Predictable and uniform.

Tool Count5/5

With only 4 tools, the server is tightly focused on routing and safety. Each tool covers a distinct aspect of the routing pipeline without redundancy, making the count well-scoped.

Completeness5/5

The tool set covers the full routing lifecycle: intent classification, slot extraction, permission checking, and safety gating. No obvious missing operations for the stated router purpose; the safety gate includes comprehensive MX-1/MX-2/MX-3 rules.

Maintenance

ActivityMaintained
ResponsivenessSyncing