专题结论
材料到图谱的主链路是真实且完整的:上传文件被解析和预处理,LLM 依据材料与推演目标生成本体,文本被分块后写入 Zep 批次,服务等待批次条目和图谱事件处理结束。最值得参照的是“确定性操作标识 + 内容摘要 + 模糊响应对账 + 完成屏障”;最不应直接照搬的是强绑定舆情场景的本体提示、只保证“最多十类”却对外要求“恰好十类”的约束落差,以及没有本地图数据库替换层的 Zep Cloud 绑定。
原始类与方法
| 原始符号 | 真实职责 | 源码定义 | 固定提交行级证据 |
|---|---|---|---|
generate_ontology()(接收文件与模拟需求并创建项目的图谱接口函数,约第 266 行) |
保存文件、抽取文本、调用本体生成服务并迁移项目状态 | 图谱接口文件 | 固定版本第 266-447 行 |
OntologyGenerator(封装提示、长文抽样、本体生成和校验的服务类,约第 194 行) |
把非结构化材料和模拟要求转换为实体/关系 schema | 本体生成文件 | 固定版本第 194-202 行 |
OntologyGenerator.generate()(调用 LLM 并把结果交给校验器的入口方法,约第 203 行) |
形成系统/用户消息,最多尝试两次结构化响应 | 本体生成文件 | 固定版本第 203-248 行 |
OntologyGenerator._validate_and_process()(清洗实体、关系、属性和兜底类型的方法,约第 432 行) |
归一化模型输出,去重、截断并修复引用 | 本体生成文件 | 固定版本第 432-626 行 |
build_graph()(恢复或发起图谱构建异步任务的接口函数,约第 449 行) |
校验项目状态、分块、恢复批次并启动云端处理 | 图谱接口文件 | 固定版本第 449-766 行 |
GraphBuilderService(集中封装 Zep 图谱操作和批次生命周期的服务类,约第 61 行) |
创建图谱、设置本体、提交批次、等待处理和读取图数据 | 图谱构建文件 | 固定版本第 61-120 行 |
GraphBuilderService.set_ontology()(把 LLM 本体转换为 Zep 数据模型的方法,约第 313 行) |
动态创建实体与关系模型并注册到云端图谱 | 图谱构建文件 | 固定版本第 313-406 行 |
GraphBuilderService.add_text_batches()(以可对账方式批量提交文本分块的方法,约第 407 行) |
建立操作标识和分块摘要,提交、核对并启动批次处理 | 图谱构建文件 | 固定版本第 407-564 行 |
GraphBuilderService._wait_for_batch()(轮询批次条目终态的方法,约第 631 行) |
等待全部条目成功,失败或超时则阻断后续完成状态 | 图谱构建文件 | 固定版本第 631-719 行 |
GraphBuilderService._wait_for_episodes()(轮询图谱事件处理完成的方法,约第 720 行) |
防止 API 已接收但事实抽取尚未完成时提前发布完成态 | 图谱构建文件 | 固定版本第 720-776 行 |
原始调用链
generate_ontology()(接收上传材料并创建项目的入口,约第 266 行)调用文件解析、文本预处理和OntologyGenerator.generate()(生成本体的主方法,约第 203 行)。OntologyGenerator.generate()(构造提示并调用结构化 LLM 的方法,约第 203 行)把返回值交给OntologyGenerator._validate_and_process()(规范化并限制本体的后处理方法,约第 432 行)。build_graph()(校验项目、分块并启动图谱构建的接口函数,约第 449 行)调用GraphBuilderService.create_graph()(创建或对账图谱的方法,约第 218 行)和GraphBuilderService.set_ontology()(注册动态实体/关系模型的方法,约第 313 行)。GraphBuilderService.add_text_batches()(提交分块并对账模糊结果的方法,约第 407 行)完成提交后,由GraphBuilderService._wait_for_batch()(等待批次条目终态的方法,约第 631 行)和GraphBuilderService._wait_for_episodes()(等待图谱事件处理完成的方法,约第 720 行)形成双重完成屏障。
源码片段一:本体生成不是规则抽取,而是 LLM 生成后校验
原项目关键片段。来源:OntologyGenerator.generate()(调用结构化 LLM 并进入校验的方法,约第 220-248 行);固定提交第 220-248 行。
原项目关键片段:
# 构建用户消息
user_message = self._build_user_message(
document_texts,
simulation_requirement,
additional_context
)
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_message}
]
result = self.llm_client.chat_json(
messages=messages,
temperature=0.3,
max_tokens=None,
max_attempts=2,
)
result = self._validate_and_process(result)
return result
这段代码证明本体来源是模型结构化输出,而不是确定性的实体识别规则。可借鉴的是“生成后进入专门校验阶段”;不能照搬的是把模型输出当作已验证领域本体。生产引入需要加入字段级错误、人工确认、版本化 schema 和材料覆盖率指标。
源码片段二:“恰好十类”实际只实现了上限和兜底
原项目关键片段。来源:OntologyGenerator._validate_and_process()(添加人物/组织兜底并执行数量上限的方法,约第 521-548 行);固定提交第 521-548 行。
原项目关键片段:
entity_names = {e["name"] for e in result["entity_types"]}
has_person = "Person" in entity_names
has_organization = "Organization" in entity_names
fallbacks_to_add = []
if not has_person:
fallbacks_to_add.append(person_fallback)
if not has_organization:
fallbacks_to_add.append(organization_fallback)
if fallbacks_to_add:
current_count = len(result["entity_types"])
needed_slots = len(fallbacks_to_add)
if current_count + needed_slots > MAX_ONTOLOGY_TYPES:
to_remove = current_count + needed_slots - MAX_ONTOLOGY_TYPES
result["entity_types"] = result["entity_types"][:-to_remove]
result["entity_types"].extend(fallbacks_to_add)
result["entity_types"] = result["entity_types"][:MAX_ONTOLOGY_TYPES]
提示要求与实现的差异是:代码能截断超量类型并加入兜底,但不会在模型只生成少量类型时补齐到十类。这里应把“恰好”改成可执行校验,或把产品文案改成“最多”。
源码片段三:云端批次不盲目重放,而是先对账
原项目关键片段。来源:GraphBuilderService.add_text_batches()(创建业务操作标识并对账批次创建结果的方法,约第 423-455 行);固定提交第 423-455 行。
原项目关键片段:
if not graph_id:
raise ValueError("graph_id is required")
self.validate_batch_chunks(chunks, batch_size=batch_size)
total_chunks = len(chunks)
operation_id = self.build_operation_id(graph_id, chunks)
if batch_created_callback:
batch_created_callback(None, operation_id)
try:
batch = self.client.batch.create(
metadata={
"mirofish_operation_id": operation_id,
"graph_id": graph_id,
"chunk_count": total_chunks,
}
)
except Exception as error:
if not is_retryable_zep_error(error):
raise
batch = self._find_batch_by_operation_id(graph_id, operation_id)
if batch is None:
raise RuntimeError(
"Zep batch creation is unconfirmed and no matching operation was found"
) from error
这段是整个项目最值得参照的后端模式之一:写请求超时不等于服务端未执行,直接重试会重复写入;先以确定性业务标识查询并恢复,才能区分“已成功但响应丢失”和“确实未创建”。引入时可复用思想,但操作标识、重试边界和服务端查询能力需要按目标基础设施重新设计。
数据与约束边界
| 边界 | 实现事实 | 影响 |
|---|---|---|
| 输入格式 | 文件解析器支持 PDF、Markdown 与文本;配置允许单文件 50MB | 不支持扫描件 OCR;大文件上限不等于模型实际完整读取 |
| 本体上下文 | 本体生成器最多向 LLM 提供 50,000 字符,并对长文等距抽样 | 覆盖首中尾但可能漏掉稀疏关键证据 |
| 图谱批次 | GraphBuilderService.validate_batch_chunks()(在首次云端写入前验证批次总量和单块长度的方法,约第 567 行)限制最多 50,000 个条目、单块 10,000 字符 |
能提前失败,避免部分提交;仍需估算 Zep 配额和费用 |
| 本体模型 | GraphBuilderService.set_ontology()(动态创建并注册实体/关系数据模型的方法,约第 313 行)把生成本体直接下发云端 |
schema 变化会影响历史图谱兼容和报告检索 |
| 状态持久化 | 项目模型使用本地 JSON 保存项目、本体、批次和错误 | 适合单机恢复,不具备关系数据库事务和多实例协调 |
对当前需求的取舍
建议局部引入批次对账、完成屏障、分阶段状态和可视化确认思路;暂不建议直接引入生成式本体与 Zep 专属数据层。PoC 至少应验证:十组真实材料的本体覆盖率、人工修订成本、重复/断网恢复、图谱事件来源、云配额与删除一致性,并把原材料事实和模拟写回放入不同命名空间。