Ontology-Oriented Software Development | Palantir | Palantir Blog
“Show me the incentive and I’ll show you the outcome” — Charlie Munger
摘要
本文探讨了“本体导向软件开发”这一理念,它通过将企业架构中的数据、逻辑和行动元素整合到一个共享的本体中,来简化软件开发,提高企业敏捷性。
Key Takeaways
- 软件行业过度关注组件开发,而忽略了系统整合,导致企业软件的实际效果远低于预期。
- 本体导向软件开发通过将数据、逻辑和行动元素整合到一个共享的本体中,简化了软件开发,提高了系统整合效率。
- 本体导向软件开发能够提高低代码应用的性能上限,并允许开发者在低代码和代码之间进行无缝迁移。
- 本体导向软件开发可以为人工智能系统提供一个统一的接口,使其能够更容易地访问和操作企业数据。
- 本体导向软件开发能够解决人类语言与计算机语言之间的差异,为人类与 AI 系统的协作创造更自然的交互方式。
Source:
https://blog.palantir.com/ontology-oriented-software-development-68d7353fdb12
全文
I.
作为一个行业,我们以日益增长的速度生产更多的软件,然而这些软件对经济生产力的影响却很小。
这种缺乏业务价值是软件行业本身组织结构固有的问题 —— 绝大多数人力资本都集中在开发模块化组件上,而不是将这些组件集成到能够实际影响业务结果的统一企业系统中。我们贬低这种集成工作,抬高组件的地位为“系统”,而不是合适地将它们概念化为更大拼图的重要部分。数字化转型已经成为一个自身的目标,而不是由技术娴熟的从业者进行的一次旅程,其目的地由企业本身定义。
我们被自己愚弄了,认为因为构建单独部分更容易,所以别人一定能将它们组装成有价值的东西。
所产生的软件架构被优化为为风险投资家带来回报,而不是为客户实现结果;它奖励供应商之间的零和竞争,而不是合作伙伴之间专注于整体系统绩效的正和协作。尽管过去二十年这些单独部分已经有了巨大的提高,但矛盾在于这是以企业系统整体为代价的。通过在单独部分中进行优化,逐渐变得更加困难在多个解体部分之间同步变更。因此,部件层面上的改进未能带来系统层面上的改进。
标准企业架构内部组件的改进未能提升系统水平,实际系统性能逐渐落后潜在系统性能。
尽管被销售成为模块化,这个软件工业综合体的客户却收到了一个片段化和解体化的产品。与其实现敏捷性,这些投资导致了刚性企业架构。这种技术上的刚性渗透到更广泛的组织文化中,并导致停滞。
在 Palantir,我们的使命是实现技术驱动进步的解放力量,为人类而服务——通过赋予组织建立定制软件工具的能力,从而催化广泛的企业转型。令人不便的是,我们很快意识到,没有一个单一的产品能够在整个经济范围内实现这一目标;也就是说,这个使命无法通过代谢一套共享要求并为所有客户分泌相同的包装软件来实现。这种工匠式的软件工程必须在现场进行。
并且为了使其在经济上可行,我们需要构建技术,可以将定制企业软件开发的边际成本降至零。
支撑这一切的架构是 Palantir 本体论,详见 Akshay 最近的博客文章[Palantir 大模型产品AIP系列 - 首席架构师详析Ontology本体论驱动决策及医疗案例]。这项技术以及其软件开发工具包(我们称之为“本体SDK”或“OSDK”)使得通过将孤立组件整合成整体系统来对企业进行“解碎块”成为可能。通过实施以决策为中心的本体论,这种本体论协调了来自整个IT景观的数据、逻辑和行动元素,使组织终于能够开始创造性地思考他们的技术资产与业务重点之间的交集。
II.
===
考虑一家航空公司试图减少停机时间并消除由机械问题导致的延误。
在例行的飞前检查中,一旦警示灯亮起,压力就开始增加,因为组织中的人们开始团结他们的选择。这些涉及真正的取舍,涉及安全、雇员健康和客户影响之间的复杂决策需要权衡。
这个问题是跨行业看到的通用资源分配问题的一个具体例子。虽然资源可能根据统计上的最优分配主动分配,但重新分配这些资源随着现实情况的改变而改变是企业敏捷性的关键衡量:企业可以多快地对现实作出反应?
在我们假想的飞行例子中,这涉及回答三个问题:
-
飞机是否需要停飞?
-
哪些机场有适当的备件和技工可以进行维修?
-
哪种选项允许我们交换飞机同时最大限度地减少对客户的影响?
使用飞机的物联网数据(每次飞行可产生超过两百万个数据点),我们需要了解飞机还剩下多少飞行小时。为此,我们的数据科学家可以加载物联网数据并运行一个预测性维护模型,以获得飞机还剩下多少寿命的初始估计。
假设航班可以起飞,我们需要开始计划修复。这意味着我们的运营团队需要查明哪些机场有适当的备件库存,并将该数据与维护团队的日程安排进行交叉参考,以确定一名具备维修能力的认证技工。我们的目标不是将问题推迟,而是立即交换飞机,以便它能尽快抵达能够完成维修的目的地,理想情况下不影响任何客户。
在这个示例中,我们的目标是交换飞机,以便它可以到达一个能够完成维修的目的地,而又不会影响顾客。
根据这个标准,我们可以确定两个交换选项,这将使飞机降落在IAD或DFW。DFW将符合维修的所有约束条件,但由于机身具有不同的座椅配置,我们将不得不让三名乘客等候,这将造成5000美元的损失以及客户满意度的降低。IAD拥有需要更换的零件和一个空的飞机库可以进行维修,但目前没有具有特定技能的机械师在飞机到达时安排工作;夜间完成修复工作将需要我们安排一名机械师加班,增加成本2000美元。
第一个解决方案更简单;第二个最佳解决方案需要交换飞机并与机械师协调 - 即需要更多上下文和更多自由度。
以程序方式找到最佳解决方案涉及协同编排许多不同的数据,逻辑和行动元素。数据元素来自机场运营数据库,航班清单和资源管理系统。这些数据元素被用作逻辑元素的输入,这些逻辑元素可以预测影响,模拟客户行为并评估风险。最后,这些逻辑元素的输出被用来构建行动元素,这些元素将决策写回适当的运营系统,并向正确的人员分发实时通知。
将此解决方案转换为今天分散的企业架构之上的应用程序需要处理来自核心交易业务系统的数据,将其与SaaS工具和自定义内部系统进行对账,并协调跨多个不同价值链部分的操作。实施此解决方案的工程师需要了解每个系统的API,与每个组织的IT团队建立连接,并构建一个直接与每个系统互动的应用程序。
这种知识 - 如何与系统交互,如何协调它们不同的数据模型,如何访问它们不连贯的逻辑元素以及如何编排它们的操作系统 - 将存在于尚未另外一个分散的应用程序或服务中,对于组织内正在尝试执行类似任务的其他团队而言,这是无法访问的。虽然API网关在启用API发现方面起着重要作用,但程序员仍然陷入使用应用程序层中的胶水代码与系统特定的表示进行协调的困境中。
本体论集中了这些知识并将其封装在共享系统中。然后,此系统与OSDK一起,作为撰写对关键业务概念,运营流程和真实任务进行协调的统一层的业务逻辑的更高级抽象。这样就允许从组件中心的表示转换为共享概念模型只发生一次,而不是每次构建新应用程序时都会发生一次。
本体论将高级系统中的碎片化数据、逻辑和动作组件连接起来。这使得可以将特定于组件的模型转化为共享的概念模型,而不是每次构建新应用程序时都需要进行转化。
并且这种知识迅速累积;通过依赖共享的以决策为中心的本体论,新应用程序可以利用现有的系统集成工作,并且通过将其数据、行为和逻辑元素与现有表示进行协调,新元件可以快速上线。²
有了本体论,新应用可以利用先前的系统集成工作。通过将它们的数据、逻辑和行动元素与共同的本体论协调一致,新组件可以快速上线。
考虑上面的例子。在本体论出现之前的世界中,程序员需要找到适当的组件,然后使用特定于组件的接口、语言和/或方言与每个组件交互。因此,代码本身从根本上是以组件为中心的。
const mysql = require('mysql');
const nodemailer = require('nodemailer');
const fetch = require('node-fetch');
// Inventory database connection configuration
const dbConfig = {
host: 'skyframe-internal',
user: 'db\_admin',
password: process.env.DB\_PASSWORD,
database: 'skyframe'
};
// Email server configuration (example using Gmail)
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'swapbot@skyframe.com',
pass: process.env.EM\_PASSWORD
}
});
// Connect to the inventory database and query for airports with inventory
async function findAirportsWithInventory(parts) {
const connection = mysql.createConnection(dbConfig);
const query = "SELECT airport\_code FROM inventory WHERE part\_number IN (?)";
return new Promise((resolve, reject) => {
connection.query(query, \[parts.map(part => part.partNumber)\], (error, results) => {
connection.end();
if (error) return reject(error);
resolve(results.map(row => new Airport(row.airport\_code)));
});
});
}
// Connect to the database and query for swappable flights
async function findSwappableFlights(flightToSwap, aircraft, targetAirports) {
const connection = mysql.createConnection(dbConfig);
const query = \`SELECT \* FROM flights
WHERE departure\_airport\_code = ? AND aircraft\_id = ? AND destination\_airport\_code IN (?)
ORDER BY departure\_time ASC\`;
return new Promise((resolve, reject) => {
connection.query(query, \[flightToSwap.departureAirportCode, aircraft.id, targetAirports.map(airport => airport.id)\], (error, results) => {
connection.end();
if (error) return reject(error);
resolve(results.map(row => new ScheduledFlight(row)));
});
});
}
// Function to calculate optimal swaps by calling an external REST endpoint
async function calculateOptimalSwaps(flightToSwap, swappableFlights) {
const requestBody = {
flightToSwap: flightToSwap,
swappableFlights: swappableFlights
};
const response = await fetch('https://skyframe-internal-gateway/api/calculateOptimalSwaps', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer Ym9zY236Ym9zY28'
},
body: JSON.stringify(requestBody)
});
if (!response.ok) {
const error = await response.text();
throw new Error(\`Failed to calculate optimal swaps: ${error}\`);
}
const swapOptions = await response.json();
return swapOptions;
}
// Function to calculate maintenance and customer impact costs by calling an external REST endpoint
async function calculateImpactCosts(swapOptions) {
const requestBody = {
swapOptions: swapOptions
};
const response = await fetch('https://skyframe-internal-gateway/api/calculateImpactCosts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer Ym9zY236Ym9zY28'
},
body: JSON.stringify(requestBody)
});
if (!response.ok) {
const error = await response.text();
throw new Error(\`Failed to calculate impact costs: ${error}\`);
}
const impactCosts = await response.json();
return impactCosts;
}
// Function to perform the swap operation
async function swapAircraft(targetAircraft, aircraft) {
const requestBody = {
targetAircraft: targetAircraft,
aircraft: aircraft
};
const response = await fetch('https://skyframe-internal-gateway/api/swapAircraft', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer Ym9zY236Ym9zY28'
},
body: JSON.stringify(requestBody)
});
if (!response.ok) {
const error = await response.text();
throw new Error(\`Failed to swap aircraft: ${error}\`);
}
console.log(\`Swapping aircraft ${targetAircraft.id} with ${aircraft.id}\`);
}
// Function to send an email for swap approval (using nodemailer)
async function emailSwapsForApproval(swapOptions) {
const mailOptions = {
from: 'swapbot@skyframe.com',
to: 'ground-control@skyframe.com',
subject: 'Flight Swap Approval Required',
text: \`Approval is required for the following swap options: ${JSON.stringify(swapOptions)}\`
};
return transporter.sendMail(mailOptions);
}
// Main function to schedule preventative maintenance
async function schedulePreventativeMaintenance(aircraft, parts) {
try {
// Assuming aircraft.getSchedule() and schedule.getNextFlight() are defined elsewhere
const schedule = await aircraft.getSchedule();
const flightToSwap = await schedule.getNextFlight();
const targetAirports = await findAirportsWithInventory(parts);
const swappableFlights = await findSwappableFlights(flightToSwap, aircraft, targetAirports);
const swapOptions = await calculateOptimalSwaps(flightToSwap, swappableFlights);
const impactCosts = await calculateImpactCosts(swapOptions);
const cheapestOption = impactCosts.reduce((cheapest, option) =>
option.cost < cheapest.cost ? option : cheapest,
{ cost: Number.MAX\_VALUE }
);
// Guardrails for Options
if (cheapestOption.cost < 1000) {
await swapAircraft(cheapestOption.swapOption.targetAircraft, aircraft);
} else {
await emailSwapsForApproval(swapOptions);
}
} catch (error) {
console.error('Failed to schedule preventative maintenance:', error);
}
}
这是一个有意缩写的示例 — API 请求和响应类型被过度简化,底层逻辑和动作系统通过标准的 API 网关模式公开,认证相对一致,错误处理非常琐碎。然而,大部分代码仍然是花在直接连接到底层系统的功能上;主调度功能仅代表总代码量的一小部分。
相比之下,采用本体论导向的开发模型,需要用来使数据、逻辑和动作在系统间协调一致的代码均存在于企业的本体论中,大大简化了客户端代码。
使用本体论软件开发工具包(OSDK),该工具包可以用惯用的 Python、Typescript 和 Java 暴露出本体论的数据、逻辑和动作元素,我们能够将大部分复杂性转移到本体论本身,并暴露出与实际业务概念直接对应的企业对象:
async function schedulePreventativeMaintenance(aircraft: Aircraft, parts: AircraftComponent\[\]) {
const schedule: FlightSchedule = await aircraft.getSchedule();
const flightToSwap: ScheduledFlight = await schedule.getNextFlight();
const targetAirports: Airport\[\] = await OntologyClient.search()
.airports()
.filter(a => a.inventory.containsAll(parts))
.all();
// Find flights from the same departure location to airports with inventory
const swappableFlights: ScheduledFlight\[\] = await flightToSwap.getDepartureAirport()
.scheduledFlights()
.filter(f => f.aircraft.airframe.exactMatch(aircraft.airframe))
.filter(f => f.destination.isOneOf(targetAirports))
.orderBy(f => f.departureTime.asc())
.all();
const swapOptions: FlightSwapOption\[\] = await OntologyClient.calculateOptimalSwaps(flightToSwap, swappableFlights);
const impactCosts: SwapOptionCost\[\] = await OntologyClient.calculateImpactCosts(swapOptions);
const cheapestOption = impactCosts.reduce((cheapestOption, option) =>
option.cost < cheapestOption.cost ? option : cheapestOption,
{ cost: Number.MAX\_VALUE },
);
// Guardrails for Options
if (cheapestOption.cost < 1000)
await OntologyClient.action.swapAircraft(cheapestOption.flight, aircraft);
else
await OntologyClient.action.emailSwapsForApproval(swapOptions);
正如布鲁克斯在他的开创性论文中所言,没有银弹:系统集成充满了必须加以管理而无法消除的基本复杂性。但是,通过强制执行封装此复杂性的设计模式在本体内,以本体为导向的客户端代码能够在更高的抽象级别上运行。就像高级语言允许程序员抽象处理组成物理计算机的硬件组件的内部实现细节一样,本体为导向的方法允许程序员抽象处理构成企业架构的软件组件的内部实现细节。
在本示例中,程序员使用OSDK与这些商业概念进行交互,并使用商业语言编写代码 —— 而不是按照行和列来表述,而是按照飞机、飞行计划和机场来表述。OSDK并没有为我们的产品提供通用API —— 它为在您的业务语言中构建API的工具包,而非行业领域提供了工具。
III.
在软件中,高层抽象总是在 Clayton Christensen 描述的精确意义上具有破坏性——它们首先着手处理最简单的程序,但由于其潜在的结构优势,发展中的技术便能够超越现有的技术。
使用本体论,跨越式发展发生在如何提升我们构建定制应用程序的能力上。任何使用低代码视觉编程工具的人都知道,这些应用程序总是会受到限制。因此,在选择低代码开发工具时,你做出的潜在决定就是要限制你所构建解决方案的雄心。
在本体论之上建立我们的应用程序构建平台以三种方式改变了这种情况。首先,它使低代码应用程序更加强大。低代码构建者可以利用基于代码的数据、逻辑和行动元素来推动其低代码应用程序的性能上升,比我们在任何其他低代码环境中看到的都要高。这种能力使构建者能够在视觉编程环境中直接开发操作型业务应用程序。第二,当构建者达到使用低代码开发环境构建所能实现的极限时,OSDK让他们能够平稳地从传统基于代码的开发环境过渡,而无需从头开始重新实现整个应用程序。第三,本体论使得构建低代码和专业代码应用程序可以轻松互操作。低代码和专业代码方法都有不同的权衡:低代码工具降低了构建东西的门槛,使应用程序开发更加易于实现,而基于代码的工具则提高了上限,使高级用户能够做更强大的事情并构建更复杂的体验。通过本体论综合这两种方法,使用户能够根据手头工作的需要选择合适的工具。
最近,我们看到与我们 AI 平台 (AIP) 提供的基本用户界面功能一起的结构优势在本体论中发挥作用,后者提供了用于构建定制 AI 系统并将其直接部署到操作性工作流程中的开发工具链。
我们为什么能够如此迅速地发布 AIP?因为从一种至关重要的观点来看,AI 系统更像是应用程序,而不像用户——它们与 API 直接交互!
与大多数人类用户不同,AI 系统与 API 直接交互。因此,为企业构建完整的 AI 系统需要存在一种体系结构,其中所有系统必须被碎片化为共享的 API 表面。
自计算机问世以来,界面设计一直在优化,旨在使数字系统对人类更具可访问性,无论是作为用户还是程序员。在过去的一个世纪里,我们已经从穿孔卡转向了命令行界面(CLIs),又从CLIs转向了图形用户界面(GUIs),始终以人类可用性为北极星。但随着我们进入一个人类直接与功能逐渐增强的AI系统合作以实现他们使命的世界,我们对可访问性的定义必须扩展,以允许自主系统承担日益复杂的任务。基于LLM的系统是按我们的形象制造的;和人类一样,它们可以使用基于文本的界面,并与世界的自然语言表示进行交互。但这种相似性是具有欺骗性的,并不适用于所有AI系统;事实上,今天并非所有AI系统都适用 — 当AlphaStar玩星际争霜时,并不与像素或文字进行交互。
通过将应用程序和数据系统拆解为它们的组成数据、逻辑和行动元素,Ontology揭示了企业的一个以决策为中心的模型。这种共享模型可以被用作一种统一呈现,通过三种不同类型的界面来公开:
- 图形用户界面(GUIs),为用户提供Ontology的视觉表示。
- 应用程序编程接口(APIs),为程序员和AI系统提供直接访问Ontology的接口。
- 自然语言界面(NLIs),允许人类和基于语言的AI系统使用自然语言与Ontology进行交互。
正如J. C. R. Licklider在《人机共生》中所述,“人类语言和计算机语言之间的基本差异可能是真正共生的最严重障碍”。Palantir Ontology是对这一语言挑战的技术解决方案:不是将每个不同的界面视为独特的语言,而是本体论代表了一种能够以图形、口头和程序形式表达的单一语言。
在企业背景下实现运营AI的潜力并非AI问题 — 而是本体论问题。
如何学习大模型 AI ?
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
第一阶段(10天):初阶应用
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
- 大模型 AI 能干什么?
- 大模型是怎样获得「智能」的?
- 用好 AI 的核心心法
- 大模型应用业务架构
- 大模型应用技术架构
- 代码示例:向 GPT-3.5 灌入新知识
- 提示工程的意义和核心思想
- Prompt 典型构成
- 指令调优方法论
- 思维链和思维树
- Prompt 攻击和防范
- …
第二阶段(30天):高阶应用
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
- 为什么要做 RAG
- 搭建一个简单的 ChatPDF
- 检索的基础概念
- 什么是向量表示(Embeddings)
- 向量数据库与向量检索
- 基于向量检索的 RAG
- 搭建 RAG 系统的扩展知识
- 混合检索与 RAG-Fusion 简介
- 向量模型本地部署
- …
第三阶段(30天):模型训练
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
- 为什么要做 RAG
- 什么是模型
- 什么是模型训练
- 求解器 & 损失函数简介
- 小实验2:手写一个简单的神经网络并训练它
- 什么是训练/预训练/微调/轻量化微调
- Transformer结构简介
- 轻量化微调
- 实验数据集的构建
- …
第四阶段(20天):商业闭环
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
- 硬件选型
- 带你了解全球大模型
- 使用国产大模型服务
- 搭建 OpenAI 代理
- 热身:基于阿里云 PAI 部署 Stable Diffusion
- 在本地计算机运行大模型
- 大模型的私有化部署
- 基于 vLLM 部署大模型
- 案例:如何优雅地在阿里云私有部署开源大模型
- 部署一套开源 LLM 项目
- 内容安全
- 互联网信息服务算法备案
- …
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。