亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定

利用Gemini API構建語義網絡的妙招

对于大型语言模型有很多热议,但我对不断涌现的聊天机器人感到颇为失望,它们只是说你想听的话。我对AI的期待远不止于此。我希望它能帮我处理任务,并帮助我梳理数字世界,让我的生活更加从容自在。

最近我了解到Gemini的结构化输出API功能。它可以根据输入的模式返回一个基于JSON的数据,而不是像以前那样用一堆纯文本来回应你的多模态输入。

所以我可以利用 Gemini 将非结构化输入转换成结构化输入,并以此为基础编写更好的程序。

例如,我经常读书。今年我读了28本书。我用Goodreads来追踪我的阅读情况,不过Goodreads现在已经没有API了。我也是纽约公共图书馆的常用户,它也不提供API。我希望能有一个系统,能自动从我的Goodreads待读书单中,向图书馆请求借阅。

为此,我想要设定一套标准格式来表示Goodreads和NYPL上的图书。除了图书之外,我还希望大型语言模型能够结构化地理解任何内容。

所以我转向了schema.org,这是一个几年前启动的一个项目,旨在帮助定义语义网络。这些类型在网络中已被广泛应用,并为各种对象提供一个通用词汇表,包括

我可以轻松地从他们GitHub仓库中的这个单一JSON文件提取所有的现有模式。这种格式是JSON,尽管它通过大量链接和属性来表示每个属性或类型。

{
    "@id": "schema:illustrator",
    "@type": "rdf:Property",
    "rdfs:comment": "这本书的插画师。",
    "rdfs:label": "illustrator",
    "schema:domainIncludes": {
        "@id": "schema:Book"
    },
    "schema:rangeIncludes": {
        "@id": "schema:Person"
    }
},
{
  "@id": "schema:Book",
  "@type": "rdfs:Class",
  "rdfs:comment": "一本普通的书。",
  "rdfs:label": "书",
  "rdfs:subClassOf": {
    "@id": "schema:CreativeWork",
    "说明": "创意作品"
  }
},

这并没有特别有帮助,因为需要将其转换成更标准的 JSON 架构格式以符合 Gemini API 的需求。

