homegraph 1.1.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (361) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +268 -243
  3. package/dist/arkts/ohos-api-index.d.ts +15 -0
  4. package/dist/arkts/ohos-api-index.d.ts.map +1 -0
  5. package/dist/arkts/ohos-api-index.js +190 -0
  6. package/dist/arkts/ohos-api-index.js.map +1 -0
  7. package/dist/arkts/ohos-sdk-input.d.ts +36 -0
  8. package/dist/arkts/ohos-sdk-input.d.ts.map +1 -0
  9. package/dist/arkts/ohos-sdk-input.js +214 -0
  10. package/dist/arkts/ohos-sdk-input.js.map +1 -0
  11. package/dist/bin/command-supervision.d.ts +12 -0
  12. package/dist/bin/command-supervision.d.ts.map +1 -0
  13. package/dist/bin/command-supervision.js +86 -0
  14. package/dist/bin/command-supervision.js.map +1 -0
  15. package/dist/bin/homegraph.d.ts +1 -1
  16. package/dist/bin/homegraph.js +971 -71
  17. package/dist/bin/homegraph.js.map +1 -1
  18. package/dist/db/index.d.ts +20 -0
  19. package/dist/db/index.d.ts.map +1 -1
  20. package/dist/db/index.js +39 -0
  21. package/dist/db/index.js.map +1 -1
  22. package/dist/db/migrations.d.ts +1 -1
  23. package/dist/db/migrations.d.ts.map +1 -1
  24. package/dist/db/migrations.js +58 -19
  25. package/dist/db/migrations.js.map +1 -1
  26. package/dist/db/queries.d.ts +14 -0
  27. package/dist/db/queries.d.ts.map +1 -1
  28. package/dist/db/queries.js +314 -143
  29. package/dist/db/queries.js.map +1 -1
  30. package/dist/db/schema.sql +172 -152
  31. package/dist/directory.d.ts +32 -0
  32. package/dist/directory.d.ts.map +1 -1
  33. package/dist/directory.js +88 -5
  34. package/dist/directory.js.map +1 -1
  35. package/dist/extraction/arkts-batch-worker.d.ts +2 -0
  36. package/dist/extraction/arkts-batch-worker.d.ts.map +1 -0
  37. package/dist/extraction/arkts-batch-worker.js +28 -0
  38. package/dist/extraction/arkts-batch-worker.js.map +1 -0
  39. package/dist/extraction/context.d.ts +11 -0
  40. package/dist/extraction/context.d.ts.map +1 -1
  41. package/dist/extraction/context.js +20 -0
  42. package/dist/extraction/context.js.map +1 -1
  43. package/dist/extraction/index.d.ts +16 -2
  44. package/dist/extraction/index.d.ts.map +1 -1
  45. package/dist/extraction/index.js +619 -380
  46. package/dist/extraction/index.js.map +1 -1
  47. package/dist/extraction/languages/arkts-state-decorators.d.ts +13 -0
  48. package/dist/extraction/languages/arkts-state-decorators.d.ts.map +1 -0
  49. package/dist/extraction/languages/arkts-state-decorators.js +26 -0
  50. package/dist/extraction/languages/arkts-state-decorators.js.map +1 -0
  51. package/dist/extraction/languages/arkts-viewtree.d.ts +4 -2
  52. package/dist/extraction/languages/arkts-viewtree.d.ts.map +1 -1
  53. package/dist/extraction/languages/arkts-viewtree.js +21 -6
  54. package/dist/extraction/languages/arkts-viewtree.js.map +1 -1
  55. package/dist/extraction/languages/arkts.d.ts +98 -2
  56. package/dist/extraction/languages/arkts.d.ts.map +1 -1
  57. package/dist/extraction/languages/arkts.js +1290 -61
  58. package/dist/extraction/languages/arkts.js.map +1 -1
  59. package/dist/extraction/languages/c-cpp.d.ts +56 -0
  60. package/dist/extraction/languages/c-cpp.d.ts.map +1 -1
  61. package/dist/extraction/languages/c-cpp.js +198 -1
  62. package/dist/extraction/languages/c-cpp.js.map +1 -1
  63. package/dist/extraction/languages/ohos-api-consumer.d.ts +34 -0
  64. package/dist/extraction/languages/ohos-api-consumer.d.ts.map +1 -0
  65. package/dist/extraction/languages/ohos-api-consumer.js +283 -0
  66. package/dist/extraction/languages/ohos-api-consumer.js.map +1 -0
  67. package/dist/extraction/parse-pool.d.ts +126 -0
  68. package/dist/extraction/parse-pool.d.ts.map +1 -0
  69. package/dist/extraction/parse-pool.js +319 -0
  70. package/dist/extraction/parse-pool.js.map +1 -0
  71. package/dist/extraction/tree-sitter-types.d.ts +17 -0
  72. package/dist/extraction/tree-sitter-types.d.ts.map +1 -1
  73. package/dist/extraction/tree-sitter.d.ts +21 -0
  74. package/dist/extraction/tree-sitter.d.ts.map +1 -1
  75. package/dist/extraction/tree-sitter.js +198 -27
  76. package/dist/extraction/tree-sitter.js.map +1 -1
  77. package/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
  78. package/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  79. package/dist/graph/traversal.d.ts.map +1 -1
  80. package/dist/graph/traversal.js +76 -17
  81. package/dist/graph/traversal.js.map +1 -1
  82. package/dist/index.d.ts +25 -0
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +78 -4
  85. package/dist/index.js.map +1 -1
  86. package/dist/installer/instructions-template.js +9 -9
  87. package/dist/installer/targets/shared.d.ts +5 -6
  88. package/dist/installer/targets/shared.d.ts.map +1 -1
  89. package/dist/installer/targets/shared.js +5 -6
  90. package/dist/installer/targets/shared.js.map +1 -1
  91. package/dist/mcp/daemon-paths.d.ts +30 -3
  92. package/dist/mcp/daemon-paths.d.ts.map +1 -1
  93. package/dist/mcp/daemon-paths.js +50 -10
  94. package/dist/mcp/daemon-paths.js.map +1 -1
  95. package/dist/mcp/daemon-registry.d.ts.map +1 -1
  96. package/dist/mcp/daemon-registry.js +7 -3
  97. package/dist/mcp/daemon-registry.js.map +1 -1
  98. package/dist/mcp/daemon.d.ts +48 -0
  99. package/dist/mcp/daemon.d.ts.map +1 -1
  100. package/dist/mcp/daemon.js +196 -32
  101. package/dist/mcp/daemon.js.map +1 -1
  102. package/dist/mcp/engine.d.ts +17 -0
  103. package/dist/mcp/engine.d.ts.map +1 -1
  104. package/dist/mcp/engine.js +73 -1
  105. package/dist/mcp/engine.js.map +1 -1
  106. package/dist/mcp/index.d.ts.map +1 -1
  107. package/dist/mcp/index.js +25 -43
  108. package/dist/mcp/index.js.map +1 -1
  109. package/dist/mcp/liveness-watchdog.js +16 -16
  110. package/dist/mcp/ppid-watchdog.d.ts +18 -0
  111. package/dist/mcp/ppid-watchdog.d.ts.map +1 -1
  112. package/dist/mcp/ppid-watchdog.js +37 -0
  113. package/dist/mcp/ppid-watchdog.js.map +1 -1
  114. package/dist/mcp/query-cache.d.ts +25 -0
  115. package/dist/mcp/query-cache.d.ts.map +1 -0
  116. package/dist/mcp/query-cache.js +191 -0
  117. package/dist/mcp/query-cache.js.map +1 -0
  118. package/dist/mcp/query-pool.d.ts +94 -0
  119. package/dist/mcp/query-pool.d.ts.map +1 -0
  120. package/dist/mcp/query-pool.js +297 -0
  121. package/dist/mcp/query-pool.js.map +1 -0
  122. package/dist/mcp/query-worker.d.ts +24 -0
  123. package/dist/mcp/query-worker.d.ts.map +1 -0
  124. package/dist/mcp/query-worker.js +87 -0
  125. package/dist/mcp/query-worker.js.map +1 -0
  126. package/dist/mcp/server-instructions.d.ts +5 -7
  127. package/dist/mcp/server-instructions.d.ts.map +1 -1
  128. package/dist/mcp/server-instructions.js +72 -74
  129. package/dist/mcp/server-instructions.js.map +1 -1
  130. package/dist/mcp/tools.d.ts +94 -4
  131. package/dist/mcp/tools.d.ts.map +1 -1
  132. package/dist/mcp/tools.js +877 -71
  133. package/dist/mcp/tools.js.map +1 -1
  134. package/dist/project-config.d.ts +20 -0
  135. package/dist/project-config.d.ts.map +1 -1
  136. package/dist/project-config.js +42 -2
  137. package/dist/project-config.js.map +1 -1
  138. package/dist/reasoning/login.js +1 -1
  139. package/dist/reasoning/login.js.map +1 -1
  140. package/dist/reasoning/reasoner.js +32 -32
  141. package/dist/resolution/c-fnptr-synthesizer.d.ts +0 -28
  142. package/dist/resolution/c-fnptr-synthesizer.d.ts.map +1 -1
  143. package/dist/resolution/c-fnptr-synthesizer.js +765 -79
  144. package/dist/resolution/c-fnptr-synthesizer.js.map +1 -1
  145. package/dist/resolution/callback-synthesizer.d.ts +1 -1
  146. package/dist/resolution/callback-synthesizer.d.ts.map +1 -1
  147. package/dist/resolution/callback-synthesizer.js +72 -11
  148. package/dist/resolution/callback-synthesizer.js.map +1 -1
  149. package/dist/resolution/cooperative-yield.d.ts +32 -0
  150. package/dist/resolution/cooperative-yield.d.ts.map +1 -0
  151. package/dist/resolution/cooperative-yield.js +42 -0
  152. package/dist/resolution/cooperative-yield.js.map +1 -0
  153. package/dist/resolution/index.d.ts +11 -2
  154. package/dist/resolution/index.d.ts.map +1 -1
  155. package/dist/resolution/index.js +72 -4
  156. package/dist/resolution/index.js.map +1 -1
  157. package/dist/resolution/name-matcher.d.ts +22 -0
  158. package/dist/resolution/name-matcher.d.ts.map +1 -1
  159. package/dist/resolution/name-matcher.js +317 -20
  160. package/dist/resolution/name-matcher.js.map +1 -1
  161. package/dist/spec/config.d.ts +39 -0
  162. package/dist/spec/config.d.ts.map +1 -0
  163. package/dist/spec/config.js +304 -0
  164. package/dist/spec/config.js.map +1 -0
  165. package/dist/spec/db/commit-node.d.ts +23 -0
  166. package/dist/spec/db/commit-node.d.ts.map +1 -0
  167. package/dist/spec/db/commit-node.js +62 -0
  168. package/dist/spec/db/commit-node.js.map +1 -0
  169. package/dist/spec/db/fragment-node.d.ts +24 -0
  170. package/dist/spec/db/fragment-node.d.ts.map +1 -0
  171. package/dist/spec/db/fragment-node.js +128 -0
  172. package/dist/spec/db/fragment-node.js.map +1 -0
  173. package/dist/spec/db/fts.d.ts +74 -0
  174. package/dist/spec/db/fts.d.ts.map +1 -0
  175. package/dist/spec/db/fts.js +324 -0
  176. package/dist/spec/db/fts.js.map +1 -0
  177. package/dist/spec/db/index.d.ts +13 -0
  178. package/dist/spec/db/index.d.ts.map +1 -0
  179. package/dist/spec/db/index.js +50 -0
  180. package/dist/spec/db/index.js.map +1 -0
  181. package/dist/spec/db/relations.d.ts +55 -0
  182. package/dist/spec/db/relations.d.ts.map +1 -0
  183. package/dist/spec/db/relations.js +158 -0
  184. package/dist/spec/db/relations.js.map +1 -0
  185. package/dist/spec/db/schema.d.ts +33 -0
  186. package/dist/spec/db/schema.d.ts.map +1 -0
  187. package/dist/spec/db/schema.js +119 -0
  188. package/dist/spec/db/schema.js.map +1 -0
  189. package/dist/spec/db/schema.sql +117 -0
  190. package/dist/spec/db/spec-node.d.ts +41 -0
  191. package/dist/spec/db/spec-node.d.ts.map +1 -0
  192. package/dist/spec/db/spec-node.js +114 -0
  193. package/dist/spec/db/spec-node.js.map +1 -0
  194. package/dist/spec/evolve/impact-locator.d.ts +13 -0
  195. package/dist/spec/evolve/impact-locator.d.ts.map +1 -0
  196. package/dist/spec/evolve/impact-locator.js +25 -0
  197. package/dist/spec/evolve/impact-locator.js.map +1 -0
  198. package/dist/spec/evolve/llm-client.d.ts +50 -0
  199. package/dist/spec/evolve/llm-client.d.ts.map +1 -0
  200. package/dist/spec/evolve/llm-client.js +176 -0
  201. package/dist/spec/evolve/llm-client.js.map +1 -0
  202. package/dist/spec/evolve/logic-checker.d.ts +12 -0
  203. package/dist/spec/evolve/logic-checker.d.ts.map +1 -0
  204. package/dist/spec/evolve/logic-checker.js +24 -0
  205. package/dist/spec/evolve/logic-checker.js.map +1 -0
  206. package/dist/spec/evolve/pipeline.d.ts +42 -0
  207. package/dist/spec/evolve/pipeline.d.ts.map +1 -0
  208. package/dist/spec/evolve/pipeline.js +567 -0
  209. package/dist/spec/evolve/pipeline.js.map +1 -0
  210. package/dist/spec/evolve/spec-rewriter.d.ts +42 -0
  211. package/dist/spec/evolve/spec-rewriter.d.ts.map +1 -0
  212. package/dist/spec/evolve/spec-rewriter.js +230 -0
  213. package/dist/spec/evolve/spec-rewriter.js.map +1 -0
  214. package/dist/spec/graph/queries.d.ts +155 -0
  215. package/dist/spec/graph/queries.d.ts.map +1 -0
  216. package/dist/spec/graph/queries.js +440 -0
  217. package/dist/spec/graph/queries.js.map +1 -0
  218. package/dist/spec/llm/client.d.ts +29 -0
  219. package/dist/spec/llm/client.d.ts.map +1 -0
  220. package/dist/spec/llm/client.js +123 -0
  221. package/dist/spec/llm/client.js.map +1 -0
  222. package/dist/spec/llm/index.d.ts +3 -0
  223. package/dist/spec/llm/index.d.ts.map +1 -0
  224. package/dist/spec/llm/index.js +11 -0
  225. package/dist/spec/llm/index.js.map +1 -0
  226. package/dist/spec/llm/prompts.d.ts +13 -0
  227. package/dist/spec/llm/prompts.d.ts.map +1 -0
  228. package/dist/spec/llm/prompts.js +75 -0
  229. package/dist/spec/llm/prompts.js.map +1 -0
  230. package/dist/spec/mining/diff-parser.d.ts +33 -0
  231. package/dist/spec/mining/diff-parser.d.ts.map +1 -0
  232. package/dist/spec/mining/diff-parser.js +166 -0
  233. package/dist/spec/mining/diff-parser.js.map +1 -0
  234. package/dist/spec/mining/git-scanner.d.ts +103 -0
  235. package/dist/spec/mining/git-scanner.d.ts.map +1 -0
  236. package/dist/spec/mining/git-scanner.js +307 -0
  237. package/dist/spec/mining/git-scanner.js.map +1 -0
  238. package/dist/spec/mining/pipeline.d.ts +53 -0
  239. package/dist/spec/mining/pipeline.d.ts.map +1 -0
  240. package/dist/spec/mining/pipeline.js +178 -0
  241. package/dist/spec/mining/pipeline.js.map +1 -0
  242. package/dist/spec/mining/scope-resolver.d.ts +45 -0
  243. package/dist/spec/mining/scope-resolver.d.ts.map +1 -0
  244. package/dist/spec/mining/scope-resolver.js +103 -0
  245. package/dist/spec/mining/scope-resolver.js.map +1 -0
  246. package/dist/spec/mining/spec-extractor.d.ts +69 -0
  247. package/dist/spec/mining/spec-extractor.d.ts.map +1 -0
  248. package/dist/spec/mining/spec-extractor.js +369 -0
  249. package/dist/spec/mining/spec-extractor.js.map +1 -0
  250. package/dist/spec/types.d.ts +149 -0
  251. package/dist/spec/types.d.ts.map +1 -0
  252. package/dist/spec/types.js +15 -0
  253. package/dist/spec/types.js.map +1 -0
  254. package/dist/spec/utils.d.ts +167 -0
  255. package/dist/spec/utils.d.ts.map +1 -0
  256. package/dist/spec/utils.js +463 -0
  257. package/dist/spec/utils.js.map +1 -0
  258. package/dist/sync/worktree.d.ts +9 -0
  259. package/dist/sync/worktree.d.ts.map +1 -1
  260. package/dist/sync/worktree.js +40 -0
  261. package/dist/sync/worktree.js.map +1 -1
  262. package/dist/types.d.ts +6 -1
  263. package/dist/types.d.ts.map +1 -1
  264. package/dist/ui/shimmer-progress.d.ts +2 -0
  265. package/dist/ui/shimmer-progress.d.ts.map +1 -1
  266. package/dist/ui/shimmer-progress.js +19 -2
  267. package/dist/ui/shimmer-progress.js.map +1 -1
  268. package/dist/upgrade/index.js +1 -1
  269. package/dist/upgrade/index.js.map +1 -1
  270. package/package.json +58 -57
  271. package/scripts/add-lang/bench.sh +60 -60
  272. package/scripts/add-lang/check-grammar.mjs +75 -75
  273. package/scripts/add-lang/dump-ast.mjs +103 -103
  274. package/scripts/add-lang/verify-extraction.mjs +70 -70
  275. package/scripts/agent-eval/ab-adoption.sh +91 -91
  276. package/scripts/agent-eval/ab-hook.sh +86 -86
  277. package/scripts/agent-eval/ab-impl.sh +78 -78
  278. package/scripts/agent-eval/ab-new-vs-baseline.sh +102 -102
  279. package/scripts/agent-eval/ab-sufficiency.sh +78 -78
  280. package/scripts/agent-eval/arms-F.sh +21 -21
  281. package/scripts/agent-eval/arms-matrix.sh +37 -37
  282. package/scripts/agent-eval/audit.sh +68 -68
  283. package/scripts/agent-eval/bench-readme.sh +28 -28
  284. package/scripts/agent-eval/bench-why-repo.sh +22 -22
  285. package/scripts/agent-eval/block-read-hook.sh +19 -19
  286. package/scripts/agent-eval/hook-settings.json +15 -15
  287. package/scripts/agent-eval/itrun.sh +120 -120
  288. package/scripts/agent-eval/offload-eval-3arm.sh +72 -72
  289. package/scripts/agent-eval/offload-eval-cost.mjs +133 -133
  290. package/scripts/agent-eval/offload-eval-effort.mjs +108 -108
  291. package/scripts/agent-eval/offload-eval-frontload-matrix.sh +25 -25
  292. package/scripts/agent-eval/offload-eval-frontload.sh +47 -47
  293. package/scripts/agent-eval/offload-eval-ground-truth.json +18 -18
  294. package/scripts/agent-eval/offload-eval-hook.mjs +84 -84
  295. package/scripts/agent-eval/offload-eval-judge.mjs +103 -103
  296. package/scripts/agent-eval/offload-eval-matrix.sh +20 -20
  297. package/scripts/agent-eval/offload-eval-metrics.mjs +94 -94
  298. package/scripts/agent-eval/offload-eval-refs1.sh +50 -50
  299. package/scripts/agent-eval/offload-eval-setup.sh +24 -24
  300. package/scripts/agent-eval/offload-eval-styles.sh +71 -71
  301. package/scripts/agent-eval/offload-eval-summarize.mjs +68 -68
  302. package/scripts/agent-eval/offload-eval.md +76 -76
  303. package/scripts/agent-eval/parse-arms.mjs +116 -116
  304. package/scripts/agent-eval/parse-bench-readme.mjs +84 -84
  305. package/scripts/agent-eval/parse-run.mjs +45 -45
  306. package/scripts/agent-eval/parse-session.mjs +93 -93
  307. package/scripts/agent-eval/probe-context.mjs +21 -21
  308. package/scripts/agent-eval/probe-explore.mjs +40 -40
  309. package/scripts/agent-eval/probe-node.mjs +20 -20
  310. package/scripts/agent-eval/probe-sweep.mjs +119 -119
  311. package/scripts/agent-eval/probe-trace.mjs +20 -20
  312. package/scripts/agent-eval/redirect-read-hook.sh +38 -38
  313. package/scripts/agent-eval/repro-concurrent-explore.mjs +119 -0
  314. package/scripts/agent-eval/repro-daemon-clients.mjs +125 -0
  315. package/scripts/agent-eval/run-agent.sh +34 -34
  316. package/scripts/agent-eval/run-all.sh +69 -69
  317. package/scripts/agent-eval/run-arms.sh +56 -56
  318. package/scripts/agent-eval/seq-matrix.mjs +137 -137
  319. package/scripts/build-bundle.sh +118 -118
  320. package/scripts/exp_boundary_eval/README.md +247 -0
  321. package/scripts/exp_boundary_eval/__pycache__/_utils.cpython-310.pyc +0 -0
  322. package/scripts/exp_boundary_eval/__pycache__/analyze.cpython-310.pyc +0 -0
  323. package/scripts/exp_boundary_eval/__pycache__/deveco_arm.cpython-310.pyc +0 -0
  324. package/scripts/exp_boundary_eval/__pycache__/run_one.cpython-310.pyc +0 -0
  325. package/scripts/exp_boundary_eval/__pycache__/run_session.cpython-310.pyc +0 -0
  326. package/scripts/exp_boundary_eval/__pycache__/setup.cpython-310.pyc +0 -0
  327. package/scripts/exp_boundary_eval/_test_mcp_chain.py +78 -0
  328. package/scripts/exp_boundary_eval/_test_stdin.py +8 -0
  329. package/scripts/exp_boundary_eval/_utils.py +1116 -0
  330. package/scripts/exp_boundary_eval/analyze.py +1313 -0
  331. package/scripts/exp_boundary_eval/data/agents.json +109 -0
  332. package/scripts/exp_boundary_eval/data/experiments.json +140 -0
  333. package/scripts/exp_boundary_eval/deveco_arm.py +519 -0
  334. package/scripts/exp_boundary_eval/run_all.py +378 -0
  335. package/scripts/exp_boundary_eval/run_one.py +165 -0
  336. package/scripts/exp_boundary_eval/run_session.py +158 -0
  337. package/scripts/exp_boundary_eval/setup.py +120 -0
  338. package/scripts/exp_boundary_eval/win_mcp_launcher.py +73 -0
  339. package/scripts/exp_boundary_eval/win_mcp_stdio_wrap.js +36 -0
  340. package/scripts/exp_boundary_eval/win_node_launcher.py +24 -0
  341. package/scripts/extract-release-notes.mjs +130 -130
  342. package/scripts/local-install.sh +41 -41
  343. package/scripts/npm-sdk.js +75 -75
  344. package/scripts/npm-shim.js +268 -246
  345. package/scripts/ohos-sdk-publish.mjs +133 -0
  346. package/scripts/pack-npm.sh +119 -119
  347. package/scripts/prepare-release.mjs +270 -270
  348. package/scripts/qa_eval/README.md +407 -404
  349. package/scripts/qa_eval/_test_deveco_probe.py +41 -41
  350. package/scripts/qa_eval/agent_runner.py +526 -526
  351. package/scripts/qa_eval/data/.gitignore +4 -4
  352. package/scripts/qa_eval/data/test-set.jsonl +2 -22
  353. package/scripts/qa_eval/eval_metrics.py +274 -233
  354. package/scripts/qa_eval/external_agent.py +976 -671
  355. package/scripts/qa_eval/llm_config.py +92 -92
  356. package/scripts/qa_eval/memory_monitor.py +132 -132
  357. package/scripts/qa_eval/my_answer_accuracy.py +187 -187
  358. package/scripts/qa_eval/requirements.txt +2 -2
  359. package/scripts/qa_eval/run_pipeline.py +804 -711
  360. package/scripts/qa_eval/stats_efficiency.py +279 -279
  361. package/scripts/qa_eval/stats_scores.py +207 -207
