ingFang SC", Cambria, Cochin, Georgia, serif;font-size: medium;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;orphans: 2;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;white-space: normal;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;">很久没写公众号了, 最近在做企业级别智能体协作系统,以便行业专家能够在AI的加持下协作高效完成价值创造,过程中天天和本体/Ontology这个概念打交道,以下这篇就和本体/Ontology强相关,供大家参考ingFang SC", Cambria, Cochin, Georgia, serif;font-size: medium;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;orphans: 2;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;white-space: normal;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;">基于大模型的记忆,一直是影响大模型Agent效果的重点环节之一, 在对信息准确度要求较高的生产环境中, 这个问题更加明显。ingFang SC", Cambria, Cochin, Georgia, serif;font-size: medium;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;orphans: 2;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;white-space: normal;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;">本文介绍了一种基于本体Ontology的记忆实现及评估系统, 以便在医疗, 法律, 金融等准确度要求较高的应用环境中, 保证领域相关的精准行业记忆的有效使用。ingFang SC", Cambria, Cochin, Georgia, serif;font-size: medium;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;orphans: 2;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;white-space: normal;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;">P.S. 本文以医疗行业为例, 会出现较多医疗行业相关名词; 另外最近的一个感悟, 就是传统行业中有太多能够使用AI提效的地方, 所以做AI的一定要深入行业, 行业领域知识及业务, 才是真正有价值的地方。ingFang SC", Cambria, Cochin, Georgia, serif;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;letter-spacing: normal;orphans: 2;text-align: start;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;white-space: normal;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;">本体记忆的生产架构(Neo4j + LangGraph)ingFang SC", Cambria, Cochin, Georgia, serif;font-size: medium;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;orphans: 2;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;white-space: normal;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;"> ingFang SC", Cambria, Cochin, Georgia, serif;font-size: medium;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;orphans: 2;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;white-space: normal;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;">在生产环境, 大型语言模型在推理、总结和生成文本方面表现很好,但当涉及记忆结构化知识时,它们会以非常微妙,且危险的方式出问题。ingFang SC", Cambria, Cochin, Georgia, serif;font-size: medium;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;orphans: 2;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;white-space: normal;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;">如果你向一个LLM询问一个本体(或者叫Ontology)驱动的领域的问题,我们经常会看到矛盾、定义漂移,或者那些看似大模型很自信却悄悄违反底层逻辑或者事实的答案。ingFang SC", Cambria, Cochin, Georgia, serif;font-size: medium;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;orphans: 2;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;white-space: normal;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;">模型知道这些词,但它并不能可靠地记住赋予这些词意义的结构。ingFang SC", Cambria, Cochin, Georgia, serif;font-size: medium;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;orphans: 2;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;white-space: normal;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;">大多数现代系统试通过检索来解决这个问题:用本体片段提示LLM,注入Graph上下文,或在查询时重新加载模式。这种模式一般能产生短暂的正向效果,但很快就会出错。检索是无状态的,Prompt是脆弱不稳定的,两者都无法让系统持久地感知它已经学习、验证或认定为真正的内容。因此,本体变成了被动的参考,而不是主动的记忆,一致性在交互中逐渐瓦解。 本文探讨了一种不同的方法:本体记忆系统,一个专用的记忆层,使AI系统能够随时间保留、验证和重用结构化知识。 我们不是将Ontology视为静态工件或一次性上下文注入,而是将它们视为可以强化、检查冲突并在推理周期中重用的演化记忆对象。 结果不仅是更好的答案,而且是行为更像可靠知识工作者而非随机文本生成器的AI系统。 设想下,你是一家大型医院的医疗编码员。你的工作是将医生的诊断——比如“患者因未控制的2型糖尿病出现高血糖”——翻译成精确的医疗代码,如E11.65。这些代码是ICD-10-CM的一部分,这是一个拥有超过70,000个诊断代码的庞大系统。一个数字就能完全改变含义,错误可能导致保险索赔被拒、收入损失或合规问题。 随着大模型的兴起,一个关键问题出现了:AI能否准确记忆和回忆这些专业代码?这个被称为本体记忆的挑战,对价值4.5万亿美元的医疗保健行业具有重大影响。
什么是本体Ontology记忆?本体是一种结构化的知识表示,它映射概念、关系和规则------就像按类型、颜色和品牌整理你的衣柜一样。在医学领域,像 ICD-10-CM 这样的本体不仅编码疾病,还编码严重程度、解剖位置和因果关系。 对于 AI 来说,记忆意味着: - 内化结构:知道 E11.65 是 E11(糖尿病)的一个子类型,属于内分泌疾病类别。
- 回忆精确代码:
- 维持关系:区分密切相关的代码,例如 E11.65 与 E11.9。
这与缓存、将数据保存到磁盘或在数据库中查找信息不同。 问题是:AI 是否已经充分学习了这些代码,以至于无需外部帮助就能回忆起它们?
与人类类比即使是经验丰富的编码员也不会记住全部 7 万个 ICD-10 编码。他们: - 理解章节系统(例如,E00-E89 = 内分泌疾病)
即使是专家,准确率也只有 80-95%,这凸显了为什么 AI 在处理罕见或细微的代码时会遇到很大的挑战
医疗行业挑战医院每天要处理数百份理赔单。每份理赔都需要多个准确编码——出错代价高昂。目前合格的编码员稀缺,错误可能导致理赔延迟或被拒。AI辅助编码(注意此处不是程序员的编程)有望提升速度和一致性:AI建议编码,人工审核,系统随时间不断改进。 不过只有在AI能可靠记忆编码时, 才能正常工作。否则,它会产生更多错误和额外工作量。 从医疗扩大至其他领域凡是存在大规模、结构化、高价值知识的地缝,本体记忆都至关重要: 当人工智能能够可靠地记住这类知识时,它是一个巨大的优势。当它做不到时,就是一种风险。 在常见代码上实现90%以上的准确率是具有变革性的;而40%的准确率则意味着根本没有什么作用。正是这一挑战促使我们构建了一个本体记忆评估系统,用以严格测试AI的性能——我们接下来将对此进行探讨。
构建评估系统:设计与架构评估指标在构建任何AI编码助手之前,我们需要严谨的数据。具体来说,我们提出了三个研究问题: RQ1:记忆准确性- 当给定一个概念标签时,大语言模型(LLM)能多准确地回忆起确切的医疗编码?例如,如果我们给LLM输入"2型糖尿病伴高血糖",它能准确输出E11.65吗?我们通过以下指标来衡量: RQ2:流行度相关性- AI在常见编码上的表现是否比罕见编码更好?这一点很重要,因为医院遇到常见病情的频率远高于罕见病情。如果AI只对常见编码有效,它可能仍然对80%的病例有价值,而人类编码员可以处理罕见的20%。我们衡量: 高流行度准确率:在前25%最常见编码上的表现 低流行度准确率:在后25%最罕见编码上的表现 准确率下降程度:高流行度与低流行度之间的差距
RQ3:预测不变性- AI的输出是否一致且稳定?如果我们问同一个问题三次,是否会得到相同的答案?如果我们稍微调整问题的表述,答案会改变吗?这对生产系统很重要——用户期望确定性的行为。我们测试: PI-1:重复完全相同的提示3次——答案相同的频率是多少? PI-2:改变温度参数(随机性)——输出变化有多大? PI-3:重新表述问题(不同措辞、不同语言)——答案是否保持不变?
这些问题让我们全面了解:不仅仅是"它是否有效?",而是"它在何时有效,效果如何,以及我们能否依赖它?" DataPipeline:从真实医疗编码到 Neo4j第一步是获取真实的医疗数据,这需要反映现实世界医疗编码的复杂性。我们获取了: ICD-10-CM 诊断编码(来自 CDC/CMS 官方来源的 51 个概念): - 常见慢性病:2 型糖尿病(E11.9, E11.65, E11.36)、高血压(I10)、慢性阻塞性肺疾病(J44.0, J44.1)
- 心理健康:重度抑郁障碍(F32.9)、焦虑症(F41.9)
每个编码都包含: - 概念 ID:实际的 ICD-10 编码(例如,E11.65)
- 标签:医学描述("Type 2 diabetes mellitus with hyperglycemia")
- 类别
- 流行度评分:基于真实世界索赔频率(高血压为 980,脂膜炎为 420)
数据摄取脚本(ingest_real_data.py)执行几个关键步骤:
步骤 1:流行度分桶- 我们不能只是随机抽取概念进行测试。如果那样做,我们很可能会得到大部分常见编码(因为它们在数据中出现得更多)。我们需要【分层抽样】:在不同流行度级别上实现均衡的代表性。 我们使用对数分布将概念划分为 50 个流行度桶: - 桶 50:最高流行度(例如,评分为 980 的高血压)
- 桶 1:最低流行度(例如,评分为 420 的脂膜炎)
这确保了当我们为评估抽取 30 个概念时,我们从每个桶级别获得 1-2 个概念,代表了从极其常见到极其罕见状况的完整谱系。 步骤 2:图结构创建- 每个概念在 Neo4j 中成为一个节点,具有以下属性: (:Concept { concept_id:"E11.65", label:"Type 2 diabetes mellitus with hyperglycemia", category:"Endocrine", popularity_score:850, popularity_bucket:43, ontology:"ICD-10-CM" })
概念链接到其本体: (:Concept)-[ ART_OF]->(:Ontology{name:"ICD-10-CM",version:"2024"})
步骤 3:验证- 摄取脚本验证: 运行python scripts/ingest_real_data.py后,我们在 Neo4j Aura 中拥有了 101 个真实的医疗概念,准备进行评估。 评估引擎:对大型语言模型进行测试数据加载完成后,我们构建了评估流程(run_full_evaluation.py) 分层抽样:比如,当评估30个概念时,系统不会只是从数据库中选取前30个。相反,它会: - 计算从每个组中抽取多少样本(大致与组的大小成比例)
这确保了平衡的代表性。如果糖尿病代码都在高流行度组,而罕见遗传病都在低流行度组,我们的样本会同时包含两者。 LLM 查询生成:对于每个概念,我们生成一个精心设计的提示。以 ICD-10-CM 为例: You are a medical coding expert. Your taskistoprovide the exact ICD-10-CM code forthe following diagnosis.Diagnosis: Type2diabetes mellituswithhyperglycemia RespondwithONLY the ICD-10-CM code,nothingelse. Format: X00.00 Your response:
为什么采用这种特定格式? - 角色设定:"You are a medical coding expert" 激活了相关的训练知识
- 任务明确:"exact ICD-10-CM code" 设定了对精确性的期望
- 约束条件:"ONLY the code, nothing else" 防止冗长的回答
- 格式示例
异步评估:系统使用 Python 的asyncio并发发送所有查询: asyncdefevaluate_all_concepts(self, concepts): tasks = [self.evaluate_concept(c)forcinconcepts] results =awaitasyncio.gather(*tasks) returnresults
响应解析:LLM 的响应并不总是干净的。以下是各种可能的例子: - 冗长的:
The ICD-10-CM code is E11.65 - 不确定的:
Probably E11.65 or E11.9
响应解析器使用特定于本体的正则表达式模式: - 对于 ICD-10-CM:
[A-Z][0-9]{2}\.?[0-9A-Z]*(匹配 E11.65, I10, Z79.4)
它从响应中提取第一个有效代码,并根据已知模式进行验证。 指标计算:对于每个概念,我们计算: - 精确匹配
- 莱文斯坦编辑距离:需要多少次单字符编辑(插入、删除、替换)来修正?
- E11.65 → E11.3 = 2(删除 '6',将 '5' 改为 '3')
- Jaccard相似度
- E11.65 对比 E11.65 = 100%(相同)
- E11.65 对比 E11.9 = 75%(共享 "E11.")
- E11.65 对比 I10 = 0%(没有共同点)
这些指标提供了超越简单对/错的细微差别。一个始终接近正确答案(E11.3 而不是 E11.65)的 LLM,经过后处理后可能仍然有用,而一个产生随机代码(用 K56.9 表示糖尿病)的 LLM 则不然。 报告生成:让数据可操作原始指标本身用处不大。报告生成器能将评估结果转化为人们真正能用上的洞见, 可以让大模型生成以下报告 Markdown 报告 JSON 导出 CSV 导出 PNG 图表
RQ1 结果:记忆准确度演变RQ2 结果:流行度偏差仍然难于解决不变的挑战:尽管准确性大幅提升,流行度偏差依然顽固存在: 看似矛盾的现象:罕见代码的准确性有所提高,但常见代码与罕见代码之间的性能差距反而扩大了。GPT-5.2 显示出最大的差距,其性能差异达到 47.6%。 这为何重要:模型似乎是为了追求整体准确性而优化的,而非均衡的性能。即使基础模型很强,罕见代码仍需要针对性技术,如上采样或微调。 业务影响:AI 呈现出明显的两级分化模式: RQ3 结果:预测不变性(综合分析)
我们进行了全面的不变性测试,以衡量每个模型输出结果的一致性。这对于生产部署至关重要——如果AI对同一个问题给出不同的答案,那它就是不可靠的。 不变性公式:PI = 1 - (U - 1) / (M - 1),其中 U = 唯一响应数,M = 提示词数量 GPT-5.2 预测不变性 不变性得分 (PI): - 相同提示词重复 (温度 = 0.0):
- 温度变化 (0.0 → 1.0):
- 提示词措辞变化:
Example variations tested: "What is the ICD-10-CM code for Type 2 diabetes with hyperglycemia?" "Provide the diagnosis code for Type 2 diabetes mellitus with hyperglycemia" "Give me the identifier for Type 2 diabetes with hyperglycemia"
关键洞察:预测不变性是衡量记忆置信度的可靠指标。当模型对某个编码不确定时,它会在不同的提示词变体下产生不一致的响应。这意味着我们可以使用PI分数来在没有真实答案的情况下估计可靠性——这对于生产环境中的筛选很有用。 业务影响:生产系统需要遵循提示词工程的最佳实践。如果没有这些保障措施,AI可能会根据问题表述方式的不同,对同一个诊断给出不同的编码——这在临床环境中是完全不可接受的。
商业应用建议:如何在现实世界中部署这项技术
系统底层工作原理步骤 0:加载真实医疗数据:python scripts/ingest_real_data.py
当提示时输入 'yes'预期:加载 101 个概念(51 个 ICD-10 + 46 个 RxNorm)第一步:从 Neo4j 加载概念当你运行python scripts/run_full_evaluation.py ICD-10-CM --model gpt-5.2 --sample-size 50时,系统首先会连接到Neo4j Aura: query=""" MATCH (c:Concept)-[ ART_OF]->(o:Ontology {name: $ontology}) RETURN c.concept_id AS concept_id, c.label AS label, c.popularity_bucket AS popularity_bucket, c.popularity_score AS popularity_score ORDER BY c.popularity_bucket DESC """ results= session.run(query,ontology="ICD-10-CM")
这段 Cypher 查询(Neo4j 的查询语言)会获取属于 ICD-10-CM 本体的所有概念,以及它们的流行度指标。结果是:一个包含 51 个概念的列表,每个概念都有 ID、标签和流行度数据。 步骤二:分层抽样抽样算法确保平衡代表性: defstratified_sample(concepts, sample_size): # Group by popularity bucket buckets = {} forconceptinconcepts: bucket = concept['popularity_bucket'] ifbucketnotinbuckets: buckets[bucket] = [] buckets[bucket].append(concept) Output:
按比例计算每个桶的样本数samples_per_bucket=max(1,sample_size//len(buckets))
从每个桶中采样sampled=[]forbucket,concepts_in_bucketinbuckets.items():n=min(samples_per_bucket,len(concepts_in_bucket))sampled.extend(random.sample(concepts_in_bucket,n))
如果还没达到样本数量,就随机补充whilelen(sampled)<sample_size:remaining=[cforcinconceptsifcnotinsampled]sampled.append(random.choice(remaining))returnsampled[:sample_size]
这样可以确保每个流行度等级都有代表性,避免偏向常见或罕见的代码。
### 第三步:LLM查询(异步处理)
针对每个医学概念,我们生成一个提示词并发送给大语言模型:
```python asyncdefevaluate_concept(concept): prompt =f"""You are a medical coding expert. Your task is to provide the exact ICD-10-CM code for the following diagnosis.Diagnosis:{concept['label']} Respond with ONLY the ICD-10-CM code, nothing else. Format: X00.00 Your response:""" # 发送给LLM(Ollama) response =awaitllm_client.aquery(prompt) # 解析响应 predicted_code = parser.extract_code(response) # 计算指标 is_correct = (predicted_code == concept['concept_id']) lev_distance = levenshtein_distance(concept['concept_id'], predicted_code) jaccard_sim = jaccard_similarity(concept['concept_id'], predicted_code) return{ 'concept_id': concept['concept_id'], 'label': concept['label'], 'predicted': predicted_code, 'is_correct': is_correct, 'levenshtein': lev_distance, 'jaccard': jaccard_sim, 'popularity_bucket': concept['popularity_bucket'] }
通过使用async/await,我们可以同时运行所有10个(或30个、50个)评估任务。Python的事件循环能高效地管理这些I/O密集型的大语言模型请求。 第四步:指标汇总在所有概念评估完成后,我们对各项指标进行汇总: defcalculate_summary_metrics(results): total =len(results) correct =sum(1forrinresultsifr['is_correct']) accuracy = correct / total avg_levenshtein =sum(r['levenshtein']forrinresults) / total avg_jaccard =sum(r['jaccard']forrinresults) / total # 流行度分析 high_pop = [rforrinresultsifr['popularity_bucket'] >=38] low_pop = [rforrinresultsifr['popularity_bucket'] <=13] high_pop_acc =sum(1forrinhigh_popifr['is_correct']) /len(high_pop) low_pop_acc =sum(1forrinlow_popifr['is_correct']) /len(low_pop)iflow_popelse0 degradation = high_pop_acc - low_pop_acc return{ 'accuracy': accuracy, 'avg_levenshtein': avg_levenshtein, 'avg_jaccard': avg_jaccard, 'high_pop_accuracy': high_pop_acc, 'low_pop_accuracy': low_pop_acc, 'degradation': degradation }
这样我们就得到了用于执行摘要的核心数据。 第五步:生成可视化图表我们使用 Matplotlib 和 Seaborn 来创建图表:
这样就生成了一个专业质量的图表,展示了流行度与准确性之间的相关性。 步骤六:生成报告最后,我们把所有内容整理成一份 Markdown 报告。 报告会保存为report_ICD-10-CM_[timestamp].md,放在data/results/目录下。
结论:本体记忆的潜力与风险我们最初想解决一个看似简单的问题:AI能否足够好地记住医学编码,从而辅助人类编码员?但答案正如AI研究中常见的那样——是微妙的。 AI能够部分记忆:它能做到70%完全正确,还有更多是“差不多对”(子类型错,但大类对)。它展示了对医学本体的语义理解能力,尽管缺乏完美的记忆。 流行度偏差真实存在且影响显著:AI擅长处理常见编码,但在罕见编码上表现不佳。这不是一个程序错误,而是神经网络从数据中学习方式的根本局限。你无法记住你几乎没见过的东西。 生产部署是可行的,但需要精心设计:作为一种验证工具或常见病例的助手,AI可以带来巨大的商业价值。但完全自动化还为时过早,且充满危险。 |