使用 Ollma 处理 Ontology 端到端生成
const p = `你是企业本体建模专家。你的任务是根据用户提供的公司业务材料,自动设计一个领域的本体(ontology)。
要求:
1. 输出一个严格的 JSON 对象,包含 concepts / relations / rules / scenarios / standards 五个数组。
2. 概念命名:localName 用 PascalCase(如 Employee、TravelRequest),labelZh 用中文,labelEn 用 PascalCase(不要带空格)。
3. 字段类型必须是:string / number / date / boolean / ref / array。枚举值(enum)必须取自材料里实际出现的值,不要套用示例里的值。
4. 复用已有核心概念(影响图谱连通性与跨领域去重):
领域概念若与「平台已有核心概念」(见下方清单)中某个同义/等价(IS-A 关系),设 mapsToCore 指向该核心概念的 localName,
系统会据此建等价关系(owl:equivalentClass),把领域概念链到核心概念——核心概念因此被引用、在图谱连通,跨领域也自动去重。
原则:
- 有同义核心概念就映射,不要新建同义概念又不映射。
- 领域特有的概念(清单里没有对应的,如某领域特有的费用类型、业务动作)不设 mapsToCore,留空。
- 宁可多映射(把能对上核心概念的领域概念都链上)也不要漏——漏了核心概念就成孤立节点。
【平台已有核心概念】(运行时从库中动态查出,复用这些而非新建同义概念):
- Person(人员:自然人,跨领域共享。员工、采购员、合同签署人、检查员都是它的具体化。)
- Organization(组织:组织单元,可以是部门、成本中心、子公司。)
- Money(金额:金额对象,含数值 + 币种。所有费用、预算、合同金额都引用此概念。)
- Document(文档:文档凭据,如发票、合同、采购单。)
5. 关系类型必须是:CONTAINS / BELONGS_TO / REFERENCES / SUBMIT / APPROVE 之一。
【关系完整性 —— 硬性约束,必须满足】
同一领域内的所有概念必须通过关系连通成一张图,不允许出现孤立概念(没有任何关系连到它的概念)。不相干的概念根本不应出现在同一个领域里——既然放进来,就必须有关系把它挂到领域图上。具体要求:
- 每个概念至少出现在一条 relation 的 source 或 target 里(degree ≥ 1)。
- 主/单据概念(如 TravelRequest、ExpenseReport、申请单)通过 SUBMIT 连到发起人(Employee),并 CONTAINS 其下所有明细概念。
- 每个明细概念(如各项费用 AccommodationFee / MealFee / TransportationFee、明细行)必须被它的主单据 CONTAINS(即作为某条 CONTAINS relation 的 target),不能孤立。
- ref 字段引用的概念(如 applicant→Employee)若该概念已在本领域,不必再为引用单独建关系;但被引用概念本身仍需通过其他关系连通。
- 输出前自检:遍历 concepts,任一概念若不在任何 relation 中,补一条合理的关系(通常是 CONTAINS 挂到主单据,或 SUBMIT 连到 Employee),使其连通。
6. 规则 DSL 必须遵守以下语法(重要!):
- when 子句支持:
* isEmpty(path) / isNotEmpty(path) / exists(path)
* path == "字符串" / path != "x" / path > 100 / path >= 100 / path < 100 / path <= 100
* path in [a, b, c] / path not_in [a, b]
* func(args) # 受治理函数调用,如 amount > std_xxx(dim1, dim2)
* all: [条件列表] # 且
* any: [条件列表] # 或
* not (条件)
- 字符串字面量必须用双引号
- message 用 {{path}} 插值
- 多行 explanation 用 | 块标量
- targetPath(重要!):当规则作用于"明细类概念"时,targetPath 必须写成"<对应数组字段名>[*]"使其逐条遍历。字段名 = 概念名转复数(AccommodationFee → accommodationFees)。示例:target: AccommodationFee, targetPath: accommodationFees[*]。不写 targetPath 的规则会在整单层求值一次。
- 引用业务属性时直接用顶层概念字段(如 employee.level),不要走 ref 字段路径(如 travelRequest.applicant.level)——ref 字段(applicant/buyer 等)只携带 {id,name} 用于关联,不含 level/department 等业务属性,走 ref 路径会取不到值导致规则不触发。
6. 规则 severity 必须是 error / warning / info 之一(小写)。
7. 至少产出 3 个概念、2 条规则。关系数量无下限硬要求,但必须满足上面的「关系完整性」约束:所有概念连通,每个明细被 CONTAINS(通常 relations 数 ≥ concepts 数 - 1)。
【受治理标准(standards)—— 重点】
材料里常有"按城市×职级的住宿上限""按餐别×职级的餐标"这类数值标准表。这些数值是数据、不是代码,必须抽到 standards 数组里,供规则 DSL 通过受治理函数 call 取用,绝对不要把具体限额写进规则文本或硬编码。
每个 standard:
- code:受治理函数名,与规则 call[0] 一致(如 std_hotel_max、std_meal_max)。命名用 std_ 前缀 + 语义。
- matrix:嵌套查找表对象,下钻顺序 = 规则 call 的参数顺序。
例:规则 when 里 amount > std_hotel_max(cityCategory, applicant.level),
则 matrix = { "北上广深": { "其他员工": 400, "部门经理/部门副经理级": 600 }, "省会直辖市": { ... } }。
叶子值必须是 number。
- matrix 的键必须与材料里的枚举值/维度值完全一致(城市类别、职级、餐别等),不要编造或套用示例值。
规则 call 的参数个数必须与 matrix 的嵌套层数一致。一个 code 对应一个 standard(一个 matrix 覆盖所有维度组合)。
若材料没有对应标准表,就不要造该 standard,相关规则也不要 call 不存在的 std_xxx。
【scenarios(使用场景/Action)抽取规则 —— 重点】
一个业务领域通常有多个"使用场景",每个场景对应材料中描述的一个具体业务动作(Action)。
你必须通读材料,把材料里出现过的每一个独立业务动作都识别为一个 scenario,而不是只给一个笼统的"提交校验"。
常见动作来源包括但不限于:
- 制度流程中各办理环节(如"出差申请"、"费用报销"、"暂借款申请"等)
- 材料中明确列出的"动作一/动作二"或编号动作,每个动作应单独成一个 scenario
对每个 scenario:code 用 snake_case 英文,name 用中文动作名,description 说明该动作触发什么校验。通常一个领域应识别出 3 个以上 scenario。
输出 JSON Schema(字段值为结构演示,实际值取自材料):
{
"concepts": [
{
"localName": "Employee",
"labelZh": "员工",
"labelEn": "Employee",
"description": "业务材料中的员工",
"mapsToCore": "Person",
"fields": [
{ "name": "id", "type": "string", "required": true, "label": "工号" },
{ "name": "name", "type": "string", "required": true, "label": "姓名" },
{ "name": "level", "type": "string", "label": "职级", "enum": ["<取自材料>"] }
]
}
],
"relations": [
{ "name": "提交", "source": "Employee", "target": "TravelRequest", "relationType": "SUBMIT", "cardinality": "1:N", "description": "员工提交出差申请" }
],
"rules": [
{
"code": "R-AC-001",
"name": "住宿费超标",
"severity": "warning",
"target": "AccommodationFee",
"targetPath": "accommodationFees[*]",
"dsl": "- id: R-AC-001\\n name: 住宿费超标\\n severity: warning\\n target: AccommodationFee\\n targetPath: accommodationFees[*]\\n when:\\n amount > std_hotel_max(cityCategory, travelRequest.applicant.level)\\n message: \\"住宿费{{amount}}元超过标准\\"\\n explanation: |\\n 住宿费按城市类别与职级有上限\\n tags: [住宿, 超标]",
"message": "住宿费{{amount}}元超过标准",
"explanation": "住宿费按城市类别与职级有上限",
"tags": ["住宿","超标"]
}
],
"scenarios": [
{ "code": "submit_travel_request", "name": "提交出差申请", "description": "出差前提交审批单" }
],
"standards": [
{
"code": "std_hotel_max",
"matrix": { "北上广深": { "其他员工": 400, "部门经理/部门副经理级": 600 }, "省会直辖市": { "其他员工": 350, "部门经理/部门副经理级": 500 } },
"description": "住宿费上限(元/间/天),按城市类别×职级"
}
]
}
注意:DSL 字符串里的换行用 \n 转义,双引号用 " 转义。standards.matrix 里的数值与维度键必须取自材料,不要套用本示例的数字。
如下是用户上传的材料:
公司业务材料:
IT系统权限申请流程:
1. 员工发起权限申请,填写申请单号、目标系统名称、申请角色、生效日期
2. 申请包含“数据导出”权限需信息安全部审核
3. 申请包含“系统管理员”角色需技术总监审批
4. 申请单必须填写明确的业务使用场景说明
5. 权限生效日期不能早于今天
6. 同一申请单号不能重复提交
7. 申请包含多个操作节点,每个节点有安全等级(常规/敏感/极敏感)
8. 极敏感操作节点必须信息安全部专项复核
`
const url = "http://localhost:11434/api/generate"
const data = {
"model": "qwen2:7b",
"prompt": p,
"stream": false
}
const response = await fetch(url, {
method: "POST",
body: JSON.stringify(data),
})
const json = await response.json();
console.log(json["response"])