TFF · KNOWLEDGE MANAGEMENT BLUEPRINT · 2026-07-22

🧠 TFF 知識管理藍圖

PostgreSQL 保存可稽核的正本知識卡,Neo4j 保存有證據的關係圖,OpenSearch 提供 chunk 級 BM25+語意混合檢索。圖與搜尋索引都是可重建的 projection,不反向成為真相來源。

設計底線:先權限、後檢索;先證據、後生成;只使用 approved 且未失效的內容。無足夠證據時必須拒答或明確標示「待人工查核」。

本頁是目標架構與資料契約,不代表任何資料庫、索引、排程或 connector 已上線。現有盤點基線為 2,345 筆,實際匯入前須依 manifest 重數、去重與對帳。

01

先定義會被使用的輸出

3 個使用情境,都必須帶證據
情境 A · 課程拓展

「可以拓展出哪些課程?」

使用者:中台課程企劃、主管。

  • 輸出延伸/重組/全新課程,附受眾、學習目標、單元與時數。
  • 對照既有課程、法規變更、市場趨勢與競品缺口。
  • 每個建議帶原始文件、版本、頁碼/投影片與信心。
情境 B · 課程+講師

「可以提供哪些相關課程與講師?」

使用者:課程 PM、業務與主管。

  • 輸出相關課程、歷次場次、候選講師與 fit score 理由。
  • 師資依據來自專長、歷史授課、講義與核可成效,不只看向量相似。
  • 無最新時程時明示「可邀狀態:待確認」。
情境 C · 趨勢→研討會

「一句市場趨勢提供一個研討會主題」

使用者:中台企劃、聯誼會負責人。

  • 輸出正規化趨勢、主題名稱、切角、受眾與候選演講/與談人。
  • 附支持證據、鮮度、衝突與風險註記,並對齊 StyleProfile。
  • 無核可證據時只產「待調查草案」,不把輸入句當市場事實。
02

三層藍圖:正本、證據圖、檢索 projection

