foxclaw 0.1.0 → 0.1.2

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 (495) hide show
  1. package/README.md +54 -29
  2. package/bin/foxclaw.cjs +51 -0
  3. package/index.js +11 -10
  4. package/package.json +46 -38
  5. package/python/.dockerignore +35 -0
  6. package/python/.env.example +165 -0
  7. package/python/.gitattributes +46 -0
  8. package/python/.github/workflows/ci.yml +122 -0
  9. package/python/.github/workflows/release.yml +183 -0
  10. package/python/Dockerfile +64 -0
  11. package/python/LICENSE +21 -0
  12. package/python/MANIFEST.in +14 -0
  13. package/python/README.md +192 -0
  14. package/python/README_CN.md +324 -0
  15. package/python/bench/bm25_audit.py +199 -0
  16. package/python/bench/bm25_regression.py +178 -0
  17. package/python/bench/bm25_stress.py +159 -0
  18. package/python/bench/cd_audit.py +238 -0
  19. package/python/bench/chaos.py +142 -0
  20. package/python/bench/concurrency.py +103 -0
  21. package/python/bench/e2e_real_models.py +95 -0
  22. package/python/bench/llm_retry_test.py +248 -0
  23. package/python/bench/load_test.py +196 -0
  24. package/python/bench/vector_ivf_upgrade.py +134 -0
  25. package/python/docs/CHANNELS.md +195 -0
  26. package/python/docs/GOVERNANCE.md +164 -0
  27. package/python/docs/RELEASE.md +109 -0
  28. package/python/docs/RUN.md +75 -0
  29. package/python/docs/RUN_CN.md +121 -0
  30. package/python/docs//351/205/215/347/275/256/346/214/207/345/215/227.md +152 -0
  31. package/python/evals/__init__.py +0 -0
  32. package/python/evals/governance/README.md +61 -0
  33. package/python/evals/governance/__init__.py +0 -0
  34. package/python/evals/governance/cases.py +152 -0
  35. package/python/evals/governance/compare_embeddings.py +94 -0
  36. package/python/evals/governance/convergence.svg +11 -0
  37. package/python/evals/governance/report.json +371 -0
  38. package/python/evals/governance/run_evals.py +400 -0
  39. package/python/examples/governance_middleware_demo.py +77 -0
  40. package/python/examples/langgraph_governance_demo.py +108 -0
  41. package/python/examples/sample_itinerary.csv +6 -0
  42. package/python/examples/sample_knowledge.csv +9 -0
  43. package/python/examples/sample_transactions.csv +14 -0
  44. package/python/foxclaw/__init__.py +526 -0
  45. package/python/foxclaw/agents/__init__.py +12 -0
  46. package/python/foxclaw/agents/agents.py +35 -0
  47. package/python/foxclaw/agents/base.py +865 -0
  48. package/python/foxclaw/agents/impl/__init__.py +1 -0
  49. package/python/foxclaw/agents/impl/_helpers.py +78 -0
  50. package/python/foxclaw/agents/impl/finance.py +248 -0
  51. package/python/foxclaw/agents/impl/office.py +367 -0
  52. package/python/foxclaw/agents/impl/supervisor.py +659 -0
  53. package/python/foxclaw/agents/mixins/_context.py +151 -0
  54. package/python/foxclaw/agents/mixins/augment.py +149 -0
  55. package/python/foxclaw/agents/mixins/llm.py +66 -0
  56. package/python/foxclaw/agents/mixins/memory.py +35 -0
  57. package/python/foxclaw/agents/mixins/preference.py +81 -0
  58. package/python/foxclaw/agents/mixins/prompt.py +77 -0
  59. package/python/foxclaw/agents/mixins/skillbox.py +58 -0
  60. package/python/foxclaw/agents/mixins/tool_exec.py +443 -0
  61. package/python/foxclaw/api/__init__.py +283 -0
  62. package/python/foxclaw/api/app.py +123 -0
  63. package/python/foxclaw/api/middleware.py +317 -0
  64. package/python/foxclaw/api/routes/ahs.py +417 -0
  65. package/python/foxclaw/api/routes/attachments.py +117 -0
  66. package/python/foxclaw/api/routes/browser.py +39 -0
  67. package/python/foxclaw/api/routes/domain.py +314 -0
  68. package/python/foxclaw/api/routes/knowledge.py +163 -0
  69. package/python/foxclaw/api/routes/llm_admin.py +461 -0
  70. package/python/foxclaw/api/routes/memory.py +89 -0
  71. package/python/foxclaw/api/routes/orchestration.py +41 -0
  72. package/python/foxclaw/api/routes/recovery.py +43 -0
  73. package/python/foxclaw/api/routes/review.py +275 -0
  74. package/python/foxclaw/api/routes/search.py +37 -0
  75. package/python/foxclaw/api/routes/system.py +327 -0
  76. package/python/foxclaw/api/routes/task.py +281 -0
  77. package/python/foxclaw/api/routes/ws.py +68 -0
  78. package/python/foxclaw/api/spine.py +55 -0
  79. package/python/foxclaw/browser/__init__.py +13 -0
  80. package/python/foxclaw/browser/action/__init__.py +1 -0
  81. package/python/foxclaw/browser/action/action_executor.py +54 -0
  82. package/python/foxclaw/browser/action/action_types.py +45 -0
  83. package/python/foxclaw/browser/action/action_verifier.py +68 -0
  84. package/python/foxclaw/browser/action/url_policy.py +147 -0
  85. package/python/foxclaw/browser/adapter/__init__.py +1 -0
  86. package/python/foxclaw/browser/adapter/base_adapter.py +23 -0
  87. package/python/foxclaw/browser/adapter/web_adapter.py +46 -0
  88. package/python/foxclaw/browser/driver/__init__.py +1 -0
  89. package/python/foxclaw/browser/driver/base_driver.py +61 -0
  90. package/python/foxclaw/browser/driver/playwright_driver.py +166 -0
  91. package/python/foxclaw/browser/perception/__init__.py +1 -0
  92. package/python/foxclaw/browser/perception/dom_extractor.py +43 -0
  93. package/python/foxclaw/browser/perception/element_indexer.py +57 -0
  94. package/python/foxclaw/browser/perception/semantic_reducer.py +33 -0
  95. package/python/foxclaw/browser/perception/snapshot.py +29 -0
  96. package/python/foxclaw/browser/service.py +148 -0
  97. package/python/foxclaw/browser/session/__init__.py +1 -0
  98. package/python/foxclaw/browser/session/session_manager.py +57 -0
  99. package/python/foxclaw/capability/__init__.py +12 -0
  100. package/python/foxclaw/capability/embedding_service.py +67 -0
  101. package/python/foxclaw/channels/__init__.py +140 -0
  102. package/python/foxclaw/channels/discord.py +184 -0
  103. package/python/foxclaw/channels/dispatcher.py +204 -0
  104. package/python/foxclaw/channels/feishu.py +225 -0
  105. package/python/foxclaw/channels/openclaw_bridge.py +156 -0
  106. package/python/foxclaw/channels/signing.py +187 -0
  107. package/python/foxclaw/channels/telegram.py +121 -0
  108. package/python/foxclaw/channels/weixin.py +225 -0
  109. package/python/foxclaw/cli.py +154 -0
  110. package/python/foxclaw/cognitive/__init__.py +4 -0
  111. package/python/foxclaw/cognitive/reasoner.py +82 -0
  112. package/python/foxclaw/cognitive/state.py +147 -0
  113. package/python/foxclaw/core/__init__.py +9 -0
  114. package/python/foxclaw/core/audit_chain.py +133 -0
  115. package/python/foxclaw/core/audit_sink.py +100 -0
  116. package/python/foxclaw/core/compaction.py +283 -0
  117. package/python/foxclaw/core/config.py +65 -0
  118. package/python/foxclaw/core/confirm_nonce.py +157 -0
  119. package/python/foxclaw/core/dispatch.py +27 -0
  120. package/python/foxclaw/core/envfile.py +48 -0
  121. package/python/foxclaw/core/failure_policy.py +151 -0
  122. package/python/foxclaw/core/gateway.py +85 -0
  123. package/python/foxclaw/core/governance_audit.py +175 -0
  124. package/python/foxclaw/core/idempotency.py +289 -0
  125. package/python/foxclaw/core/injection_guard.py +221 -0
  126. package/python/foxclaw/core/memory_adapter.py +516 -0
  127. package/python/foxclaw/core/metrics.py +89 -0
  128. package/python/foxclaw/core/model_router.py +1097 -0
  129. package/python/foxclaw/core/paths.py +103 -0
  130. package/python/foxclaw/core/recovery.py +362 -0
  131. package/python/foxclaw/core/scheduler.py +144 -0
  132. package/python/foxclaw/core/secrets.py +134 -0
  133. package/python/foxclaw/core/sessions.py +157 -0
  134. package/python/foxclaw/core/settings.py +430 -0
  135. package/python/foxclaw/core/shared_write.py +198 -0
  136. package/python/foxclaw/core/steering_queue.py +153 -0
  137. package/python/foxclaw/core/system_bus.py +84 -0
  138. package/python/foxclaw/core/thought_chain.py +318 -0
  139. package/python/foxclaw/core/token_meter.py +235 -0
  140. package/python/foxclaw/core/tools.py +172 -0
  141. package/python/foxclaw/core/tracing.py +390 -0
  142. package/python/foxclaw/core/truth_pipeline.py +114 -0
  143. package/python/foxclaw/core/vec_recall_adapter.py +503 -0
  144. package/python/foxclaw/core/verify.py +181 -0
  145. package/python/foxclaw/core/web_search.py +242 -0
  146. package/python/foxclaw/finance/__init__.py +15 -0
  147. package/python/foxclaw/finance/evidence.py +153 -0
  148. package/python/foxclaw/finance/fx.py +100 -0
  149. package/python/foxclaw/finance/graph.py +188 -0
  150. package/python/foxclaw/finance/models.py +150 -0
  151. package/python/foxclaw/finance/nlparse.py +575 -0
  152. package/python/foxclaw/finance/policy.py +329 -0
  153. package/python/foxclaw/finance/reasoner.py +129 -0
  154. package/python/foxclaw/finance/reasoning.py +340 -0
  155. package/python/foxclaw/finance/reconcile.py +180 -0
  156. package/python/foxclaw/finance/runtime.py +527 -0
  157. package/python/foxclaw/governance/__init__.py +24 -0
  158. package/python/foxclaw/governance/middleware.py +184 -0
  159. package/python/foxclaw/harness/__init__.py +17 -0
  160. package/python/foxclaw/harness/_base.py +72 -0
  161. package/python/foxclaw/harness/_policy.py +359 -0
  162. package/python/foxclaw/harness/drift.py +89 -0
  163. package/python/foxclaw/harness/event_bus.py +31 -0
  164. package/python/foxclaw/harness/harness.py +446 -0
  165. package/python/foxclaw/harness/loop_detection.py +152 -0
  166. package/python/foxclaw/itinerary/__init__.py +10 -0
  167. package/python/foxclaw/itinerary/amap_client.py +117 -0
  168. package/python/foxclaw/itinerary/models.py +80 -0
  169. package/python/foxclaw/itinerary/reasoner.py +101 -0
  170. package/python/foxclaw/itinerary/reasoning.py +174 -0
  171. package/python/foxclaw/itinerary/runtime.py +148 -0
  172. package/python/foxclaw/kernel/__init__.py +13 -0
  173. package/python/foxclaw/kernel/capabilities.py +73 -0
  174. package/python/foxclaw/kernel/policy_gate.py +114 -0
  175. package/python/foxclaw/kernel/process_manager.py +205 -0
  176. package/python/foxclaw/kernel/scheduler.py +131 -0
  177. package/python/foxclaw/kernel/sentinel.py +453 -0
  178. package/python/foxclaw/kernel/sentinel_client.py +246 -0
  179. package/python/foxclaw/kernel/spine.py +97 -0
  180. package/python/foxclaw/knowledge/__init__.py +376 -0
  181. package/python/foxclaw/knowledge/truth_crystallizer.py +92 -0
  182. package/python/foxclaw/learning/__init__.py +12 -0
  183. package/python/foxclaw/learning/knowledge_graph.py +149 -0
  184. package/python/foxclaw/learning/models.py +93 -0
  185. package/python/foxclaw/learning/reasoner.py +70 -0
  186. package/python/foxclaw/learning/runtime.py +363 -0
  187. package/python/foxclaw/learning/spaced_repetition.py +96 -0
  188. package/python/foxclaw/mcp_server.py +189 -0
  189. package/python/foxclaw/orchestration/__init__.py +184 -0
  190. package/python/foxclaw/pipeline/__init__.py +23 -0
  191. package/python/foxclaw/pipeline/cognitive_pipeline.py +481 -0
  192. package/python/foxclaw/pipeline/supervisor_stages.py +230 -0
  193. package/python/foxclaw/planning/__init__.py +17 -0
  194. package/python/foxclaw/planning/llm_planner.py +232 -0
  195. package/python/foxclaw/planning/planner.py +99 -0
  196. package/python/foxclaw/search/__init__.py +24 -0
  197. package/python/foxclaw/search/agent_port.py +194 -0
  198. package/python/foxclaw/search/evidence.py +113 -0
  199. package/python/foxclaw/search/external_search.py +50 -0
  200. package/python/foxclaw/search/hkc_read_port.py +60 -0
  201. package/python/foxclaw/search/internal_search.py +73 -0
  202. package/python/foxclaw/search/models.py +129 -0
  203. package/python/foxclaw/search/planner.py +44 -0
  204. package/python/foxclaw/search/retriever.py +39 -0
  205. package/python/foxclaw/search/runtime.py +134 -0
  206. package/python/foxclaw/search/strategy.py +44 -0
  207. package/python/foxclaw/skills/__init__.py +613 -0
  208. package/python/foxclaw/skills/hooks_spec.py +73 -0
  209. package/python/foxclaw/skills/lifecycle.py +278 -0
  210. package/python/foxclaw/skills/markdown_skill.py +174 -0
  211. package/python/foxclaw/skills/markdown_store.py +39 -0
  212. package/python/foxclaw/skills/mcp_client.py +190 -0
  213. package/python/foxclaw/skills/mcp_store.py +43 -0
  214. package/python/foxclaw/skills/permission.py +180 -0
  215. package/python/foxclaw/skills/pipeline.py +386 -0
  216. package/python/foxclaw/skills/policy.py +80 -0
  217. package/python/foxclaw/skills/review.py +72 -0
  218. package/python/foxclaw/skills/store.py +57 -0
  219. package/python/frontend/assets/core_bg.jpg +0 -0
  220. package/python/frontend/deck/auth.js +32 -0
  221. package/python/frontend/deck/chat_model_picker.js +537 -0
  222. package/python/frontend/deck/domain.js +296 -0
  223. package/python/frontend/deck/main.js +1950 -0
  224. package/python/frontend/deck/markdown.js +48 -0
  225. package/python/frontend/deck/settings.js +324 -0
  226. package/python/frontend/deck/state.js +6 -0
  227. package/python/frontend/deck/util.js +6 -0
  228. package/python/frontend/deck/visuals.js +212 -0
  229. package/python/frontend/deck.css +855 -0
  230. package/python/frontend/deck.html +686 -0
  231. package/python/frontend/deck_v2.html +341 -0
  232. package/python/frontend/planet/index.html +266 -0
  233. package/python/frontend/planet/planet.css +579 -0
  234. package/python/frontend/planet/planet.js +1637 -0
  235. package/python/frontend/planet/vendor/three.core.min.js +6 -0
  236. package/python/frontend/planet/vendor/three.module.min.js +6 -0
  237. package/python/frontend/v3/assets/index-BD7VPbMV.js +33 -0
  238. package/python/frontend/v3/assets/index-CAvnYWGH.css +1 -0
  239. package/python/frontend/v3/index.html +15 -0
  240. package/python/frontend/widget_runtime.js +295 -0
  241. package/python/frontend/widgets.js +696 -0
  242. package/python/frontend_src/index.html +14 -0
  243. package/python/frontend_src/package-lock.json +1376 -0
  244. package/python/frontend_src/package.json +19 -0
  245. package/python/frontend_src/src/App.vue +62 -0
  246. package/python/frontend_src/src/api.js +89 -0
  247. package/python/frontend_src/src/components/ChatDock.vue +377 -0
  248. package/python/frontend_src/src/components/ComposerMenu.vue +75 -0
  249. package/python/frontend_src/src/components/DocViewer.vue +171 -0
  250. package/python/frontend_src/src/components/Inspector.vue +241 -0
  251. package/python/frontend_src/src/components/LeftRail.vue +394 -0
  252. package/python/frontend_src/src/components/MenuPopover.vue +130 -0
  253. package/python/frontend_src/src/components/MessageStream.vue +401 -0
  254. package/python/frontend_src/src/components/ModelPicker.vue +355 -0
  255. package/python/frontend_src/src/components/QuickIngest.vue +118 -0
  256. package/python/frontend_src/src/components/Stage.vue +226 -0
  257. package/python/frontend_src/src/components/ToolMenu.vue +144 -0
  258. package/python/frontend_src/src/components/TopBar.vue +90 -0
  259. package/python/frontend_src/src/components/panels/ActivityPanel.vue +84 -0
  260. package/python/frontend_src/src/components/panels/AgentsPanel.vue +86 -0
  261. package/python/frontend_src/src/components/panels/KnowledgePanel.vue +441 -0
  262. package/python/frontend_src/src/components/panels/MemoryPanel.vue +118 -0
  263. package/python/frontend_src/src/components/panels/PanelBox.vue +51 -0
  264. package/python/frontend_src/src/components/panels/ReviewPanel.vue +308 -0
  265. package/python/frontend_src/src/components/panels/SettingsPanel.vue +254 -0
  266. package/python/frontend_src/src/components/panels/TasksPanel.vue +74 -0
  267. package/python/frontend_src/src/components/panels/ToolsPanel.vue +640 -0
  268. package/python/frontend_src/src/components/panels/UsagePanel.vue +105 -0
  269. package/python/frontend_src/src/lib/fmt.js +48 -0
  270. package/python/frontend_src/src/lib/md.js +204 -0
  271. package/python/frontend_src/src/main.js +7 -0
  272. package/python/frontend_src/src/stores/app.js +106 -0
  273. package/python/frontend_src/src/stores/chat.js +484 -0
  274. package/python/frontend_src/src/stores/model.js +104 -0
  275. package/python/frontend_src/src/stores/panels.js +330 -0
  276. package/python/frontend_src/src/styles/base.css +138 -0
  277. package/python/frontend_src/src/styles/panel.css +117 -0
  278. package/python/frontend_src/src/styles/tokens.css +78 -0
  279. package/python/frontend_src/vite.config.js +46 -0
  280. package/python/pyproject.toml +265 -0
  281. package/python/skills/README.md +123 -0
  282. package/python/skills/execution-contract/SKILL.md +30 -0
  283. package/python/skills/general-assistant/SKILL.md +34 -0
  284. package/python/skills/planner/SKILL.md +33 -0
  285. package/python/skills/schedule-itinerary/SKILL.md +35 -0
  286. package/python/skills/skill-composition/SKILL.md +31 -0
  287. package/python/skills/skill-guard/SKILL.md +67 -0
  288. package/python/skills/skill-memory/SKILL.md +50 -0
  289. package/python/skills/skill-router/SKILL.md +35 -0
  290. package/python/tests/conftest.py +76 -0
  291. package/python/tests/run_all.py +263 -0
  292. package/python/tests/test_agent_lifecycle.py +360 -0
  293. package/python/tests/test_ahs_p2.py +414 -0
  294. package/python/tests/test_anthropic_tools.py +206 -0
  295. package/python/tests/test_api.py +87 -0
  296. package/python/tests/test_api_auth.py +83 -0
  297. package/python/tests/test_api_host_guard.py +69 -0
  298. package/python/tests/test_attachments.py +167 -0
  299. package/python/tests/test_audit_chain.py +121 -0
  300. package/python/tests/test_audit_sink.py +136 -0
  301. package/python/tests/test_browser.py +153 -0
  302. package/python/tests/test_browser_request_guard.py +184 -0
  303. package/python/tests/test_capabilities.py +158 -0
  304. package/python/tests/test_channel_signing.py +192 -0
  305. package/python/tests/test_channel_trust.py +109 -0
  306. package/python/tests/test_channels.py +184 -0
  307. package/python/tests/test_cli.py +46 -0
  308. package/python/tests/test_compaction_toolpair.py +119 -0
  309. package/python/tests/test_config_layering.py +65 -0
  310. package/python/tests/test_confirm_nonce.py +197 -0
  311. package/python/tests/test_context_compaction_wiring.py +130 -0
  312. package/python/tests/test_dispatch.py +95 -0
  313. package/python/tests/test_domain_evals.py +403 -0
  314. package/python/tests/test_domain_import.py +78 -0
  315. package/python/tests/test_domain_reasoners.py +148 -0
  316. package/python/tests/test_e2e.py +178 -0
  317. package/python/tests/test_emb_cache.py +107 -0
  318. package/python/tests/test_embedding_singleton.py +89 -0
  319. package/python/tests/test_envfile.py +83 -0
  320. package/python/tests/test_evals_smoke.py +114 -0
  321. package/python/tests/test_event_spine.py +182 -0
  322. package/python/tests/test_evidence_semantic.py +101 -0
  323. package/python/tests/test_evidence_sse.py +97 -0
  324. package/python/tests/test_file_ingest.py +146 -0
  325. package/python/tests/test_finance.py +153 -0
  326. package/python/tests/test_finance_ledger.py +252 -0
  327. package/python/tests/test_finance_persist.py +178 -0
  328. package/python/tests/test_gateway.py +144 -0
  329. package/python/tests/test_governance.py +468 -0
  330. package/python/tests/test_governance_audit.py +359 -0
  331. package/python/tests/test_governance_middleware.py +143 -0
  332. package/python/tests/test_hkc.py +286 -0
  333. package/python/tests/test_hkc_index_build_manager.py +339 -0
  334. package/python/tests/test_hkc_manifest_reuse.py +317 -0
  335. package/python/tests/test_hkc_search_event_sync.py +533 -0
  336. package/python/tests/test_import_path_guard.py +161 -0
  337. package/python/tests/test_injection_guard.py +211 -0
  338. package/python/tests/test_integration.py +141 -0
  339. package/python/tests/test_itinerary.py +103 -0
  340. package/python/tests/test_kee_semantic.py +89 -0
  341. package/python/tests/test_kernel.py +222 -0
  342. package/python/tests/test_learning.py +110 -0
  343. package/python/tests/test_llm_admin.py +160 -0
  344. package/python/tests/test_llm_planner.py +366 -0
  345. package/python/tests/test_markdown_skills.py +73 -0
  346. package/python/tests/test_markdown_skills_openclaw.py +71 -0
  347. package/python/tests/test_mcp_connectors.py +109 -0
  348. package/python/tests/test_mcp_guardian_discovery.py +91 -0
  349. package/python/tests/test_mcp_no_memory.py +43 -0
  350. package/python/tests/test_mcp_policy_gate.py +77 -0
  351. package/python/tests/test_mcp_server.py +95 -0
  352. package/python/tests/test_memory_recall.py +95 -0
  353. package/python/tests/test_metrics.py +66 -0
  354. package/python/tests/test_multi_chain.py +102 -0
  355. package/python/tests/test_multimodal.py +135 -0
  356. package/python/tests/test_negotiation.py +146 -0
  357. package/python/tests/test_orchestration.py +119 -0
  358. package/python/tests/test_orchestration_decoupled.py +109 -0
  359. package/python/tests/test_p3_p5_hardening.py +338 -0
  360. package/python/tests/test_parallel_dispatch.py +215 -0
  361. package/python/tests/test_pipeline.py +533 -0
  362. package/python/tests/test_plan_visible.py +86 -0
  363. package/python/tests/test_planner.py +95 -0
  364. package/python/tests/test_policy_gate.py +109 -0
  365. package/python/tests/test_reasoner.py +185 -0
  366. package/python/tests/test_release_check.py +263 -0
  367. package/python/tests/test_resume_checkpoint.py +156 -0
  368. package/python/tests/test_routing.py +95 -0
  369. package/python/tests/test_scheduler.py +141 -0
  370. package/python/tests/test_search.py +204 -0
  371. package/python/tests/test_secrets.py +159 -0
  372. package/python/tests/test_semantic.py +78 -0
  373. package/python/tests/test_sessions.py +91 -0
  374. package/python/tests/test_settings.py +281 -0
  375. package/python/tests/test_shadow_learning.py +330 -0
  376. package/python/tests/test_shared_write.py +207 -0
  377. package/python/tests/test_skill_taint_gate.py +154 -0
  378. package/python/tests/test_skills.py +133 -0
  379. package/python/tests/test_stats_and_events.py +165 -0
  380. package/python/tests/test_streaming.py +60 -0
  381. package/python/tests/test_supervisor_pipeline.py +177 -0
  382. package/python/tests/test_supervisor_triage.py +108 -0
  383. package/python/tests/test_taint.py +117 -0
  384. package/python/tests/test_task_stream.py +85 -0
  385. package/python/tests/test_thinking_sse.py +67 -0
  386. package/python/tests/test_thinking_stream.py +76 -0
  387. package/python/tests/test_tool_center.py +129 -0
  388. package/python/tests/test_tool_parallel.py +113 -0
  389. package/python/tests/test_tool_params.py +103 -0
  390. package/python/tests/test_tool_use.py +242 -0
  391. package/python/tests/test_toplevel_concurrency.py +117 -0
  392. package/python/tests/test_tracing.py +290 -0
  393. package/python/tests/test_truth_crystallizer.py +127 -0
  394. package/python/tests/test_truth_flow.py +93 -0
  395. package/python/tests/test_url_policy.py +53 -0
  396. package/python/tests/test_verify_evidence.py +225 -0
  397. package/python/tests/test_version_sync.py +71 -0
  398. package/python/tests/test_web_trigger.py +99 -0
  399. package/python/tools/check_faiss_type.py +31 -0
  400. package/python/tools/check_log.ps1 +46 -0
  401. package/python/tools/ivf_status.py +72 -0
  402. package/python/tools/mypy_baseline.txt +5 -0
  403. package/python/tools/mypy_ratchet.py +97 -0
  404. package/python/tools/pack.py +178 -0
  405. package/python/tools/release_check.py +349 -0
  406. package/python/tools/restart_foxclaw.ps1 +74 -0
  407. package/python/tools/shared_write_audit.py +204 -0
  408. package/python/tools/size_baseline.txt +28 -0
  409. package/python/tools/size_check.py +102 -0
  410. package/python/tools/verify_env.bat +2 -0
  411. package/python/tools/verify_env.py +25 -0
  412. package/python/vendor/HMR_LICENSE +37 -0
  413. package/python/vendor/HMR_README.md +65 -0
  414. package/python/vendor/hkc/LICENSE +37 -0
  415. package/python/vendor/hkc/hkc_ace/__init__.py +0 -0
  416. package/python/vendor/hkc/hkc_ace/ace.py +457 -0
  417. package/python/vendor/hkc/hkc_ace/skill_taxonomy.json +465 -0
  418. package/python/vendor/hkc/hkc_api/__init__.py +0 -0
  419. package/python/vendor/hkc/hkc_api/container.py +416 -0
  420. package/python/vendor/hkc/hkc_api/ingest_jobs.py +103 -0
  421. package/python/vendor/hkc/hkc_api/main.py +157 -0
  422. package/python/vendor/hkc/hkc_api/routes/__init__.py +0 -0
  423. package/python/vendor/hkc/hkc_api/routes/ability.py +151 -0
  424. package/python/vendor/hkc/hkc_api/routes/config.py +51 -0
  425. package/python/vendor/hkc/hkc_api/routes/knowledge.py +630 -0
  426. package/python/vendor/hkc/hkc_api/routes/search.py +88 -0
  427. package/python/vendor/hkc/hkc_api/schemas.py +153 -0
  428. package/python/vendor/hkc/hkc_api/synthesis.py +179 -0
  429. package/python/vendor/hkc/hkc_core/__init__.py +0 -0
  430. package/python/vendor/hkc/hkc_core/graph/__init__.py +0 -0
  431. package/python/vendor/hkc/hkc_core/graph/base.py +188 -0
  432. package/python/vendor/hkc/hkc_core/graph/sqlite_store.py +789 -0
  433. package/python/vendor/hkc/hkc_core/models/__init__.py +0 -0
  434. package/python/vendor/hkc/hkc_core/models/enums.py +65 -0
  435. package/python/vendor/hkc/hkc_core/models/ku.py +307 -0
  436. package/python/vendor/hkc/hkc_core/utils/__init__.py +0 -0
  437. package/python/vendor/hkc/hkc_core/utils/id_gen.py +113 -0
  438. package/python/vendor/hkc/hkc_crystallizer/__init__.py +44 -0
  439. package/python/vendor/hkc/hkc_crystallizer/adapters.py +94 -0
  440. package/python/vendor/hkc/hkc_crystallizer/candidate.py +145 -0
  441. package/python/vendor/hkc/hkc_crystallizer/crystallizer.py +179 -0
  442. package/python/vendor/hkc/hkc_crystallizer/ingress.py +90 -0
  443. package/python/vendor/hkc/hkc_kde/__init__.py +0 -0
  444. package/python/vendor/hkc/hkc_kde/chunker.py +197 -0
  445. package/python/vendor/hkc/hkc_kde/extractor.py +623 -0
  446. package/python/vendor/hkc/hkc_kde/kde.py +298 -0
  447. package/python/vendor/hkc/hkc_kde/loaders/__init__.py +0 -0
  448. package/python/vendor/hkc/hkc_kde/loaders/ebook_loader.py +153 -0
  449. package/python/vendor/hkc/hkc_kde/loaders/office_loader.py +194 -0
  450. package/python/vendor/hkc/hkc_kde/loaders/pdf_loader.py +162 -0
  451. package/python/vendor/hkc/hkc_kde/loaders/text_loader.py +157 -0
  452. package/python/vendor/hkc/hkc_kde/loaders/web_loader.py +91 -0
  453. package/python/vendor/hkc/hkc_kde/models.py +74 -0
  454. package/python/vendor/hkc/hkc_kde/packager.py +329 -0
  455. package/python/vendor/hkc/hkc_kde/relation_disc.py +151 -0
  456. package/python/vendor/hkc/hkc_kee/__init__.py +0 -0
  457. package/python/vendor/hkc/hkc_kee/dedup.py +227 -0
  458. package/python/vendor/hkc/hkc_kee/fingerprint.py +106 -0
  459. package/python/vendor/hkc/hkc_kee/kee.py +541 -0
  460. package/python/vendor/hkc/hkc_kep/__init__.py +0 -0
  461. package/python/vendor/hkc/hkc_kep/event_bus.py +205 -0
  462. package/python/vendor/hkc/hkc_sdk/__init__.py +63 -0
  463. package/python/vendor/hkc/hkc_sdk/base.py +151 -0
  464. package/python/vendor/hkc/hkc_sdk/direct_client.py +153 -0
  465. package/python/vendor/hkc/hkc_sdk/exceptions.py +39 -0
  466. package/python/vendor/hkc/hkc_sdk/http_client.py +166 -0
  467. package/python/vendor/hkc/hkc_sdk/models.py +171 -0
  468. package/python/vendor/hkc/hkc_search/__init__.py +0 -0
  469. package/python/vendor/hkc/hkc_search/bm25.py +325 -0
  470. package/python/vendor/hkc/hkc_search/embedding_backends.py +374 -0
  471. package/python/vendor/hkc/hkc_search/graph_search.py +132 -0
  472. package/python/vendor/hkc/hkc_search/hybrid.py +331 -0
  473. package/python/vendor/hkc/hkc_search/index_build_manager.py +378 -0
  474. package/python/vendor/hkc/hkc_search/vector_search.py +484 -0
  475. package/python/vendor/hmr/__init__.py +0 -0
  476. package/python/vendor/hmr/core/__init__.py +0 -0
  477. package/python/vendor/hmr/core/hmr.py +684 -0
  478. package/python/vendor/hmr/core/models.py +310 -0
  479. package/python/vendor/hmr/engines/__init__.py +0 -0
  480. package/python/vendor/hmr/engines/evolution.py +710 -0
  481. package/python/vendor/hmr/engines/jit_compiler.py +472 -0
  482. package/python/vendor/hmr/engines/lifecycle.py +264 -0
  483. package/python/vendor/hmr/engines/policy.py +625 -0
  484. package/python/vendor/hmr/engines/recall.py +260 -0
  485. package/python/vendor/hmr/engines/runtime_state.py +158 -0
  486. package/python/vendor/hmr/engines/scheduler.py +323 -0
  487. package/python/vendor/hmr/engines/semantic.py +150 -0
  488. package/python/vendor/hmr/engines/temporal.py +261 -0
  489. package/python/vendor/hmr/engines/thought_chain.py +635 -0
  490. package/python/vendor/hmr/graph/__init__.py +0 -0
  491. package/python/vendor/hmr/graph/cwg.py +338 -0
  492. package/python/vendor/hmr/graph/memory_graph.py +530 -0
  493. package/python/vendor/hmr/storage/__init__.py +0 -0
  494. package/python/vendor/hmr/storage/memory_fs.py +300 -0
  495. package/python/vendor/hmr/storage/vector_store.py +520 -0
