foxclaw 0.1.0 → 0.1.3

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 (514) hide show
  1. package/README.md +54 -29
  2. package/bin/foxclaw.cjs +51 -0
  3. package/index.js +4 -10
  4. package/package.json +46 -38
  5. package/python/.dockerignore +35 -0
  6. package/python/.env.example +173 -0
  7. package/python/.gitattributes +46 -0
  8. package/python/.github/workflows/ci.yml +167 -0
  9. package/python/.github/workflows/release.yml +183 -0
  10. package/python/CHANGELOG.md +57 -0
  11. package/python/CHANGELOG_CN.md +57 -0
  12. package/python/Dockerfile +64 -0
  13. package/python/LICENSE +21 -0
  14. package/python/MANIFEST.in +14 -0
  15. package/python/README.md +201 -0
  16. package/python/README_CN.md +336 -0
  17. package/python/bench/bm25_audit.py +186 -0
  18. package/python/bench/bm25_regression.py +177 -0
  19. package/python/bench/bm25_stress.py +155 -0
  20. package/python/bench/cd_audit.py +215 -0
  21. package/python/bench/chaos.py +140 -0
  22. package/python/bench/concurrency.py +103 -0
  23. package/python/bench/e2e_real_models.py +94 -0
  24. package/python/bench/llm_retry_test.py +241 -0
  25. package/python/bench/load_test.py +193 -0
  26. package/python/bench/vector_ivf_upgrade.py +128 -0
  27. package/python/docs/CHANNELS.md +195 -0
  28. package/python/docs/GOVERNANCE.md +164 -0
  29. package/python/docs/RELEASE.md +110 -0
  30. package/python/docs/RUN.md +75 -0
  31. package/python/docs/RUN_CN.md +121 -0
  32. package/python/docs//351/205/215/347/275/256/346/214/207/345/215/227.md +152 -0
  33. package/python/evals/__init__.py +0 -0
  34. package/python/evals/governance/README.md +61 -0
  35. package/python/evals/governance/__init__.py +0 -0
  36. package/python/evals/governance/cases.py +115 -0
  37. package/python/evals/governance/compare_embeddings.py +94 -0
  38. package/python/evals/governance/convergence.svg +11 -0
  39. package/python/evals/governance/report.json +371 -0
  40. package/python/evals/governance/run_evals.py +368 -0
  41. package/python/examples/governance_middleware_demo.py +72 -0
  42. package/python/examples/langgraph_governance_demo.py +104 -0
  43. package/python/examples/sample_itinerary.csv +6 -0
  44. package/python/examples/sample_knowledge.csv +9 -0
  45. package/python/examples/sample_transactions.csv +14 -0
  46. package/python/foxclaw/__init__.py +408 -0
  47. package/python/foxclaw/agents/__init__.py +12 -0
  48. package/python/foxclaw/agents/agents.py +35 -0
  49. package/python/foxclaw/agents/base.py +794 -0
  50. package/python/foxclaw/agents/impl/__init__.py +1 -0
  51. package/python/foxclaw/agents/impl/_helpers.py +70 -0
  52. package/python/foxclaw/agents/impl/finance.py +231 -0
  53. package/python/foxclaw/agents/impl/office.py +350 -0
  54. package/python/foxclaw/agents/impl/supervisor.py +592 -0
  55. package/python/foxclaw/agents/mixins/_context.py +128 -0
  56. package/python/foxclaw/agents/mixins/augment.py +143 -0
  57. package/python/foxclaw/agents/mixins/llm.py +67 -0
  58. package/python/foxclaw/agents/mixins/memory.py +41 -0
  59. package/python/foxclaw/agents/mixins/preference.py +131 -0
  60. package/python/foxclaw/agents/mixins/prompt.py +71 -0
  61. package/python/foxclaw/agents/mixins/skillbox.py +58 -0
  62. package/python/foxclaw/agents/mixins/tool_exec.py +417 -0
  63. package/python/foxclaw/api/__init__.py +224 -0
  64. package/python/foxclaw/api/_state.py +43 -0
  65. package/python/foxclaw/api/bootstrap.py +96 -0
  66. package/python/foxclaw/api/middleware.py +294 -0
  67. package/python/foxclaw/api/routes/ahs.py +404 -0
  68. package/python/foxclaw/api/routes/attachments.py +116 -0
  69. package/python/foxclaw/api/routes/browser.py +35 -0
  70. package/python/foxclaw/api/routes/domain.py +306 -0
  71. package/python/foxclaw/api/routes/knowledge.py +145 -0
  72. package/python/foxclaw/api/routes/llm_admin.py +449 -0
  73. package/python/foxclaw/api/routes/memory.py +105 -0
  74. package/python/foxclaw/api/routes/orchestration.py +39 -0
  75. package/python/foxclaw/api/routes/recovery.py +44 -0
  76. package/python/foxclaw/api/routes/review.py +266 -0
  77. package/python/foxclaw/api/routes/search.py +33 -0
  78. package/python/foxclaw/api/routes/system.py +311 -0
  79. package/python/foxclaw/api/routes/task.py +263 -0
  80. package/python/foxclaw/api/routes/ws.py +64 -0
  81. package/python/foxclaw/api/spine.py +52 -0
  82. package/python/foxclaw/browser/__init__.py +13 -0
  83. package/python/foxclaw/browser/action/__init__.py +1 -0
  84. package/python/foxclaw/browser/action/action_executor.py +50 -0
  85. package/python/foxclaw/browser/action/action_types.py +44 -0
  86. package/python/foxclaw/browser/action/action_verifier.py +68 -0
  87. package/python/foxclaw/browser/action/url_policy.py +143 -0
  88. package/python/foxclaw/browser/adapter/__init__.py +1 -0
  89. package/python/foxclaw/browser/adapter/base_adapter.py +23 -0
  90. package/python/foxclaw/browser/adapter/web_adapter.py +46 -0
  91. package/python/foxclaw/browser/driver/__init__.py +1 -0
  92. package/python/foxclaw/browser/driver/base_driver.py +61 -0
  93. package/python/foxclaw/browser/driver/playwright_driver.py +170 -0
  94. package/python/foxclaw/browser/perception/__init__.py +1 -0
  95. package/python/foxclaw/browser/perception/dom_extractor.py +42 -0
  96. package/python/foxclaw/browser/perception/element_indexer.py +56 -0
  97. package/python/foxclaw/browser/perception/semantic_reducer.py +33 -0
  98. package/python/foxclaw/browser/perception/snapshot.py +29 -0
  99. package/python/foxclaw/browser/service.py +144 -0
  100. package/python/foxclaw/browser/session/__init__.py +1 -0
  101. package/python/foxclaw/browser/session/session_manager.py +57 -0
  102. package/python/foxclaw/capability/__init__.py +12 -0
  103. package/python/foxclaw/capability/embedding_service.py +65 -0
  104. package/python/foxclaw/channels/__init__.py +138 -0
  105. package/python/foxclaw/channels/discord.py +175 -0
  106. package/python/foxclaw/channels/dispatcher.py +179 -0
  107. package/python/foxclaw/channels/feishu.py +215 -0
  108. package/python/foxclaw/channels/openclaw_bridge.py +151 -0
  109. package/python/foxclaw/channels/signing.py +187 -0
  110. package/python/foxclaw/channels/telegram.py +120 -0
  111. package/python/foxclaw/channels/weixin.py +215 -0
  112. package/python/foxclaw/cli.py +183 -0
  113. package/python/foxclaw/cognitive/__init__.py +4 -0
  114. package/python/foxclaw/cognitive/reasoner.py +81 -0
  115. package/python/foxclaw/cognitive/state.py +141 -0
  116. package/python/foxclaw/core/__init__.py +9 -0
  117. package/python/foxclaw/core/audit_chain.py +131 -0
  118. package/python/foxclaw/core/audit_sink.py +100 -0
  119. package/python/foxclaw/core/compaction.py +265 -0
  120. package/python/foxclaw/core/config.py +65 -0
  121. package/python/foxclaw/core/confirm_nonce.py +151 -0
  122. package/python/foxclaw/core/dispatch.py +27 -0
  123. package/python/foxclaw/core/envfile.py +48 -0
  124. package/python/foxclaw/core/failure_policy.py +142 -0
  125. package/python/foxclaw/core/gateway.py +81 -0
  126. package/python/foxclaw/core/governance_audit.py +171 -0
  127. package/python/foxclaw/core/idempotency.py +282 -0
  128. package/python/foxclaw/core/injection_guard.py +202 -0
  129. package/python/foxclaw/core/memory_adapter.py +489 -0
  130. package/python/foxclaw/core/metrics.py +88 -0
  131. package/python/foxclaw/core/model_router.py +985 -0
  132. package/python/foxclaw/core/paths.py +103 -0
  133. package/python/foxclaw/core/recovery.py +349 -0
  134. package/python/foxclaw/core/scheduler.py +141 -0
  135. package/python/foxclaw/core/secrets.py +131 -0
  136. package/python/foxclaw/core/sessions.py +155 -0
  137. package/python/foxclaw/core/settings.py +414 -0
  138. package/python/foxclaw/core/shared_write.py +186 -0
  139. package/python/foxclaw/core/steering_queue.py +154 -0
  140. package/python/foxclaw/core/system_bus.py +84 -0
  141. package/python/foxclaw/core/thought_chain.py +316 -0
  142. package/python/foxclaw/core/token_meter.py +231 -0
  143. package/python/foxclaw/core/tools.py +164 -0
  144. package/python/foxclaw/core/tracing.py +375 -0
  145. package/python/foxclaw/core/truth_pipeline.py +110 -0
  146. package/python/foxclaw/core/update_check.py +149 -0
  147. package/python/foxclaw/core/vec_recall_adapter.py +477 -0
  148. package/python/foxclaw/core/verify.py +173 -0
  149. package/python/foxclaw/core/web_search.py +218 -0
  150. package/python/foxclaw/finance/__init__.py +15 -0
  151. package/python/foxclaw/finance/evidence.py +149 -0
  152. package/python/foxclaw/finance/fx.py +98 -0
  153. package/python/foxclaw/finance/graph.py +179 -0
  154. package/python/foxclaw/finance/models.py +146 -0
  155. package/python/foxclaw/finance/nlparse.py +515 -0
  156. package/python/foxclaw/finance/policy.py +301 -0
  157. package/python/foxclaw/finance/reasoner.py +121 -0
  158. package/python/foxclaw/finance/reasoning.py +319 -0
  159. package/python/foxclaw/finance/reconcile.py +175 -0
  160. package/python/foxclaw/finance/runtime.py +500 -0
  161. package/python/foxclaw/governance/__init__.py +24 -0
  162. package/python/foxclaw/governance/middleware.py +176 -0
  163. package/python/foxclaw/harness/__init__.py +17 -0
  164. package/python/foxclaw/harness/_base.py +66 -0
  165. package/python/foxclaw/harness/_policy.py +340 -0
  166. package/python/foxclaw/harness/drift.py +77 -0
  167. package/python/foxclaw/harness/event_bus.py +28 -0
  168. package/python/foxclaw/harness/harness.py +358 -0
  169. package/python/foxclaw/harness/loop_detection.py +145 -0
  170. package/python/foxclaw/itinerary/__init__.py +10 -0
  171. package/python/foxclaw/itinerary/amap_client.py +114 -0
  172. package/python/foxclaw/itinerary/models.py +80 -0
  173. package/python/foxclaw/itinerary/reasoner.py +96 -0
  174. package/python/foxclaw/itinerary/reasoning.py +168 -0
  175. package/python/foxclaw/itinerary/runtime.py +144 -0
  176. package/python/foxclaw/kernel/__init__.py +13 -0
  177. package/python/foxclaw/kernel/capabilities.py +72 -0
  178. package/python/foxclaw/kernel/policy_gate.py +108 -0
  179. package/python/foxclaw/kernel/process_manager.py +186 -0
  180. package/python/foxclaw/kernel/scheduler.py +125 -0
  181. package/python/foxclaw/kernel/sentinel.py +443 -0
  182. package/python/foxclaw/kernel/sentinel_client.py +243 -0
  183. package/python/foxclaw/kernel/spine.py +97 -0
  184. package/python/foxclaw/knowledge/__init__.py +360 -0
  185. package/python/foxclaw/knowledge/truth_crystallizer.py +89 -0
  186. package/python/foxclaw/learning/__init__.py +12 -0
  187. package/python/foxclaw/learning/knowledge_graph.py +144 -0
  188. package/python/foxclaw/learning/models.py +93 -0
  189. package/python/foxclaw/learning/reasoner.py +67 -0
  190. package/python/foxclaw/learning/runtime.py +351 -0
  191. package/python/foxclaw/learning/spaced_repetition.py +91 -0
  192. package/python/foxclaw/mcp_server.py +188 -0
  193. package/python/foxclaw/orchestration/__init__.py +167 -0
  194. package/python/foxclaw/pipeline/__init__.py +23 -0
  195. package/python/foxclaw/pipeline/cognitive_pipeline.py +429 -0
  196. package/python/foxclaw/pipeline/stage_meta.py +21 -0
  197. package/python/foxclaw/pipeline/supervisor_stages.py +201 -0
  198. package/python/foxclaw/planning/__init__.py +17 -0
  199. package/python/foxclaw/planning/llm_planner.py +232 -0
  200. package/python/foxclaw/planning/planner.py +94 -0
  201. package/python/foxclaw/search/__init__.py +24 -0
  202. package/python/foxclaw/search/agent_port.py +190 -0
  203. package/python/foxclaw/search/evidence.py +109 -0
  204. package/python/foxclaw/search/external_search.py +50 -0
  205. package/python/foxclaw/search/hkc_read_port.py +60 -0
  206. package/python/foxclaw/search/internal_search.py +71 -0
  207. package/python/foxclaw/search/models.py +127 -0
  208. package/python/foxclaw/search/planner.py +44 -0
  209. package/python/foxclaw/search/retriever.py +39 -0
  210. package/python/foxclaw/search/runtime.py +127 -0
  211. package/python/foxclaw/search/strategy.py +44 -0
  212. package/python/foxclaw/skills/__init__.py +559 -0
  213. package/python/foxclaw/skills/archive.py +111 -0
  214. package/python/foxclaw/skills/fetch.py +226 -0
  215. package/python/foxclaw/skills/hooks_spec.py +69 -0
  216. package/python/foxclaw/skills/install_cmd.py +216 -0
  217. package/python/foxclaw/skills/lifecycle.py +252 -0
  218. package/python/foxclaw/skills/markdown_skill.py +175 -0
  219. package/python/foxclaw/skills/markdown_store.py +39 -0
  220. package/python/foxclaw/skills/mcp_client.py +184 -0
  221. package/python/foxclaw/skills/mcp_store.py +40 -0
  222. package/python/foxclaw/skills/permission.py +145 -0
  223. package/python/foxclaw/skills/pipeline.py +354 -0
  224. package/python/foxclaw/skills/policy.py +74 -0
  225. package/python/foxclaw/skills/review.py +72 -0
  226. package/python/foxclaw/skills/store.py +57 -0
  227. package/python/frontend/assets/core_bg.jpg +0 -0
  228. package/python/frontend/deck/auth.js +25 -0
  229. package/python/frontend/deck/chat_model_picker.js +483 -0
  230. package/python/frontend/deck/domain.js +276 -0
  231. package/python/frontend/deck/main.js +1806 -0
  232. package/python/frontend/deck/markdown.js +44 -0
  233. package/python/frontend/deck/settings.js +307 -0
  234. package/python/frontend/deck/state.js +5 -0
  235. package/python/frontend/deck/util.js +4 -0
  236. package/python/frontend/deck/visuals.js +190 -0
  237. package/python/frontend/deck.css +855 -0
  238. package/python/frontend/deck.html +686 -0
  239. package/python/frontend/deck_v2.html +341 -0
  240. package/python/frontend/planet/index.html +266 -0
  241. package/python/frontend/planet/planet.css +579 -0
  242. package/python/frontend/planet/planet.js +1436 -0
  243. package/python/frontend/planet/vendor/three.core.min.js +6 -0
  244. package/python/frontend/planet/vendor/three.module.min.js +6 -0
  245. package/python/frontend/v3/assets/index-CQSBdxk4.js +34 -0
  246. package/python/frontend/v3/assets/index-D5nIsjms.css +1 -0
  247. package/python/frontend/v3/index.html +15 -0
  248. package/python/frontend/widget_runtime.js +252 -0
  249. package/python/frontend/widgets.js +639 -0
  250. package/python/frontend_src/index.html +14 -0
  251. package/python/frontend_src/package-lock.json +1376 -0
  252. package/python/frontend_src/package.json +19 -0
  253. package/python/frontend_src/src/App.vue +58 -0
  254. package/python/frontend_src/src/api.js +82 -0
  255. package/python/frontend_src/src/components/ChatDock.vue +365 -0
  256. package/python/frontend_src/src/components/ComposerMenu.vue +75 -0
  257. package/python/frontend_src/src/components/DocViewer.vue +162 -0
  258. package/python/frontend_src/src/components/Inspector.vue +235 -0
  259. package/python/frontend_src/src/components/LeftRail.vue +415 -0
  260. package/python/frontend_src/src/components/MenuPopover.vue +128 -0
  261. package/python/frontend_src/src/components/MessageStream.vue +389 -0
  262. package/python/frontend_src/src/components/ModelPicker.vue +350 -0
  263. package/python/frontend_src/src/components/QuickIngest.vue +112 -0
  264. package/python/frontend_src/src/components/Stage.vue +272 -0
  265. package/python/frontend_src/src/components/ToolMenu.vue +141 -0
  266. package/python/frontend_src/src/components/TopBar.vue +118 -0
  267. package/python/frontend_src/src/components/panels/ActivityPanel.vue +82 -0
  268. package/python/frontend_src/src/components/panels/AgentsPanel.vue +83 -0
  269. package/python/frontend_src/src/components/panels/KnowledgePanel.vue +425 -0
  270. package/python/frontend_src/src/components/panels/MemoryPanel.vue +117 -0
  271. package/python/frontend_src/src/components/panels/PanelBox.vue +50 -0
  272. package/python/frontend_src/src/components/panels/ReviewPanel.vue +292 -0
  273. package/python/frontend_src/src/components/panels/SettingsPanel.vue +358 -0
  274. package/python/frontend_src/src/components/panels/TasksPanel.vue +73 -0
  275. package/python/frontend_src/src/components/panels/ToolsPanel.vue +746 -0
  276. package/python/frontend_src/src/components/panels/UsagePanel.vue +105 -0
  277. package/python/frontend_src/src/lib/fmt.js +44 -0
  278. package/python/frontend_src/src/lib/i18n.js +53 -0
  279. package/python/frontend_src/src/lib/locales/en.js +173 -0
  280. package/python/frontend_src/src/lib/locales/zh.js +174 -0
  281. package/python/frontend_src/src/lib/md.js +190 -0
  282. package/python/frontend_src/src/main.js +9 -0
  283. package/python/frontend_src/src/stores/app.js +91 -0
  284. package/python/frontend_src/src/stores/chat.js +450 -0
  285. package/python/frontend_src/src/stores/model.js +96 -0
  286. package/python/frontend_src/src/stores/panels.js +289 -0
  287. package/python/frontend_src/src/stores/profile.js +31 -0
  288. package/python/frontend_src/src/stores/system.js +25 -0
  289. package/python/frontend_src/src/styles/base.css +138 -0
  290. package/python/frontend_src/src/styles/panel.css +117 -0
  291. package/python/frontend_src/src/styles/tokens.css +78 -0
  292. package/python/frontend_src/vite.config.js +39 -0
  293. package/python/pyproject.toml +268 -0
  294. package/python/requirements.txt +311 -0
  295. package/python/skills/README.md +123 -0
  296. package/python/skills/execution-contract/SKILL.md +30 -0
  297. package/python/skills/general-assistant/SKILL.md +34 -0
  298. package/python/skills/planner/SKILL.md +33 -0
  299. package/python/skills/schedule-itinerary/SKILL.md +35 -0
  300. package/python/skills/skill-composition/SKILL.md +31 -0
  301. package/python/skills/skill-guard/SKILL.md +67 -0
  302. package/python/skills/skill-memory/SKILL.md +50 -0
  303. package/python/skills/skill-router/SKILL.md +35 -0
  304. package/python/tests/conftest.py +54 -0
  305. package/python/tests/run_all.py +200 -0
  306. package/python/tests/test_agent_lifecycle.py +326 -0
  307. package/python/tests/test_ahs_p2.py +393 -0
  308. package/python/tests/test_anthropic_tools.py +192 -0
  309. package/python/tests/test_api.py +97 -0
  310. package/python/tests/test_api_auth.py +79 -0
  311. package/python/tests/test_api_host_guard.py +69 -0
  312. package/python/tests/test_attachments.py +151 -0
  313. package/python/tests/test_audit_chain.py +116 -0
  314. package/python/tests/test_audit_sink.py +130 -0
  315. package/python/tests/test_browser.py +149 -0
  316. package/python/tests/test_browser_request_guard.py +180 -0
  317. package/python/tests/test_capabilities.py +152 -0
  318. package/python/tests/test_channel_signing.py +185 -0
  319. package/python/tests/test_channel_trust.py +109 -0
  320. package/python/tests/test_channels.py +174 -0
  321. package/python/tests/test_cli.py +46 -0
  322. package/python/tests/test_compaction_toolpair.py +119 -0
  323. package/python/tests/test_config_layering.py +61 -0
  324. package/python/tests/test_confirm_nonce.py +186 -0
  325. package/python/tests/test_context_compaction_wiring.py +124 -0
  326. package/python/tests/test_dispatch.py +89 -0
  327. package/python/tests/test_domain_evals.py +334 -0
  328. package/python/tests/test_domain_import.py +72 -0
  329. package/python/tests/test_domain_reasoners.py +134 -0
  330. package/python/tests/test_e2e.py +161 -0
  331. package/python/tests/test_emb_cache.py +97 -0
  332. package/python/tests/test_embedding_singleton.py +87 -0
  333. package/python/tests/test_envfile.py +83 -0
  334. package/python/tests/test_evals_smoke.py +90 -0
  335. package/python/tests/test_event_spine.py +174 -0
  336. package/python/tests/test_evidence_semantic.py +88 -0
  337. package/python/tests/test_evidence_sse.py +94 -0
  338. package/python/tests/test_file_ingest.py +135 -0
  339. package/python/tests/test_finance.py +144 -0
  340. package/python/tests/test_finance_ledger.py +245 -0
  341. package/python/tests/test_finance_persist.py +174 -0
  342. package/python/tests/test_gateway.py +125 -0
  343. package/python/tests/test_governance.py +421 -0
  344. package/python/tests/test_governance_audit.py +324 -0
  345. package/python/tests/test_governance_middleware.py +137 -0
  346. package/python/tests/test_hkc.py +268 -0
  347. package/python/tests/test_hkc_index_build_manager.py +306 -0
  348. package/python/tests/test_hkc_manifest_reuse.py +294 -0
  349. package/python/tests/test_hkc_search_event_sync.py +482 -0
  350. package/python/tests/test_import_path_guard.py +155 -0
  351. package/python/tests/test_injection_guard.py +193 -0
  352. package/python/tests/test_integration.py +117 -0
  353. package/python/tests/test_itinerary.py +100 -0
  354. package/python/tests/test_kee_semantic.py +84 -0
  355. package/python/tests/test_kernel.py +212 -0
  356. package/python/tests/test_learning.py +101 -0
  357. package/python/tests/test_llm_admin.py +145 -0
  358. package/python/tests/test_llm_planner.py +344 -0
  359. package/python/tests/test_markdown_skills.py +62 -0
  360. package/python/tests/test_markdown_skills_openclaw.py +66 -0
  361. package/python/tests/test_mcp_connectors.py +97 -0
  362. package/python/tests/test_mcp_guardian_discovery.py +87 -0
  363. package/python/tests/test_mcp_no_memory.py +43 -0
  364. package/python/tests/test_mcp_policy_gate.py +76 -0
  365. package/python/tests/test_mcp_server.py +92 -0
  366. package/python/tests/test_memory_recall.py +92 -0
  367. package/python/tests/test_metrics.py +58 -0
  368. package/python/tests/test_multi_chain.py +90 -0
  369. package/python/tests/test_multimodal.py +122 -0
  370. package/python/tests/test_negotiation.py +136 -0
  371. package/python/tests/test_orchestration.py +109 -0
  372. package/python/tests/test_orchestration_decoupled.py +103 -0
  373. package/python/tests/test_p3_p5_hardening.py +324 -0
  374. package/python/tests/test_parallel_dispatch.py +207 -0
  375. package/python/tests/test_pipeline.py +492 -0
  376. package/python/tests/test_plan_visible.py +86 -0
  377. package/python/tests/test_planner.py +88 -0
  378. package/python/tests/test_policy_gate.py +108 -0
  379. package/python/tests/test_profile.py +67 -0
  380. package/python/tests/test_readme_claims.py +163 -0
  381. package/python/tests/test_reasoner.py +170 -0
  382. package/python/tests/test_release_check.py +252 -0
  383. package/python/tests/test_resume_checkpoint.py +148 -0
  384. package/python/tests/test_routing.py +85 -0
  385. package/python/tests/test_scheduler.py +138 -0
  386. package/python/tests/test_search.py +194 -0
  387. package/python/tests/test_secrets.py +153 -0
  388. package/python/tests/test_semantic.py +69 -0
  389. package/python/tests/test_sessions.py +90 -0
  390. package/python/tests/test_settings.py +265 -0
  391. package/python/tests/test_shadow_learning.py +290 -0
  392. package/python/tests/test_shared_write.py +200 -0
  393. package/python/tests/test_skill_taint_gate.py +145 -0
  394. package/python/tests/test_skills.py +117 -0
  395. package/python/tests/test_skills_fetch.py +282 -0
  396. package/python/tests/test_stats_and_events.py +162 -0
  397. package/python/tests/test_streaming.py +55 -0
  398. package/python/tests/test_supervisor_pipeline.py +176 -0
  399. package/python/tests/test_supervisor_triage.py +102 -0
  400. package/python/tests/test_taint.py +112 -0
  401. package/python/tests/test_task_stream.py +74 -0
  402. package/python/tests/test_thinking_sse.py +66 -0
  403. package/python/tests/test_thinking_stream.py +76 -0
  404. package/python/tests/test_tool_center.py +113 -0
  405. package/python/tests/test_tool_parallel.py +113 -0
  406. package/python/tests/test_tool_params.py +99 -0
  407. package/python/tests/test_tool_use.py +229 -0
  408. package/python/tests/test_toplevel_concurrency.py +116 -0
  409. package/python/tests/test_tracing.py +256 -0
  410. package/python/tests/test_truth_crystallizer.py +125 -0
  411. package/python/tests/test_truth_flow.py +88 -0
  412. package/python/tests/test_update_check.py +95 -0
  413. package/python/tests/test_url_policy.py +53 -0
  414. package/python/tests/test_verify_evidence.py +212 -0
  415. package/python/tests/test_version_sync.py +100 -0
  416. package/python/tests/test_web_trigger.py +82 -0
  417. package/python/tools/check_faiss_type.py +30 -0
  418. package/python/tools/check_log.ps1 +38 -0
  419. package/python/tools/ivf_status.py +72 -0
  420. package/python/tools/mypy_baseline.txt +13 -0
  421. package/python/tools/mypy_ratchet.py +143 -0
  422. package/python/tools/pack.py +173 -0
  423. package/python/tools/release_check.py +349 -0
  424. package/python/tools/restart_foxclaw.ps1 +71 -0
  425. package/python/tools/shared_write_audit.py +190 -0
  426. package/python/tools/size_baseline.txt +28 -0
  427. package/python/tools/size_check.py +102 -0
  428. package/python/tools/strip_comments.py +228 -0
  429. package/python/tools/verify_env.bat +2 -0
  430. package/python/tools/verify_env.py +25 -0
  431. package/python/vendor/HMR_LICENSE +37 -0
  432. package/python/vendor/HMR_README.md +65 -0
  433. package/python/vendor/hkc/LICENSE +37 -0
  434. package/python/vendor/hkc/hkc_ace/__init__.py +0 -0
  435. package/python/vendor/hkc/hkc_ace/ace.py +457 -0
  436. package/python/vendor/hkc/hkc_ace/skill_taxonomy.json +465 -0
  437. package/python/vendor/hkc/hkc_api/__init__.py +0 -0
  438. package/python/vendor/hkc/hkc_api/container.py +416 -0
  439. package/python/vendor/hkc/hkc_api/ingest_jobs.py +103 -0
  440. package/python/vendor/hkc/hkc_api/main.py +157 -0
  441. package/python/vendor/hkc/hkc_api/routes/__init__.py +0 -0
  442. package/python/vendor/hkc/hkc_api/routes/ability.py +151 -0
  443. package/python/vendor/hkc/hkc_api/routes/config.py +51 -0
  444. package/python/vendor/hkc/hkc_api/routes/knowledge.py +630 -0
  445. package/python/vendor/hkc/hkc_api/routes/search.py +88 -0
  446. package/python/vendor/hkc/hkc_api/schemas.py +153 -0
  447. package/python/vendor/hkc/hkc_api/synthesis.py +179 -0
  448. package/python/vendor/hkc/hkc_core/__init__.py +0 -0
  449. package/python/vendor/hkc/hkc_core/graph/__init__.py +0 -0
  450. package/python/vendor/hkc/hkc_core/graph/base.py +188 -0
  451. package/python/vendor/hkc/hkc_core/graph/sqlite_store.py +789 -0
  452. package/python/vendor/hkc/hkc_core/models/__init__.py +0 -0
  453. package/python/vendor/hkc/hkc_core/models/enums.py +65 -0
  454. package/python/vendor/hkc/hkc_core/models/ku.py +307 -0
  455. package/python/vendor/hkc/hkc_core/utils/__init__.py +0 -0
  456. package/python/vendor/hkc/hkc_core/utils/id_gen.py +113 -0
  457. package/python/vendor/hkc/hkc_crystallizer/__init__.py +44 -0
  458. package/python/vendor/hkc/hkc_crystallizer/adapters.py +94 -0
  459. package/python/vendor/hkc/hkc_crystallizer/candidate.py +145 -0
  460. package/python/vendor/hkc/hkc_crystallizer/crystallizer.py +179 -0
  461. package/python/vendor/hkc/hkc_crystallizer/ingress.py +90 -0
  462. package/python/vendor/hkc/hkc_kde/__init__.py +0 -0
  463. package/python/vendor/hkc/hkc_kde/chunker.py +197 -0
  464. package/python/vendor/hkc/hkc_kde/extractor.py +623 -0
  465. package/python/vendor/hkc/hkc_kde/kde.py +298 -0
  466. package/python/vendor/hkc/hkc_kde/loaders/__init__.py +0 -0
  467. package/python/vendor/hkc/hkc_kde/loaders/ebook_loader.py +153 -0
  468. package/python/vendor/hkc/hkc_kde/loaders/office_loader.py +194 -0
  469. package/python/vendor/hkc/hkc_kde/loaders/pdf_loader.py +162 -0
  470. package/python/vendor/hkc/hkc_kde/loaders/text_loader.py +157 -0
  471. package/python/vendor/hkc/hkc_kde/loaders/web_loader.py +91 -0
  472. package/python/vendor/hkc/hkc_kde/models.py +74 -0
  473. package/python/vendor/hkc/hkc_kde/packager.py +329 -0
  474. package/python/vendor/hkc/hkc_kde/relation_disc.py +151 -0
  475. package/python/vendor/hkc/hkc_kee/__init__.py +0 -0
  476. package/python/vendor/hkc/hkc_kee/dedup.py +227 -0
  477. package/python/vendor/hkc/hkc_kee/fingerprint.py +106 -0
  478. package/python/vendor/hkc/hkc_kee/kee.py +541 -0
  479. package/python/vendor/hkc/hkc_kep/__init__.py +0 -0
  480. package/python/vendor/hkc/hkc_kep/event_bus.py +205 -0
  481. package/python/vendor/hkc/hkc_sdk/__init__.py +63 -0
  482. package/python/vendor/hkc/hkc_sdk/base.py +151 -0
  483. package/python/vendor/hkc/hkc_sdk/direct_client.py +153 -0
  484. package/python/vendor/hkc/hkc_sdk/exceptions.py +39 -0
  485. package/python/vendor/hkc/hkc_sdk/http_client.py +166 -0
  486. package/python/vendor/hkc/hkc_sdk/models.py +171 -0
  487. package/python/vendor/hkc/hkc_search/__init__.py +0 -0
  488. package/python/vendor/hkc/hkc_search/bm25.py +325 -0
  489. package/python/vendor/hkc/hkc_search/embedding_backends.py +374 -0
  490. package/python/vendor/hkc/hkc_search/graph_search.py +132 -0
  491. package/python/vendor/hkc/hkc_search/hybrid.py +331 -0
  492. package/python/vendor/hkc/hkc_search/index_build_manager.py +378 -0
  493. package/python/vendor/hkc/hkc_search/vector_search.py +484 -0
  494. package/python/vendor/hmr/__init__.py +0 -0
  495. package/python/vendor/hmr/core/__init__.py +0 -0
  496. package/python/vendor/hmr/core/hmr.py +684 -0
  497. package/python/vendor/hmr/core/models.py +310 -0
  498. package/python/vendor/hmr/engines/__init__.py +0 -0
  499. package/python/vendor/hmr/engines/evolution.py +710 -0
  500. package/python/vendor/hmr/engines/jit_compiler.py +472 -0
  501. package/python/vendor/hmr/engines/lifecycle.py +264 -0
  502. package/python/vendor/hmr/engines/policy.py +625 -0
  503. package/python/vendor/hmr/engines/recall.py +260 -0
  504. package/python/vendor/hmr/engines/runtime_state.py +158 -0
  505. package/python/vendor/hmr/engines/scheduler.py +323 -0
  506. package/python/vendor/hmr/engines/semantic.py +150 -0
  507. package/python/vendor/hmr/engines/temporal.py +261 -0
  508. package/python/vendor/hmr/engines/thought_chain.py +635 -0
  509. package/python/vendor/hmr/graph/__init__.py +0 -0
  510. package/python/vendor/hmr/graph/cwg.py +338 -0
  511. package/python/vendor/hmr/graph/memory_graph.py +530 -0
  512. package/python/vendor/hmr/storage/__init__.py +0 -0
  513. package/python/vendor/hmr/storage/memory_fs.py +300 -0
  514. package/python/vendor/hmr/storage/vector_store.py +520 -0