于是我就写了一个简短的脚本来处理这个文件。

    const schemas = require('../src/schemaorg-jsonld.json')  
    const graph = schemas['@graph']  

    const geminiGraph: Record<string, Schema> = {}  
    const possibleTypes: string[] = []  

    const simpleDataTypes = {  
      'schema:Text': SchemaType.STRING,  
      'schema:URL': SchemaType.STRING,  
      'schema:Boolean': SchemaType.BOOLEAN,  
      'schema:Number': SchemaType.NUMBER,  
      'schema:Integer': SchemaType.INTEGER,  
      'schema:Time': SchemaType.STRING, // 时间字符串  
      'schema:DateTime': SchemaType.STRING, // 时间字符串  
      'schema:Date': SchemaType.STRING, // 时间字符串  
    }  

    // 第一次遍历为图中的每个项目创建一个条目  
    for (const property of graph) {  
      const id = property['@id']  
      if (simpleDataTypes[id]) {  
        geminiGraph[id] = {  
          type: simpleDataTypes[id],  
          description: property['rdfs:comment'] || property['rdfs:label'] || "",  
        }  
        continue  
      }  
      geminiGraph[id] = {  
        type: simpleDataTypes[property['@type']] ?? SchemaType.OBJECT,  
        description: property['rdfs:comment'] || property['rdfs:label'] || "",  
      }  
      // 这样就开始了内存映射的过程  
      if (!simpleDataTypes[property['@type']]) {  
        geminiGraph[id].properties = {}  
      }  

      if (property['rdfs:subClassOf']) {  
        if (property['rdfs:subClassOf']['@id'] === 'schema:Enumeration') {  
          // 这是一个枚举  
          geminiGraph[property['@id']].type = SchemaType.STRING  
          geminiGraph[property['@id']].enum = []  
          continue  
        }  
      }  
    }  

    // 下一次遍历将图中的条目连接起来  
    for (const property of graph) {  
      if (geminiGraph[property['@id']].type !== 'object') {  
        continue  
      }  
      const potentialTypes: string[] = (() => {  
        const sp = property['schema:rangeIncludes']  
        if (sp === undefined) return [0] // 如果 sp 未定义则返回 [0]  
        if (Array.isArray(sp)) {  
          return sp.map(x => x['@id'])  
        }  
        return Object.values(sp)  
      })()  

      const type = potentialTypes[0]  
      // for (const type of potentialTypes) {  
        const id = (() => {  
          const origId = property['@id']  
          if (potentialTypes.length === 1) {  
            return origId  
          }  
          // return `${origId}_${type}`  
          return `${origId}`  
        })()  
        geminiGraph[id] = {...geminiGraph[property['@id']]}  

        if (property['schema:rangeIncludes']) {  
          const ref = simpleDataTypes[type] ?  
              { type: simpleDataTypes[type] } :  
              geminiGraph[type]  
          if (geminiGraph[id].properties) {  
            geminiGraph[id].properties![type] = ref  
          } else {  
            geminiGraph[id].properties = {  
              [type]: ref  
            }  
          }  
        }  

        const superProperties: string[] = (() => {  
          const sp = property['schema:domainIncludes']  
          if (sp === undefined) return []  
          if (Array.isArray(sp)) {  
            return sp.map(x => x['@id'])  
          }  
          return Object.values(sp)  
        })()  
        for (const superProperty of superProperties) {  
          if (!geminiGraph[superProperty].properties) {  
            geminiGraph[superProperty]!.properties = {  
              [id]: geminiGraph[id]  
            }  
          } else {  
            geminiGraph[superProperty]!.properties![id] = geminiGraph[id]  
          }  
        }  
      // }  
    }  

    // 枚举遍历  
    for (const property of graph) {  
      const ptype = property['@type']  
      if (geminiGraph[ptype]?.enum !== undefined) {  
        geminiGraph[ptype].enum?.push(property['rdfs:label'])  
      }  
    }  

    function subclassPass(property: any) {  
      const log = false  
      const subClassOf = (() => {  
        const sp = property['rdfs:subClassOf']  
        if (sp === undefined) return []  
        if (Array.isArray(sp)) {  
          return sp.map(x => x['@id'])  
        }  
        return Object.values(sp)  
      })()  
      if (log) { console.log('sarr', subClassOf) }  
      for (const sco of subClassOf) {  
        if (log) { console.log(sco) }  
        if (log) { console.log(geminiGraph[sco]) }  
        if (sco === 'schema:Enumeration') return  
        if (!geminiGraph[sco]) continue  
        const graphSco = graph.find(x => x['@id'] === sco)  
        if (graphSco['rdfs:subClassOf']) {  
          if (log) { console.log('scp', sco) }  
          subclassPass(graphSco)  
        }  

        const superclass = geminiGraph[sco]  
        if (!superclass || !superclass.properties) continue  
        if (!geminiGraph[property['@id']].properties) {  
          geminiGraph[property['@id']].properties = {}  
        }  

        for (const [k, v] of Object.entries<Schema>(superclass.properties ?? {})) {  
          const pid = property['@id']  
          if (log) { console.log('kv', k) }  
          geminiGraph[pid]!.properties![k] = v  
        }  
      }  
    }  

    // 子类遍历过程  
    for (const property of graph) {  
      if (!property['rdfs:subClassOf']) continue  
      subclassPass(property)  
    }  

    const schema = 'schema:Book'  
    const jsonout = geminiGraph[schema]  
    console.log(JSON.stringify(jsonout))

花了点时间反复调整才达到这个阶段,使用内存引用确保顺序不影响结果。最后我再做了一次调整,确保枚举正常工作。当然这并不是最高效的脚本,但我只需要在预处理阶段运行一次即可,将所有类型转换完成。之后在实际运行中就能用上了。

