门诊待入院登记
版本 1.1 · 2026-10-21 · 变更见同目录 CHANGELOG.md
把「微信群里的门诊患者信息」整理成一张可打电话的表格,并每天生成「明日新入」名单发到治疗组群。
这个技能处理什么
治疗组微信群里,上级在门诊接诊后会把患者信息发到群里,格式极不统一,且混杂大量诊断讨论、紧急情况沟通。本技能负责:
- 录入 —— 从整段聊天记录里挑出患者条目,格式化写入腾讯智能表格
- 汇总 —— 追踪状态变化,生成「明日新入」名单供复制发群
- 查询 —— 回答「今天登记了谁」「还有谁没叫」「某人叫了没」
不做医疗决策。 只做信息整理与提醒。
一、数据源与去向
| 环节 | 载体 |
|---|---|
| 输入 | Agent Mail 邮箱里的邮件(主);对话中粘贴的截图(辅) |
| 存储 | 腾讯智能表格(smartsheet) |
| 输出 | 对话中的纯文本名单,用户自行复制发群 |
二、前置检查(每次执行先做)
tdoc_init→ 必须返回READY。返回connector_disabled时提示用户:左侧栏 → 专家·技能·连接器 → 连接器 → 腾讯文档 → 开启 → 扫码授权。- 调腾讯文档 API 必须设置
TDOC_API_BASE_URL=https://docs.qq.com,否则 502。 - 读
config.json拿file_id、sheet_id、allowlist、group_name。file_id为空即视为未初始化 → 走 §2.5。
二之二、首次初始化(file_id 为空时执行)
触发:用户说「初始化」「装好了」「开始用」,或 config.json 的 file_id 为空。不要等用户提,检测到空配置就主动发起。
2.5.1 三问(一次问完,不要分三轮)
| 问 | 用途 | 默认 |
|---|---|---|
| 收件邮箱地址 | 填 allowlist,只认这个发件人 |
无,必答 |
| 组名 | 出文时的抬头,如「张组明日新入」 | 本组 |
| 是否给上级开只读 | 只影响分享链接,不影响功能 | 不开 |
2.5.2 建表(14 字段,顺序不能变)
用腾讯文档连接器的智能表格能力:先建空表拿到 file_id / sheet_id,再按序 add_fields,最后 delete_records 清掉建表自带的空行。
调用前先 tdoc_schema <service> <tool> 核对签名——工具名和参数各版本可能不同,不要凭记忆猜。
| # | 字段 | 类型 | 额外属性 |
|---|---|---|---|
| 1 | 登记日期 | dateTime | property_date_time: {"auto_fill": false, "format": "yyyy-mm-dd"} |
| 2 | 状态 | singleSelect | 选项见 2.5.3 |
| 3 | 姓名 | text | |
| 4 | 病情术式 | text | |
| 5 | 性别年龄 | text | |
| 6 | 电话 | phoneNumber | 手机端点开可直接拨号 |
| 7 | 联系人 | text | |
| 8 | 登记号 | text | |
| 9 | 备注 | text | |
| 10 | 来源 | text | |
| 11 | 出现次数 | number | |
| 12 | 首次登记日期 | dateTime | 同 #1 |
| 13 | 消息时间 | text | 建议隐藏,仅用于同日排序 |
| 14 | 已上报日期 | dateTime | 同 #1;不要填默认值 |
> dateTime 漏写 property_date_time 会报 22018 create smartsheet mutation failed,错误码看不出原因。
> 电话写成 text 类型也能用,但手机端点开不能拨号——必须 phoneNumber。
2.5.3 状态选项(7 个,顺序与配色)
未叫(灰) → 已叫(蓝) → 未接(橙) → 已定(绿) → 不来(红) → 已入院(紫) → 待确认(粉)
配色只能手动设,接口不支持。建完提示用户自己点一遍颜色,或直接跳过——不影响功能。
2.5.4 写回配置
把 file_id、sheet_id、allowlist、group_name 写进 config.json,field_map 至少填 登记日期/状态/姓名 三个 id。
2.5.5 自检(必做,别跳过)
按第五节格式矩阵写 1 条测试记录 → 回读 → 确认电话不为空 → 删除该记录。电话为空说明用了 text_value,改 string_value 重试。
自检通过再告诉用户「可以用了」,并把表格链接发给他。
三、能力一:录入
3.1 取信
- 只处理收件箱中发件人 ∈ allowlist 的邮件。其他邮件一律不读正文、不入库、不删除、不回复。
- 未读优先,但也要处理「已读但未入库」的(用户可能手动点开过)。判据:邮件主题含「聊天记录」或正文含日期分隔线。
- 解析完成后删除该邮件,邮箱不留存患者信息。
3.2 三种输入格式(都要认)
格式 A —— 微信「添加至邮件」导出(主)
微信群"<群名>"的聊天记录如下:
————— 2026-10-20 —————
发言人昵称 上午11:58
张一,肝消融联合栓塞,13800001234,
<空行>
发言人昵称 下午2:06
患者姓名0000000001 李二 13900001234 谢谢,周四日间
特征:发言人昵称与时间在同一行,以 上午/下午H:MM 结尾;内容在下一行;条目之间用空行分隔。
邮件 body_format 为 HTML,需先剥标签,并把 &nbsp; 转空格、&quot; 转引号。
格式 B —— 微信「合并转发」截图或复制文本(辅)
发言人昵称
2026年10月20日 上午11:58
张一,肝消融联合栓塞,13800001234,
特征:发言人、时间、内容各占一行。
格式 C —— 分行式(另一类治疗组常用,最野)
张教授(介入) 10月14日 上午 9:06
张一,女,80岁。肝癌。营口。
13800001234(女儿)
特征与坑:
| 特征 | 处理 |
|---|---|
时间与发言人在同一行,但带 M月D日 且无年份 |
年份按当前年补;同时从 sender 里剥掉日期部分 |
| 内容跨两行:第一行「姓名,性别,年龄。诊断。地名。」,第二行「电话(联系人)」 | 两行需合并后统一切分 |
| 句号「。」兼作分隔符 | 切分符须包含 。 |
电话带括号标联系人:13800001234(女儿)、(儿子)、(本人) |
必须先剥括号再匹配 11 位,否则整条判待确认、电话丢失 |
| 末尾地名(营口/朝阳/丹东/内蒙…) | 从病情里剥离,写入备注「来源地:XX」,避免污染诊断 |
姓名形如「周X」(周二、周天),与星期同形 |
仅当它是本条首段且下一段就是性别(周二,男,62岁)才当姓名;其余位置仍按星期写入备注 |
> 这一格式最容易在「电话带括号」上翻车:正则 ^1\d{10}$ 直接匹配会失败,导致全部条目掉进待确认且电话为空。
> 两种格式的日期行/发言人行的行数不同,解析时必须先判断再切块。
3.3 抽取规则
每条内容行按「中文逗号 / 全角空格 / 顿号」切分为若干片段,然后归类:
| 片段特征 | 归入字段 |
|---|---|
| 2–4 个汉字,且不是病名/术式 | 姓名 |
| 11 位数字且以 1 开头 | 电话(括号后缀 → 联系人) |
| 10 位纯数字 | 登记号 |
| 含「岁」或独立 1–3 位数字紧跟性别 | 性别年龄 |
| 其余描述性文字 | 病情术式(多个片段合并,保留原文) |
姓名是唯一不允许出错的字段。 拿不准时不要猜——整条进「待确认」。
常见变体要能处理:
姓名 : 张一/手机 : 13800001234—— 带标签的键值对形式患者姓名0000000001 李二 13900001234 谢谢,周四日间—— 登记号在姓名前,姓名在中间老李,肝,138xxxxxxxx—— 只有简称,无法确定全名 → 待确认- 一条消息里塞了两个人 → 拆成两条,拆不干净则整条进待确认
3.4 字段映射
不要翻译口语缩写。「phc」「梗黄」「ctace」原样保留,科室内部认得。仅在明显笔误时于备注标注。
| 表字段 | 类型 | 来源 |
|---|---|---|
| 登记日期 | dateTime | 该条消息所属日期(不是邮件日期) |
| 状态 | singleSelect | 新入一律 未叫;抽不全则 待确认 |
| 姓名 | text | 原文照抄 |
| 病情术式 | text | 合并,原文 |
| 性别年龄 | text | 女 66 形式;原文没有则留空 |
| 电话 | phoneNumber | 完整 11 位;不完整则留空 + 待确认 |
| 联系人 | text | 电话后括号里的人(13800001234(女儿) → 女儿);无则留空 |
| 登记号 | text | 10 位数字,保留前导 0 |
| 备注 | text | 剥离出的附加信息(增强CT检查中、周四日间、来源地:营口) |
| 来源 | text | 发言人昵称 |
| 出现次数 | number | 同名同电话已存在则 +1,否则 1 |
| 首次登记日期 | dateTime | 首次出现时的登记日期 |
| 消息时间 | text | 24 小时制 HH:MM(下午2:06 → 14:06),用于同日内排序 |
| 已上报日期 | dateTime | 由汇总能力写入,录入时留空 |
3.5 校验与存疑
以下情况整条标为 待确认,仍写入表格(不丢弃),并在回报中单列:
- 姓名缺失、或不足 2 字、或疑似简称
- 电话不足 11 位
- 无法确定哪一段是病情描述
- 一条消息疑似包含多个患者且拆不干净
宁多一条待确认,不能少一个患者。
3.6 去重
判重键 = 姓名 + 电话。
| 情况 | 处理 |
|---|---|
| 姓名电话都相同,已存在 | 不新增;出现次数 +1;新信息(如更详细的病情)追加到备注,不覆盖原文 |
| 同名但电话不同 | 视为两人,新增 |
| 同电话但姓名不同 | 视为换号或笔误,新增并在备注标注「与 X 电话相同,待核」 |
跨邮件、跨日期一律全局判重。同一患者多天后再次出现,保留原状态不重置。
> 缺姓名或缺电话的条目没有判重键。这类只能靠「同一封邮件不重复处理」保证,若同一份记录被投递两次会产生重复行——回报时要提示用户去表里删。
四、能力二:明日新入
4.1 筛选逻辑
业务节奏是头一天叫第二天的人,而录入速度通常快于叫号速度。因此判据是状态变化,不是登记日期。
| 条件 | 归属 |
|---|---|
状态 = 已定 且 已上报日期 为空 |
新增 → 进正式名单 |
状态 = 已定 且 已上报日期 非空 |
滞留 → 进提醒区 |
状态 ∈ {已入院, 不来, 未叫, 已叫, 未接, 待确认} |
不显示 |
> 为什么必须有「滞留」区:患者已定名单已发,但第二天没来。若无此区,他会被永久跳过,人就这么漏了。
4.2 输出格式
<组名>明日新入(N 人)
1. 姓名,诊断,性别年龄,登记号,电话
2. …
【仍未入院】
· 姓名,诊断,性别年龄,电话(M-D 已报,还没来)
—— 新增确认 N 人。确认无误回「确认」,我记为已上报;要改哪条直接说。
规则:
- 序号按登记日期先后排
- 诊断原文照抄,不翻译缩写
- 性别年龄用
女 66,不写「66岁」 - 登记号、性别年龄、电话缺失时跳过该段,不留占位符
- 名单中保留电话(用户需据此联系未按时到达的患者)
- 「仍未入院」区为空时整段省略
4.3 确认与落库
- 生成名单后不立即写库,等用户确认。
- 用户回「确认」→ 把本批全部记录的
已上报日期写为今天,然后回报「已记为上报 N 人」。 - 用户提出修改 → 按修改重生成,再次等待确认。
- 用户未确认就去做别的 → 不落库,下次生成时这批仍会作为新增出现(这是期望行为,宁可重复不可遗漏)。
五、写入格式矩阵(实测,必须照做)
腾讯智能表格各字段类型的写入格式不同,用错类型不会报错,会静默丢数据。
| 类型 | 写法 |
|---|---|
| text | {"field":"姓名","text_value":{"items":[{"text":"张一","type":"text"}]}} type 必填 |
| singleSelect | {"field":"状态","option_value":{"items":[{"text":"未叫"}]}} 只传文本,服务端解析为选项 id |
| dateTime | {"field":"登记日期","string_value":"<毫秒时间戳字符串>"} |
| number | {"field":"出现次数","number_value":1} |
| phoneNumber | {"field":"电话","string_value":"13800001234"} |
> ⚠️ 电话字段用 text_value 会被静默忽略:不报错、不提示,回读为空。必须用 string_value。这是最容易踩且最难排查的坑。
其他实测注意:
- 智能表格新建自带 5 条空记录,需
smartsheet.delete_records(参数record_ids数组)清理,否则干扰排序与导出。 list_records需显式传limit,默认只回 100 条。tdoc_schema需要两个参数:tdoc_schema <service> <tool>。- 本地时区固定
UTC+8,时间戳按此刻转换,不要手算。
五之二、脚本与执行顺序
scripts/ 下四个文件,职责单一,可独立测试。
| 脚本 | 作用 | 输入 → 输出 |
|---|---|---|
parse.py |
邮件正文 → 患者条目 | --file / --text / --stdin → 条目 JSON |
merge.py |
新条目与表内记录判重合并 | --new + --exist → {to_add, to_update, pending} |
summary.py |
生成明日新入名单 | --records + --group → 文本 + --out-json |
common.py |
记录拍平、毫秒转日期 | 被上面三个引用 |
录入流程:
1. 从邮箱取正文 → 存为临时文件(或直接 --text 传入)
2. parse.py → 条目数组
3. 调 smartsheet.list_records 拿现有记录 → 存 JSON
4. merge.py --new 条目 --exist 现有 → 得到 to_add / to_update / pending
5. to_update → smartsheet.update_records(出现次数、备注)
6. to_add → smartsheet.add_records(按第五节格式矩阵转换)
7. pending → 同样入表,状态填「待确认」,并在回报中单列
8. 删除已解析的邮件
9. 回报:新增 N 条、更新 M 条、待确认 K 条(逐条列出原因)
汇总流程:
1. list_records → 存 JSON
2. summary.py --records x.json --group 本组 --out-json sum.json
3. 把文本原样回给用户(不要改写、不要加解释)
4. 等用户回「确认」
5. 确认后:把 sum.json 里的 record_ids 逐条 update_records,写「已上报日期」= 当前毫秒时间戳
6. 回报「已记为上报 N 人」
> 第 4 步的等待是强制的。未确认就不写「已上报日期」,否则这批人下次不会再出现在新增里,会造成永久漏人。
五之三、踩过的 API 坑
| 现象 | 原因与解法 |
|---|---|
add_fields 报 code:22018 create smartsheet mutation failed |
dateTime 字段必须带 property_date_time,例如 {"auto_fill":false,"format":"yyyy-mm-dd"}。只传 title+type 会失败 |
业务错误看不到:structuredContent.error 为空但实际失败 |
业务错误包在 JSON-RPC 顶层 error 里,不在 structuredContent 里。判断成功要看顶层有没有 error 键 |
| 电话写入后回读为空 | 见第五节,必须用 string_value |
tdoc_schema 报缺少 tool 参数 |
语法是 tdoc_schema <service> <tool>,两个参数都要 |
| 建表后出现 5 条空记录 | 智能表格自带,用 delete_records(参数 record_ids 数组)清理 |
六、配置
config.json:
{
"group_name": "本组",
"file_id": "<智能表格 file_id>",
"sheet_id": "<工作表 sheet_id>",
"allowlist": ["<发件邮箱>"],
"field_map": { "登记日期": "fj6Yt2", "状态": "f90Fq0", "姓名": "f5nQnY" }
}
allowlist是硬门槛,不在名单里的邮件一律不处理。field_map仅作参考,实际写入按字段标题匹配,不只依赖 id。- 字段缺失时(如没有「性别年龄」列),在回报中提示,不静默跳过。
七、边界与隐私
- 表格含患者完整姓名与手机号,属敏感个人信息。禁止主动外发、导出、分享链接。
- 所有对外动作(发群、发邮件)由用户自己执行,本技能只生成文本。
- 邮箱中的原始邮件解析后即删,不留存。
- 所有示例、文档、报错文案中的患者信息必须使用虚构值:姓+中文数字(张一、李二),电话保留前 3 后 4(
138****1234)。 - 只做登记与提醒,不给出任何诊疗建议。
八、常见故障
| 现象 | 排查 |
|---|---|
| API 报 502 | 未设 TDOC_API_BASE_URL=https://docs.qq.com |
connector_disabled |
腾讯文档连接器未授权,需扫码 |
| 电话列写入后为空 | 用了 text_value,改 string_value |
| 表格有多余空行 | 建表自带,需 delete_records |
| 某个患者一直不出现 | 检查状态是否为 已定;若已上报过且未入院,去「仍未入院」区找 |
| 姓名识别错 | 原文含简称或同条消息多人,已判待确认,需人工核对 |