@@ -0,0 +1,50 @@
1
+ """
2
+ BaiduWebSearchPort —— 把已有的 foxclaw.core.web_search(百度 AI 搜索)
3
+ 包装成 HSR 的外部检索端口(ExternalSearchPort),接入 Retriever。
4
+
5
+ 没有另起一套联网搜索实现:Foxclaw 已经在用百度 AI 搜索
6
+ (foxclaw/core/web_search.py,Agent 的 `_web_augment` 也在用它),
7
+ 这里只是把它的返回结果(answer + references)转成 RawEvidence,
8
+ 让 HSR 的证据融合/策略循环可以复用它,而不是绕开它另建一套。
9
+
10
+ 未配置 BAIDU_SEARCH_KEY 或网络不通时,底层 web_search.search_answer()
11
+ 本身就会静默返回空 dict,这里顺势返回空列表,不影响 HSR 主流程。
12
+ """
13
+ from __future__ import annotations
14
+ from ..core import web_search as _ws
15
+ from .models import RawEvidence, SourceType
16
+
17
+
18
+ class BaiduWebSearchPort:
19
+ name = "baidu_web_search"
20
+
21
+ def search(self, text: str, top_k: int = 5) -> list[RawEvidence]:
22
+ payload = _ws.search_answer(text)
23
+ if not payload:
24
+ return []
25
+
26
+ out: list[RawEvidence] = []
27
+
28
+ answer = payload.get("answer", "")
29
+ if answer:
30
+ out.append(RawEvidence(
31
+ id="baidu:answer",
32
+ fact=answer,
33
+ source=SourceType.WEB,
34
+ origin="baidu_ai_search",
35
+ confidence=0.65,
36
+ ))
37
+
38
+ for i, ref in enumerate(payload.get("references", [])[:top_k]):
39
+ snippet = ref.get("content", "") or ref.get("abstract", "")
40
+ if not snippet:
41
+ continue
42
+ out.append(RawEvidence(
43
+ id=f"baidu:ref:{i}",
44
+ fact=snippet,
45
+ source=SourceType.WEB,
46
+ origin=ref.get("url", "") or ref.get("web_anchor", ""),
47
+ confidence=0.55,
48
+ ))
49
+
50
+ return out
@@ -0,0 +1,60 @@
1
+ """
2
+ HKCReadPort —— 把 foxclaw-hkc 的检索能力接入 HSR 的 knowledge_port。
3
+
4
+ 融合之后:HKC 是 Foxclaw 内部的一个模块,不是外挂的独立系统。
5
+ Foxclaw 启动时会在同一个 Python 进程里直接装配 HKCContainer
6
+ (`foxclaw/__init__.py` 里的 `_init_hkc()`),这里只是拿 hkc_sdk 的
7
+ DirectClient 包一层,调用的是同进程里的对象方法,不发 HTTP 请求、
8
+ 不需要单独起服务、不存在"服务没启动"这种降级场景。
9
+
10
+ 这一层只暴露 query(),没有 ingest_text()——写不写知识回 HKC
11
+ 不是 HSR 的职责范围,那是 foxclaw-hkc 自己的 KDE/KEE 摄入管线,
12
+ 或者未来 Runtime 层的决定。
13
+
14
+ 如果确实需要把 foxclaw-hkc 单独部署成另一个服务(比如给其它系统复用),
15
+ client 换成 hkc_sdk.connect(base_url=...) 返回的 HTTPClient 依然能用——
16
+ DirectClient/HTTPClient 方法签名完全一致,这里的代码不用改一行。
17
+ """
18
+ from __future__ import annotations
19
+ import logging
20
+
21
+ from .models import RawEvidence, SourceType
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ class HKCReadPort:
27
+ """只读查询 foxclaw-hkc。需要一个已经连好的 hkc_sdk 客户端
28
+ (DirectClient 或 HTTPClient,两者接口一致);没有就永远返回空列表,
29
+ 不影响 HSR 主流程——和整个项目的降级哲学一致(没配就跳过,不报错)。"""
30
+
31
+ def __init__(self, client=None):
32
+ self._client = client
33
+
34
+ @property
35
+ def available(self) -> bool:
36
+ return self._client is not None
37
+
38
+ def query(self, text: str, top_k: int = 5) -> list[RawEvidence]:
39
+ if self._client is None:
40
+ return []
41
+
42
+ try:
43
+ hits = self._client.search(text, mode="hybrid", top_k=top_k)
44
+ except Exception as e:
45
+ logger.info(f"[HKC] 搜索「{text}」失败({e})")
46
+ return []
47
+
48
+ out: list[RawEvidence] = []
49
+ for h in hits:
50
+ fact = (h.summary or h.name or "").strip()
51
+ if not fact:
52
+ continue
53
+ out.append(RawEvidence(
54
+ id=h.ku_id,
55
+ fact=fact,
56
+ source=SourceType.KNOWLEDGE,
57
+ origin=f"hkc:{h.ku_id}",
58
+ confidence=float(h.score) if h.score else 0.6,
59
+ ))
60
+ return out
@@ -0,0 +1,71 @@
1
+ """
2
+ Internal Search Engine —— HSR-I 路由的内部检索器
3
+
4
+ 串联 HMR(记忆)与 HKC(知识,在同一进程内直连,Foxclaw 启动时装配好后
5
+ 通过 knowledge_port 传进来):
6
+
7
+ 1. 若配置了 knowledge_port:先查它拿权威知识候选
8
+ 2. 查 HMR 拿原始记忆,补充上下文 + 给①做溯源材料
9
+ 3. 交叉关联:knowledge 候选如果能在 HMR 里找到对应记忆,
10
+ 标记 grounded_by;找不到就做置信度惩罚(孤证)
11
+ 4. 没配置 knowledge_port / 没查到候选时,自然退化为纯 HMR 检索
12
+
13
+ knowledge_port 只需要实现 query(text, top_k) -> list[RawEvidence],
14
+ 这里不关心它具体是 foxclaw-hkc、还是别的什么知识源。
15
+ """
16
+ from __future__ import annotations
17
+ from difflib import SequenceMatcher
18
+ from .models import RawEvidence
19
+
20
+ _GROUNDING_SIMILARITY = 0.5
21
+ _UNGROUNDED_PENALTY = 0.15
22
+
23
+
24
+ class InternalSearchEngine:
25
+ def __init__(self, memory_port=None, knowledge_port=None, agent_port=None):
26
+ self.memory_port = memory_port
27
+ self.knowledge_port = knowledge_port
28
+ self.agent_port = agent_port
29
+
30
+ @staticmethod
31
+ def _safe_query(port, text: str, top_k: int) -> list[RawEvidence]:
32
+ """【深审修复·内部源隔离】逐个内部源 try/except → [],与 external 端口的隔离对称:
33
+ 任一内部源(知识/记忆/Agent)失败都只让该源返回空,**不掀翻整个 search**。
34
+ 现有三端口虽各自兜异常,但此处防御式隔离让"接入不兜异常的自定义 port"也安全。"""
35
+ if port is None:
36
+ return []
37
+ try:
38
+ return port.query(text, top_k) or []
39
+ except Exception:
40
+ return []
41
+
42
+ def search(self, text: str, top_k: int = 5) -> list[RawEvidence]:
43
+ from foxclaw.core.tracing import span as _span
44
+ with _span("search.internal", top_k=top_k):
45
+ knowledge_hits = self._safe_query(self.knowledge_port, text, top_k)
46
+ memory_hits = self._safe_query(self.memory_port, text, top_k)
47
+ agent_hits = self._safe_query(self.agent_port, text, top_k)
48
+
49
+ if knowledge_hits:
50
+ knowledge_hits = self._cross_link(knowledge_hits, memory_hits + agent_hits)
51
+
52
+ return knowledge_hits + memory_hits + agent_hits
53
+
54
+ def _cross_link(self, knowledge_hits, memory_hits) -> list[RawEvidence]:
55
+ linked = []
56
+ for k in knowledge_hits:
57
+ trace = self._find_trace(k, memory_hits)
58
+ if trace:
59
+ k.grounded_by = trace.id
60
+ else:
61
+ k.confidence = max(0.0, k.confidence - _UNGROUNDED_PENALTY)
62
+ linked.append(k)
63
+ return linked
64
+
65
+ def _find_trace(self, knowledge: RawEvidence, memory_hits: list[RawEvidence]):
66
+ best, best_sim = None, 0.0
67
+ for m in memory_hits:
68
+ sim = SequenceMatcher(None, knowledge.fact, m.fact).ratio()
69
+ if sim >= _GROUNDING_SIMILARITY and sim > best_sim:
70
+ best, best_sim = m, sim
71
+ return best
@@ -0,0 +1,127 @@
1
+ """
2
+ Foxclaw — Search 内核数据模型 (HSR)
3
+
4
+ 设计原则:这一层只产出"证据",不做任何持久化决策。
5
+ 是否把搜索结果沉淀回 HMR、是否提交知识库(未来的 foxclaw-hkc),
6
+ 由调用方(Agent / Runtime)决定,这里的数据结构都是只读产物。
7
+ """
8
+ from __future__ import annotations
9
+ from dataclasses import dataclass, field
10
+ from enum import Enum
11
+ from typing import Any, Optional
12
+
13
+
14
+ class RouteType(str, Enum):
15
+ INTERNAL = "HSR-I"
16
+ EXTERNAL = "HSR-E"
17
+ HYBRID = "HYBRID"
18
+
19
+
20
+ class SourceType(str, Enum):
21
+ MEMORY = "memory"
22
+ KNOWLEDGE = "knowledge"
23
+ AGENT = "agent"
24
+ WEB = "web"
25
+
26
+
27
+ @dataclass
28
+ class SubQuery:
29
+ id: str
30
+ text: str
31
+ source_hint: Optional[SourceType] = None
32
+ depends_on: list[str] = field(default_factory=list)
33
+
34
+
35
+ @dataclass
36
+ class SearchPlan:
37
+ route: RouteType
38
+ steps: list[SubQuery]
39
+ reasoning: str = ""
40
+
41
+
42
+ @dataclass
43
+ class RawEvidence:
44
+ id: str
45
+ fact: str
46
+ source: SourceType
47
+ origin: str
48
+ confidence: float = 0.5
49
+ raw: Any = None
50
+ grounded_by: Optional[str] = None
51
+
52
+
53
+ @dataclass
54
+ class EvidenceNode:
55
+ id: str
56
+ fact: str
57
+ source: SourceType
58
+ origin: str
59
+ confidence: float
60
+ grounded_by: Optional[str] = None
61
+
62
+
63
+ @dataclass
64
+ class EvidenceEdge:
65
+ src: str
66
+ dst: str
67
+ relation: str
68
+
69
+
70
+ @dataclass
71
+ class EvidenceGraph:
72
+ nodes: dict[str, EvidenceNode] = field(default_factory=dict)
73
+ edges: list[EvidenceEdge] = field(default_factory=list)
74
+
75
+ def add_node(self, node: EvidenceNode) -> None:
76
+ self.nodes[node.id] = node
77
+
78
+ def add_edge(self, edge: EvidenceEdge) -> None:
79
+ self.edges.append(edge)
80
+
81
+ def contradictions(self) -> list[EvidenceEdge]:
82
+ return [e for e in self.edges if e.relation == "contradicts"]
83
+
84
+ def to_dict(self) -> dict:
85
+ """给 API / ThoughtChain 用的精简序列化。"""
86
+ return {
87
+ "nodes": [
88
+ {
89
+ "id": n.id, "fact": n.fact, "source": n.source.value,
90
+ "origin": n.origin, "confidence": round(n.confidence, 2),
91
+ "grounded_by": n.grounded_by,
92
+ }
93
+ for n in self.nodes.values()
94
+ ],
95
+ "edges": [
96
+ {"src": e.src, "dst": e.dst, "relation": e.relation}
97
+ for e in self.edges
98
+ ],
99
+ }
100
+
101
+
102
+ @dataclass
103
+ class StrategyDecision:
104
+ action: str
105
+ reason: str
106
+ next_steps: list[SubQuery] = field(default_factory=list)
107
+
108
+
109
+ @dataclass
110
+ class SearchResult:
111
+ """search() 的唯一返回值。到这里为止,Search 内核不再做任何事。"""
112
+ query: str
113
+ route: RouteType
114
+ graph: EvidenceGraph
115
+ iterations: int
116
+ confidence: float
117
+ trace: list[str] = field(default_factory=list)
118
+
119
+ def to_dict(self) -> dict:
120
+ return {
121
+ "query": self.query,
122
+ "route": self.route.value,
123
+ "confidence": round(self.confidence, 2),
124
+ "iterations": self.iterations,
125
+ "graph": self.graph.to_dict(),
126
+ "trace": self.trace,
127
+ }
@@ -0,0 +1,44 @@
1
+ """
2
+ Query Planner —— 决定路由(HSR-I / HSR-E / HYBRID),并把任务拆成多跳检索步骤。
3
+
4
+ 当前是规则实现(触发词 + 标点拆句),接口(SearchPlan)保持稳定,
5
+ 未来要换成 LLM function-calling 结构化拆解时,只需替换这个文件内部实现,
6
+ 不影响 Retriever / EvidenceFusion / SearchStrategy。
7
+ """
8
+ from __future__ import annotations
9
+ from .models import SearchPlan, SubQuery, RouteType
10
+ from ..planning import planner as P
11
+
12
+ _MEMORY_HINTS = ("我之前", "我们讨论过", "上次", "记得", "history", "之前说")
13
+ _HYBRID_HINTS = ("为什么", "对比", "分析", "综合", "评估")
14
+
15
+
16
+ class QueryPlanner:
17
+ def route(self, query: str, context: dict | None = None) -> RouteType:
18
+ if any(h in query for h in _MEMORY_HINTS):
19
+ return RouteType.INTERNAL
20
+ if any(h in query for h in _HYBRID_HINTS):
21
+ return RouteType.HYBRID
22
+ return RouteType.EXTERNAL
23
+
24
+ def plan(self, query: str, context: dict | None = None) -> SearchPlan:
25
+ route = self.route(query, context)
26
+
27
+ if route == RouteType.INTERNAL:
28
+ steps = [SubQuery(id="s1", text=query)]
29
+ elif route == RouteType.EXTERNAL:
30
+ steps = self._decompose(query)
31
+ else:
32
+ steps = [SubQuery(id="s1", text=query), *self._decompose(query, offset=2)]
33
+
34
+ return SearchPlan(
35
+ route=route,
36
+ steps=steps,
37
+ reasoning=f"route={route.value}, steps={len(steps)}",
38
+ )
39
+
40
+ def _decompose(self, query: str, offset: int = 1) -> list[SubQuery]:
41
+ parts = P.split_on(query, P.LIST_SEPS)
42
+ if len(parts) <= 1:
43
+ parts = [query]
44
+ return [SubQuery(id=f"s{offset + i}", text=p) for i, p in enumerate(parts)]
@@ -0,0 +1,39 @@
1
+ """
2
+ Retriever —— 内部检索(委托 InternalSearchEngine 串联 HMR/未来的 HKC)
3
+ 与外部检索(联网搜索,当前是百度 AI 搜索)的统一入口。
4
+ """
5
+ from __future__ import annotations
6
+ from .models import SubQuery, RawEvidence, RouteType
7
+ from .internal_search import InternalSearchEngine
8
+
9
+
10
+ class Retriever:
11
+ def __init__(
12
+ self,
13
+ memory_port=None,
14
+ knowledge_port=None,
15
+ external_ports: list | None = None,
16
+ internal_search: InternalSearchEngine | None = None,
17
+ ):
18
+ self.internal_search = internal_search or InternalSearchEngine(
19
+ memory_port=memory_port, knowledge_port=knowledge_port,
20
+ )
21
+ self.external_ports = external_ports or []
22
+
23
+ def retrieve(self, steps: list[SubQuery], route: RouteType) -> list[RawEvidence]:
24
+ results: list[RawEvidence] = []
25
+ for step in steps:
26
+ if route in (RouteType.INTERNAL, RouteType.HYBRID):
27
+ results += self.internal_search.search(step.text)
28
+ if route in (RouteType.EXTERNAL, RouteType.HYBRID):
29
+ results += self._retrieve_external(step)
30
+ return results
31
+
32
+ def _retrieve_external(self, step: SubQuery) -> list[RawEvidence]:
33
+ out: list[RawEvidence] = []
34
+ for port in self.external_ports:
35
+ try:
36
+ out += port.search(step.text)
37
+ except Exception:
38
+ continue
39
+ return out
@@ -0,0 +1,127 @@
1
+ """
2
+ Foxclaw — Search: 认知搜索内核 (HSR)
3
+ =======================================
4
+ 把"查内部记忆(HMR)"、"查内部知识(HKC)"和"查外部世界(联网搜索)"统一成
5
+ 一次 search() 调用,供五个 Agent 共享。和 Finance / Learning / Itinerary
6
+ 三个领域内核一样,遵循同一条边界:
7
+
8
+ 内核只产出证据 / 建议,不越权替调用方做决策。
9
+ Finance 内核只建议、不动钱;Search 内核只给证据、不代替 Agent
10
+ 决定"这次搜索结果值不值得写回 HMR"——那是 Agent 自己的事
11
+ (Agent 已经有 `_store_memory()`,Search 不需要再造一个)。
12
+
13
+ HKC 融合说明:
14
+ HKC(foxclaw-hkc)现在是 Foxclaw 内部的一个模块,不是外挂的独立系统。
15
+ `foxclaw/__init__.py` 在启动时会在同一个 Python 进程里直接装配
16
+ HKCContainer,用 hkc_sdk.DirectClient 包成 HKCReadPort,再传进这里
17
+ 的 knowledge_port——全程没有 HTTP、没有第二个服务、没有端口。
18
+ SearchRuntime 本身完全不关心 knowledge_port 具体是不是 HKC,
19
+ 只要实现了 query(text, top_k) 就行,这条边界保持干净。
20
+ """
21
+ from __future__ import annotations
22
+ from typing import Optional
23
+
24
+ from .models import SearchResult, RawEvidence, SourceType, RouteType
25
+ from .planner import QueryPlanner
26
+ from .internal_search import InternalSearchEngine
27
+ from .external_search import BaiduWebSearchPort
28
+ from .retriever import Retriever
29
+ from .evidence import EvidenceFusion
30
+ from .strategy import SearchStrategy
31
+
32
+
33
+ class _HMRMemoryReadPort:
34
+ """把 HMRAdapter / MockHMR 的 recall() 适配成 InternalSearchEngine
35
+ 需要的只读查询接口:query(text, top_k) -> list[RawEvidence]。
36
+
37
+ 故意只暴露 query,不暴露 ingest——HSR 内部检索只读 HMR,
38
+ 写不写记忆由 Agent 层的 `_store_memory()` 决定。"""
39
+
40
+ def __init__(self, hmr, agent_id: Optional[str]):
41
+ self.hmr = hmr
42
+ self.agent_id = agent_id
43
+
44
+ def query(self, text: str, top_k: int = 5) -> list[RawEvidence]:
45
+ try:
46
+ results = self.hmr.recall(query=text, agent_id=self.agent_id, top_k=top_k)
47
+ except Exception:
48
+ return []
49
+ out: list[RawEvidence] = []
50
+ for r in results or []:
51
+ content = r.get("content", "") or r.get("title", "")
52
+ if not content:
53
+ continue
54
+ conf = r.get("confidence", r.get("score", 0.6))
55
+ out.append(RawEvidence(
56
+ id=r.get("id", "") or f"hmr:{len(out)}",
57
+ fact=content,
58
+ source=SourceType.MEMORY,
59
+ origin=f"hmr:{r.get('id', '')}",
60
+ confidence=float(conf) if conf is not None else 0.6,
61
+ ))
62
+ return out
63
+
64
+
65
+ class SearchRuntime:
66
+ """Foxclaw 的认知搜索内核。一次初始化,五个 Agent 共享调用
67
+ (与 self.hmr / self.hkc / self.router / self.token_meter 同级)。"""
68
+
69
+ def __init__(self, hmr=None, knowledge_port=None, agent_port=None,
70
+ external_ports: list | None = None):
71
+ self.hmr = hmr
72
+ self.knowledge_port = knowledge_port
73
+ self.agent_port = agent_port
74
+ self.planner = QueryPlanner()
75
+ self.fusion = EvidenceFusion()
76
+ self.strategy = SearchStrategy()
77
+ self.external_ports = (
78
+ external_ports if external_ports is not None else [BaiduWebSearchPort()]
79
+ )
80
+
81
+ def search(self, query: str, agent_id: str | None = None, context: dict | None = None,
82
+ internal_only: bool = False) -> SearchResult:
83
+ """唯一公开入口。给定 query,返回 SearchResult,过程中不写任何持久化状态。
84
+
85
+ internal_only=True:只查内部(HMR记忆 + HKC知识 + Agent态势),跳过联网。
86
+ 供 Agent 反向调用内部搜索作工具时用(双向连通的「Agent→搜索」方向)。"""
87
+ memory_port = _HMRMemoryReadPort(self.hmr, agent_id) if self.hmr else None
88
+ internal = InternalSearchEngine(
89
+ memory_port=memory_port, knowledge_port=self.knowledge_port,
90
+ agent_port=self.agent_port,
91
+ )
92
+ external_ports = [] if internal_only else self.external_ports
93
+ retriever = Retriever(internal_search=internal, external_ports=external_ports)
94
+
95
+ trace: list[str] = []
96
+ plan = self.planner.plan(query, context)
97
+ if internal_only and plan.route == RouteType.EXTERNAL:
98
+ plan.route = RouteType.INTERNAL
99
+ trace.append("[internal_only] 路由改写为 HSR-I(仅内部)")
100
+ trace.append(f"[plan] {plan.reasoning}")
101
+
102
+ raw = retriever.retrieve(plan.steps, plan.route)
103
+ graph = self.fusion.build(raw)
104
+ confidence = self.fusion.confidence(graph)
105
+
106
+ iteration = 0
107
+ while True:
108
+ decision = self.strategy.decide(graph, confidence, iteration, query)
109
+ trace.append(f"[loop {iteration}] {decision.action}: {decision.reason}")
110
+
111
+ if decision.action == "stop":
112
+ break
113
+
114
+ more = retriever.retrieve(decision.next_steps, plan.route)
115
+ if not more:
116
+ trace.append(f"[loop {iteration}] 无新证据,提前停止")
117
+ break
118
+
119
+ raw += more
120
+ graph = self.fusion.build(raw)
121
+ confidence = self.fusion.confidence(graph)
122
+ iteration += 1
123
+
124
+ return SearchResult(
125
+ query=query, route=plan.route, graph=graph,
126
+ iterations=iteration, confidence=confidence, trace=trace,
127
+ )
@@ -0,0 +1,44 @@
1
+ """
2
+ Search Strategy —— 决定 continue / stop / switch_query / switch_source。
3
+ 纯函数式判断(无状态、无 IO),可独立单测和演进。
4
+ """
5
+ from __future__ import annotations
6
+ from .models import EvidenceGraph, StrategyDecision, SubQuery
7
+
8
+ _CONFIDENCE_STOP_THRESHOLD = 0.75
9
+ _MAX_ITERATIONS = 3
10
+
11
+
12
+ class SearchStrategy:
13
+ def decide(
14
+ self,
15
+ graph: EvidenceGraph,
16
+ confidence: float,
17
+ iteration: int,
18
+ original_query: str,
19
+ ) -> StrategyDecision:
20
+ if confidence >= _CONFIDENCE_STOP_THRESHOLD:
21
+ return StrategyDecision(action="stop", reason=f"置信度 {confidence:.2f} 已达标")
22
+
23
+ if iteration >= _MAX_ITERATIONS:
24
+ return StrategyDecision(action="stop", reason="达到最大迭代次数,防止无限循环")
25
+
26
+ if graph.contradictions():
27
+ return StrategyDecision(
28
+ action="switch_source",
29
+ reason="存在冲突证据,尝试切换更权威数据源",
30
+ next_steps=[SubQuery(id=f"retry{iteration}", text=original_query)],
31
+ )
32
+
33
+ if len(graph.nodes) < 2:
34
+ return StrategyDecision(
35
+ action="switch_query",
36
+ reason="证据不足,尝试改写查询",
37
+ next_steps=[SubQuery(id=f"rephrase{iteration}", text=original_query + " 详细信息")],
38
+ )
39
+
40
+ return StrategyDecision(
41
+ action="continue",
42
+ reason="证据尚可但置信度不足,继续补充检索",
43
+ next_steps=[SubQuery(id=f"more{iteration}", text=original_query)],
44
+ )