逐行运行脚本时,一切正常。当我尝试使用 JSON.stringify 时,却遇到了一个意外的错误。错误信息提到有循环引用。这是怎么一回事。

schema.org 类型的问题在于它们太过于灵活且强类型。这在某些方面非常好,但这也使得数据序列化变得复杂。

如果你查看 Book 类型,你会注意到有像 Boolean abridgedText isbn 这样的属性。它继承了来自 CreativeWork 类型的属性,例如 Number copyrightYear,以及来自 Thing 类型的属性,例如 Text name。这也意味着它会带入很多不太关键的属性,比如 Text interactivityType。这使得类型变得非常冗长,但这些不太重要的属性可以被忽略,也不是什么大事。

更大的问题是这样的,一本书有一个叫做 Person illustrator 的属性,它指向一个标准的 Person 类型。这种 person 类型有一个叫做 Person children 的属性,表示该人物的后代。当你尝试序列化整个结构时,如果包含了这个人的后代,就会引发无限递归的问题。这确实是个大问题。

可惜的是,也没有太好的解决办法。我对于这个想了很多。

有一次,我尝试将很多类型硬编码为字符串类型。因为虽然我的书有一个Person插画师挺不错,但这意味着我最终可能会存储插画师的children,这些childrendeathPlace,这些deathPlacereview,以及reviewcopyrightHolder等等,诸如此类。

    const simpleDataTypes = {  
      'schema:Text': SchemaType.STRING,  
      'schema:URL': SchemaType.STRING,  
      'schema:Boolean': SchemaType.BOOLEAN,  
      'schema:Number': SchemaType.NUMBER,  
      'schema:Integer': SchemaType.INTEGER,  
      'schema:Time': SchemaType.STRING, // 时间戳  
      'schema:DateTime': SchemaType.STRING, // 时间戳  
      'schema:Date': SchemaType.STRING, // 时间戳  
      'schema:ListItem': SchemaType.STRING, // 列表项  
      'schema:DefinedTerm': SchemaType.STRING, // 定义术语  
      'schema:Taxon': SchemaType.STRING, // 处理方式  
      'schema:BioChemEntity': SchemaType.STRING, // 处理方式  
      'schema:DefinedTermSet': SchemaType.STRING, // 处理方式  
      'schema:ImageObject': SchemaType.STRING, // 处理方式  
      'schema:MediaObject': SchemaType.STRING, // 处理方式  
      'schema:TextObject': SchemaType.STRING, // 处理方式  
      'schema:VideoObject': SchemaType.STRING, // 处理方式  
      'schema:AudioObject': SchemaType.STRING, // 处理方式  
      'schema:Language': SchemaType.STRING, // 处理方式  
      'schema:QuantitativeValue': SchemaType.NUMBER, // 数量值  
      'schema:AboutPage': SchemaType.STRING, // 处理方式  
      'schema:Audience': SchemaType.STRING, // 处理方式  
      'schema:Claim': SchemaType.STRING, // 处理方式  
      'schema:Comment': SchemaType.STRING, // 处理方式  
      'schema:bioChemInteraction': SchemaType.STRING, // 处理方式  
      'schema:bioChemSimilarity': SchemaType.STRING, // 处理方式  
      'schema:hasBioChemEntityPart': SchemaType.STRING, // 处理方式  
      'schema:softwareAddOn': SchemaType.STRING, // 处理方式  
      'schema:worksFor': SchemaType.STRING, // 处理方式  
      'schema:parents': SchemaType.STRING, // 处理方式  
      'schema:advanceBookingRequirement': SchemaType.STRING, // 处理方式  
      'schema:potentialAction': SchemaType.STRING, // 处理方式  
      'schema:publisherImprint': SchemaType.STRING, // 处理方式  
      'schema:subjectOf': SchemaType.STRING, // 处理方式  
      'schema:offeredBy': SchemaType.STRING, // 处理方式  
      'schema:interactionType': SchemaType.STRING, // 处理方式  
      'schema:address': SchemaType.STRING, // 处理方式  
      'schema:spatial': SchemaType.STRING, // 处理方式  
      'schema:geoTouches': SchemaType.STRING, // 处理方式  
      'schema:sourceOrganization': SchemaType.STRING, // 处理方式  
      'schema:mainEntityOfPage': SchemaType.STRING, // 处理方式  
      'schema:isBasedOnUrl': SchemaType.STRING, // 处理方式  
      'schema:servicePostalAddress': SchemaType.STRING, // 处理方式  
      'schema:publishedOn': SchemaType.STRING, // 处理方式  
      'schema:diversityStaffingReport': SchemaType.STRING, // 处理方式  
      'schema:archivedAt': SchemaType.STRING, // 处理方式  
      'schema:publishingPrinciples': SchemaType.STRING, // 处理方式  
      'schema:occupationLocation': SchemaType.STRING, // 处理方式  
      'schema:educationRequirements': SchemaType.STRING, // 处理方式  
      'schema:performerIn': SchemaType.STRING, // 处理方式  
      'schema:correctionsPolicy': SchemaType.STRING, // 处理方式  
      'schema:hostingOrganization': SchemaType.STRING, // 处理方式  
      'schema:composer': SchemaType.STRING, // 处理方式  
      'schema:funding': SchemaType.STRING, // 处理方式  
      'schema:recordedAt': SchemaType.STRING, // 处理方式  
      'schema:material': SchemaType.STRING, // 处理方式  
      'schema:license': SchemaType.STRING, // 处理方式  
      'schema:usageInfo': SchemaType.STRING, // 处理方式  
      'schema:producer': SchemaType.STRING, // 处理方式  
      'schema:countryOfOrigin': SchemaType.STRING, // 处理方式  
      'schema:exampleOfWork': SchemaType.STRING, // 处理方式  
      'schema:workExample': SchemaType.STRING, // 处理方式  
      'schema:hasCertification': SchemaType.STRING, // 处理方式  
      'schema:hasCredential': SchemaType.STRING, // 处理方式  
      'schema:containedIn': SchemaType.STRING, // 处理方式  
      'schema:department': SchemaType.STRING, // 处理方式  
      'schema:makesOffer': SchemaType.STRING, // 处理方式  
      'schema:translationOfWork': SchemaType.STRING, // 处理方式  
      'schema:serviceSmsNumber': SchemaType.STRING, // 处理方式  
      'schema:subEvent': SchemaType.STRING, // 处理方式  
      'schema:eventSchedule': SchemaType.STRING, // 处理方式  
      'schema:shippingOrigin': SchemaType.STRING, // 处理方式  
      'schema:validForMemberTier': SchemaType.STRING, // 处理方式  
      'schema:openingHoursSpecification': SchemaType.STRING, // 处理方式  
      'schema:geoCrosses': SchemaType.STRING, // 处理方式  
      'schema:contributor': SchemaType.STRING, // 处理方式  
      'schema:accountablePerson': SchemaType.STRING, // 处理方式  
      'schema:affiliation': SchemaType.STRING, // 处理方式  
      'schema:funder': SchemaType.STRING, // 处理方式  
      'schema:alumniOf': SchemaType.STRING, // 处理方式  
      'schema:brand': SchemaType.STRING, // 处理方式  
      'schema:memberOf': SchemaType.STRING, // 处理方式  
      'schema:recordedIn': SchemaType.STRING, // 处理方式  
      'schema:deathPlace': SchemaType.STRING, // 处理方式  
      'schema:homeLocation': SchemaType.STRING, // 处理方式  
      'schema:workLocation': SchemaType.STRING, // 处理方式  
      'schema:locationCreated': SchemaType.STRING, // 处理方式  
      'schema:spatialCoverage': SchemaType.STRING, // 处理方式  
      'schema:attendee': SchemaType.STRING, // 处理方式  
      'schema:workFeatured': SchemaType.STRING, // 处理方式  
      'schema:workPerformed': SchemaType.STRING, // 处理方式  
      'schema:itemOffered': SchemaType.STRING, // 处理方式  
      'schema:availableAtOrFrom': SchemaType.STRING, // 处理方式  
      'schema:parentOrganization': SchemaType.STRING, // 处理方式  
      'schema:manufacturer': SchemaType.STRING, // 处理方式  
      'schema:isRelatedTo': SchemaType.STRING, // 处理方式  
      'schema:birthPlace': SchemaType.STRING, // 处理方式  
      'schema:character': SchemaType.STRING, // 处理方式  
      'schema:illustrator': SchemaType.STRING, // 处理方式  
      'schema:sponsor': SchemaType.STRING, // 处理方式  
      'schema:author': SchemaType.STRING, // 处理方式  
      'schema:creator': SchemaType.STRING, // 处理方式  
      'schema:editor': SchemaType.STRING, // 处理方式  
      'schema:maintainer': SchemaType.STRING, // 处理方式  
      'schema:provider': SchemaType.STRING, // 处理方式  
      'schema:translator': SchemaType.STRING, // 处理方式  
      'schema:publisher': SchemaType.STRING, // 处理方式  
      'schema:sdPublisher': SchemaType.STRING, // 处理方式  
      'schema:seller': SchemaType.STRING, // 处理方式  
      'schema:contentLocation': SchemaType.STRING, // 处理方式  
      'schema:publishedBy': SchemaType.STRING, // 处理方式  
      'schema:director': SchemaType.STRING, // 处理方式  
      'schema:directors': SchemaType.STRING, // 处理方式  
      'schema:attendees': SchemaType.STRING, // 处理方式  
      'schema:founder': SchemaType.STRING, // 处理方式  
      'schema:members': SchemaType.STRING, // 处理方式  
      'schema:actor': SchemaType.STRING, // 处理方式  
      'schema:actors': SchemaType.STRING, // 处理方式  
      'schema:organizer': SchemaType.STRING, // 处理方式  
      'schema:copyrightHolder': SchemaType.STRING, // 处理方式  
      'schema:musicBy': SchemaType.STRING, // 处理方式  
      'schema:partOfEpisode': SchemaType.STRING, // 处理方式  
      'schema:partOfSeason': SchemaType.STRING, // 处理方式  
      'schema:partOfSeries': SchemaType.STRING, // 处理方式  
      'schema:productionCompany': SchemaType.STRING, // 处理方式  
      'schema:performer': SchemaType.STRING, // 处理方式  
      'schema:performers': SchemaType.STRING, // 处理方式  
      'schema:eligibleTransactionVolume': SchemaType.STRING, // 处理方式  
      'schema:superEvent': SchemaType.STRING, // 处理方式  
      'schema:subEvents': SchemaType.STRING, // 处理方式  
      'schema:video': SchemaType.STRING, // 处理方式  
      'schema:workTranslation': SchemaType.STRING, // 处理方式  
      'schema:isPartOf': SchemaType.STRING, // 处理方式  
      'schema:hasPart': SchemaType.STRING, // 处理方式  
      'schema:isVariantOf': SchemaType.STRING, // 处理方式  
      'schema:isSimilarTo': SchemaType.STRING, // 处理方式  
      'schema:isAccessoryOrSparePartFor': SchemaType.STRING, // 处理方式  
      'schema:predecessorOf': SchemaType.STRING, // 处理方式  
      'schema:successorOf': SchemaType.STRING, // 处理方式  
      'schema:model': SchemaType.STRING, // 处理方式  
      'schema:isConsumableFor': SchemaType.STRING, // 处理方式  
      'schema:sdLicense': SchemaType.STRING, // 处理方式  
      'schema:warranty': SchemaType.STRING, // 处理方式  
      'schema:hasProductReturnPolicy': SchemaType.STRING, // 处理方式  
      'schema:hasMerchantReturnPolicy': SchemaType.STRING, // 处理方式  
      'schema:mentions': SchemaType.STRING, // 处理方式  
      'schema:educationalAlignment': SchemaType.STRING, // 处理方式  
      'schema:about': SchemaType.STRING, // 处理方式  
      'schema:mainEntity': SchemaType.STRING, // 处理方式  
      'schema:additionalProperty': SchemaType.STRING, // 处理方式  
      'schema:interactionStatistic': SchemaType.NUMBER, // 互动统计  
    }

