在实际医疗场景里,AI Agent 的价值不在于能生成一段像模像样的自然语言回复,而在于它生成的结果能被医院、保险公司、清算所和 EHR 系统真实接收并处理。换句话说,Agent 的“执行层”必须先解决合规和互操作问题。对医疗数据交换来说,这个执行层的底层约束就是 X12 标准。
这篇文章会从一个最小可运行的医疗资格查询 Agent 入手,解释为什么 X12 不是一堆已经过时的 EDI 报文字段,而是决定 Agent 输出是否可落地的关键契约。你会看到完整的 270/271 交易集生成、发送、接收和解析过程,也会看到 Agent 在提取意图、生成报文、处理响应时最容易踩的坑。学完之后,你能把同样的思路迁移到 837 理赔、835 付款通知、278 转诊授权等更多 X12 交易集上。
1. 先理解医疗 AI Agent 为什么必须面对执行层问题
1.1 自然语言生成结果不等于可执行结果
大多数 AI Agent demo 停留在“用户问一句,Agent 答一段”的阶段。但在医疗领域,一段自然语言答案不能直接被保险公司或清算所接受,也不能自动进入 EHR 的预约、理赔、授权流程。医疗信息系统之间交换的是结构化数据,而结构化数据必须符合行业标准。这个标准就是 X12,它规定了报文由哪些段构成、段的顺序、字段长度、枚举值、日期格式、层级关系,以及交易对手之间如何确认收到。
如果把 Agent 的输出直接丢给下游系统,会引发三类问题。第一,语义不完整,系统不知道这笔请求是查资格、提交理赔还是查授权。第二,结构不对,即使内容是 270 资格查询,段的顺序或嵌套错误也会被网关拒绝。第三,字段不规范,比如日期不是 CCYYMMDD 格式、诊断码不是 ICD-10、服务类型代码不在枚举范围内。这些问题都不是“优化提示词”能解决的,需要在 Agent 架构中增加一个懂 X12 的执行模块。
1.2 执行层在 Agent 架构里承担什么职责
一个医疗 Agent 的典型流程是:接收用户输入,调用 LLM 做意图识别和实体抽取,形成结构化请求,然后调用后端系统完成实际动作。这个过程中,执行层负责把结构化请求转换为外部系统能接受的协议,并处理返回值。
对医疗 AI Agent 而言,执行层至少要包含三块能力:
- 交易集映射:把 Agent 识别出的业务语义映射到 X12 交易集的段和元素上。
- 报文生成与解析:生成符合语法规则的 EDI 文本,解析来自交易所或保险公司的响应。
- 校验与审计:在发送前检查必填项、枚举值、长度和循环嵌套,在接收后保留原始报文用于审计和回溯。
如果缺少这个执行层,Agent 只是一个“会聊天的高智商客服”;有了它,Agent 才真正具备“替用户办理业务”的能力。这也是 X12 在医疗 Agent 项目中被称为关键约束的原因:约束不是限制,而是保证结果可落地的必要条件。
2. X12 的语法和核心交易集是执行层绕不开的基线
2.1 EDI 文本的层次结构:段、元素、循环
X12 EDI 报文本质是纯文本,它依靠分隔符和段层级来表达业务含义。一个完整报文从外到内是:
- ISA/IEA 控制层:整个交换信封,包含发送方、接收方、控制号、日期时间。
- GS/GE 功能组层:一组同类型交易集的集合,包含功能组编码。
- ST/SE 交易集层:一个具体交易集,比如 270 或 837,包含交易集控制号。
- 业务段:每个段以两到三个字母的段标识开头,如 NM1 表示姓名、EQ 表示服务类型。
- 数据元素:段内用元素分隔符分开的字段。
- 复合元素:用复合元素分隔符分开的子字段,常见于 DTP、REF 等段。
ISA 段是定长的,比如发送方 ID 必须占 15 位,不够补空格。GS 和 ST 层则是变长的,只靠元素分隔符区分字段。这种混用规则是新手容易出错的地方。
2.2 医疗服务中最常见的 X12 交易集
医疗领域不是只有一个 X12 报文,而是按业务场景分成多个交易集。理解它们之间的关系,有助于 Agent 在执行层做好交易集路由。
| 交易集 | 中文场景 | 业务方向 | 常见用途 |
|---|---|---|---|
| 270/271 | 资格查询与响应 | 请求/响应 | 查询患者保险是否有效、覆盖范围、自付额 |
| 837 | 医疗理赔提交 | 请求 | 医院或诊所提交费用理赔 |
| 835 | 付款/汇款通知 | 响应 | 保险公司说明付款金额和原因 |
| 278 | 转诊授权 | 请求/响应 | 申请或返回转诊授权 |
| 834 | 保险注册 | 请求 | 批量投保登记 |
| 820 | 保费支付 | 请求 | 支付保费 |
对于一个 AI Agent 平台,通常先接 270/271,因为它逻辑相对简单、参与方结构清晰,而且对患者和前台运营价值高:快速确认保险是否有效、有无自付额、免赔额还剩多少。
2.3 为什么 270/271 适合作为 Agent 第一个落地点
270/271 交易集的特点是请求和响应一一对应。请求方发出 270,接收方返回 271,两边都使用相同的 HL 层级结构表达信息提供方、信息接收方和患者。这种对应关系让 Agent 的实现难度集中在“如何正确生成 270”和“如何解析 271”上,不涉及复杂的批次对账和状态机。跑通之后,再扩展到 837 理赔或 278 授权,核心 X12 组件可以复用。
3. 搭建一个可本地运行的最小资格查询 Agent
3.1 技术选型和环境准备
为了让例子可以离线复现,这里使用 Python 3.10 + FastAPI + httpx,通过一个模拟 EDI 网关完成 270/271 交换。LLM 部分使用 OpenAI 兼容接口,但核心 X12 生成和解析不依赖特定模型。
环境要求:
| 组件 | 版本/说明 |
|---|---|
| Python | 3.10 或更高 |
| FastAPI | 0.104 或更高 |
| uvicorn | 0.24 或更高 |
| httpx | 0.25 或更高 |
| pydantic | 2.x |
| openai | 1.x,仅用于调用 LLM |
| 操作系统 | Windows/Linux/macOS 均可 |
安装依赖:
python -m venv .venv source .venv/bin/activate # Windows 下为 .venv\Scripts\activate pip install fastapi uvicorn[standard] httpx pydantic openai3.2 项目结构
medical_x12_agent/ ├── agent_main.py # Agent 主流程,FastAPI 接口 ├── x12_builder.py # 生成 270 报文的 X12 构造器 ├── x12_parser.py # 解析 271 报文的 X12 解析器 ├── llm_extract.py # 调用 LLM,把自然语言转成结构化请求 ├── mock_edi_server.py # 模拟保险公司 EDI 网关 └── requirements.txt这种分层刻意把 LLM 和 X12 拆开。LLM 负责从自由文本中提取业务意图,X12 模块负责把结构化意图变成合法报文。这样即使后续更换模型,X12 逻辑也不会被破坏。
3.3 模拟 EDI 网关
先实现模拟网关,方便 Agent 后续发送 270 后能拿到一个固定的 271 响应。
# mock_edi_server.py from fastapi import FastAPI, Request from fastapi.responses import PlainTextResponse app = FastAPI() SAMPLE_271 = """ISA*00* *00* *ZZ*RECEIVERID01 *ZZ*SENDERID001 *231101*1200*^*00501*000000002*0*P*:~ GS*HS*RECEIVERID01*SENDERID001*20231101*1200*0001*X*005010X279A1~ ST*271*0001~ BHT*0022*11*10001234*20231101*1200*ET~ HL*1**20*1~ NM1*PR*2*MEDICAID*****PI*RECEIVERID01~ HL*2*1*21*1~ NM1*1P*1*DOE*JOHN****XX*1234567891~ HL*3*2*22*0~ NM1*IL*1*JACKSON*MICHAEL****MI*JACKSONM~ EB*1*D*73***30**DIS$150 COPAY AFTER DED~ REF*6R*2500~ DTP*307*D8*20231115~ SE*11*0001~ GE*1*0001~ IEA*1*000000002~""" @app.post("/edi/270") async def receive_270(request: Request): raw = await request.body() # 实际网关会先做语法校验,这里简化为收到合法 270 就返回 271 return PlainTextResponse(SAMPLE_271, media_type="text/plain") if __name__ == "__main__": import uvicorn uvicorn.run(app, host="127.0.0.1", port=8001)这个模拟网关没有真正解析收到的 270,只用于演示读写流程。实际对接清算所时,发送方还要处理认证、批量分包、回执和重试。
4. 核心实现:从自然语言到 270 报文
4.1 用 LLM 提取结构化资格查询意图
用户输入可能是“查一下 Michael Jackson 的 Medicaid 是否有效,生日是 1980-01-15”,也可能是“患者 JACKSONM,家庭医生是 John Doe,NPI 1234567891”。LLM 需要把这句话映射成三个参与方加一个服务类型。
这里用 Pydantic 定义提取结果,约束 LLM 输出结构:
# llm_extract.py from pydantic import BaseModel from openai import OpenAI class EligibilityRequest(BaseModel): provider_last_name: str provider_first_name: str provider_npi: str patient_last_name: str patient_first_name: str patient_member_id: str service_type_code: str = "30" def extract_eligibility_request(user_text: str, client: OpenAI) -> EligibilityRequest: prompt = f""" 你是医疗前台系统中的保险资格识别助手。 请从用户的自然语言中提取以下信息: - provider_last_name: 服务提供者姓氏 - provider_first_name: 服务提供者名字 - provider_npi: 服务提供者 NPI 编号 - patient_last_name: 患者姓氏 - patient_first_name: 患者名字 - patient_member_id: 患者保险会员号 - service_type_code: 默认 30,表示健康计划覆盖 只输出 JSON,不要解释。 用户输入: {user_text} """ resp = client.chat.completions.create( model="gpt-4o-mini", response_format={"type": "json_object"}, messages=[{"role": "user", "content": prompt}], ) return EligibilityRequest.model_validate_json(resp.choices[0].message.content)之所以不直接让 LLM 输出 X12 文本,是因为 LLM 对定长段、控制号和枚举值的记忆不可靠,很容易出现字段顺序错误、空格数量不对、枚举值非法等问题。让 LLM 先输出 JSON,再由专门模块生成 X12,错误率会低很多,也更容易做单元测试。
4.2 用 X12 构造器生成 270 报文
270 请求的核心是把三个参与方放进 HL 循环:
- HL 1:信息源,即交易接收方,例如保险公司。
- HL 2:信息接收方,即发起请求的诊所或服务提供者。
- HL 3:患者或会员。
这三层通过 HL 的父子关系嵌套,然后用 NM1 描述各自姓名和 ID,用 EQ 声明服务类型。
# x12_builder.py from datetime import datetime from llm_extract import EligibilityRequest INFORMATION_SOURCE_ID = "RECEIVERID01" INFORMATION_RECEIVER_ID = "SENDERID001" def build_270(req: EligibilityRequest, control_number: int = 1) -> str: now = datetime.now() date_str = now.strftime("%Y%m%d") time_str = now.strftime("%H%M") st01 = "270" st02 = "0001" isa13 = str(control_number).zfill(9) group_no = "0001" sender_id = INFORMATION_RECEIVER_ID.ljust(15) receiver_id = INFORMATION_SOURCE_ID.ljust(15) lines = [] lines.append( f"ISA*00* *00* *ZZ*{sender_id}*ZZ*{receiver_id}" f"*{date_str}*{time_str}*^*00501*{isa13}*0*P*:~" ) lines.append( f"GS*HS*{INFORMATION_RECEIVER_ID}*{INFORMATION_SOURCE_ID}" f"*{date_str}*{time_str}*{group_no}*X*005010X279A1~" ) lines.append(f"ST*{st01}*{st02}~") lines.append((f"BHT*0022*13*{date_str}{time_str}*{date_str}*{time_str}*ET~")) lines.append("HL*1**20*1~") lines.append(f"NM1*PR*2*MEDICAID*****PI*{INFORMATION_SOURCE_ID}~") lines.append("HL*2*1*21*1~") lines.append( f"NM1*1P*1*{req.provider_last_name}*{req.provider_first_name}" f"****XX*{req.provider_npi}~" ) lines.append("HL*3*2*22*0~") lines.append( f"NM1*IL*1*{req.patient_last_name}*{req.patient_first_name}" f"****MI*{req.patient_member_id}~" ) lines.append(f"EQ*{req.service_type_code}~") lines.append(f"SE*9*0001~") lines.append(f"GE*1*0001~") lines.append(f"IEA*1*{isa13}~") return "\n".join(lines)这段代码最容易出错的地方有三个。
第一,ISA 段中发送方和接收方 ID 必须补齐 15 位。ljust(15)会把 ID 补空格到 15 位,否则定长校验会直接失败。
第二,NM1 段里使用了****来跳过多余字段。270 中 NM1 的布局是NM1*实体代码*类型*姓*名*中间名*后缀*ID 代码限定符*ID,如果中间名和留空,就要用空元素占位。
第三,HL 层的父子关系不能随意改。HL1**201 表示第一层是信息源,第 20 号实体类型;HL21211 以第一层为父层,表示信息接收方;HL32220 以第二层为父层,表示患者。这里的数字一旦写错,接收方解析时会把 NM1 分错角色。
4.3 Agent 主流程:生成、发送、接收
Agent 主流程把 LLM 提取、270 生成、HTTP 发送、271 解析串起来。
# agent_main.py from fastapi import FastAPI from pydantic import BaseModel from openai import OpenAI from llm_extract import extract_eligibility_request from x12_builder import build_270 from x12_parser import parse_271 import httpx app = FastAPI() client = OpenAI() MOCK_EDI_URL = "http://127.0.0.1:8001/edi/270" class UserInput(BaseModel): text: str class AgentResponse(BaseModel): eligibility_status: str message: str deducible: str copay: str original_271: str @app.post("/agent/eligibility", response_model=AgentResponse) async def eligibility(user_input: UserInput): req = extract_eligibility_request(user_input.text, client) x12_270 = build_270(req) async with httpx.AsyncClient() as http: resp = await http.post(MOCK_EDI_URL, content=x12_270, headers={"Content-Type": "text/plain"}) x12_271 = resp.text result = parse_271(x12_271) return AgentResponse( eligibility_status=result["status"], message=result["message"], deducible=result["deductible"], copay=result["copay"], original_271=x12_271, ) if __name__ == "__main__": import uvicorn uvicorn.run(app, host="127.0.0.1", port=8000)这一步的关键是“Agent 的决策点”。LLM 决定用户意图,但真正发出去的动作由 X12 模块执行。架构上不要让 LLM 直接操作网络,否则模型异常输出会影响下游。
5. 解析 271 响应:把 EDI 文本转回业务语义
5.1 271 的段结构与关键字段
271 响应和 270 请求使用同样的 HL 层级。前三层依然是信息源、信息接收方、患者。但 270 中 EQ 段表示“我想查什么服务”,271 中 EB 段表示“该服务的资格状态”,EB 后通常跟 REF、DTP 等补充段。
一个简化版 271 的关键字段:
| 段 | 含义 | 关键元素 |
|---|---|---|
| HL | 层级 | 1/2/3 层,业务角色 |
| NM1 | 参与方姓名 | 第 2 个元素:PR/1P/IL |
| EB | 资格或福利信息 | 第 2 个元素:资格状态码;第 4 个元素:覆盖层级 |
| REF | 参考信息 | 第 2 个元素:可扣除额;第 3 个元素:金额 |
| DTP | 日期 | 日期限定符和日期值 |
EB 段的第 2 个元素是资格状态码,常用值:
| 状态码 | 含义 |
|---|---|
| 1 | 有效 |
| 2 | 无效 |
| 3 | 未确认 |
| 4 | 已确认,但存在限制 |
5.2 用行遍历法解析 271
271 解析器不需要完整的 EDI 引擎,按行遍历已经能覆盖多数 Agent 场景。
# x12_parser.py def parse_271(raw_271: str) -> dict: result = { "status": "UNKNOWN", "message": "", "deductible": "", "copay": "", } in_patient_loop = False for line in raw_271.splitlines(): line = line.rstrip("\n") seg = line.split("*") seg_id = seg[0] elements = [e.replace("~", "") for e in seg] if seg_id == "HL": hl_code = elements[3] if len(elements) > 3 else "" in_patient_loop = (hl_code == "22") elif seg_id == "EB" and in_patient_loop: status_map = {"1": "ACTIVE", "2": "INACTIVE", "3": "UNCONFIRMED", "4": "RESTRICTED"} result["status"] = status_map.get(elements[1], elements[1]) result["message"] = elements[6] if len(elements) > 6 and elements[6] else "" elif seg_id == "REF" and in_patient_loop: ref_code = elements[1] ref_value = elements[2] if len(elements) > 2 else "" if ref_code == "6R": result["deductible"] = ref_value elif ref_code == "F5": result["copay"] = ref_value return result上面的代码只解析了 271 的最简结构。真实 271 中,EB 段可能有多条,一条代表住院覆盖,另一条代表门诊覆盖;REF 也可能有多条。生产版本应该把多个 EB 段组织成列表,而不是覆盖单个字段。
5.3 让 Agent 输出对用户可读的结果
解析完状态码后,Agent 的最终输出应该回到业务语言。
患者 Michael Jackson 的保险状态为有效。 自付额剩余:2500 共付额:150 原始 271 报文已保存到审计日志。这一步不能省略。用户不需要读 EDI,但运营人员和系统审计一定需要保存原始 271,以便出现争议时查证。
6. 运行验证:从启动网关到最终结果
6.1 启动步骤
先启动模拟 EDI 网关:
python mock_edi_server.py再启动 Agent 服务:
python agent_main.py然后用 curl 或 HTTP 客户端发送一条自然语言请求:
curl -X POST http://127.0.0.1:8000/agent/eligibility \ -H "Content-Type: application/json" \ -d '{"text": "查一下 Michael Jackson 的 Medicaid 是否有效,会员号 JACKSONM,服务提供者是 John Doe,NPI 是 1234567891。"}'6.2 预期结果与自检点
正常情况下,响应 JSON 包含:
{ "eligibility_status": "ACTIVE", "message": "DIS$150 COPAY AFTER DED", "deducible": "2500", "copay": "", "original_271": "ISA*00*..." }如果出现eligibility_status: UNKNOWN,优先检查:
- 模拟网关是否返回了 271 文本。
- 271 中 HL*3 后是否跟着 EB 段。
- 解析器是否把
REF*6R*2500之后的内容识别为 REF 段。
注意:只验证“接口返回 200”是不够的。要分别验证三个层面:HTTP 请求成功、270 报文通过语法检查、271 解析结果与期望一致。三层全通过,才算执行层可用。
7. 常见报错和排查链路
7.1 报错现象、原因和处理方案
| 报错现象 | 常见原因 | 检查位置 | 处理建议 |
|---|---|---|---|
| 270 报文被网关拒绝 | ISA 段发送方/接收方 ID 不足 15 位 | ISA 第 6、8 个元素 | 用ljust(15)补齐空格 |
| 270 报文被网关拒绝 | 控制段 GS/ST/IEA 控制号不一致 | GS6、ST2、IEA2 | 发送前检查三个控制号是否匹配 |
| 271 解析不到 EB 状态 | HL 层级判断错误 | 271 中的 HL 段与 NM1 段 | 确认 HL*3 是否表示患者层(22) |
| Agent 返回空状态 | 模拟网关没收到 POST 或返回非 271 文本 | 模拟网关日志、响应头 | 打印原始响应文本,确认格式 |
| LLM 提取字段缺失 | 用户输入缺少 NPI 或会员号 | LLM 返回的 JSON | 在提示词里增加必填字段约束,或增加校验 |
| 日期格式错误 | 使用 YYYY-MM-DD 而不是 X12 的 CCYYMMDD | ISA/GS/DTP 段 | 统一使用datetime.strftime("%Y%m%d") |
| 循环嵌套错误 | HL 父层编号写错 | HL 的第 2 个元素 | 按照 HL1 父为空、HL2 父为 1、HL3 父为 2 检查 |
7.2 排查顺序:先结构后业务
X12 报错排查和普通 HTTP 调试不同,要按从外到内的顺序查:
- 交换层:ISA/IEA 是否合法,发送方和接收方 ID 是否在对方系统有登记。
- 功能组层:GS/GE 是否合法,功能组代码是否为 HS(医疗资格)。
- 交易集层:ST/SE 是否存在,交易集控制号是否唯一。
- 段顺序:是否按 X12 标准定义的段顺序排列。
- 循环嵌套:HL 父层关系是否正确,每个循环的起点终点是否明确。
- 元素级别:必填元素是否缺失,枚举值是否合法,长度是否超限。
实际排错时,建议先打印原始 EDI 文本,用行号标注段序列,再对照 X12 交易集手册逐段检查。不要只看错误码,因为很多网关只会返回“TRANSACTION REJECTED”,把具体原因藏在配套的 TA1 或 999 回执里。生产对接时必须解析 TA1 和 999 回执,才能定位到具体段。
8. 把最小 Agent 扩展成生产级医疗执行层
8.1 本地示例和生产环境的差距
本地模拟网关把 270 发到 HTTP 接口就能收到 271,但真实医疗网络远不止如此。生产环境要面对:
- 保险公司或清算所提供的接入地址通常是 SFTP、HTTPS API 或 VAN,且要求 TLS 证书。
- 必须实现 TA1 确认、997/999 功能性确认,才能知道报文是否被接收方语法接受。
- 同一笔交易可能产生多条响应,271 可能因分页或多次返回而拆分。
- 发送方和接收方 ID、ISA 限定符、GS 版本号需要在对接测试环境中预先注册。
- 生产报文需要保存完整原文、哈希或数字签名,满足审计要求。
这些差异决定了“本地跑通”和“生产上线”之间还有大量工程工作。
8.2 X12 校验器是执行层最后一道闸门
在 Agent 输出 270 后、真正发送之前,必须插入一个独立的校验器。校验器应该检查:
- 必填段是否存在。
- 枚举值是否合法,例如服务类型代码 30 表示健康计划覆盖,不能写成 33。
- 日期格式是否严格为
D8序列对应的CCYYMMDD。 - 每个 HL 循环是否满足父子关系。
- ISA 定长字段是否满足长度要求。
- 控制号是否在当前交换批次内唯一。
建议把校验器做成 pytest 可调用的纯函数,给每类交易集写 fixture,这样 Agent 版本升级或模型替换后,校验逻辑依然稳定。
8.3 多交易集扩展方向
跑通 270/271 之后,同一个 Agent 平台可以扩展:
| 交易集 | 扩展点 | 新增能力 |
|---|---|---|
| 837 | 服务行、诊断码、CLM 段 | Agent 拆解病历,生成理赔服务行 |
| 835 | 支付金额和调整原因 | 自动对账,识别拒付原因 |
| 278 | 授权请求和响应 | 用 Agent 提前校验授权规则 |
| 834 | 批次注册 | 批量处理成员变更 |
扩展时不要为每个交易集重复造轮子。把 ISA/GS/ST 控制层、段遍历器、元素校验器做成公共模块,每个交易集只实现自己的业务映射规则。
8.4 审计与可观测性
医疗 Agent 如果要进入正式业务,必须能回答三个问题:这个结果是谁生成的、依据哪条原始报文、实际发给了谁。因此,日志至少要记录:
- 用户原始输入。
- LLM 提取出的结构化 JSON。
- Agent 生成的原始 270 文本。
- 网关返回的原始 271、999 或 TA1。
- 解析后的业务结果。
- 本次交易的控制号和发送时间。
这些日志不能只存在业务表里,建议同时写一份独立的审计文件,保留 EDI 原文,方便后续交给合规团队检查。
9. 最佳实践清单
9.1 开发阶段清单
- 先把 X12 生成器和解析器写成纯函数,不依赖 LLM,确保输入输出可测试。
- 用固定 fixture 测试 ISA 层、GS 层、ST 层,不要只测业务段。
- 对每次生成都保留控制段快照,避免控制号重复。
- 用模拟网关先跑通 270/271,再接入真实测试环境。
9.2 上线前清单
- 确认发送方 ID、接收方 ID 已经在目标环境注册。
- 确认交易集版本,例如 005010X279A1,与接收方网关一致。
- 接入 TA1 和 999 回执解析,处理语法拒绝和业务拒绝。
- 对 LLM 提取结果增加字段级校验:必填、长度、枚举值。
- 配置 X12 原文日志和审计留存周期。
9.3 思考方式清单
- 不要认为“LLM 能生成合法 JSON”就等于“能生成合法 X12”。
- 不要用正则或字符串拼接硬扛复杂交易集。
- 不要跳过回执解析,否则你永远不知道报文为什么被拒。
- 不要让下游系统直接消费自然语言结果,先转成结构化业务对象。
10. 下一步怎么深化
医疗 AI Agent 的竞争力不在提示词技巧,而在执行层能不能稳定、合规、可审计地连接医疗数据交换网络。X12 标准在这里扮演的是约束,也是基础设施。一个 Agent 能生成几百字漂亮的理赔解释,比不上一个能发出合法 837、解析 835 回执、自动识别拒付原因的 Agent 更有实际价值。
读完这篇文章,建议先不要急着接入真实保险公司。把最小 270/271 Agent 跑起来,改几组字段,看看模拟网关返回什么,再写一个简单的校验器来拦截错误报文。这个过程会帮你建立对 X12 段结构、循环嵌套和控制号的直觉。下一步再考虑扩展 837 理赔、835 对账,或者引入多 Agent 协同:一个 Agent 负责解析用户输入,一个 Agent 负责生成交易集,一个 Agent 负责解析回执并总结异常。这种架构下,每个 Agent 的职责边界会更清晰,而 X12 标准就是所有 Agent 共同遵守的通信协议。