{
 "ticket": "U82 #119",
 "generatedAt": "2026-10-27T22:40:03.672685+08:00",
 "startedAt": "2026-10-27T22:39:54.671248+08:00",
 "purpose": "Spring AI 2.0.2 贯通 RAG：文档处理/真实 ES 向量检索/ACL/引用/流式取消；LangChain4j 1.22.0 对照；模型为确定性替身",
 "exitCode": 0,
 "testCount": 27,
 "reports": [
  {
   "module": "rag-lab",
   "name": "study.airag.DocumentPipelineTest",
   "tests": "4",
   "failures": "0",
   "errors": "0",
   "skipped": "0",
   "time": "0.14",
   "file": "rag-lab/target/surefire-reports/TEST-study.airag.DocumentPipelineTest.xml"
  },
  {
   "module": "rag-lab",
   "name": "study.airag.ElasticsearchRetrievalTest",
   "tests": "7",
   "failures": "0",
   "errors": "0",
   "skipped": "0",
   "time": "0.511",
   "file": "rag-lab/target/surefire-reports/TEST-study.airag.ElasticsearchRetrievalTest.xml"
  },
  {
   "module": "rag-lab",
   "name": "study.airag.LangChain4jContrastTest",
   "tests": "4",
   "failures": "0",
   "errors": "0",
   "skipped": "0",
   "time": "0.352",
   "file": "rag-lab/target/surefire-reports/TEST-study.airag.LangChain4jContrastTest.xml"
  },
  {
   "module": "rag-lab",
   "name": "study.airag.RagAssistantTest",
   "tests": "4",
   "failures": "0",
   "errors": "0",
   "skipped": "0",
   "time": "0.32",
   "file": "rag-lab/target/surefire-reports/TEST-study.airag.RagAssistantTest.xml"
  },
  {
   "module": "rag-lab",
   "name": "study.airag.StreamingCancellationTest",
   "tests": "5",
   "failures": "0",
   "errors": "0",
   "skipped": "0",
   "time": "1.492",
   "file": "rag-lab/target/surefire-reports/TEST-study.airag.StreamingCancellationTest.xml"
  },
  {
   "module": "es9-boundary",
   "name": "study.aiboundary.Es9ClientBoundaryTest",
   "tests": "3",
   "failures": "0",
   "errors": "0",
   "skipped": "0",
   "time": "0.227",
   "file": "es9-boundary/target/surefire-reports/TEST-study.aiboundary.Es9ClientBoundaryTest.xml"
  }
 ],
 "results": [
  {
   "name": "clean-test",
   "command": [
    "/Users/gaochao/develop/maven-3.9.9/bin/mvn",
    "-B",
    "-q",
    "clean",
    "test"
   ],
   "exitCode": 0,
   "runnerExitCode": 0,
   "timedOut": false,
   "failure": null,
   "cleanupError": null,
   "observed": [
    "U82 streamComplete=PASS tokens=11",
    "U82 ragStreamOnce=PASS invocations=1 retrievedInPrompt=true",
    "U82 streamCancel=PASS consumed=3 emitted=3 cancelled=1",
    "U82 streamTimeout=PASS error=TimeoutException",
    "U82 dispose=PASS received=3 cancelled=1 emitted=3",
    "U82 splitMetadata=PASS chunks=4",
    "U82 docMetadata=PASS categories=[header_1, header_2]",
    "U82 docReader=PASS blocks=11 confidential=1 metadata=[acl, category, doc, tenant, title]",
    "U82 splitter=PASS shortBlocks=1 longBlocks=3",
    "U82 filterSyntax=PASS sameStringSyntax=false",
    "U82 lc4jParity=PASS sameTop=true scoreSpring=0.405 scoreLc4j=0.7024",
    "U82 lc4jFilter=PASS springHits=10 lc4jHits=10 springDsl=tenant == tenant-a && (acl == public || acl == internal)",
    "U82 defaultsContrast=PASS springTopK=4 lc4jTopK=3 springTopScore=0.411",
    "U82 ragAdvisor=PASS retrieved=2 maxPerDocument=1 citations=2",
    "U82 aclLeak=publishBlocked retrieved=1",
    "U82 qaAdvisor=PASS retrieved=4 citations=[[kb/platform-handbook.md#1], [kb/platform-handbook.md#2], [kb/platform-handbook.md#4], [kb/public-faq.md#3]]",
    "U82 emptyContext=PASS prompt=refusal-template",
    "U82 aclMalformed=FAIL-FAST parserRejects=true",
    "U82 topKThreshold=PASS topK4=4 topK1=1 aboveTop=0 highest=0.411",
    "U82 tenantIsolation=PASS crossTenantHits=0 ownHits=5",
    "U82 aclFilter=PASS internalHits=10 confidentialHits=10",
    "U82 aclPublicOnly=PASS hits=5",
    "U82 esKnn=PASS server=8.19.23 hits=3 topScore=0.405",
    "U82 deleteByFilter=PASS remaining=6",
    "U82 es8PlainJson=PASS status=200",
    "U82 es9Boundary=PASS clientMajor=9 transport=rest5",
    "U82 es9VsEs8=PASS failure=media_type_header_exception"
   ]
  }
 ],
 "observations": [
  "U82 aclFilter",
  "U82 aclLeak",
  "U82 aclMalformed",
  "U82 aclPublicOnly",
  "U82 defaultsContrast",
  "U82 deleteByFilter",
  "U82 dispose",
  "U82 docMetadata",
  "U82 docReader",
  "U82 emptyContext",
  "U82 es8PlainJson",
  "U82 es9Boundary",
  "U82 es9VsEs8",
  "U82 esKnn",
  "U82 filterSyntax",
  "U82 lc4jFilter",
  "U82 lc4jParity",
  "U82 qaAdvisor",
  "U82 ragAdvisor",
  "U82 ragStreamOnce",
  "U82 splitMetadata",
  "U82 splitter",
  "U82 streamCancel",
  "U82 streamComplete",
  "U82 streamTimeout",
  "U82 tenantIsolation",
  "U82 topKThreshold"
 ],
 "environment": {
  "os": "macOS-27.2-arm64-arm-64bit",
  "arch": "arm64",
  "javaVersion": "openjdk version \"21.0.11\" 2026-04-21 LTS",
  "maven": "Apache Maven 3.9.9 (8e8579a9e76f7d015ee5ec7bfcdc97d260186937)",
  "elasticsearch": {
   "uri": "http://127.0.0.1:9200",
   "version": "8.19.23"
  },
  "dependencies": {
   "elasticsearch-java": [
    "8.19.23",
    "9.2.9"
   ],
   "elasticsearch-rest-client": [
    "8.19.18"
   ],
   "langchain4j": [
    "1.22.0"
   ],
   "langchain4j-core": [
    "1.22.0"
   ],
   "spring-ai-client-chat": [
    "2.0.2"
   ],
   "spring-ai-commons": [
    "2.0.2"
   ],
   "spring-ai-elasticsearch-store": [
    "2.0.2"
   ],
   "spring-ai-model": [
    "2.0.2"
   ],
   "spring-ai-rag": [
    "2.0.2"
   ],
   "spring-ai-vector-store": [
    "2.0.2"
   ],
   "spring-ai-vector-store-advisor": [
    "2.0.2"
   ],
   "spring-boot": [
    "4.0.9"
   ],
   "spring-core": [
    "7.0.10"
   ]
  }
 },
 "files": {
  "README.md": "c347457b8360f76e900c710b8960a0aea21c2a94932fc5cc5fc370fe0bbda3cb",
  "es9-boundary/pom.xml": "d949e290e97d1b64991ed1a7d0c702c808f86761d2f88048a6d1008a45f38492",
  "es9-boundary/src/test/java/study/aiboundary/Es9ClientBoundaryTest.java": "1d015f446d68803e1e2ff15d12f4627ad6abaef05f7719276af75980ad1de05b",
  "pom.xml": "179b5b2fccf3a9d124dd2bbfebcfd90621231079d14600e315944aa6600e6276",
  "rag-lab/pom.xml": "b6df6ed02ff9e1685a57167b5bcd320bd34b84e90b2bec72ec915d67e4cbda9b",
  "rag-lab/src/main/java/study/airag/Elasticsearch8VectorStore.java": "82b7257d10d36b0767b34f0e1c72065126d3119f734248a0f6d8824bb232b5b1",
  "rag-lab/src/main/java/study/airag/EsSupport.java": "021568fdb9ea48e7a810a7ebf569fabb245f9cab3a9bcbe14e5f41b23099e99f",
  "rag-lab/src/main/java/study/airag/HashingEmbeddingModel.java": "706dfaddc4d63f2621c67dcfa4240cea973e9dac2ca1d410cd77eeb9ee460129",
  "rag-lab/src/main/java/study/airag/KnowledgeBase.java": "9681d9ae996bea49ea02aa303814e68e783bec3a35867414c8f4f8dc67429ce8",
  "rag-lab/src/main/java/study/airag/RagAssistant.java": "2eb5002198c5665fa388fec6344ab0775763e992dadf99121992e3696c337cb0",
  "rag-lab/src/main/java/study/airag/ScoreAndDiversityPostProcessor.java": "78e08a73206bbc220c69b94efe904b272f1db2d4fedd004d1b5e4196884b0e0e",
  "rag-lab/src/main/java/study/airag/ScriptedChatModel.java": "a3f2e4ce2b121cecf2d803c76ac483b075bebd03a03fcf7f5ac6cb066f7e2ab1",
  "rag-lab/src/main/java/study/airag/langchain4j/LangChain4jHashingEmbeddingModel.java": "ea4957304911284fafbb218bc12a14e6b46b8f0689e6e021dc58002bcdf788b6",
  "rag-lab/src/main/resources/kb/platform-handbook.md": "04e955a6d8db863aff4e329140fbb6edfd92f78fd51f27e1304f1525f2a20988",
  "rag-lab/src/main/resources/kb/public-faq.md": "891d04bfca7a37dbc0b0f1106ff3208ee8365883599f09b9bda9ac8c506a1017",
  "rag-lab/src/test/java/study/airag/DocumentPipelineTest.java": "d1f02a48cad79901f6726a6961891f4a99216013fbe423df3935f1f73bb7ff7b",
  "rag-lab/src/test/java/study/airag/ElasticsearchRetrievalTest.java": "e3ac8b4719b748ed3ad166a28d19e6b52aa2da28d0f3d3dda8a95e7806ee0dca",
  "rag-lab/src/test/java/study/airag/LangChain4jContrastTest.java": "a70ea3c02b8a0ac382f0f7da6cf61574501dd407f2e6688f7d795ef2baeab94f",
  "rag-lab/src/test/java/study/airag/RagAssistantTest.java": "ffe1a692e00ef97e6d13c6f5532894918283141a27f14606f8b36033de169a64",
  "rag-lab/src/test/java/study/airag/StreamingCancellationTest.java": "48401b04ea7110255f2a6947438dff9e0477d2162d8799077db83baa6decb18e",
  "run.py": "6e3519e042ec8c9e6943cc904d989c1a46ada4a3b987afa4ca436f278e1b307d"
 },
 "moduleBoundaries": {
  "rag-lab": "真实 Elasticsearch 8.19.23（DBngin）+ Spring AI VectorStore 契约 + 8.19.23 低层客户端；ChatModel/EmbeddingModel 为本地替身，不调用真实 LLM。",
  "es9-boundary": "官方 spring-ai-elasticsearch-store 的 elasticsearch-java 9.x rest5 传输对本机 8.19.23 服务端返回 media_type_header_exception；同一服务端接受普通 HTTP。"
 },
 "limits": [
  "模型是确定性哈希嵌入 + 脚本化聊天替身：检索编排、ACL、引用与取消可验证，回答质量/引用忠实度与真实模型效果不可由此推断。",
  "真实 LLM 调用与模型评测需要外部凭据，按票面口径归 U84 并如实标受阻，不冒充实机。",
  "Elasticsearch 为单节点本机 DBngin 实例（8.19.23，xpack.security 关闭），不代表集群、认证、分片/副本或生产容量。",
  "ACL 以元数据标签 + 检索过滤为教学模型；没有独立授权服务、审计流水与元数据篡改防护。",
  "取消验证基于 Reactor 协作式取消；真实模型 HTTP 流断开、计费与重试语义未运行。",
  "LangChain4j 使用官方 InMemoryEmbeddingStore 对照；其 Elasticsearch 模块仍为 1.22.0-beta32 且依赖 9.3.1 客户端，未在本机 8.19.23 服务端运行。"
 ]
}