结果事情有点失控,我不断发现需要将更多字段强制转换为字符串,而不是富类型。

不幸的是,即使做了这么多工作,仍然无法得到一个合理的结果。拥有这么多复杂的模式类型导致了不断寻找下一个循环引用的斗争。

我尝试了几种库来解决循环问题,也尝试断掉所有循环引用,但最后还是会得到一个乱七八糟且很长的 JSON 字符串,Gemini 处理起来比较棘手。这是在 AI Studio 上经过几小时尝试后决定放弃这种方法的时候的截图。

经过一番挣扎,我终于决定我的方法从根本上讲是错误的。虽然我可以将Schema类型和字段作为初始参考点,但我不能直接使用它们仓库中的类型。我必须从零开始重新构建它们,并确保它们可以被正确序列化,以便它们可以被正确序列化。

    const simpleString = (description: string) => ({  
      type: SchemaType.STRING,  
      description,  
    })  
    const simpleBool = (description: string) => ({  
      type: SchemaType.BOOLEAN,  
      description,  
    })  

    schemaGraph['bookFormatType'] = simpleEnum('书籍的出版格式。', [  
      'AudiobookFormat', 'EBook', 'GraphicNovel',  
      'Hardcover', 'Paperback',  
    ])  
    schemaGraph['Book'] = {  
      type: SchemaType.OBJECT,  
      properties: {  
        ...schemaGraph['creativeWork'].properties,  
        abridged: simpleBool('表示该书是否为缩印本。'),  
        bookEdition: simpleString('该书的版本。'),  
        bookFormat: schemaGraph['bookFormatType'],  
        illustrator: simpleString('该书的插画者。'),  
        isbn: simpleString('该书的ISBN号。'),  
        numberOfPages: simpleInt('该书的页码数。'),  
      }  
    }