@@ -1,4 +1,4 @@
1
- # test-set.jsonl tracked; reports + other artifacts stay local.
2
- *
3
- !.gitignore
4
- !test-set.jsonl
1
+ # test-set.jsonl tracked; reports + other artifacts stay local.
2
+ *
3
+ !.gitignore
4
+ !test-set.jsonl
@@ -1,22 +1,2 @@
1
- {"id": "R01", "level0": "camera", "level1": "ColorUtil", "category_l1": "检索", "category_l2": "函数", "difficulty": "简单", "language": "ArkTS", "query": "getColorString 函数在哪个文件里定义?", "reference_answer": "getColorString 定义在 camera/common/src/main/ets/utils/ColorUtil.ets,是一个 export function。"}
2
- {"id": "R02", "level0": "platform", "level1": "BenchmarkHub", "category_l1": "检索", "category_l2": "函数", "difficulty": "简单", "language": "ArkTS", "query": "launchPhotosFromDesktop 函数定义在哪个文件?", "reference_answer": "launchPhotosFromDesktop 定义在 platform/src/main/ets/BenchmarkHub.ets,与 BenchmarkHub 类同文件导出。"}
3
- {"id": "R03", "level0": "photos", "level1": "BenchmarkPhotosLink", "category_l1": "检索", "category_l2": "函数", "difficulty": "简单", "language": "ArkTS", "query": "photosBenchmarkEntry 函数在哪里?", "reference_answer": "photosBenchmarkEntry 定义在 photos/common/src/main/ets/benchmark/BenchmarkPhotosLink.ets。"}
4
- {"id": "R04", "level0": "platform", "level1": "BenchmarkHub", "category_l1": "检索", "category_l2": "类", "difficulty": "简单", "language": "ArkTS", "query": "BenchmarkHub 类在哪个文件?", "reference_answer": "BenchmarkHub 类定义在 platform/src/main/ets/BenchmarkHub.ets。"}
5
- {"id": "R05", "level0": "photos", "level1": "DeviceInfo", "category_l1": "检索", "category_l2": "类", "difficulty": "简单", "language": "ArkTS", "query": "BenchmarkHub 引用的 Photos DeviceInfo 类在哪个路径?", "reference_answer": "BenchmarkHub 从 @benchmark/photos-common/src/main/ets/default/utils/DeviceInfo.ets 导入 DeviceInfo;类定义在 photos/common/src/main/ets/default/utils/DeviceInfo.ets。"}
6
- {"id": "R06", "level0": "platform", "level1": "BenchmarkHub", "category_l1": "检索", "category_l2": "常量", "difficulty": "简单", "language": "ArkTS", "query": "BenchmarkHub 里 PHOTOS_BUNDLE 常量的值是什么?", "reference_answer": "PHOTOS_BUNDLE 在 platform/src/main/ets/BenchmarkHub.ets 中定义为字符串 'com.ohos.photos'。"}
7
- {"id": "R07", "level0": "platform", "level1": "package", "category_l1": "检索", "category_l2": "文件", "difficulty": "简单", "language": "ArkTS", "query": "@benchmark/platform 包对外 re-export 了哪些 BenchmarkHub 相关符号?", "reference_answer": "platform/index.ets 重新导出 BenchmarkHub、launchPhotosFromDesktop、openCameraWithScene,来源均为 ./src/main/ets/BenchmarkHub。"}
8
-
9
- {"id": "E01", "level0": "platform", "level1": "BenchmarkHub", "category_l1": "解读", "category_l2": "模块/功能/算法", "difficulty": "简单", "language": "ArkTS", "query": "BenchmarkHub 这个模块在整个 benchmark 工程里是干什么的?", "reference_answer": "BenchmarkHub 是跨应用集成 hub,用于 monorepo 模块互联的 homegraph benchmark fixture;文件头注释说明其职责是 cross-app integration hub / monorepo module wiring。"}
10
- {"id": "E02", "level0": "monorepo", "level1": "workspace", "category_l1": "解读", "category_l2": "模块/功能/算法", "difficulty": "困难", "language": "ArkTS", "query": "这个 benchmark 工程是如何把 photos、camera、scene_board 和 platform 组织成 monorepo 的?", "reference_answer": "根目录 build-profile.json5 声明约 153 个模块;根 package.json 用 npm workspaces 映射 @benchmark/* 包;platform 为 @benchmark/platform,photos/common、camera/common、scene_board 的 windowsceneinterfaces 分别对应 @benchmark/photos-common、@benchmark/camera-common、@benchmark/windowscene-interfaces。各 bridge 文件通过 package import 而非相对路径引用。"}
11
- {"id": "E03", "level0": "platform", "level1": "BenchmarkHub", "category_l1": "解读", "category_l2": "函数", "difficulty": "简单", "language": "ArkTS", "query": "BenchmarkHub.isTabletLayout() 是如何判断平板布局的?", "reference_answer": "BenchmarkHub.isTabletLayout() 直接调用从 photos-common 导入的 PhotosDeviceInfo.isTablet(),自身不包含判断逻辑,而是委托 photos 模块的 DeviceInfo。"}
12
- {"id": "E04", "level0": "camera", "level1": "ColorUtil", "category_l1": "解读", "category_l2": "函数", "difficulty": "简单", "language": "ArkTS", "query": "getColorString() 在什么条件下返回 #0A59F7,什么条件下返回 #FF0000?", "reference_answer": "在 ColorUtil.ets 中,若 CameraAppCapability.getInstance().getIsNovaProduct() 为真,或 DeviceInfo.isTablet() 为真,则返回 '#0A59F7';否则返回 '#FF0000'。"}
13
- {"id": "E05", "level0": "monorepo", "level1": "bridge", "category_l1": "解读", "category_l2": "设计模式/系统架构", "difficulty": "困难", "language": "ArkTS", "query": "BenchmarkPhotosLink、BenchmarkCameraLink、BenchmarkSceneLink 这类 bridge 文件的设计意图是什么?", "reference_answer": "它们是各子应用模块指向 platform hub 的 benchmark 桥接文件,使用 @benchmark/platform 等 package import(非相对路径),刻意制造跨 workspace 包的静态依赖,供 homegraph 索引和跨模块调用链评测。"}
14
- {"id": "E06", "level0": "platform", "level1": "BenchmarkHub", "category_l1": "解读", "category_l2": "代码片段", "difficulty": "简单", "language": "ArkTS", "query": "launchPhotosFromDesktop() 返回的字符串格式是什么?", "reference_answer": "返回形如 launch:${PHOTOS_BUNDLE}:${layout} 的字符串,其中 layout 由 isTabletLayout() 决定为 'tablet' 或 'phone',PHOTOS_BUNDLE 为 com.ohos.photos。示例:launch:com.ohos.photos:tablet。"}
15
- {"id": "E07", "level0": "platform", "level1": "BenchmarkHub", "category_l1": "解读", "category_l2": "类", "difficulty": "简单", "language": "ArkTS", "query": "BenchmarkHub.productSuite() 返回什么?", "reference_answer": "返回 string[],按顺序包含 SCENE_BUNDLE、PHOTOS_BUNDLE、CAMERA_BUNDLE,即 com.ohos.sceneboard、com.ohos.photos、com.ohos.camera。"}
16
-
17
- {"id": "D01", "level0": "platform", "level1": "BenchmarkHub", "category_l1": "依赖", "category_l2": "跨语言依赖", "difficulty": "困难", "language": "ArkTS", "query": "BenchmarkHub.ets 通过 @benchmark 包 import 依赖了哪些其他 workspace 模块?", "reference_answer": "BenchmarkHub.ets 依赖三处 @benchmark 包:@benchmark/photos-common 的 DeviceInfo.ets、@benchmark/camera-common 的 ColorUtil.ets(getColorString)、@benchmark/windowscene-interfaces 的 Context.d.ts。"}
18
- {"id": "D02", "level0": "platform", "level1": "BenchmarkHub", "category_l1": "依赖", "category_l2": "函数", "difficulty": "简单", "language": "ArkTS", "query": "openCameraWithScene() 内部调用了 BenchmarkHub 的哪些方法?", "reference_answer": "openCameraWithScene 只调用 BenchmarkHub.cameraAccentColor() 获取 accent 颜色,再拼进返回字符串 open:${CAMERA_BUNDLE}:accent=${color}。"}
19
- {"id": "D03", "level0": "scene_board", "level1": "BenchmarkSceneLink", "category_l1": "依赖", "category_l2": "模块/功能/算法", "difficulty": "困难", "language": "ArkTS", "query": "sceneBoardBenchmarkEntry() 的跨模块调用链是怎样的?", "reference_answer": "sceneBoardBenchmarkEntry(BenchmarkSceneLink.ets)依次调用 BenchmarkHub.cameraAccentColor()、launchPhotosFromDesktop()、openCameraWithScene();后两者在 platform/BenchmarkHub.ets,cameraAccentColor 又依赖 camera-common 的 getColorString,launchPhotosFromDesktop 依赖 photos-common 的 DeviceInfo.isTablet()。"}
20
- {"id": "D04", "level0": "photos", "level1": "BenchmarkPhotosLink", "category_l1": "依赖", "category_l2": "函数", "difficulty": "简单", "language": "ArkTS", "query": "photosBenchmarkEntry() 如何连接到 camera 相关逻辑?", "reference_answer": "photosBenchmarkEntry 从 @benchmark/platform 导入 openCameraWithScene,调用 BenchmarkHub.productSuite() 和 openCameraWithScene(),从而经 platform hub 间接依赖 camera-common 的 getColorString。"}
21
- {"id": "D05", "level0": "camera", "level1": "BenchmarkCameraLink", "category_l1": "依赖", "category_l2": "函数", "difficulty": "简单", "language": "ArkTS", "query": "cameraBenchmarkEntry() 如何依赖 photos 模块?", "reference_answer": "cameraBenchmarkEntry 调用 BenchmarkHub.isTabletLayout()(委托 photos-common DeviceInfo.isTablet())和 launchPhotosFromDesktop()(同样依赖 isTabletLayout),并通过 launchPhotosFromDesktop 连到 photos bundle 常量。"}
22
- {"id": "D06", "level0": "platform", "level1": "BenchmarkHub", "category_l1": "依赖", "category_l2": "类", "difficulty": "困难", "language": "ArkTS", "query": "从 photos 的 DeviceInfo.isTablet() 到 sceneBoardBenchmarkEntry 的完整静态依赖路径是什么?", "reference_answer": "DeviceInfo.isTablet()(photos-common)← BenchmarkHub.isTabletLayout() ← launchPhotosFromDesktop()(platform)← sceneBoardBenchmarkEntry()(BenchmarkSceneLink.ets,scene_board 经 @benchmark/platform 导入)。sceneBoardBenchmarkEntry 还在同一函数内调用 cameraAccentColor 和 openCameraWithScene。"}
1
+ {"id": "R01", "level0": "camera", "level1": "ColorUtil", "category_l1": "检索", "category_l2": "函数", "difficulty": "简单", "language": "ArkTS", "query": "getColorString 函数在哪个文件里定义?", "reference_answer": "getColorString 定义在 camera/common/src/main/ets/utils/ColorUtil.ets,是一个 export function。"}
2
+ {"id": "R02", "level0": "platform", "level1": "BenchmarkHub", "category_l1": "检索", "category_l2": "函数", "difficulty": "简单", "language": "ArkTS", "query": "launchPhotosFromDesktop 函数定义在哪个文件?", "reference_answer": "launchPhotosFromDesktop 定义在 platform/src/main/ets/BenchmarkHub.ets,与 BenchmarkHub 类同文件导出。"}
@@ -1,233 +1,274 @@
1
- #!/usr/bin/env python3
2
- """
3
- Stage 2 — RAG Answer Accuracy Evaluation (LLM-as-Judge).
4
-
5
- Reads agent-produced JSONL (query + output_answer + reference_answer),
6
- strips tool-call blocks, scores 0–1 via MyAnswerAccuracy.
7
-
8
- Usage:
9
- export DASHSCOPE_API_KEY="sk-..."
10
- python scripts/qa_eval/eval_metrics.py \\
11
- -i scripts/qa_eval/log/result-with-builtin.jsonl \\
12
- -o scripts/qa_eval/log/result-with-builtin-scored.jsonl \\
13
- -w 2
14
- """
15
-
16
- from __future__ import annotations
17
-
18
- import argparse
19
- import asyncio
20
- import json
21
- import logging
22
- import os
23
- import statistics
24
- import sys
25
- import threading
26
- from concurrent.futures import ThreadPoolExecutor, as_completed
27
- from pathlib import Path
28
-
29
- from tqdm import tqdm
30
-
31
- _SCRIPT_DIR = Path(__file__).resolve().parent
32
- LOG_DIR = _SCRIPT_DIR / "log"
33
- LOG_DIR.mkdir(parents=True, exist_ok=True)
34
-
35
- if str(_SCRIPT_DIR) not in sys.path:
36
- sys.path.insert(0, str(_SCRIPT_DIR))
37
-
38
- from my_answer_accuracy import MyAnswerAccuracy # noqa: E402
39
- from llm_config import PROVIDER_DASHSCOPE, PROVIDER_ZHIPU, provider_help, resolve_llm_config # noqa: E402
40
-
41
- os.environ.setdefault("NO_PROXY", "localhost,127.0.0.1")
42
-
43
- logging.basicConfig(
44
- level=logging.INFO,
45
- format="%(asctime)s - %(levelname)s - %(message)s",
46
- handlers=[
47
- logging.FileHandler(LOG_DIR / "eval_metrics.log"),
48
- logging.StreamHandler(),
49
- ],
50
- )
51
- logger = logging.getLogger(__name__)
52
-
53
- file_write_lock = threading.Lock()
54
-
55
- DEFAULT_INPUT = LOG_DIR / "result-with-builtin.jsonl"
56
- DEFAULT_OUTPUT = LOG_DIR / "result-with-builtin-scored.jsonl"
57
- DEFAULT_MODEL = "qwen3-235b-a22b-instruct-2507"
58
- DEFAULT_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1"
59
- DEFAULT_API_KEY_ENV = "DASHSCOPE_API_KEY"
60
-
61
-
62
- class RAGEvaluator:
63
- """Answer accuracy judge with concurrent processing."""
64
-
65
- def __init__(
66
- self,
67
- llm_model: str,
68
- *,
69
- api_key: str,
70
- base_url: str,
71
- extra_body: dict | None = None,
72
- ) -> None:
73
- self.scorer = MyAnswerAccuracy.create(
74
- api_key=api_key,
75
- base_url=base_url,
76
- model=llm_model,
77
- extra_body=extra_body,
78
- )
79
-
80
- async def evaluate_answer_accuracy_single(self, sample_data: dict) -> dict:
81
- return await self.scorer.evaluate_answer_accuracy_single(sample_data)
82
-
83
-
84
- def process_single_item_sync(evaluator: RAGEvaluator, item: dict) -> dict:
85
- loop = asyncio.new_event_loop()
86
- asyncio.set_event_loop(loop)
87
- try:
88
- return loop.run_until_complete(evaluator.evaluate_answer_accuracy_single(item))
89
- finally:
90
- loop.close()
91
-
92
-
93
- def load_jsonl(path: Path) -> list[dict]:
94
- items: list[dict] = []
95
- with path.open("r", encoding="utf-8") as f:
96
- for line_no, line in enumerate(f, 1):
97
- line = line.strip()
98
- if not line:
99
- continue
100
- try:
101
- items.append(json.loads(line))
102
- except json.JSONDecodeError as e:
103
- logger.error("第 %s 行 JSON 解析失败: %s", line_no, e)
104
- return items
105
-
106
-
107
- def resolve_api_key(env_name: str) -> str | None:
108
- key = os.environ.get(env_name, "").strip()
109
- if key:
110
- return key
111
- fallback = os.environ.get("OPENAI_API_KEY", "").strip()
112
- return fallback or None
113
-
114
-
115
- def main() -> int:
116
- parser = argparse.ArgumentParser(description="RAG Answer Accuracy Evaluation (LLM-as-Judge)")
117
- parser.add_argument("--input", "-i", type=str, default=str(DEFAULT_INPUT), help="Agent 产出的 JSONL")
118
- parser.add_argument("--output", "-o", type=str, default=str(DEFAULT_OUTPUT), help="打分后的 JSONL")
119
- parser.add_argument("--workers", "-w", type=int, default=1, help="并发线程数")
120
- parser.add_argument(
121
- "--provider",
122
- choices=[PROVIDER_DASHSCOPE, PROVIDER_ZHIPU],
123
- default=None,
124
- help=provider_help(),
125
- )
126
- parser.add_argument("--model", "-m", type=str, default=None, help="Judge 模型(默认随 provider)")
127
- parser.add_argument("--base-url", type=str, default=None, help="OpenAI 兼容 API 端点(默认随 provider)")
128
- args = parser.parse_args()
129
-
130
- input_file = Path(args.input)
131
- output_file = Path(args.output)
132
-
133
- try:
134
- llm = resolve_llm_config(provider=args.provider, model=args.model, base_url=args.base_url)
135
- except RuntimeError as e:
136
- print(f"错误: {e}", file=sys.stderr)
137
- return 1
138
-
139
- print(f"输入文件: {input_file}")
140
- print(f"输出文件: {output_file}")
141
- print(f"并发线程数: {args.workers}")
142
- print(f"LLM provider: {llm.provider}")
143
- print(f"评估模型: {llm.model}")
144
-
145
- if not input_file.is_file():
146
- print(f"错误: 输入文件 {input_file} 不存在")
147
- return 1
148
-
149
- api_key = llm.api_key
150
-
151
- all_items = load_jsonl(input_file)
152
- total_items = len(all_items)
153
- print(f"读取到 {total_items} 个样本")
154
-
155
- if total_items == 0:
156
- print("没有找到有效的样本数据")
157
- return 1
158
-
159
- for i, item in enumerate(all_items, 1):
160
- if not str(item.get("query", "")).strip():
161
- print(f"错误: 第 {i} 条缺少 query")
162
- return 1
163
- if not str(item.get("reference_answer", "")).strip():
164
- print(f"错误: {i} 条缺少 reference_answer")
165
- return 1
166
- if not str(item.get("output_answer", "")).strip():
167
- logger.warning(" %s 条 (%s) output_answer 为空,Judge 可能得 0 分", i, item.get("id", "?"))
168
-
169
- output_file.parent.mkdir(parents=True, exist_ok=True)
170
- output_file.write_text("", encoding="utf-8")
171
-
172
- evaluator = RAGEvaluator(
173
- llm.model,
174
- api_key=api_key,
175
- base_url=llm.base_url,
176
- extra_body=llm.extra_body,
177
- )
178
- print("初始化 Judge 完成")
179
-
180
- processed_items: list[dict] = []
181
- results_lock = threading.Lock()
182
-
183
- with ThreadPoolExecutor(max_workers=max(1, args.workers)) as executor:
184
- futures = {
185
- executor.submit(process_single_item_sync, evaluator, item): item
186
- for item in all_items
187
- }
188
- for future in tqdm(as_completed(futures), total=len(all_items), desc="评估答案准确性"):
189
- try:
190
- processed_item = future.result()
191
- with results_lock:
192
- processed_items.append(processed_item)
193
- if len(processed_items) % 5 == 0:
194
- with file_write_lock:
195
- with output_file.open("a", encoding="utf-8") as writer:
196
- for row in processed_items[-5:]:
197
- writer.write(json.dumps(row, ensure_ascii=False) + "\n")
198
- except Exception as e:
199
- logger.error("样本处理任务异常: %s", e)
200
-
201
- remaining = len(processed_items) % 5
202
- if remaining:
203
- with file_write_lock:
204
- with output_file.open("a", encoding="utf-8") as writer:
205
- for row in processed_items[-remaining:]:
206
- writer.write(json.dumps(row, ensure_ascii=False) + "\n")
207
-
208
- if processed_items:
209
- successful = [x for x in processed_items if x.get("evaluation_status") == "success"]
210
- scores = [float(x.get("answer_accuracy_score", 0.0)) for x in processed_items]
211
- print("\n评估完成!")
212
- print(f"总样本数: {total_items}")
213
- print(f"成功评估: {len(successful)}")
214
- print(f"失败评估: {total_items - len(successful)}")
215
- if successful:
216
- ok_scores = [float(x["answer_accuracy_score"]) for x in successful]
217
- print(f"平均答案准确性得分: {statistics.mean(ok_scores):.4f}")
218
- print(f"最高得分: {max(ok_scores):.4f}")
219
- print(f"最低得分: {min(ok_scores):.4f}")
220
- print(f"\n结果已写入: {output_file}")
221
- else:
222
- print("没有成功处理任何样本")
223
- return 1
224
-
225
- return 0
226
-
227
-
228
- if __name__ == "__main__":
229
- try:
230
- raise SystemExit(main())
231
- except KeyboardInterrupt:
232
- print("\n用户中断了程序执行")
233
- raise SystemExit(130)
1
+ #!/usr/bin/env python3
2
+ """
3
+ Stage 2 — RAG Answer Accuracy Evaluation (LLM-as-Judge).
4
+
5
+ Reads agent-produced JSONL (query + output_answer + reference_answer),
6
+ strips tool-call blocks, scores 0–1 via MyAnswerAccuracy.
7
+
8
+ Usage:
9
+ export DASHSCOPE_API_KEY="sk-..."
10
+ python scripts/qa_eval/eval_metrics.py \\
11
+ -i scripts/qa_eval/log/result-with-builtin.jsonl \\
12
+ -o scripts/qa_eval/log/result-with-builtin-scored.jsonl \\
13
+ -w 2
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import argparse
19
+ import asyncio
20
+ import json
21
+ import logging
22
+ import os
23
+ import statistics
24
+ import sys
25
+ import threading
26
+ from concurrent.futures import ThreadPoolExecutor, as_completed
27
+ from pathlib import Path
28
+
29
+ from tqdm import tqdm
30
+
31
+ _SCRIPT_DIR = Path(__file__).resolve().parent
32
+ LOG_DIR = _SCRIPT_DIR / "log"
33
+ LOG_DIR.mkdir(parents=True, exist_ok=True)
34
+
35
+ if str(_SCRIPT_DIR) not in sys.path:
36
+ sys.path.insert(0, str(_SCRIPT_DIR))
37
+
38
+ from my_answer_accuracy import MyAnswerAccuracy # noqa: E402
39
+ from llm_config import PROVIDER_DASHSCOPE, PROVIDER_ZHIPU, provider_help, resolve_llm_config # noqa: E402
40
+
41
+ os.environ.setdefault("NO_PROXY", "localhost,127.0.0.1")
42
+
43
+ logging.basicConfig(
44
+ level=logging.INFO,
45
+ format="%(asctime)s - %(levelname)s - %(message)s",
46
+ handlers=[
47
+ logging.FileHandler(LOG_DIR / "eval_metrics.log"),
48
+ logging.StreamHandler(),
49
+ ],
50
+ )
51
+ logger = logging.getLogger(__name__)
52
+
53
+ file_write_lock = threading.Lock()
54
+
55
+ DEFAULT_INPUT = LOG_DIR / "result-with-builtin.jsonl"
56
+ DEFAULT_OUTPUT = LOG_DIR / "result-with-builtin-scored.jsonl"
57
+ DEFAULT_MODEL = "qwen3-235b-a22b-instruct-2507"
58
+ DEFAULT_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1"
59
+ DEFAULT_API_KEY_ENV = "DASHSCOPE_API_KEY"
60
+
61
+
62
+ class RAGEvaluator:
63
+ """Answer accuracy judge with concurrent processing."""
64
+
65
+ def __init__(
66
+ self,
67
+ llm_model: str,
68
+ *,
69
+ api_key: str,
70
+ base_url: str,
71
+ extra_body: dict | None = None,
72
+ ) -> None:
73
+ self.scorer = MyAnswerAccuracy.create(
74
+ api_key=api_key,
75
+ base_url=base_url,
76
+ model=llm_model,
77
+ extra_body=extra_body,
78
+ )
79
+
80
+ async def evaluate_answer_accuracy_single(self, sample_data: dict) -> dict:
81
+ return await self.scorer.evaluate_answer_accuracy_single(sample_data)
82
+
83
+ async def close(self) -> None:
84
+ await self.scorer.client.close()
85
+
86
+
87
+ async def evaluate_all_async(evaluator: RAGEvaluator, items: list[dict]) -> list[dict]:
88
+ processed: list[dict] = []
89
+ try:
90
+ for item in items:
91
+ processed.append(await evaluator.evaluate_answer_accuracy_single(item))
92
+ finally:
93
+ await evaluator.close()
94
+ return processed
95
+
96
+
97
+ def process_single_item_sync(
98
+ *,
99
+ llm_model: str,
100
+ api_key: str,
101
+ base_url: str,
102
+ extra_body: dict | None,
103
+ item: dict,
104
+ ) -> dict:
105
+ loop = asyncio.new_event_loop()
106
+ asyncio.set_event_loop(loop)
107
+ evaluator = RAGEvaluator(
108
+ llm_model,
109
+ api_key=api_key,
110
+ base_url=base_url,
111
+ extra_body=extra_body,
112
+ )
113
+ try:
114
+ return loop.run_until_complete(evaluator.evaluate_answer_accuracy_single(item))
115
+ finally:
116
+ loop.run_until_complete(evaluator.close())
117
+ loop.close()
118
+ asyncio.set_event_loop(None)
119
+
120
+
121
+ def load_jsonl(path: Path) -> list[dict]:
122
+ items: list[dict] = []
123
+ with path.open("r", encoding="utf-8") as f:
124
+ for line_no, line in enumerate(f, 1):
125
+ line = line.strip()
126
+ if not line:
127
+ continue
128
+ try:
129
+ items.append(json.loads(line))
130
+ except json.JSONDecodeError as e:
131
+ logger.error("第 %s 行 JSON 解析失败: %s", line_no, e)
132
+ return items
133
+
134
+
135
+ def resolve_api_key(env_name: str) -> str | None:
136
+ key = os.environ.get(env_name, "").strip()
137
+ if key:
138
+ return key
139
+ fallback = os.environ.get("OPENAI_API_KEY", "").strip()
140
+ return fallback or None
141
+
142
+
143
+ def main() -> int:
144
+ parser = argparse.ArgumentParser(description="RAG Answer Accuracy Evaluation (LLM-as-Judge)")
145
+ parser.add_argument("--input", "-i", type=str, default=str(DEFAULT_INPUT), help="Agent 产出的 JSONL")
146
+ parser.add_argument("--output", "-o", type=str, default=str(DEFAULT_OUTPUT), help="打分后的 JSONL")
147
+ parser.add_argument("--workers", "-w", type=int, default=1, help="并发线程数")
148
+ parser.add_argument(
149
+ "--provider",
150
+ choices=[PROVIDER_DASHSCOPE, PROVIDER_ZHIPU],
151
+ default=None,
152
+ help=provider_help(),
153
+ )
154
+ parser.add_argument("--model", "-m", type=str, default=None, help="Judge 模型(默认随 provider)")
155
+ parser.add_argument("--base-url", type=str, default=None, help="OpenAI 兼容 API 端点(默认随 provider)")
156
+ args = parser.parse_args()
157
+
158
+ input_file = Path(args.input)
159
+ output_file = Path(args.output)
160
+
161
+ try:
162
+ llm = resolve_llm_config(provider=args.provider, model=args.model, base_url=args.base_url)
163
+ except RuntimeError as e:
164
+ print(f"错误: {e}", file=sys.stderr)
165
+ return 1
166
+
167
+ print(f"输入文件: {input_file}")
168
+ print(f"输出文件: {output_file}")
169
+ print(f"并发线程数: {args.workers}")
170
+ print(f"LLM provider: {llm.provider}")
171
+ print(f"评估模型: {llm.model}")
172
+
173
+ if not input_file.is_file():
174
+ print(f"错误: 输入文件 {input_file} 不存在")
175
+ return 1
176
+
177
+ api_key = llm.api_key
178
+
179
+ all_items = load_jsonl(input_file)
180
+ total_items = len(all_items)
181
+ print(f"读取到 {total_items} 个样本")
182
+
183
+ if total_items == 0:
184
+ print("没有找到有效的样本数据")
185
+ return 1
186
+
187
+ for i, item in enumerate(all_items, 1):
188
+ if not str(item.get("query", "")).strip():
189
+ print(f"错误: 第 {i} 条缺少 query")
190
+ return 1
191
+ if not str(item.get("reference_answer", "")).strip():
192
+ print(f"错误: 第 {i} 条缺少 reference_answer")
193
+ return 1
194
+ if not str(item.get("output_answer", "")).strip():
195
+ logger.warning("第 %s 条 (%s) output_answer 为空,Judge 可能得 0 分", i, item.get("id", "?"))
196
+
197
+ output_file.parent.mkdir(parents=True, exist_ok=True)
198
+ output_file.write_text("", encoding="utf-8")
199
+
200
+ evaluator = RAGEvaluator(
201
+ llm.model,
202
+ api_key=api_key,
203
+ base_url=llm.base_url,
204
+ extra_body=llm.extra_body,
205
+ )
206
+ print("初始化 Judge 完成")
207
+
208
+ processed_items: list[dict] = []
209
+ results_lock = threading.Lock()
210
+
211
+ if args.workers <= 1:
212
+ processed_items = asyncio.run(evaluate_all_async(evaluator, all_items))
213
+ with output_file.open("w", encoding="utf-8") as writer:
214
+ for row in processed_items:
215
+ writer.write(json.dumps(row, ensure_ascii=False) + "\n")
216
+ else:
217
+ with ThreadPoolExecutor(max_workers=max(1, args.workers)) as executor:
218
+ futures = {
219
+ executor.submit(
220
+ process_single_item_sync,
221
+ llm_model=llm.model,
222
+ api_key=api_key,
223
+ base_url=llm.base_url,
224
+ extra_body=llm.extra_body,
225
+ item=item,
226
+ ): item
227
+ for item in all_items
228
+ }
229
+ for future in tqdm(as_completed(futures), total=len(all_items), desc="评估答案准确性"):
230
+ try:
231
+ processed_item = future.result()
232
+ with results_lock:
233
+ processed_items.append(processed_item)
234
+ if len(processed_items) % 5 == 0:
235
+ with file_write_lock:
236
+ with output_file.open("a", encoding="utf-8") as writer:
237
+ for row in processed_items[-5:]:
238
+ writer.write(json.dumps(row, ensure_ascii=False) + "\n")
239
+ except Exception as e:
240
+ logger.error("样本处理任务异常: %s", e)
241
+
242
+ remaining = len(processed_items) % 5
243
+ if remaining:
244
+ with file_write_lock:
245
+ with output_file.open("a", encoding="utf-8") as writer:
246
+ for row in processed_items[-remaining:]:
247
+ writer.write(json.dumps(row, ensure_ascii=False) + "\n")
248
+
249
+ if processed_items:
250
+ successful = [x for x in processed_items if x.get("evaluation_status") == "success"]
251
+ scores = [float(x.get("answer_accuracy_score", 0.0)) for x in processed_items]
252
+ print("\n评估完成!")
253
+ print(f"总样本数: {total_items}")
254
+ print(f"成功评估: {len(successful)}")
255
+ print(f"失败评估: {total_items - len(successful)}")
256
+ if successful:
257
+ ok_scores = [float(x["answer_accuracy_score"]) for x in successful]
258
+ print(f"平均答案准确性得分: {statistics.mean(ok_scores):.4f}")
259
+ print(f"最高得分: {max(ok_scores):.4f}")
260
+ print(f"最低得分: {min(ok_scores):.4f}")
261
+ print(f"\n结果已写入: {output_file}")
262
+ else:
263
+ print("没有成功处理任何样本")
264
+ return 1
265
+
266
+ return 0
267
+
268
+
269
+ if __name__ == "__main__":
270
+ try:
271
+ raise SystemExit(main())
272
+ except KeyboardInterrupt:
273
+ print("\n用户中断了程序执行")
274
+ raise SystemExit(130)