{
 "ticket": "U83 #120",
 "generatedAt": "2026-10-28T11:49:03.476191+08:00",
 "startedAt": "2026-10-28T11:49:00.405369+08:00",
 "purpose": "Spring AI 2.0.2 工具调用/记忆/结构化输出/权限与人工确认/预算与取消；LangChain4j 1.22.0 工具抽象对照；模型为脚本替身",
 "exitCode": 0,
 "testCount": 26,
 "reports": [
  {
   "name": "study.aiagent.LangChain4jToolContrastTest",
   "tests": "4",
   "failures": "0",
   "errors": "0",
   "skipped": "0",
   "time": "0.081",
   "file": "target/surefire-reports/TEST-study.aiagent.LangChain4jToolContrastTest.xml"
  },
  {
   "name": "study.aiagent.MemoryTest",
   "tests": "5",
   "failures": "0",
   "errors": "0",
   "skipped": "0",
   "time": "0.009",
   "file": "target/surefire-reports/TEST-study.aiagent.MemoryTest.xml"
  },
  {
   "name": "study.aiagent.PromptInjectionTest",
   "tests": "5",
   "failures": "0",
   "errors": "0",
   "skipped": "0",
   "time": "0.214",
   "file": "target/surefire-reports/TEST-study.aiagent.PromptInjectionTest.xml"
  },
  {
   "name": "study.aiagent.StructuredOutputBudgetTest",
   "tests": "6",
   "failures": "0",
   "errors": "0",
   "skipped": "0",
   "time": "0.374",
   "file": "target/surefire-reports/TEST-study.aiagent.StructuredOutputBudgetTest.xml"
  },
  {
   "name": "study.aiagent.ToolLoopTest",
   "tests": "6",
   "failures": "0",
   "errors": "0",
   "skipped": "0",
   "time": "0.021",
   "file": "target/surefire-reports/TEST-study.aiagent.ToolLoopTest.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": [
    "U83 injectionApproval=PASS deniedAttempts=2",
    "U83 safeGuard=PASS blockedAtAdvisor=true modelInvocations=1",
    "U83 injectionDenied=PASS deniedTool=payrollLookup invocations=2",
    "U83 injectionData=PASS searchHits=0",
    "U83 safeGuardBypass=PASS paraphrasedPassed=true",
    "U83 approval=PASS deniedBefore=true executedAfter=true",
    "U83 toolContext=PASS support=hits0 admin=hits1",
    "U83 repeatCalls=PASS searchExecutions=3 invocations=3",
    "U83 toolDenied=PASS deniedCount=1 invocations=2",
    "U83 approvalReject=PASS executed=false status=REJECTED",
    "U83 toolLoop=PASS invocations=2 audit=1 messagesSecondRound=3",
    "U83 structuredOutput=PASS approved=true amount=99.0",
    "U83 streamCancelTool=PASS received=1 toolCalls=0 invocations=1",
    "U83 budget=PASS executed=2 limit=2 behavior=THROW-default",
    "U83 structuredFailFast=PASS error=conversion",
    "U83 budgetReturn=PASS executed=1 invocations=2",
    "U83 budgetMath=PASS worstToolCalls=6 worstCostRmb=0.022",
    "U83 lc4jContext=PASS secondParam=Object",
    "U83 lc4jSpec=PASS tools=2 params=[orderId]",
    "U83 lc4jExecutor=PASS result=eligible",
    "U83 lc4jPolicy=PASS blockedBefore=true executedAfter=true",
    "U83 toolLimit=PASS executions=1 defaultPerTool=40 defaultTotal=150",
    "U83 memoryWindow=PASS size=3 first=SYSTEM firstNonSystem=USER last=回答4",
    "U83 toolMemoryTurn=PASS secondRoundMessages=3",
    "U83 memoryAdvisor=PASS messages=3",
    "U83 memoryIsolation=PASS tenantA=cleared tenantB=kept"
   ]
  }
 ],
 "observations": [
  "U83 approval",
  "U83 approvalReject",
  "U83 budget",
  "U83 budgetMath",
  "U83 budgetReturn",
  "U83 injectionApproval",
  "U83 injectionData",
  "U83 injectionDenied",
  "U83 lc4jContext",
  "U83 lc4jExecutor",
  "U83 lc4jPolicy",
  "U83 lc4jSpec",
  "U83 memoryAdvisor",
  "U83 memoryIsolation",
  "U83 memoryWindow",
  "U83 repeatCalls",
  "U83 safeGuard",
  "U83 safeGuardBypass",
  "U83 streamCancelTool",
  "U83 structuredFailFast",
  "U83 structuredOutput",
  "U83 toolContext",
  "U83 toolDenied",
  "U83 toolLimit",
  "U83 toolLoop",
  "U83 toolMemoryTurn"
 ],
 "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)",
  "dependencies": {
   "langchain4j": [
    "1.22.0"
   ],
   "langchain4j-core": [
    "1.22.0"
   ],
   "spring-ai-client-chat": [
    "2.0.2"
   ],
   "spring-ai-commons": [
    "2.0.2"
   ],
   "spring-ai-model": [
    "2.0.2"
   ],
   "spring-ai-vector-store": [
    "2.0.2"
   ],
   "spring-boot": [
    "4.0.9"
   ],
   "spring-core": [
    "7.0.10"
   ]
  }
 },
 "files": {
  "README.md": "1d41faf69ecff40b80a0b9c78aa6c26a6fc71febb2dcb504857faa76786ac0da",
  "pom.xml": "57eff9d94f9375fe2a5b3b597ad0c848ad71bcee7a5387903785d026b74b59da",
  "run.py": "c2b9809e71e27046e09016800d41be60b159a2291e2f4d5b5b685958042958f0",
  "src/main/java/study/aiagent/ApprovalGate.java": "fcf94cc7459f01eabe27f06f7006a9bd2d309803ca73dc7e9b9b3c21abaa8629",
  "src/main/java/study/aiagent/ScriptedToolChatModel.java": "be23e1461d10e6b660007cfca1feb78a832b78995467a2395dc6ac9c1e10c2f4",
  "src/main/java/study/aiagent/SupportTools.java": "70d662ccbe23c942f25dd389b917505b29575461a9aacca3551020897a14ec6d",
  "src/test/java/study/aiagent/LangChain4jToolContrastTest.java": "ee5d83945cbb020b12f8625f8b55237bf95490506e7808e17b4d5c0d7f8165e8",
  "src/test/java/study/aiagent/MemoryTest.java": "8325782550d53ebae1fb362b041dfc4470183686a89c341d8a2ba28a90684c32",
  "src/test/java/study/aiagent/PromptInjectionTest.java": "fbeeaf503b13588d74e516f8966fe8dcc693a4a80c69287da923f96a51962d8d",
  "src/test/java/study/aiagent/StructuredOutputBudgetTest.java": "fadff87dcca0f1fd7be4e3d22ae1084009796e749bbd8de901c7d346ae70a424",
  "src/test/java/study/aiagent/ToolLoopTest.java": "db6a3db06f6e416a9fae5b96504032833d8526d07e3aa90bffedda6a75b1c3a4"
 },
 "moduleBoundaries": {
  "agent-lab": "真实 Spring AI ToolCallingAdvisor/DefaultToolCallingManager/MessageWindowChatMemory/SafeGuardAdvisor 链路；ChatModel 为脚本替身，按最坏情况模拟\"模型被注入后调用危险工具\"。"
 },
 "limits": [
  "模型是脚本替身：验证的是框架层权限、确认、限额、记忆与取消机制；真实模型是否被注入策反、拒答率与回答质量不可由此推断（U84）。",
  "知识库在内存中，不是 U82 的 Elasticsearch 检索；\"检索作为工具\"的结构相同，数据面不同。",
  "人工确认由测试代码模拟\"人\"批准/拒绝，没有真实审批系统、角色目录或审计存储。",
  "预算算术（调用次数 × 单价）是教学算例，不是真实账单；无外部凭据，真实计费未运行。",
  "LangChain4j 只对照 @Tool/ToolSpecification/DefaultToolExecutor 与执行上下文形态，未运行其 AiServices 编排与真实模型。"
 ]
}