最后,我可以从Goodreads上截图,Gemini能够正确地将截图转换成高质量的JSON数据。

但我不仅仅如此。我还需要一个初步处理,将输入获取为截图中对象的实际类型。这可能不总是 Book。所以我需要从所有这些标题格式的类型中创建一个枚举,并用它作为初始的提示。

    const typeEnums = {  
      type: SchemaType.STRING,  
      description: '最能代表输入的最优类型',  
      enum: Object.keys(schemaGraph).过滤(x => {  
        const x0 = x.substr(0, 1)  
        return x0 === x0.toUpperCase()  
      })  
    }

因为它正确地识别了截图中的书籍,所以可以继续执行第二个查询,获取书籍的模式和JSON输出。

这篇最初的帖子详细介绍了我早期使用AI来开始构建一个更新的语义网的工作。通过使用大语言模型将非结构化数据转化为结构化数据,我可以更轻松地继续推进我的工作。通过标准化有限类型的集合,我可以将不同的网站连接起来,并使数据迁移更加容易。

很快我就会再写一篇帖子,来跟进这项初始工作以及我学到的东西。

點擊查看更多內容
TA 點贊

若覺得本文不錯,就分享一下吧!

評論

作者其他優質文章

正在加載中
Web前端工程師
手記
粉絲
49
獲贊與收藏
218

關注作者,訂閱最新文章

閱讀免費教程

  • 推薦
  • 評論
  • 收藏
  • 共同學習,寫下你的評論
感謝您的支持,我會繼續努力的~
掃碼打賞,你說多少就多少
贊賞金額會直接到老師賬戶
支付方式
打開微信掃一掃,即可進行掃碼打賞哦
今天注冊有機會得

100積分直接送

付費專欄免費學

大額優惠券免費領

立即參與 放棄機會
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號

舉報

0/150
提交
取消