@@ -0,0 +1,230 @@
1
+ """
2
+ 主管专用 pipeline stages(P1-1)—— 把 SupervisorAgent 原 `_run_task_body` 的路由分发逻辑
3
+ 拆成可组合、可观测、可中断的 stage,使主管改走 base 统一模板 `_run_task_core`,从而获得
4
+ hooks / 流式(run_task_stream)/ stop_event 中断 / 统一 `_emit_execution` / blocked 顶层提升。
5
+
6
+ 设计约束(与文稿 P1-1 规格 + 现行为逐点对齐):
7
+ · stage 是操作 ctx 的可调用(async 函数 + stage_name 属性),与 cognitive_pipeline 既有约定一致;
8
+ · 只操作 ctx 与 agent 的既有方法(_route/_schedule/_plan_task/_dispatch_*),不新增语义;
9
+ · 分诊 prompt / 自答 prompt / 调度 think 文案全部**逐字搬移**自 agents.py 原实现,不改行为;
10
+ · 主管自答(无关键词命中+模型可用)是 route 的**决策**、dispatch 的**执行**——决策与执行分 stage,
11
+ 让 stop_event 在"决定自答"与"花钱调 LLM"之间有检查点;
12
+ · execution.completed 不再由 dispatch 手动 publish(改由 base 模板 `_emit_execution` 统一发,
13
+ 文稿 P1-1 Step 4);无总线时的手动兜底沉淀(_store_memory)保留(standalone/测试场景);
14
+ · 内部短路标记放 ctx.extra 的 `_meta_answered`/`_self_answer`,aggregate 负责清除,不外泄到返回体。
15
+ """
16
+ import asyncio
17
+ from datetime import datetime
18
+
19
+ from .cognitive_pipeline import PipelineContext, stage_recall # noqa: F401 (recall 复用默认 stage)
20
+ from ..core.dispatch import dispatch_failed # 谓词在 core:避免 pipeline 反向 import agents
21
+
22
+
23
+ # ── Stage 2: meta_check —— 元问题判断(你是谁/能做什么 → 主管以总管身份自答,不分派)──
24
+ async def sv_meta_check(ctx: PipelineContext) -> None:
25
+ agent = ctx.agent
26
+ if not agent._is_meta(ctx.task):
27
+ return
28
+ agent._think(
29
+ content=f"元问题「{ctx.task}」由主管直接回答(系统介绍,不分派)",
30
+ node_type="decision", conf=0.92,
31
+ )
32
+ subagents = getattr(agent, "_subagents", {})
33
+ # 拓扑 5 → 3:助手/行政已下放为指令技能,不再出现在团队介绍里。
34
+ sub_names = {
35
+ "finance": "财务 Agent(理财、收支、预算、风控)",
36
+ "office": "办公学习 Agent(学习、知识管理、研究、写作、日程与行程)",
37
+ }
38
+ available = [v for k, v in sub_names.items() if k in subagents]
39
+ team = "\n".join(f"· {x}" for x in available) if available else "(子 Agent 尚未注册)"
40
+ intro_prompt = (
41
+ f"你是 Foxclaw 的主管 Agent(Supervisor),负责统筹协调整个智能体团队。"
42
+ f"用户问:「{ctx.task}」。请以主管身份友好、简洁地直接回答用户这个问题。\n"
43
+ f"背景信息(供你回答时参考):\n"
44
+ f"- 你统领的团队:\n{team}\n"
45
+ f"- 关于联网:Foxclaw 已接入百度 AI 搜索,具备联网查询实时信息的能力"
46
+ f"(当用户问最新版本、新闻、价格等时会自动联网)。所以如果用户问你能不能联网,"
47
+ f"应回答「可以联网」,而不是说没有联网能力。\n"
48
+ f"请针对用户的实际问题回答,控制在 150 字内,口吻像专业总管,不要自称助手,"
49
+ f"不要答非所问地只做自我介绍。"
50
+ )
51
+ reply = await agent._call_llm([{"role": "user", "content": intro_prompt}])
52
+ agent._store_memory(
53
+ content=f"主管自答元问题「{ctx.task}」",
54
+ mem_type="decision",
55
+ title=f"主管介绍_{datetime.now().strftime('%Y%m%d_%H%M')}",
56
+ shared=True,
57
+ )
58
+ ctx.result = {"dispatched_to": "supervisor", "sub_result": reply,
59
+ "self_answered": True}
60
+ ctx.extra["_meta_answered"] = True # 短路标记:后续 stage 跳过(aggregate 清除)
61
+ sv_meta_check.stage_name = "meta_check"
62
+
63
+
64
+ # ── Stage 3: schedule —— 认知调度(优先级/负载/抢占)────────────────
65
+ async def sv_schedule(ctx: PipelineContext) -> None:
66
+ if ctx.extra.get("_meta_answered"):
67
+ return
68
+ agent = ctx.agent
69
+ subagents = getattr(agent, "_subagents", {})
70
+ ctx.sched = agent._schedule(ctx.task, subagents)
71
+ agent._think(
72
+ content=(f"认知调度:{ctx.sched.reason}"
73
+ + (f" · 抢占({ctx.sched.preemption_reason})" if ctx.sched.preemptible else "")),
74
+ node_type="decision", conf=0.9,
75
+ )
76
+ sv_schedule.stage_name = "schedule"
77
+
78
+
79
+ # ── Stage 4: route —— 路由决策(关键词+调度意图;未命中且模型可用 → 决策主管自答)──
80
+ async def sv_route(ctx: PipelineContext) -> None:
81
+ if ctx.extra.get("_meta_answered"):
82
+ return
83
+ agent = ctx.agent
84
+ target = agent._route(ctx.task, ctx.sched)
85
+ ctx.target = target
86
+ ctx.route = {"target": target, "reason": "主管关键词+调度意图分诊"}
87
+ # 无关键词命中 = 正面对话/泛化问题 → 主管**直接自答一次**,省掉"LLM 分诊 + 派发"的
88
+ # 第二次串行 LLM 调用(每次约 5s 网络往返,正面对话不值当)。只有命中领域关键词
89
+ # (财务/行政/办公/…)才派发给对应子 Agent。stub 下保持原路径(零额外调用、离线测试确定)。
90
+ # 权衡:少数本该派给领域 Agent 的"无关键词"任务会由主管自答——用户明确选速度优先。
91
+ # (本 stage 只做**决策**置标记;自答的 LLM 调用在 dispatch stage 执行。)
92
+ if not agent._has_keyword_hit(ctx.task) and not agent.router.is_stub:
93
+ agent._think(
94
+ content="无关键词命中 → 主管直接自答(省掉二次分诊派发,正面对话提速)",
95
+ node_type="decision", conf=0.88)
96
+ ctx.extra["_self_answer"] = True
97
+ return
98
+ agent._think(
99
+ content=f"任务分诊:「{ctx.task}」→ 派给 {target} Agent。已有相关记忆 {len(ctx.memories)} 条",
100
+ node_type="decision",
101
+ conf=0.9,
102
+ )
103
+ sv_route.stage_name = "route"
104
+
105
+
106
+ # ── Stage 5: plan —— planner 拆解(多步?跨 Agent?)────────────────
107
+ async def sv_plan(ctx: PipelineContext) -> None:
108
+ if ctx.extra.get("_meta_answered") or ctx.extra.get("_self_answer"):
109
+ return
110
+ agent = ctx.agent
111
+ # P0-1:LLM 规划含同步网络调用(call_tools),放线程池避免阻塞事件循环;
112
+ # 规则拆解路径是纯 CPU,放线程池开销可忽略、行为不变。
113
+ plan_obj, step_routes = await asyncio.to_thread(agent._plan_task, ctx.task, ctx.sched)
114
+ if plan_obj is not None:
115
+ routes_desc = " → ".join(f"{name}[{ag}]" for name, ag in step_routes)
116
+ agent._think(
117
+ content=f"planner 判定为多步任务({len(plan_obj.tasks)}步):{routes_desc}",
118
+ node_type="decision", conf=0.85,
119
+ )
120
+ # 若子任务跨不同 Agent,标记为多 Agent 顺序执行
121
+ distinct_agents = {ag for _, ag in step_routes}
122
+ ctx.multi_agent = len(distinct_agents) > 1
123
+ else:
124
+ ctx.multi_agent = False
125
+ ctx.plan_obj = plan_obj
126
+ ctx.step_routes = step_routes or []
127
+ sv_plan.stage_name = "plan"
128
+
129
+
130
+ # ── Stage 6: dispatch —— 分发执行(自答 / 多 Agent / 单 Agent / 兜底)──
131
+ async def sv_dispatch(ctx: PipelineContext) -> None:
132
+ agent = ctx.agent
133
+ if ctx.extra.get("_meta_answered"):
134
+ return
135
+
136
+ # 主管自答(route stage 的决策在此执行)
137
+ if ctx.extra.get("_self_answer"):
138
+ _msgs = [{"role": "user", "content": (
139
+ f"你是 Foxclaw 的主管 Agent(Supervisor),统筹整个智能体团队。"
140
+ f"用户说:「{ctx.task}」。请以主管身份**直接、友好、专业**地回答,针对用户的实际"
141
+ f"问题作答,不要只做自我介绍、不要答非所问。若确属需要专门领域深办的任务,可"
142
+ f"简要引导用户去对应能力,但仍先给出有用的直接回应。"
143
+ )}]
144
+ # 联网增强:新闻/实时类(_web_augment 内 needs_search 命中)走百度搜索注入真实结果
145
+ # 再作答,避免主管凭空编造(系统 prompt 明令严禁模拟搜索)。纯闲聊 needs_search=False,
146
+ # 不搜、不减速。阻塞 I/O 放线程池(与 base._execute 同法),不堵 async 事件循环。
147
+ _msgs = await asyncio.to_thread(agent._web_augment, ctx.task, _msgs)
148
+ reply = await agent._call_llm(_msgs)
149
+ agent._store_memory(
150
+ content=f"主管自答正面对话「{ctx.task}」", mem_type="decision",
151
+ title=f"主管自答_{datetime.now().strftime('%Y%m%d_%H%M')}", shared=True)
152
+ ctx.dispatch_result = {
153
+ "result": {"dispatched_to": "supervisor", "sub_result": reply,
154
+ "self_answered": True},
155
+ }
156
+ return
157
+
158
+ subagents = getattr(agent, "_subagents", {})
159
+
160
+ # 多 Agent 执行:并行计划(无依赖)→ fork-join 并发;否则顺序执行后汇聚
161
+ if ctx.multi_agent and ctx.step_routes:
162
+ if getattr(ctx.plan_obj, "flow_type", "sequential") == "parallel":
163
+ agent._think(
164
+ content=f"计划为并行({len(ctx.step_routes)}个无依赖子任务)→ fork-join 并发调度",
165
+ node_type="decision", conf=0.88,
166
+ )
167
+ ctx.dispatch_result = await agent._dispatch_parallel(
168
+ ctx.task, ctx.step_routes, ctx.sched, ctx.session_id,
169
+ subagents, ctx.context, ctx.memories)
170
+ else:
171
+ ctx.dispatch_result = await agent._dispatch_multi(
172
+ ctx.task, ctx.step_routes, ctx.sched, ctx.session_id,
173
+ subagents, ctx.context, ctx.memories)
174
+ return
175
+
176
+ # 单 Agent 分发
177
+ if ctx.target in subagents:
178
+ ctx.dispatch_result = await agent._dispatch_single(
179
+ ctx.task, ctx.target, ctx.sched, ctx.session_id,
180
+ subagents, ctx.context, ctx.memories, ctx.plan_obj)
181
+ return
182
+
183
+ # 子 Agent 不可用(未注册)→ 主管自处理
184
+ ctx.dispatch_result = await agent._dispatch_fallback(ctx.task, ctx.context)
185
+ sv_dispatch.stage_name = "dispatch"
186
+
187
+
188
+ # ── Stage 6.5: replan —— 反思重规划(P0-1:失败步骤 → LLM 反思 → 修正计划 → 重执行)──
189
+ async def sv_replan(ctx: PipelineContext) -> None:
190
+ if ctx.extra.get("_meta_answered") or ctx.extra.get("_self_answer"):
191
+ return
192
+ agent = ctx.agent
193
+ dr = ctx.dispatch_result or {}
194
+ if not dispatch_failed(dr):
195
+ return # 无失败/未完成步骤 → 无需重规划
196
+ if not agent._replan_enabled():
197
+ return # opt-in 关 / 不支持 tools → 保留原结果(fail-safe)
198
+ subagents = getattr(agent, "_subagents", {})
199
+ max_rounds = int((ctx.context or {}).get("replan_max_rounds", 2))
200
+ ctx.dispatch_result = await agent._replan_and_redo(
201
+ ctx.task, ctx.sched, ctx.session_id, subagents, ctx.context, ctx.memories,
202
+ dr, max_rounds=max_rounds)
203
+ sv_replan.stage_name = "replan"
204
+
205
+
206
+ # ── Stage 7: aggregate —— 结果组装(dispatch_result → ctx.result/extra)──
207
+ async def sv_aggregate(ctx: PipelineContext) -> None:
208
+ # 清除内部短路标记,不外泄到返回体(它们会经 **ctx.extra 透传进 resp)
209
+ ctx.extra.pop("_meta_answered", None)
210
+ ctx.extra.pop("_self_answer", None)
211
+ dr = ctx.dispatch_result or {}
212
+ if not dr:
213
+ return # meta 路径:result 已在 meta_check 设好
214
+ ctx.result = dr.get("result", ctx.result)
215
+ # 白名单透传(与原主管返回体逐字一致的顶层键);session_id/agent/thought_summary
216
+ # 由 base 模板统一组装,不从 dispatch_result 取。
217
+ for k in ("schedule", "plan", "process_id"):
218
+ if k in dr:
219
+ ctx.extra[k] = dr[k]
220
+ # 「未完成 ⇒ 不作为已完成真值沉淀」:任一子任务被 Harness 拦截 → 提升为顶层
221
+ # status=blocked(与 base 模板的 skill_status 提升同一机制),执行事件 blocked=True,
222
+ # 真值链不沉淀(呼应 P3-5)。成功/自答路径不带此键,行为逐字不变。
223
+ if isinstance(ctx.result, dict) and ctx.result.get("blocked"):
224
+ ctx.extra["skill_status"] = "blocked"
225
+ sv_aggregate.stage_name = "aggregate"
226
+
227
+
228
+ # 主管 pipeline 的 stage 序列(recall 复用默认实现:与旧 `self._recall_memory(task)` 逐字等价)
229
+ SUPERVISOR_STAGES = [stage_recall, sv_meta_check, sv_schedule, sv_route,
230
+ sv_plan, sv_dispatch, sv_replan, sv_aggregate]
@@ -0,0 +1,17 @@
1
+ """
2
+ foxclaw.planning —— 统一规划原语(P1-2)。
3
+
4
+ 三处路由(主管→子Agent / AHS→skill / search→数据源)共用的机制单一真源:
5
+ 否定词检测、标点拆分、marker 常量、触发词打分。
6
+ """
7
+ from .planner import (
8
+ NEGATIONS, SPLIT_MARKERS, PARALLEL_TRIGGERS, LIST_SEPS,
9
+ is_negated, split_on, keyword_hits,
10
+ ScoredCandidate, TriggerRoute, trigger_route,
11
+ )
12
+
13
+ __all__ = [
14
+ "NEGATIONS", "SPLIT_MARKERS", "PARALLEL_TRIGGERS", "LIST_SEPS",
15
+ "is_negated", "split_on", "keyword_hits",
16
+ "ScoredCandidate", "TriggerRoute", "trigger_route",
17
+ ]
@@ -0,0 +1,232 @@
1
+ """
2
+ LLM 驱动规划器(P0-1)—— 把主管的多步任务拆解从「规则拆分」升级为 LLM function-calling 规划。
3
+
4
+ 病根(与 docs/internal/TOOL_USE_PLAN.md 同源):planning/planner.py 的拆解靠
5
+ 标点/连接词/触发词("然后""同时""接着"),只能做表面切分,无法理解任务的语义结构;
6
+ LLM 从没机会决定「这个任务该拆成几步、每步派给哪个 Agent」。于是「帮我查最近一周支出,
7
+ 再据此定下月预算,最后整理成邮件」这类真实任务,规则拆解经常切错边界、派错 Agent。
8
+
9
+ 本模块给出单一真源:把「规划」声明成一个 function-calling 工具(submit_plan),交给
10
+ LLM 自主拆解,返回结构化计划(每步带目标 Agent 与依赖),由 SupervisorAgent 转成
11
+ Plan 兼容对象。
12
+
13
+ 后半段「反思重规划」:多步执行出现失败/未完成时,再调一次 submit_plan,把上一轮
14
+ 各步结果作为反馈喂给 LLM,让它决定「换 Agent / 换表述 / 拆更细 / 补前置 / 放弃」,
15
+ 产出修正计划并重新执行(llm_replan)。这就是 plan → 执行 → 反思 → 重规划的闭环。
16
+
17
+ opt-in + fail-safe(与 FOXCLAW_TOOL_USE 同哲学,默认关、可逆):
18
+ · FOXCLAW_LLM_PLAN=1 才开启初次规划;
19
+ · FOXCLAW_LLM_REPLAN=1 才开启反思重规划;
20
+ · provider 不支持 tools / 调用失败 / 模型未产出有效计划 → 返回 None,
21
+ 上层自动回退(规划回退规则拆解;重规划回退「保留原结果」)。
22
+ """
23
+ from __future__ import annotations
24
+
25
+ import os
26
+
27
+
28
+ def _env_flag(name: str) -> bool:
29
+ return os.environ.get(name, "0").strip() in ("1", "true", "True", "yes", "on")
30
+
31
+
32
+ def llm_plan_enabled() -> bool:
33
+ """是否启用 LLM 规划。默认关(behavior-preserving / 可逆)。"""
34
+ return _env_flag("FOXCLAW_LLM_PLAN")
35
+
36
+
37
+ def replan_enabled() -> bool:
38
+ """是否启用反思重规划。默认关(behavior-preserving / 可逆)。"""
39
+ return _env_flag("FOXCLAW_LLM_REPLAN")
40
+
41
+
42
+ _PLAN_SYSTEM = (
43
+ "你是 Foxclaw 的任务规划器。唯一职责:把用户任务拆解成可执行计划,"
44
+ "并为每个子任务指派最合适的 Agent。\n"
45
+ "规则:\n"
46
+ "1. 只拆解真正需要多步或跨领域协作的任务;简单单步任务就返回单个 step。\n"
47
+ "2. 每个子任务独立可执行、边界清晰、动词开头、不含模糊表述。\n"
48
+ "3. 子任务有先后依赖(后一步要用前一步的结果)→ flow=sequential;"
49
+ "彼此独立可同时做 → flow=parallel。\n"
50
+ "4. 必须通过 submit_plan 工具提交计划,不要输出计划之外的任何文本。\n\n"
51
+ "可派发的 Agent 及其职责:\n{agent_desc}"
52
+ )
53
+
54
+ _REPLAN_SYSTEM = (
55
+ "你是 Foxclaw 的反思规划器。上一轮多步计划的执行结果已经给出,其中存在失败或未完成的步骤。"
56
+ "你的唯一职责:分析失败原因,产出修正后的计划,让任务最终完成。\n"
57
+ "规则:\n"
58
+ "1. 只重排**失败/未完成**的步骤,已经成功的步骤不要重复执行。\n"
59
+ "2. 可采取的修正手段:换 Agent、换表述、拆得更细、补前置步骤、合并步骤。\n"
60
+ "3. 若确认任务无法完成(超出能力 / 信息不足 / 反复失败),返回空 steps 数组表示放弃。\n"
61
+ "4. 必须通过 submit_plan 工具提交计划,不要输出计划之外的任何文本。\n\n"
62
+ "可派发的 Agent 及其职责:\n{agent_desc}"
63
+ )
64
+
65
+
66
+ def build_plan_tool(agent_catalog: dict) -> dict:
67
+ """构造 submit_plan 的 function schema(OpenAI tools 通用格式)。
68
+
69
+ agent_catalog: {agent_id: 职责描述},用于约束 agent 字段的取值枚举,
70
+ 让 LLM 只能在真实存在的 Agent 里选,避免幻觉出不存在的能力。
71
+
72
+ 工具描述保持中性(planning / replanning 共用同一工具),具体行为由
73
+ system prompt(_PLAN_SYSTEM 或 _REPLAN_SYSTEM)引导。
74
+ """
75
+ agent_ids = list(agent_catalog.keys())
76
+ return {
77
+ "type": "function",
78
+ "function": {
79
+ "name": "submit_plan",
80
+ "description": (
81
+ "提交任务执行计划(planning/replanning 共用)。steps 为子任务列表,"
82
+ "每步含 name(一句话、动词开头) / agent(负责的 agent id) / depends_on"
83
+ "(依赖的前序步骤序号,0-based)。flow 为执行模式。若无需拆解或确认"
84
+ "无法完成,返回空 steps。"
85
+ ),
86
+ "parameters": {
87
+ "type": "object",
88
+ "properties": {
89
+ "flow": {
90
+ "type": "string",
91
+ "enum": ["single", "sequential", "parallel"],
92
+ "description": "single=单步;sequential=步骤有先后依赖;"
93
+ "parallel=步骤彼此独立可并行",
94
+ },
95
+ "steps": {
96
+ "type": "array",
97
+ "description": "子任务列表(flow=single 时仅 1 项)",
98
+ "items": {
99
+ "type": "object",
100
+ "properties": {
101
+ "name": {
102
+ "type": "string",
103
+ "description": "子任务的一句话描述,动词开头",
104
+ },
105
+ "agent": {
106
+ "type": "string",
107
+ "enum": agent_ids,
108
+ "description": "负责该子任务的 agent id",
109
+ },
110
+ "depends_on": {
111
+ "type": "array",
112
+ "items": {"type": "integer"},
113
+ "description": "依赖的前序步骤序号(0-based);"
114
+ "无依赖传空数组",
115
+ },
116
+ },
117
+ "required": ["name", "agent"],
118
+ },
119
+ },
120
+ },
121
+ "required": ["flow", "steps"],
122
+ },
123
+ },
124
+ }
125
+
126
+
127
+ def _agent_desc(agent_catalog: dict) -> str:
128
+ return "\n".join(f"- {a}:{d}" for a, d in agent_catalog.items())
129
+
130
+
131
+ def _invoke_plan_tool(router, system: str, user_content: str, agent_catalog: dict,
132
+ max_tokens: int):
133
+ """调用 submit_plan 工具并解析为 {intent, flow, steps}。
134
+
135
+ 返回 None = 不可用 / 不支持 tools / 调用失败 / 参数解析失败。
136
+ 成功时 steps 可能为空列表(模型判定「无需拆解」或「放弃」),由上层决定含义。
137
+ """
138
+ if not agent_catalog or not user_content or not str(user_content).strip():
139
+ return None
140
+ try:
141
+ tool = build_plan_tool(agent_catalog)
142
+ res = router.call_tools(
143
+ system,
144
+ [{"role": "user", "content": str(user_content).strip()}],
145
+ [tool],
146
+ tool_choice="auto",
147
+ max_tokens=max_tokens,
148
+ )
149
+ except Exception:
150
+ return None
151
+ if not res.get("supported"):
152
+ return None
153
+ calls = res.get("tool_calls") or []
154
+ if not calls:
155
+ return None # 模型未调用工具 → 不可用,回退
156
+ if calls[0].get("args_parse_failed"):
157
+ return None # 参数 JSON 解析失败 → 不可信,回退
158
+ args = calls[0].get("args") or {}
159
+ steps = args.get("steps")
160
+ if not isinstance(steps, list):
161
+ return None
162
+ norm = []
163
+ for s in steps:
164
+ if not isinstance(s, dict):
165
+ continue
166
+ name = str(s.get("name") or "").strip()
167
+ if not name:
168
+ continue
169
+ agent = str(s.get("agent") or "office").strip()
170
+ if agent not in agent_catalog:
171
+ agent = "office" # 幻觉出不存在的 Agent(含已下放的 assistant/admin)→ 兜底办公学习
172
+ dep = s.get("depends_on") or []
173
+ if not isinstance(dep, list):
174
+ dep = []
175
+ norm.append({
176
+ "name": name,
177
+ "agent": agent,
178
+ "depends_on": [int(d) for d in dep if isinstance(d, (int, float))],
179
+ })
180
+ flow = str(args.get("flow") or "sequential").strip()
181
+ if flow not in ("single", "sequential", "parallel"):
182
+ flow = "sequential"
183
+ if len(norm) == 1:
184
+ flow = "single"
185
+ return {"intent": str(user_content).strip(), "flow": flow, "steps": norm}
186
+
187
+
188
+ def llm_plan(router, task: str, agent_catalog: dict, max_tokens: int = 1024):
189
+ """初次规划:用 function-calling 让 LLM 产出结构化计划。
190
+
191
+ 返回 {intent, flow, steps: [{name, agent, depends_on}]};
192
+ 返回 None = 不可用 / 不支持 tools / 调用失败 / 模型未产出有效计划(上层回退规则拆解)。
193
+ """
194
+ out = _invoke_plan_tool(
195
+ router,
196
+ _PLAN_SYSTEM.format(agent_desc=_agent_desc(agent_catalog)),
197
+ task, agent_catalog, max_tokens)
198
+ if not out or not out["steps"]:
199
+ return None
200
+ return out
201
+
202
+
203
+ def _format_prior_results(prior_results) -> str:
204
+ """把上一轮各步结果序列化为 LLM 可读的反馈文本。"""
205
+ if not prior_results:
206
+ return "(无执行结果)"
207
+ lines = []
208
+ for i, s in enumerate(prior_results, 1):
209
+ status = "失败" if (s.get("blocked") or s.get("failed")) else "成功"
210
+ lines.append(
211
+ f"第{i}步[{s.get('agent', '?')}]({status}):{s.get('subtask', '')}\n"
212
+ f" 输出:{s.get('output', '')}")
213
+ return "\n".join(lines)
214
+
215
+
216
+ def llm_replan(router, task: str, prior_results, agent_catalog: dict,
217
+ max_tokens: int = 1024):
218
+ """反思重规划:把上一轮执行结果作为反馈,让 LLM 产出修正计划。
219
+
220
+ prior_results: 上一轮各步结果列表,形如 [{subtask, agent, output, blocked, failed}, ...]
221
+ 返回同 llm_plan 的结构;steps 为空 = 模型判定「放弃/无需重试」;
222
+ 返回 None = 不可用 / 不支持 / 失败 / 解析失败(上层保留原结果)。
223
+ """
224
+ feedback = _format_prior_results(prior_results)
225
+ user_content = (
226
+ f"原始任务:{task}\n\n"
227
+ f"上一轮执行结果:\n{feedback}\n\n"
228
+ f"请反思失败原因,产出修正后的计划(若确认无法完成则返回空 steps)。")
229
+ return _invoke_plan_tool(
230
+ router,
231
+ _REPLAN_SYSTEM.format(agent_desc=_agent_desc(agent_catalog)),
232
+ user_content, agent_catalog, max_tokens)
@@ -0,0 +1,99 @@
1
+ """
2
+ 统一规划原语(P1-2:三个 Planner 合并为一个引擎)。
3
+
4
+ 审计结论:主管路由(→子Agent)、AHS(→skill)、search QueryPlanner(→数据源)三处
5
+ 各自重复实现了同一套机制——否定词检测、标点拆分、marker 常量、触发词打分。
6
+ 本模块把这些机制收敛为**单一真源**,三处保留各自的公共入口与候选空间,内部委托到这里。
7
+
8
+ 不假设三者路由到同一目标:它们的候选集不同(agent/skill/source),但"怎么从文本里
9
+ 匹配、拆分、打分选候选"是同一套逻辑,这一套只应有一处定义。
10
+ """
11
+ from __future__ import annotations
12
+ import re
13
+ from dataclasses import dataclass, field
14
+
15
+ # ── 单一真源:否定词 + 顺序/并行/列表 标记 ──────────────────────
16
+ NEGATIONS = ["不", "别", "无需", "不要", "取消", "甭", "don't", "no need", "without"]
17
+ # 顺序拆分信号(明确的顺序连接词;不用"和/并"等高风险常用字,避免误拆名词短语)
18
+ SPLIT_MARKERS = ["然后", "接着", "之后", "再", "最后", ";", ";"]
19
+ # 并行信号:出现这些"同时性"连接词 → 子任务彼此无依赖,可并行 fork
20
+ PARALLEL_TRIGGERS = ["同时", "分别", "并行", "一并"]
21
+ # 列表分隔符(仅在检出并行触发词时才当拆分点,避免误拆普通名词短语)
22
+ LIST_SEPS = ["、", ",", ",", ";", ";"]
23
+
24
+
25
+ def is_negated(task: str, trigger: str, window: int = 4) -> bool:
26
+ """触发词在任务中是否被否定词紧邻(前 window 字)修饰。"""
27
+ idx = (task or "").find(trigger)
28
+ if idx < 0:
29
+ return False
30
+ prefix = task[max(0, idx - window):idx]
31
+ return any(neg in prefix for neg in NEGATIONS)
32
+
33
+
34
+ def split_on(task: str, markers: list) -> list:
35
+ """按 markers 把文本切成非空片段(去空白)。"""
36
+ pat = "|".join(map(re.escape, markers))
37
+ return [p.strip() for p in re.split(pat, task or "") if p.strip()]
38
+
39
+
40
+ def keyword_hits(task: str, keywords: list, negation: bool = False) -> int:
41
+ """命中的关键词数(negation=True 时排除被否定词修饰的命中)。
42
+ 主管路由用 negation=False(保持原计数行为);AHS 触发打分用 negation=True。"""
43
+ task = task or ""
44
+ return sum(1 for kw in keywords
45
+ if kw in task and not (negation and is_negated(task, kw)))
46
+
47
+
48
+ @dataclass
49
+ class ScoredCandidate:
50
+ """打分候选:id + 触发词 + 描述(领域匹配)。三处把各自候选适配成它。"""
51
+ id: str
52
+ triggers: list = field(default_factory=list)
53
+ description: str = ""
54
+
55
+
56
+ @dataclass
57
+ class TriggerRoute:
58
+ primary: str = None
59
+ confidence: float = 0.0
60
+ candidates: list = field(default_factory=list) # [(id, score)]
61
+ fallback: str = None
62
+ needs_composition: bool = False
63
+ reason: str = ""
64
+
65
+
66
+ def trigger_route(task: str, candidates: list, fallback_id: str = None,
67
+ min_conf: float = 0.15, near_gap: float = 0.1) -> TriggerRoute:
68
+ """触发词路由三步打分:否定过滤后的触发命中(0.8)+ 描述领域匹配(0.2),确定性排序。
69
+ 这是 AHS 原 route 的算法,现作为三处共用的通用引擎。"""
70
+ if not candidates:
71
+ return TriggerRoute(reason="候选为空")
72
+
73
+ scored = []
74
+ for c in candidates:
75
+ triggers = c.triggers or []
76
+ if not triggers:
77
+ scored.append((c.id, 0.0)); continue
78
+ # 第1步:否定过滤后的触发命中数 → 命中即给强基础分,不被触发词总数稀释
79
+ hits = keyword_hits(task, triggers, negation=True)
80
+ hit_score = 0.0 if hits == 0 else min(0.6 + 0.15 * (hits - 1), 1.0)
81
+ # 第2步:领域匹配(描述里的词出现在任务中,粗略语义近似)
82
+ desc_words = [w for w in re.split(r"[\s,,、]", c.description or "") if len(w) >= 2]
83
+ domain_hit = sum(1 for w in desc_words if w in task)
84
+ domain_score = min(domain_hit / max(len(desc_words), 1), 1.0)
85
+ score = 0.8 * hit_score + 0.2 * domain_score
86
+ scored.append((c.id, round(score, 3)))
87
+
88
+ scored.sort(key=lambda x: (-x[1], x[0])) # 分降序,平局按 id 字典序(确定性)
89
+ top_id, top_score = scored[0]
90
+ cands = [(i, s) for i, s in scored if s > 0]
91
+
92
+ fb = fallback_id or candidates[-1].id
93
+ if top_score < min_conf:
94
+ return TriggerRoute(primary=fb, confidence=top_score, candidates=cands,
95
+ fallback=fb, reason="无显著匹配,使用兜底")
96
+ near = [c for c in cands if abs(c[1] - top_score) < near_gap]
97
+ return TriggerRoute(primary=top_id, confidence=top_score, candidates=cands,
98
+ fallback=fb, needs_composition=len(near) > 1,
99
+ reason=f"主候选 {top_id}(分 {top_score})")
@@ -0,0 +1,24 @@
1
+ """Foxclaw — Search: 认知搜索内核 (HSR)
2
+ 统一内部记忆(HMR)与外部联网检索,供五个 Agent 共享调用。"""
3
+ from .models import (
4
+ RouteType, SourceType, SubQuery, SearchPlan,
5
+ RawEvidence, EvidenceNode, EvidenceEdge, EvidenceGraph,
6
+ StrategyDecision, SearchResult,
7
+ )
8
+ from .planner import QueryPlanner
9
+ from .internal_search import InternalSearchEngine
10
+ from .external_search import BaiduWebSearchPort
11
+ from .hkc_read_port import HKCReadPort
12
+ from .agent_port import AgentSourcePort
13
+ from .retriever import Retriever
14
+ from .evidence import EvidenceFusion
15
+ from .strategy import SearchStrategy
16
+ from .runtime import SearchRuntime
17
+
18
+ __all__ = [
19
+ "RouteType", "SourceType", "SubQuery", "SearchPlan",
20
+ "RawEvidence", "EvidenceNode", "EvidenceEdge", "EvidenceGraph",
21
+ "StrategyDecision", "SearchResult",
22
+ "QueryPlanner", "InternalSearchEngine", "BaiduWebSearchPort", "HKCReadPort",
23
+ "AgentSourcePort", "Retriever", "EvidenceFusion", "SearchStrategy", "SearchRuntime",
24
+ ]