Single write model → rebuildable read models
flowchart LR
  classDef src fill:#e4f3f7,stroke:#253047,stroke-width:2px,color:#172033;
  classDef core fill:#f6d0c4,stroke:#cf5f3a,stroke-width:3px,color:#172033,font-weight:bold;
  classDef proj fill:#e5ddf1,stroke:#7c6fab,stroke-width:2px,color:#172033,font-weight:bold;
  classDef gate fill:#dcecd0,stroke:#5f9b75,stroke-width:3px,color:#172033;
  SRC["內外部來源
connectors/檔案/API"]:::src --> ING["進料、正規化、驗證"]:::src ING --> PG[("PostgreSQL
正本 CardEnvelope/版本/ACL/審核")]:::core PG --> OUT["transactional outbox"]:::core OUT --> OS[("OpenSearch
ChunkProjection/BM25/向量")]:::proj OUT --> NEO[("Neo4j
RelationshipAssertion/證據圖")]:::proj PG --> GOV{"審核與時效閥門"}:::gate OS --> QA["混合檢索與問答"]:::gate NEO --> QA GOV --> QA QA --> AUD[("引用/稽核紀錄")]:::core

PostgreSQL · 唯一正本

卡片內容、版本、content hash、來源、ACL、狀態與審核紀錄。所有變更先在單一 transaction 寫入卡片與 outbox。

Neo4j · 證據關係

只接受通過契約驗證的 RelationshipAssertion;每個主張可逆查到 chunk 與文件。可由 PostgreSQL 全量重建。

OpenSearch · chunk 檢索

保存可搜尋文字、欄位、ACL projection 與 embedding;回傳 chunkId 不當作最終證據正本。

不變量 不做 PostgreSQL/Neo4j/OpenSearch 三邊雙寫;outbox consumer 使用 eventId 冪等,projection 延遲可觀測,也可一鍵重放。
03

來源全目錄、connector 現況與進料治理

先盤點、再接線;未接線不等於已上線
內部受管資料

六類必備資產

  • 講師名單:身分、專長、產官學背景、可邀狀態。
  • 課程清單:課代、課名、分類、單元、時數與歷次場次。
  • Word/PowerPoint(PPT/PPTX;.ppt.pptx)/PDF:課程計畫、講義、教材與附件。
  • 課程成效:招生、滿意度、續約、採用與學習回饋。
  • 研討會主題:歷年題目、與談內容、主講人與會員需求。
  • 風格樣本:核定名稱、常用語、禁用語與修改前後版本。
目前 connector · 僅 5 組

已盤點的連接範圍

FSC(金管會公開資訊)、TFF(本會公開課程)、TABF(金研院)、SFI(證基會)、TII(保發中心)。

除此以外均不得標示為現行 connector。內部檔案仍須走受管上傳、權限確認與人工核可,不因位於共享槽就視為可自動進料。

規劃 connector

其餘來源逐站建置

每個 connector 必須先登錄 owner、使用條款、robots/API 限制、擷取頻率、去重鍵、schema mapping、失敗告警與停用程序;完成驗收前只列為 planned。

FPAT 名稱待確認,禁止排程與自動進料。

目錄完整來源清單connector 狀態
內部來源講師名單、課程清單、Word/PowerPoint(PPT/PPTX)/PDF、課程成效、研討會主題、風格樣本。受管上傳/批次匯入候選;逐批確認 ACL、授權與 owner。
外部來源金管會、央行、證交所、台經院、中經院、公開資訊觀測站、行政院、國發會、財政部、數位發展部、經濟日報、工商時報、公司治理平台、FPAT(名稱待確認、不得自動進料)、四大會計師事務所、智庫、金研院、保發中心、證基會、犯防中心、信託公會、公司治理協會、永續金融評鑑平台、各金融機構(銀保證)公開網頁。FSC/TABF/SFI/TII 為現況;其餘逐站規劃。TFF 為現況的本會公開來源。
來源預設側寫典型來源使用規則
A 官方/一手主管機關、政府機關、法規與依法揭露平台。只表示該來源通常適合提供官方事實;不能取代主張層級的適用性判斷。
B 專業/研究研究機構、智庫、專業機構、評鑑與會計專業內容。通常用於解釋、方法與趨勢佐證;仍須檢查日期、方法與原始證據。
C 媒體/市場訊號新聞媒體、金融機構公開網頁與一般公開頁面。適合發現議題與市場熱度,不應單獨承擔高風險法規結論。
權威判定不是貼一次標籤就結束 A/B/C 僅是來源的 defaultAuthority 預設側寫;每一個 RelationshipAssertion 都必須另存 authorityFit、支持/反駁方向 polarity、證據 chunk 與人工 reviewStatus,依「這個來源是否有權、且是否足以支持這個主張」判定。相同來源可對不同主張得到不同結果,衝突時並列並送人工審核,不以預設級別自動勝出。
flowchart LR
  classDef src fill:#e4f3f7,stroke:#253047,stroke-width:2px,color:#172033;
  classDef proc fill:#fff7e4,stroke:#253047,stroke-width:2px,color:#172033;
  classDef gate fill:#dcecd0,stroke:#5f9b75,stroke-width:3px,color:#172033;
  classDef store fill:#f6d0c4,stroke:#cf5f3a,stroke-width:3px,color:#172033,font-weight:bold;
  classDef proj fill:#e5ddf1,stroke:#7c6fab,stroke-width:2px,color:#172033,font-weight:bold;
  classDef err fill:#f7d7cf,stroke:#b85c63,stroke-width:2px,color:#172033;
  SRC["內部受管資料/已核准 connector"]:::src --> RAW[("raw snapshot
原始位元組+擷取 manifest")]:::store RAW --> DRAFT["draft card
先建不可檢索草稿"]:::proc DRAFT --> PARSE["parse/OCR
版面、頁碼、投影片定位"]:::proc PARSE --> SEC["PII/ACL
偵測、分級、遮罩與權限"]:::proc SEC --> EXT["chunk/entity/assertion extraction
切塊、實體消歧、候選主張"]:::proc EXT --> HUMAN{"human approval
內容、證據、權限、有效期"}:::gate HUMAN -->|核准| PG[("PostgreSQL
CardEnvelope/版本/審核")]:::store HUMAN -->|退回補正| DRAFT PG --> OUT[("transactional outbox")]:::store OUT --> OS[("OpenSearch
ChunkProjection")]:::proj OUT --> NEO[("Neo4j
RelationshipAssertion")]:::proj PARSE -.可重試失敗.-> RETRY["retry
退避、上限、idempotency key"]:::err SEC -.可重試失敗.-> RETRY EXT -.可重試失敗.-> RETRY OUT -.投影失敗.-> RETRY RETRY -->|未達上限| AGAIN["回到原失敗步驟"]:::proc AGAIN --> PARSE RETRY -->|超過上限| DLQ[("DLQ
保留 payload、錯誤與 traceId")]:::err DLQ --> REPLAY["人工修正/核准 replay"]:::gate REPLAY --> RAW
可重放、不繞過治理 raw snapshot 永不被解析結果覆寫;重試與 replay 沿用同一 idempotencyKey。DLQ 重放仍須重新執行 PII/ACL 與人工核可,projection 可由 PostgreSQL 正本與 outbox 全量重建。
04

16 個 metadata 維度與落點

每張卡都對應三個使用問題與三層儲存
問題代號 A=「可以拓展出哪些課程?」;B=「可以提供哪些相關課程與講師?」;C=「一句市場趨勢提供一個研討會主題」。PG 保存正本;Neo4j 保存有證據的關係;OpenSearch 保存可重建的檢索欄位與向量。

1身分與實體消歧

canonicalIdaliasesexternalIds

放置 PG:正名與外部 ID;Neo4j:實體節點與合併映射;OpenSearch:別名檢索欄位。

支援 A/B/C。

2語義、同義詞與 taxonomy

keywordscategorytagssynonyms

放置 PG:受控詞表正本;Neo4j:分類/主題關係;OpenSearch:分析器、filter 與查詢擴展。

支援 A/B/C。

3來源與證據

sourceIdurlcontentHashevidenceChunkIds

放置 PG:來源、hash 與證據正本;Neo4j:SUPPORTED_BY 與來源節點;OpenSearch:來源與證據篩選欄位。

支援 A/B/C。

4頁碼/投影片/章節/chunk lineage

documentIdchunkIdpageNumberslideNumbersectionPathquoteSpan

放置 PG:不可變 lineage;Neo4j:證據指標;OpenSearch:chunk 定位與 citation 回傳欄位。

支援 A/B/C。

5發布、觀測、有效期與版本

publishedAtobservedAtingestedAtvalidFromvalidTosupersedesversionisCurrent

放置 PG:版本鏈與有效期正本;Neo4j:關係有效區間;OpenSearch:時效 filter 與排序欄位。

支援 A/B/C。

6權威、品質、交叉佐證與衝突

defaultAuthorityauthorityFitconfidencecorroborationconflictStatus

放置 PG:判定與審核正本;Neo4j:主張層級 authorityFit、佐證與衝突邊;OpenSearch:品質 filter/ranking signal。

支援 A/B/C。

7法規與適用範圍

jurisdictionregulatorregulatedHours

放置 PG:適用範圍正本;Neo4j:以 AssertionSUBJECTOBJECT 串接法規與適用對象;OpenSearch:轄區、機關與時數 filter。

支援 A;並輔助 B。

8市場、競品、熱度與急迫性

marketSegmentcompetitortrendDirectionimpacturgencyevidenceCount

放置 PG:觀測與評分正本;Neo4j:競品/趨勢/影響關係;OpenSearch:市場 filter 與排序信號。

支援 B/C;並輔助 A 排優先級。

9學習目標、對象、程度、先備知識與能力

learningObjectivestargetAudiencelevelprerequisitescompetencyCodes

放置 PG:課程設計正本;Neo4j:課程—能力—對象關係;OpenSearch:課程匹配欄位。

支援 A/B/C。

10課程形式、時數、單元與認列時數

deliveryModedurationmodulesassessmentMethodregulatedHours

放置 PG:課程結構正本;Neo4j:HAS_MODULESATISFIES;OpenSearch:形式、時數與單元檢索欄位。

支援 A/B/C。

11講師專長、歷史成效、角色與可邀狀態

expertisesectorhistoricalPerformanceroleavailabilityStatus

放置 PG:師資正本與受限狀態;Neo4j:EXPERT_INTAUGHT_BY;OpenSearch:專長與可邀狀態檢索。

支援 B/C;並輔助 A 找更新講師。

12招生、滿意度、續約與採用回饋

enrollmentsatisfactionrenewaladoptionvalueScore

放置 PG:受權限保護的明細與彙總;Neo4j:僅投影核准的聚合成效;OpenSearch:僅索引可揭露的聚合排序值。

支援 B/C;並輔助 A 排改版順序。

13命名與品牌風格

namingRulesglossaryregulatorPreferenceexamples

放置 PG:主管核定 StyleProfile;Neo4j:NAMED_BY;OpenSearch:範例與詞彙檢索。

支援 A/C 的命名排序,並維持 B 的一致表述。

14核可、owner、reviewer 與 review due date

statusownerreviewerreviewedAtreviewDueAt

放置 PG:workflow 與稽核正本;Neo4j:只投影核准狀態/責任參照;OpenSearch:僅發布 approved、未逾期版本。

支援 A/B/C 的可發布性。

15ACL、PII、機密、授權、著作權與保存期限

aclGroupspiiLevelconfidentialitylicensecopyrightOwnerretentionPolicy

放置 PG:治理與授權正本;Neo4j:最小化 ACL group 參照;OpenSearch:強制 ACL projection 與禁索引遮罩。

支援 A/B/C;在檢索前先裁切可見資料。

16embedding/chunk/index 版本及關係邊 metadata

embeddingModelversiondimensionschunkStrategyVersiontokenCountindexVersionindexedAt;邊:relationIdpredicateconfidenceevidenceChunkIdsextractionMethodextractionModelextractionVersionreviewStatuspolarityvalidFromvalidTo

放置 PG:projection 設定、版本與 outbox 正本;Neo4j:核准邊與證據屬性;OpenSearch:向量、chunk 與索引版本。

支援 A/B/C,確保結果可重現、可重建。

05

三份跨儲存資料契約

PostgreSQL 是正本;圖與搜尋皆為衍生 projection

CardEnvelope · PostgreSQL canonical

identity
  cardId, cardType, schemaVersion
  canonicalId, externalIds[]

content
  title, body, language
  taxonomy, attributes

source + lineage
  sourceId, sourceUrl, contentHash
  rawSnapshotUri, documentId
  parentCardId, supersedes, version
  publishedAt, observedAt, ingestedAt
  validFrom, validTo, isCurrent

governance
  status, owner, reviewer
  reviewedAt, reviewDueAt
  aclGroups[], piiLevel, confidentiality
  license, copyrightOwner, retentionPolicy

audit
  createdAt, createdBy, updatedAt, traceId

ChunkProjection · OpenSearch rebuildable

identity + join
  chunkId, cardId, cardVersion, cardType
  sourceId, documentId, contentHash

text + location
  text, title, language
  pageNumber, slideNumber, sectionPath
  quoteSpan, tokenCount

retrieval
  keywords[], category[], tags[], synonyms[]
  embedding, embeddingModel, embeddingVersion
  dimensions, chunkStrategyVersion
  indexVersion, indexedAt

security + lifecycle
  aclGroups[], piiLevel, piiStatus, confidentiality
  status, validFrom, validTo, isCurrent
  publishedAt, observedAt

RelationshipAssertion · Neo4j evidence edge

identity + endpoints
  relationId, subjectId, predicate
  objectId, objectType

claim semantics
  polarity, confidence
  defaultAuthority, authorityFit
  validFrom, validTo

evidence + extraction
  evidenceChunkIds[]
  extractionMethod, extractionModel, extractionVersion
  corroboration, conflictStatus

governance
  reviewStatus, reviewer, reviewedAt
  sourceCardId, sourceCardVersion
  schemaVersion, createdAt, traceId

CardEnvelope 治理語義

同一 cardId 的版本採 append-only;contentHash 與來源外部 ID 共同支援冪等與去重。只有 approvedisCurrent=true、仍在有效期且 ACL 允許的版本能進 outbox。退件、撤銷與到期保留稽核軌跡,不覆寫歷史。

ChunkProjection 治理語義

它是 CardEnvelope 的可丟棄讀模型,不是新真相來源。chunk 必須能以 cardId + cardVersion + quoteSpan 回到正本;先套 ACL、有效期與核可狀態,再做 BM25/向量召回。模型、切塊或索引版本變更時可全量重建。

RelationshipAssertion 治理語義

每條邊都是可審核主張,不是無來源的關聯。至少一個有效 evidenceChunkIds,並保存主張專屬 authorityFit、方向 polarity 與有效期;只有 reviewStatus=approved 的邊可供正式回答,衝突邊並存並交由人判定。

發布不變量 卡片與 outbox 同一個 PostgreSQL transaction;consumer 以 eventId/relationId/chunkId 冪等。Neo4j 與 OpenSearch 不接受繞過 CardEnvelope 審核流程的直接寫入,任何 citation 都必須可逆查到 raw snapshot、文件版本與原始位置。
06

Neo4j:可稽核的領域關係

Assertion 把結論、證據、時效與審核放在一起
flowchart LR
  ORG[Organization] -->|PUBLISHED| DOC[Document]
  DOC -->|DERIVED_FROM| SRC[Source]
  DOC -->|HAS_CHUNK| CH[Chunk]
  CH -->|NEXT_CHUNK| CH2[Chunk]
  CH -->|MENTIONS| TOP[Topic]
  AS[Assertion] -->|SUBJECT| TOP
  AS -->|OBJECT| REG[Regulation]
  AS -->|SUPPORTED_BY| CH
  TREND[Trend] -->|IMPACTS| TOP
  COURSE[Course] -->|HAS_MODULE| MOD[CourseModule]
  COURSE -->|HAS_OFFERING| OFF[CourseOffering]
  COURSE -->|COVERS| TOP
  COURSE -->|TARGETS| AUD[Audience]
  COURSE -->|SATISFIES| COMP[Competency]
  COURSE -->|USES| MAT[Material]
  OFF -->|TAUGHT_BY| INS[Instructor]
  INS -->|EXPERT_IN| TOP
  SEM[SeminarTheme] -->|DERIVED_FROM| TREND
  SEM -->|NAMED_BY| STYLE[StyleProfile]
  
類別Ontology 白名單
NodesOrganization · Source · Document · Chunk · Assertion · Topic · Trend · Regulation · Course · CourseOffering · CourseModule · Instructor · Material · SeminarTheme · Audience · Competency · StyleProfile
RelationshipsPUBLISHED · HAS_CHUNK · NEXT_CHUNK · MENTIONS · SUBJECT · OBJECT · SUPPORTED_BY · HAS_OFFERING · HAS_MODULE · COVERS · TAUGHT_BY · EXPERT_IN · USES · TARGETS · SATISFIES · IMPACTS · DERIVED_FROM · NAMED_BY
Edge minimumrelationId · confidence · evidenceChunkIds · reviewStatus · validFrom/To · extractionMethod/version
兩個必要分拆 複雜主張用 Assertion 中介節點,不建無來源的 RELATED_TOCourse 放穩定課程設計;CourseOffering 才放開課日、講師、價格、形式、招生與評價。

Constraints

CREATE CONSTRAINT document_id IF NOT EXISTS
FOR (d:Document) REQUIRE d.id IS UNIQUE;

CREATE CONSTRAINT chunk_id IF NOT EXISTS
FOR (c:Chunk) REQUIRE c.id IS UNIQUE;

CREATE CONSTRAINT assertion_id IF NOT EXISTS
FOR (a:Assertion) REQUIRE a.id IS UNIQUE;

MERGE 使用已驗證的 canonical ID

// 呼叫前由應用 schema 拒絕 null/空字串 ID;
// UNIQUE constraint 防重,但不代表屬性必然存在。
MERGE (d:Document {id: $documentId})
  ON CREATE SET d.createdAt = datetime()
SET d.revisionId = $revisionId,
    d.status = $status;
MERGE (c:Chunk {id: $chunkId})
SET c.text = $text;
MERGE (d)-[r:HAS_CHUNK]->(c)
SET r.relationId = $lineageRelationId,
    r.confidence = 1.0,
    r.evidenceChunkIds = [$chunkId],
    r.reviewStatus = 'approved',
    r.extractionMethod = 'deterministic-parser',
    r.extractionVersion = $parserVersion;

Assertion:主張、端點與證據

MATCH (s:Trend {id: $subjectId}),
      (o:Topic {id: $objectId})
WITH s, o, $evidenceChunkIds AS evidenceChunkIds
WHERE size(evidenceChunkIds) > 0
UNWIND evidenceChunkIds AS evidenceChunkId
MATCH (c:Chunk {id: evidenceChunkId})
WITH s, o, evidenceChunkIds,
     collect(DISTINCT c) AS evidenceChunks
WHERE size(evidenceChunks) = size(evidenceChunkIds)
MERGE (a:Assertion {id: $relationId})
SET a.predicate = $validatedPredicate,
    a.confidence = $confidence,
    a.polarity = $polarity,
    a.authorityFit = $authorityFit,
    a.evidenceChunkIds = $evidenceChunkIds,
    a.reviewStatus = $reviewStatus,
    a.extractionMethod = $extractionMethod,
    a.extractionModel = $extractionModel,
    a.extractionVersion = $extractionVersion,
    a.validFrom = CASE WHEN $validFrom IS NULL
      THEN null ELSE datetime($validFrom) END,
    a.validTo = CASE WHEN $validTo IS NULL
      THEN null ELSE datetime($validTo) END
MERGE (a)-[:SUBJECT]->(s)
MERGE (a)-[:OBJECT]->(o)
FOREACH (c IN evidenceChunks |
  MERGE (a)-[:SUPPORTED_BY]->(c)
);

OpenSearch hits → graph expansion

UNWIND $hits AS hit
MATCH (c:Chunk {id: hit.chunkId})
MATCH (d:Document)-[:HAS_CHUNK]->(c)
OPTIONAL MATCH (a:Assertion)-[:SUPPORTED_BY]->(c)
WHERE a.reviewStatus = 'approved'
  AND (a.validFrom IS NULL OR a.validFrom <= datetime())
  AND (a.validTo IS NULL OR datetime() < a.validTo)
OPTIONAL MATCH (a)-[:SUBJECT|OBJECT]->(e)
RETURN hit.rank, c.id, d.id,
       [item IN collect(DISTINCT CASE WHEN a IS NULL
         THEN null ELSE {
           assertionId: a.id, entityId: e.id,
           polarity: a.polarity,
           conflictStatus: a.conflictStatus,
           confidence: a.confidence
         } END)
        WHERE item IS NOT NULL] AS evidenceGraph;
Constraint 邊界 部署版若有 Enterprise existence constraint/node key 可再加強;未鎖定 edition 前,本藍圖以應用層 JSON Schema 驗證必填 ID,再用 IS UNIQUE 防止重複。evidenceChunkIds 同時要求 minItems: 1uniqueItems: true;Cypher 只有在全部 chunk 存在時才建立 Assertion,服務層必須把零筆更新視為驗證失敗,而非成功。$validatedPredicate 必須先通過 ontology 白名單,不接受使用者自由輸入 label 或 relationship type。
07

OpenSearch:chunk 級混合檢索

一 chunk 一 document;索引是可重建的 read model
用途Physical indexStable alias
來源/文件級投影tff-kb-sources-v1tff-kb-sources-read
chunk 全文/向量/ACLtff-kb-chunks-v1tff-kb-chunks-read

以下 768 是可解析的範例值;正式 dimension 必須與選定 embedding model 完全一致。範例中的 cjk 只代表待評測的候選 mapping,不代表已勝出;若 ICU 測試較佳,須以已安裝並鎖版的 ICU analyzer 建立新版本索引。mapping 改型別或更換模型時建 v2、reindex,驗證後以 POST /_aliases 原子切換。

Explicit mapping · valid JSON body

{
  "settings": { "index.knn": true },
  "mappings": {
    "dynamic": "strict",
    "properties": {
      "chunk_id": { "type": "keyword" },
      "card_id": { "type": "keyword" },
      "revision_id": { "type": "keyword" },
      "card_type": { "type": "keyword" },
      "source_id": { "type": "keyword" },
      "document_id": { "type": "keyword" },
      "language": { "type": "keyword" },
      "title": {
        "type": "text", "analyzer": "cjk",
        "fields": { "raw": { "type": "keyword", "ignore_above": 512 } }
      },
      "content": { "type": "text", "analyzer": "cjk" },
      "section_path": { "type": "text", "analyzer": "cjk" },
      "topic_ids": { "type": "keyword" },
      "neo4j_node_ids": { "type": "keyword" },
      "keywords": { "type": "keyword" },
      "category": { "type": "keyword" },
      "tags": { "type": "keyword" },
      "synonyms": { "type": "keyword" },
      "status": { "type": "keyword" },
      "is_current": { "type": "boolean" },
      "pii_level": { "type": "keyword" },
      "pii_status": { "type": "keyword" },
      "confidentiality": { "type": "keyword" },
      "acl_groups": { "type": "keyword" },
      "page_number": { "type": "integer" },
      "slide_number": { "type": "integer" },
      "quote_span": {
        "properties": {
          "start": { "type": "integer" },
          "end": { "type": "integer" }
        }
      },
      "valid_from": { "type": "date" },
      "valid_to": { "type": "date" },
      "published_at": { "type": "date" },
      "observed_at": { "type": "date" },
      "content_hash": { "type": "keyword" },
      "embedding_model_id": { "type": "keyword" },
      "embedding_version": { "type": "keyword" },
      "embedding_dimensions": { "type": "integer" },
      "chunk_strategy_version": { "type": "keyword" },
      "token_count": { "type": "integer" },
      "index_version": { "type": "keyword" },
      "indexed_at": { "type": "date" },
      "embedding": {
        "type": "knn_vector", "dimension": 768,
        "method": {
          "name": "hnsw", "engine": "lucene",
          "space_type": "cosinesimil",
          "parameters": { "ef_construction": 100, "m": 16 }
        }
      }
    }
  }
}

Serializer 契約:canonical camelCase 轉成 index snake_case,例如 cardVersion→revision_idtext→contentembeddingModel→embedding_model_idpii_status 是由核可流程產生的安全投影欄位。送入 dynamic: strict 前先以同版 JSON Schema 驗證,不傳未映射欄位。

Search pipeline · valid JSON body

{
  "phase_results_processors": [
    {
      "normalization-processor": {
        "normalization": { "technique": "min_max" },
        "combination": {
          "technique": "arithmetic_mean",
          "parameters": { "weights": [0.4, 0.6] }
        }
      }
    }
  ]
}

Top-level hybrid query · valid JSON body

{
  "size": 20,
  "_source": { "excludes": ["embedding"] },
  "query": {
    "hybrid": {
      "filter": {
        "bool": { "filter": [
          { "term": { "status": "approved" } },
          { "term": { "is_current": true } },
          { "term": { "pii_status": "cleared" } },
          { "bool": { "should": [
            { "bool": { "must_not": { "exists": { "field": "valid_from" } } } },
            { "range": { "valid_from": { "lte": "now" } } }
          ], "minimum_should_match": 1 } },
          { "bool": { "should": [
            { "bool": { "must_not": { "exists": { "field": "valid_to" } } } },
            { "range": { "valid_to": { "gt": "now" } } }
          ], "minimum_should_match": 1 } },
          { "terms": { "acl_groups": ["ACL_GROUP_FROM_TRUSTED_ADAPTER"] } }
        ] }
      },
      "queries": [
        { "multi_match": {
          "query": "永續金融公司治理課程",
          "fields": ["title^3", "section_path^2", "content"]
        } },
        { "neural": { "embedding": {
          "query_text": "永續金融公司治理課程",
          "model_id": "DEPLOYED_MODEL_ID", "k": 100
        } } }
      ]
    }
  }
}

Atomic alias switch · valid JSON body

{
  "actions": [
    { "remove": {
      "index": "tff-kb-chunks-v1",
      "alias": "tff-kb-chunks-read"
    } },
    { "add": {
      "index": "tff-kb-chunks-v2",
      "alias": "tff-kb-chunks-read"
    } }
  ]
}
安全層部署契約不可替代項
DLSapproved ∧ is_current ∧ effective-now ∧ pii_status=cleared ∧ acl_groups∩effectiveGroups≠∅由 Security plugin role 或可信 adapter 強制;群組只取自登入身分,不採用 prompt/前端參數。
FLS回答服務採 allowlist,只回傳正文、引用定位、核可後實體 ID 與可揭露 metadata;隱藏 embedding、原始 PII 與治理內欄位。_source.excludes 只是縮小回應,不是 FLS。
版本鎖定正式上線前固定 OpenSearch/Security plugin version、edition 與 role API,建立未授權帳號的負向測試。未完成鎖版前,不把示意 query 當 production role 設定。
繁中分詞與安全邊界 不先指定 ICU 或 CJK 誰最好;用 POST /_analyze 與 TFF 金融詞彙判斷集比較 Recall@k、MRR/nDCG 與引用正確率。查詢中的 ACL_GROUP_FROM_TRUSTED_ADAPTER 是 server-side 注入位置,不可原樣送出,也不可取自使用者問題。ACL 在召回前過濾,並以 DLS 限制可見文件、FLS 隱藏無需回傳的敏感欄位;未採用 Security plugin 時,必須由可信服務層執行同等政策。post-filter、filtered alias 與前端 filter 都不是資安邊界。
可重建索引的發布程序 projection worker 以 _bulk 寫入新的 physical index,逐筆檢查 bulk item error 並依 chunk_id+revision_id 冪等重試;需要回灌時使用 _reindex 或由 PostgreSQL 正本全量重建。完成 mapping、ACL、抽樣查詢與引用驗證後,在同一個 POST /_aliases action 內移除舊索引並加入新索引,才算 atomic alias switch。
08

從問題到可引用答案

先召回,再展開圖,最後生成
flowchart LR
  Q["使用者問題/一句趨勢"] --> I["意圖/實體/同義詞正規化"]
  I --> O["OpenSearch BM25+vector+ACL prefilter"]
  O --> H["hits: chunkId/rank/score"]
  H --> N["Neo4j 展開主題/法規/課程/講師/Assertion"]
  N --> F["RRF/各源正規化"]
  F --> RR["rerank/交叉佐證/衝突保留"]
  RR --> G{"approved/未失效/ACL 可見/PII 已處理?"}
  G -->|yes| A["答案+理由+頁碼/投影片引用+信心"]
  G -->|no| GAP["資料不足/無權限/待人工查核"]
  
分數原則 BM25、vector cosine、圖遍歷與成效分數不在同一尺度,不可直接相加。先各自排名,以 RRF 或正規化融合,再用 TFF 黃金問答集調整。OpenSearch 與 Neo4j 由應用層 adapter 編排,不宣稱有原生整合 retriever。
09

三個核心使用情境

每個輸出都要能反查證據
1 · 課程拓展

「可以拓展出哪些課程?」

  • 課名/命名理由,分類為延伸、重組或全新缺口。
  • 受眾、學習目標、建議單元、時數/形式。
  • 市場/法規/競品依據、引用、時效與信心。
2 · 課程+講師

「可以提供哪些相關課程與講師?」

  • 現有課程、場次、候選講師與 fit score 分解。
  • fit 來自專長、歷史授課、講義與學員回饋,不只是向量相似。
  • 無最新邀課資料時明示 availability:待確認
3 · 趨勢→研討會

「一句市場趨勢提供一個研討會主題」

  • 輸出 normalizedTrend、seminarTitle、切角、受眾、候選演講/與談人。
  • 附支持證據、freshness、衝突與 riskNotes。
  • 無核可證據時只產「待調查草案」,不把輸入句當市場事實。
10

核可、權限與反模式

金融知識庫的答案邊界
✅ 只索引 approved、未失效、未被 supersede 且 ACL 可見的 revision。
✅ PII 未去識別、授權不明或超過 retention 的內容不進可答 corpus。
✅ 衝突主張並列呈現來源、時間與 authorityFit,不由 LLM 靜默選邊。
✅ 同名講師/機構依 canonical ID+aliases+外部 ID 消歧,不用姓名直接 MERGE。
✅ 向量相似度是可重算的檢索訊號,不永久寫成領域真實。
✅ 生產 ontology 只允許白名單 node/relationship,不接受 FREE schema 或無證據 RELATED_TO
11

分期落地與驗收

先讓三個問題可驗證,再擴大自動化
P1 · 可答試點

2,345 筆基線+內部資產

建正本契約、解析 Word/PPT/PDF、整理講師與 CourseOffering,完成三個黃金查詢與引用驗收。

P2 · 來源擴張

20+ connector 與實體消歧

逐站完成授權/更新頻率,加入法規失效、衝突與交叉佐證治理,建立 ICU/CJK 與混合權重判斷集。

P3 · 成效閉環

採用、續約與品質監測

將人工採用、招生、滿意度、續約與點閱回饋模型,監測檢索漂移、資料鮮度與 projection lag。

驗收:每個建議至少回溯一個核可 chunk,顯示來源、頁碼/投影片、版本、時效與信心。
驗收:過期、被取代、未核可、無權限或 PII 未處理的內容不得出現。
驗收:講師建議有專長/歷史授課依據;無時程資料顯示「待確認」。
驗收:趨勢證據不足時輸出調查缺口,不將推測包裝成市場事實。

官方技術依據