foxclaw 0.1.2 → 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.
- package/index.js +1 -8
- package/package.json +1 -1
- package/python/.env.example +8 -0
- package/python/.github/workflows/ci.yml +47 -2
- package/python/CHANGELOG.md +57 -0
- package/python/CHANGELOG_CN.md +57 -0
- package/python/README.md +11 -2
- package/python/README_CN.md +32 -20
- package/python/bench/bm25_audit.py +1 -14
- package/python/bench/bm25_regression.py +0 -1
- package/python/bench/bm25_stress.py +0 -4
- package/python/bench/cd_audit.py +0 -23
- package/python/bench/chaos.py +6 -8
- package/python/bench/e2e_real_models.py +0 -1
- package/python/bench/llm_retry_test.py +2 -9
- package/python/bench/load_test.py +5 -8
- package/python/bench/vector_ivf_upgrade.py +1 -7
- package/python/docs/RELEASE.md +4 -3
- package/python/docs/RUN_CN.md +2 -2
- package/python/evals/governance/cases.py +0 -37
- package/python/evals/governance/run_evals.py +5 -37
- package/python/examples/governance_middleware_demo.py +1 -6
- package/python/examples/langgraph_governance_demo.py +7 -11
- package/python/foxclaw/__init__.py +34 -152
- package/python/foxclaw/agents/base.py +65 -136
- package/python/foxclaw/agents/impl/_helpers.py +2 -10
- package/python/foxclaw/agents/impl/finance.py +4 -21
- package/python/foxclaw/agents/impl/office.py +7 -24
- package/python/foxclaw/agents/impl/supervisor.py +12 -79
- package/python/foxclaw/agents/mixins/_context.py +0 -23
- package/python/foxclaw/agents/mixins/augment.py +10 -16
- package/python/foxclaw/agents/mixins/llm.py +7 -6
- package/python/foxclaw/agents/mixins/memory.py +7 -1
- package/python/foxclaw/agents/mixins/preference.py +58 -8
- package/python/foxclaw/agents/mixins/prompt.py +0 -6
- package/python/foxclaw/agents/mixins/skillbox.py +6 -6
- package/python/foxclaw/agents/mixins/tool_exec.py +16 -42
- package/python/foxclaw/api/__init__.py +18 -77
- package/python/foxclaw/api/_state.py +43 -0
- package/python/foxclaw/api/{app.py → bootstrap.py} +5 -32
- package/python/foxclaw/api/middleware.py +5 -28
- package/python/foxclaw/api/routes/ahs.py +60 -73
- package/python/foxclaw/api/routes/attachments.py +5 -6
- package/python/foxclaw/api/routes/browser.py +7 -11
- package/python/foxclaw/api/routes/domain.py +18 -26
- package/python/foxclaw/api/routes/knowledge.py +3 -21
- package/python/foxclaw/api/routes/llm_admin.py +21 -33
- package/python/foxclaw/api/routes/memory.py +28 -12
- package/python/foxclaw/api/routes/orchestration.py +3 -5
- package/python/foxclaw/api/routes/recovery.py +4 -3
- package/python/foxclaw/api/routes/review.py +2 -11
- package/python/foxclaw/api/routes/search.py +3 -7
- package/python/foxclaw/api/routes/system.py +33 -49
- package/python/foxclaw/api/routes/task.py +31 -49
- package/python/foxclaw/api/routes/ws.py +2 -6
- package/python/foxclaw/api/spine.py +1 -4
- package/python/foxclaw/browser/action/action_executor.py +1 -5
- package/python/foxclaw/browser/action/action_types.py +0 -1
- package/python/foxclaw/browser/action/url_policy.py +1 -5
- package/python/foxclaw/browser/adapter/web_adapter.py +1 -1
- package/python/foxclaw/browser/driver/playwright_driver.py +20 -16
- package/python/foxclaw/browser/perception/dom_extractor.py +0 -1
- package/python/foxclaw/browser/perception/element_indexer.py +1 -2
- package/python/foxclaw/browser/service.py +7 -11
- package/python/foxclaw/capability/embedding_service.py +4 -6
- package/python/foxclaw/channels/__init__.py +9 -11
- package/python/foxclaw/channels/discord.py +4 -13
- package/python/foxclaw/channels/dispatcher.py +2 -27
- package/python/foxclaw/channels/feishu.py +10 -20
- package/python/foxclaw/channels/openclaw_bridge.py +4 -9
- package/python/foxclaw/channels/signing.py +9 -9
- package/python/foxclaw/channels/telegram.py +3 -4
- package/python/foxclaw/channels/weixin.py +7 -17
- package/python/foxclaw/cli.py +42 -13
- package/python/foxclaw/cognitive/reasoner.py +9 -10
- package/python/foxclaw/cognitive/state.py +18 -24
- package/python/foxclaw/core/audit_chain.py +1 -3
- package/python/foxclaw/core/audit_sink.py +3 -3
- package/python/foxclaw/core/compaction.py +8 -26
- package/python/foxclaw/core/config.py +2 -2
- package/python/foxclaw/core/confirm_nonce.py +6 -12
- package/python/foxclaw/core/envfile.py +2 -2
- package/python/foxclaw/core/failure_policy.py +17 -26
- package/python/foxclaw/core/gateway.py +6 -10
- package/python/foxclaw/core/governance_audit.py +3 -7
- package/python/foxclaw/core/idempotency.py +15 -22
- package/python/foxclaw/core/injection_guard.py +10 -29
- package/python/foxclaw/core/memory_adapter.py +30 -57
- package/python/foxclaw/core/metrics.py +4 -5
- package/python/foxclaw/core/model_router.py +36 -148
- package/python/foxclaw/core/paths.py +1 -1
- package/python/foxclaw/core/recovery.py +21 -34
- package/python/foxclaw/core/scheduler.py +23 -26
- package/python/foxclaw/core/secrets.py +0 -3
- package/python/foxclaw/core/sessions.py +4 -6
- package/python/foxclaw/core/settings.py +16 -32
- package/python/foxclaw/core/shared_write.py +11 -23
- package/python/foxclaw/core/steering_queue.py +12 -11
- package/python/foxclaw/core/system_bus.py +1 -1
- package/python/foxclaw/core/thought_chain.py +30 -32
- package/python/foxclaw/core/token_meter.py +9 -13
- package/python/foxclaw/core/tools.py +4 -12
- package/python/foxclaw/core/tracing.py +25 -40
- package/python/foxclaw/core/truth_pipeline.py +5 -9
- package/python/foxclaw/core/update_check.py +149 -0
- package/python/foxclaw/core/vec_recall_adapter.py +11 -37
- package/python/foxclaw/core/verify.py +6 -14
- package/python/foxclaw/core/web_search.py +4 -28
- package/python/foxclaw/finance/evidence.py +14 -18
- package/python/foxclaw/finance/fx.py +1 -3
- package/python/foxclaw/finance/graph.py +9 -18
- package/python/foxclaw/finance/models.py +20 -24
- package/python/foxclaw/finance/nlparse.py +12 -72
- package/python/foxclaw/finance/policy.py +8 -36
- package/python/foxclaw/finance/reasoner.py +3 -11
- package/python/foxclaw/finance/reasoning.py +13 -34
- package/python/foxclaw/finance/reconcile.py +5 -10
- package/python/foxclaw/finance/runtime.py +19 -46
- package/python/foxclaw/governance/middleware.py +13 -21
- package/python/foxclaw/harness/_base.py +2 -8
- package/python/foxclaw/harness/_policy.py +19 -38
- package/python/foxclaw/harness/drift.py +1 -13
- package/python/foxclaw/harness/event_bus.py +2 -5
- package/python/foxclaw/harness/harness.py +9 -97
- package/python/foxclaw/harness/loop_detection.py +11 -18
- package/python/foxclaw/itinerary/amap_client.py +2 -5
- package/python/foxclaw/itinerary/models.py +7 -7
- package/python/foxclaw/itinerary/reasoner.py +1 -6
- package/python/foxclaw/itinerary/reasoning.py +15 -21
- package/python/foxclaw/itinerary/runtime.py +8 -12
- package/python/foxclaw/kernel/capabilities.py +5 -6
- package/python/foxclaw/kernel/policy_gate.py +4 -10
- package/python/foxclaw/kernel/process_manager.py +17 -36
- package/python/foxclaw/kernel/scheduler.py +3 -9
- package/python/foxclaw/kernel/sentinel.py +13 -23
- package/python/foxclaw/kernel/sentinel_client.py +12 -15
- package/python/foxclaw/kernel/spine.py +9 -9
- package/python/foxclaw/knowledge/__init__.py +4 -20
- package/python/foxclaw/knowledge/truth_crystallizer.py +5 -8
- package/python/foxclaw/learning/knowledge_graph.py +4 -9
- package/python/foxclaw/learning/models.py +13 -13
- package/python/foxclaw/learning/reasoner.py +1 -4
- package/python/foxclaw/learning/runtime.py +19 -31
- package/python/foxclaw/learning/spaced_repetition.py +2 -7
- package/python/foxclaw/mcp_server.py +2 -3
- package/python/foxclaw/orchestration/__init__.py +16 -33
- package/python/foxclaw/pipeline/cognitive_pipeline.py +45 -97
- package/python/foxclaw/pipeline/stage_meta.py +21 -0
- package/python/foxclaw/pipeline/supervisor_stages.py +13 -42
- package/python/foxclaw/planning/llm_planner.py +3 -3
- package/python/foxclaw/planning/planner.py +6 -11
- package/python/foxclaw/search/agent_port.py +7 -11
- package/python/foxclaw/search/evidence.py +0 -4
- package/python/foxclaw/search/internal_search.py +3 -5
- package/python/foxclaw/search/models.py +11 -13
- package/python/foxclaw/search/planner.py +3 -3
- package/python/foxclaw/search/retriever.py +1 -1
- package/python/foxclaw/search/runtime.py +1 -8
- package/python/foxclaw/skills/__init__.py +46 -100
- package/python/foxclaw/skills/archive.py +111 -0
- package/python/foxclaw/skills/fetch.py +226 -0
- package/python/foxclaw/skills/hooks_spec.py +21 -25
- package/python/foxclaw/skills/install_cmd.py +216 -0
- package/python/foxclaw/skills/lifecycle.py +16 -42
- package/python/foxclaw/skills/markdown_skill.py +10 -9
- package/python/foxclaw/skills/mcp_client.py +5 -11
- package/python/foxclaw/skills/mcp_store.py +0 -3
- package/python/foxclaw/skills/permission.py +13 -48
- package/python/foxclaw/skills/pipeline.py +21 -53
- package/python/foxclaw/skills/policy.py +4 -10
- package/python/foxclaw/skills/review.py +3 -3
- package/python/frontend/deck/auth.js +0 -7
- package/python/frontend/deck/chat_model_picker.js +0 -54
- package/python/frontend/deck/domain.js +0 -20
- package/python/frontend/deck/main.js +0 -144
- package/python/frontend/deck/markdown.js +0 -4
- package/python/frontend/deck/settings.js +1 -18
- package/python/frontend/deck/state.js +0 -1
- package/python/frontend/deck/util.js +0 -2
- package/python/frontend/deck/visuals.js +0 -22
- package/python/frontend/planet/planet.js +0 -201
- package/python/frontend/v3/assets/index-CQSBdxk4.js +34 -0
- package/python/frontend/v3/assets/index-D5nIsjms.css +1 -0
- package/python/frontend/v3/index.html +3 -3
- package/python/frontend/widget_runtime.js +0 -43
- package/python/frontend/widgets.js +0 -57
- package/python/frontend_src/index.html +1 -1
- package/python/frontend_src/package-lock.json +2 -2
- package/python/frontend_src/package.json +1 -1
- package/python/frontend_src/src/App.vue +3 -7
- package/python/frontend_src/src/api.js +0 -7
- package/python/frontend_src/src/components/ChatDock.vue +27 -39
- package/python/frontend_src/src/components/DocViewer.vue +0 -9
- package/python/frontend_src/src/components/Inspector.vue +0 -6
- package/python/frontend_src/src/components/LeftRail.vue +60 -39
- package/python/frontend_src/src/components/MenuPopover.vue +9 -11
- package/python/frontend_src/src/components/MessageStream.vue +18 -30
- package/python/frontend_src/src/components/ModelPicker.vue +0 -5
- package/python/frontend_src/src/components/QuickIngest.vue +0 -6
- package/python/frontend_src/src/components/Stage.vue +71 -25
- package/python/frontend_src/src/components/ToolMenu.vue +0 -3
- package/python/frontend_src/src/components/TopBar.vue +38 -10
- package/python/frontend_src/src/components/panels/ActivityPanel.vue +0 -2
- package/python/frontend_src/src/components/panels/AgentsPanel.vue +2 -5
- package/python/frontend_src/src/components/panels/KnowledgePanel.vue +0 -16
- package/python/frontend_src/src/components/panels/MemoryPanel.vue +0 -1
- package/python/frontend_src/src/components/panels/PanelBox.vue +6 -7
- package/python/frontend_src/src/components/panels/ReviewPanel.vue +0 -16
- package/python/frontend_src/src/components/panels/SettingsPanel.vue +142 -38
- package/python/frontend_src/src/components/panels/TasksPanel.vue +0 -1
- package/python/frontend_src/src/components/panels/ToolsPanel.vue +125 -19
- package/python/frontend_src/src/lib/fmt.js +0 -4
- package/python/frontend_src/src/lib/i18n.js +53 -0
- package/python/frontend_src/src/lib/locales/en.js +173 -0
- package/python/frontend_src/src/lib/locales/zh.js +174 -0
- package/python/frontend_src/src/lib/md.js +0 -14
- package/python/frontend_src/src/main.js +2 -0
- package/python/frontend_src/src/stores/app.js +17 -32
- package/python/frontend_src/src/stores/chat.js +0 -34
- package/python/frontend_src/src/stores/model.js +0 -8
- package/python/frontend_src/src/stores/panels.js +0 -41
- package/python/frontend_src/src/stores/profile.js +31 -0
- package/python/frontend_src/src/stores/system.js +25 -0
- package/python/frontend_src/vite.config.js +0 -7
- package/python/pyproject.toml +6 -3
- package/python/requirements.txt +311 -0
- package/python/tests/conftest.py +0 -22
- package/python/tests/run_all.py +3 -66
- package/python/tests/test_agent_lifecycle.py +4 -38
- package/python/tests/test_ahs_p2.py +4 -25
- package/python/tests/test_anthropic_tools.py +1 -15
- package/python/tests/test_api.py +19 -9
- package/python/tests/test_api_auth.py +1 -5
- package/python/tests/test_attachments.py +9 -25
- package/python/tests/test_audit_chain.py +2 -7
- package/python/tests/test_audit_sink.py +0 -6
- package/python/tests/test_browser.py +2 -6
- package/python/tests/test_browser_request_guard.py +0 -4
- package/python/tests/test_capabilities.py +0 -6
- package/python/tests/test_channel_signing.py +2 -9
- package/python/tests/test_channels.py +1 -11
- package/python/tests/test_config_layering.py +0 -4
- package/python/tests/test_confirm_nonce.py +1 -12
- package/python/tests/test_context_compaction_wiring.py +1 -7
- package/python/tests/test_dispatch.py +0 -6
- package/python/tests/test_domain_evals.py +11 -80
- package/python/tests/test_domain_import.py +0 -6
- package/python/tests/test_domain_reasoners.py +0 -14
- package/python/tests/test_e2e.py +0 -17
- package/python/tests/test_emb_cache.py +7 -17
- package/python/tests/test_embedding_singleton.py +1 -3
- package/python/tests/test_evals_smoke.py +0 -24
- package/python/tests/test_event_spine.py +10 -18
- package/python/tests/test_evidence_semantic.py +1 -14
- package/python/tests/test_evidence_sse.py +3 -6
- package/python/tests/test_file_ingest.py +0 -11
- package/python/tests/test_finance.py +0 -9
- package/python/tests/test_finance_ledger.py +5 -12
- package/python/tests/test_finance_persist.py +8 -12
- package/python/tests/test_gateway.py +3 -22
- package/python/tests/test_governance.py +5 -52
- package/python/tests/test_governance_audit.py +4 -39
- package/python/tests/test_governance_middleware.py +1 -7
- package/python/tests/test_hkc.py +6 -24
- package/python/tests/test_hkc_index_build_manager.py +0 -33
- package/python/tests/test_hkc_manifest_reuse.py +4 -27
- package/python/tests/test_hkc_search_event_sync.py +3 -54
- package/python/tests/test_import_path_guard.py +0 -6
- package/python/tests/test_injection_guard.py +18 -36
- package/python/tests/test_integration.py +5 -29
- package/python/tests/test_itinerary.py +0 -3
- package/python/tests/test_kee_semantic.py +0 -5
- package/python/tests/test_kernel.py +1 -11
- package/python/tests/test_learning.py +3 -12
- package/python/tests/test_llm_admin.py +4 -19
- package/python/tests/test_llm_planner.py +1 -23
- package/python/tests/test_markdown_skills.py +3 -14
- package/python/tests/test_markdown_skills_openclaw.py +4 -9
- package/python/tests/test_mcp_connectors.py +3 -15
- package/python/tests/test_mcp_guardian_discovery.py +2 -6
- package/python/tests/test_mcp_no_memory.py +3 -3
- package/python/tests/test_mcp_policy_gate.py +0 -1
- package/python/tests/test_mcp_server.py +6 -9
- package/python/tests/test_memory_recall.py +1 -4
- package/python/tests/test_metrics.py +0 -8
- package/python/tests/test_multi_chain.py +1 -13
- package/python/tests/test_multimodal.py +5 -18
- package/python/tests/test_negotiation.py +3 -13
- package/python/tests/test_orchestration.py +1 -11
- package/python/tests/test_orchestration_decoupled.py +0 -6
- package/python/tests/test_p3_p5_hardening.py +3 -17
- package/python/tests/test_parallel_dispatch.py +4 -12
- package/python/tests/test_pipeline.py +17 -58
- package/python/tests/test_plan_visible.py +4 -4
- package/python/tests/test_planner.py +0 -7
- package/python/tests/test_policy_gate.py +2 -3
- package/python/tests/test_profile.py +67 -0
- package/python/tests/test_readme_claims.py +163 -0
- package/python/tests/test_reasoner.py +2 -17
- package/python/tests/test_release_check.py +2 -13
- package/python/tests/test_resume_checkpoint.py +7 -15
- package/python/tests/test_routing.py +2 -12
- package/python/tests/test_scheduler.py +7 -10
- package/python/tests/test_search.py +1 -11
- package/python/tests/test_secrets.py +4 -10
- package/python/tests/test_semantic.py +1 -10
- package/python/tests/test_sessions.py +3 -4
- package/python/tests/test_settings.py +2 -18
- package/python/tests/test_shadow_learning.py +3 -43
- package/python/tests/test_shared_write.py +0 -7
- package/python/tests/test_skill_taint_gate.py +1 -10
- package/python/tests/test_skills.py +2 -18
- package/python/tests/test_skills_fetch.py +282 -0
- package/python/tests/test_stats_and_events.py +2 -5
- package/python/tests/test_streaming.py +2 -7
- package/python/tests/test_supervisor_pipeline.py +4 -5
- package/python/tests/test_supervisor_triage.py +4 -10
- package/python/tests/test_taint.py +2 -7
- package/python/tests/test_task_stream.py +1 -12
- package/python/tests/test_thinking_sse.py +0 -1
- package/python/tests/test_tool_center.py +1 -17
- package/python/tests/test_tool_parallel.py +2 -2
- package/python/tests/test_tool_params.py +1 -5
- package/python/tests/test_tool_use.py +0 -13
- package/python/tests/test_toplevel_concurrency.py +3 -4
- package/python/tests/test_tracing.py +7 -41
- package/python/tests/test_truth_crystallizer.py +4 -6
- package/python/tests/test_truth_flow.py +2 -7
- package/python/tests/test_update_check.py +95 -0
- package/python/tests/test_verify_evidence.py +8 -21
- package/python/tests/test_version_sync.py +35 -6
- package/python/tests/test_web_trigger.py +0 -17
- package/python/tools/check_faiss_type.py +0 -1
- package/python/tools/check_log.ps1 +0 -8
- package/python/tools/mypy_baseline.txt +12 -4
- package/python/tools/mypy_ratchet.py +52 -6
- package/python/tools/pack.py +0 -5
- package/python/tools/release_check.py +7 -7
- package/python/tools/restart_foxclaw.ps1 +5 -8
- package/python/tools/shared_write_audit.py +1 -15
- package/python/tools/size_check.py +1 -1
- package/python/tools/strip_comments.py +228 -0
- package/python/frontend/v3/assets/index-BD7VPbMV.js +0 -33
- package/python/frontend/v3/assets/index-CAvnYWGH.css +0 -1
|
@@ -10,19 +10,22 @@ CognitiveAgent — 认知增强 Agent 基类
|
|
|
10
10
|
|
|
11
11
|
import threading
|
|
12
12
|
import uuid
|
|
13
|
+
from typing import TYPE_CHECKING, Any
|
|
13
14
|
|
|
14
15
|
from ..core.thought_chain import ThoughtChain, ThoughtNode
|
|
15
16
|
from ..harness.harness import Harness, HarnessResult
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from ..core.recovery import RestartRecovery
|
|
20
|
+
from ..core.system_bus import SystemBus
|
|
21
|
+
from ..kernel import ProcessManager
|
|
22
|
+
from ..skills.lifecycle import HookManager
|
|
23
|
+
from ..skills.markdown_skill import MarkdownSkillRegistry
|
|
24
|
+
|
|
20
25
|
_THINK_LOCK = threading.RLock()
|
|
21
26
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
# 各职责方法搬至 mixins/{llm,augment,memory,preference,prompt,tool_exec}.py;
|
|
25
|
-
# 主循环(会话管理 / _think / stream_reply / run_task 生命周期)留在本文件。
|
|
27
|
+
_GATE_UNSET = object()
|
|
28
|
+
|
|
26
29
|
from .mixins._context import (
|
|
27
30
|
_agent_ctx_get,
|
|
28
31
|
_agent_ctx_set,
|
|
@@ -46,7 +49,6 @@ from .mixins.preference import PreferenceMixin
|
|
|
46
49
|
from .mixins.prompt import PromptMixin, KERNEL_PROMPT
|
|
47
50
|
from .mixins.tool_exec import ToolExecMixin
|
|
48
51
|
|
|
49
|
-
# 历史导入兼容:`from foxclaw.agents.base import X` 的 X 在此 re-export(测试/外部模块仍按此路径取符号)。
|
|
50
52
|
__all__ = [
|
|
51
53
|
"CognitiveAgent", "HarnessBlockedError", "KERNEL_PROMPT",
|
|
52
54
|
"_suppress_memory", "_act_stream", "_taint_get", "_taint_raise", "_taint_reset",
|
|
@@ -92,28 +94,32 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
92
94
|
AGENT_NAME: str = "Base Agent"
|
|
93
95
|
ALLOWED_TOOL_CATEGORIES: set[str] = {"read"}
|
|
94
96
|
|
|
97
|
+
md_skills: "MarkdownSkillRegistry"
|
|
98
|
+
hooks: "HookManager"
|
|
99
|
+
_system_bus: "SystemBus"
|
|
100
|
+
_process_manager: "ProcessManager"
|
|
101
|
+
_recovery: "RestartRecovery"
|
|
102
|
+
|
|
95
103
|
def __init__(
|
|
96
104
|
self,
|
|
97
|
-
hmr,
|
|
105
|
+
hmr,
|
|
98
106
|
thought_chain: ThoughtChain,
|
|
99
107
|
client=None,
|
|
100
108
|
data_dir: str = "./foxclaw_data",
|
|
101
109
|
shadow_rounds: int = 20,
|
|
102
|
-
router=None,
|
|
103
|
-
spine=None,
|
|
104
|
-
caps=None,
|
|
110
|
+
router=None,
|
|
111
|
+
spine=None,
|
|
112
|
+
caps=None,
|
|
105
113
|
):
|
|
106
114
|
self.hmr = hmr
|
|
107
115
|
self.thought_chain = thought_chain
|
|
108
116
|
self.client = client
|
|
109
|
-
# 多模型路由:优先用注入的 router;否则惰性创建(自动探测可用模型)
|
|
110
117
|
if router is None:
|
|
111
118
|
from ..core.model_router import ModelRouter
|
|
112
119
|
router = ModelRouter()
|
|
113
120
|
self.router = router
|
|
114
121
|
self.data_dir = data_dir
|
|
115
122
|
|
|
116
|
-
# 独立 Harness
|
|
117
123
|
self.harness = Harness(
|
|
118
124
|
agent_id=self.AGENT_ID,
|
|
119
125
|
thought_chain=thought_chain,
|
|
@@ -121,20 +127,12 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
121
127
|
shadow_rounds=shadow_rounds,
|
|
122
128
|
spine=spine,
|
|
123
129
|
)
|
|
124
|
-
# 调度层(主管)豁免语义漂移拦截:接收任意输入是其本职,不算异常。
|
|
125
|
-
# 领域 Agent 保持语义拦截:执行业务时跑题(如财务去写诗)才该拦。
|
|
126
130
|
self.harness.block_on_semantic = not getattr(self, "IS_ROUTER", False)
|
|
127
131
|
|
|
128
|
-
# 获取或创建 HMR Workspace
|
|
129
132
|
self.workspace = hmr.get_workspace(self.AGENT_ID)
|
|
130
133
|
|
|
131
|
-
# 每任务会话状态改由 contextvars 承载(见文件顶部)。这里只需一个 per-实例 token
|
|
132
|
-
# 作为分槽 key;_session_id / _parent_node_id 是路由到 contextvar 的 property,
|
|
133
|
-
# 故 start_session / _think 里既有的读写代码完全无需改动。
|
|
134
134
|
self._ctx_token = uuid.uuid4().hex
|
|
135
135
|
|
|
136
|
-
# 【P1-3】能力注册中枢:Agent 不再直接持有 search_runtime,而是按名惰性解析
|
|
137
|
-
# (打断 agents⇄search_runtime⇄agent_port⇄agents 的直接互持环)。
|
|
138
136
|
self._caps = caps
|
|
139
137
|
|
|
140
138
|
def search_internal(self, query: str, top_k: int = 5):
|
|
@@ -146,7 +144,7 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
146
144
|
return None
|
|
147
145
|
return rt.search(query, agent_id=self.AGENT_ID, internal_only=True)
|
|
148
146
|
|
|
149
|
-
def available_abilities(self, keys: list = None) -> list:
|
|
147
|
+
def available_abilities(self, keys: list | None = None) -> list:
|
|
150
148
|
"""【HKC 第2档 Step2·能力自省】证据驱动地查 HKC 的 ACE 能力:哪些的知识覆盖已达标
|
|
151
149
|
(可编译 sufficient=True)、哪些还差(sufficient=False + missing_skills)。
|
|
152
150
|
|
|
@@ -170,14 +168,12 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
170
168
|
out.append({"ability_key": ab.ability_key, "display_name": ab.display_name,
|
|
171
169
|
"domain": ab.domain, "sufficient": True, "coverage": ab.coverage})
|
|
172
170
|
except Exception as e:
|
|
173
|
-
if hasattr(e, "missing_skills"):
|
|
171
|
+
if hasattr(e, "missing_skills"):
|
|
174
172
|
out.append({"ability_key": key, "sufficient": False,
|
|
175
173
|
"coverage": getattr(e, "coverage", {}),
|
|
176
174
|
"missing_skills": getattr(e, "missing_skills", [])})
|
|
177
|
-
# 其它异常(未知 key/图错误)→ 跳过该能力,不污染结果
|
|
178
175
|
return out
|
|
179
176
|
|
|
180
|
-
# ── 每任务会话指针(contextvar 支撑,property 透明路由)──────
|
|
181
177
|
@property
|
|
182
178
|
def _session_id(self):
|
|
183
179
|
return _agent_ctx_get(self._ctx_token).get("session_id")
|
|
@@ -194,23 +190,20 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
194
190
|
def _parent_node_id(self, value):
|
|
195
191
|
_agent_ctx_set(self._ctx_token, parent_node_id=value)
|
|
196
192
|
|
|
197
|
-
# ── 会话管理 ───────────────────────────────────────────
|
|
198
193
|
|
|
199
|
-
def start_session(self, goal: str, entities: list[str] = None) -> str:
|
|
194
|
+
def start_session(self, goal: str, entities: list[str] | None = None) -> str:
|
|
200
195
|
"""开始一个新任务会话,注册目标基线"""
|
|
201
196
|
self._session_id = f"sess_{uuid.uuid4().hex[:10]}"
|
|
202
197
|
self._parent_node_id = None
|
|
203
|
-
_taint_reset(self)
|
|
204
|
-
_evidence_reset(self)
|
|
198
|
+
_taint_reset(self)
|
|
199
|
+
_evidence_reset(self)
|
|
205
200
|
|
|
206
|
-
# 注册 Harness 目标基线
|
|
207
201
|
self.harness.register_goal(
|
|
208
202
|
goal_text=goal,
|
|
209
203
|
allowed_tool_categories=self.ALLOWED_TOOL_CATEGORIES,
|
|
210
204
|
baseline_entities=set(entities or []),
|
|
211
205
|
)
|
|
212
206
|
|
|
213
|
-
# 在 ThoughtChain 里记录目标基线节点
|
|
214
207
|
goal_node = ThoughtNode.create(
|
|
215
208
|
session_id=self._session_id,
|
|
216
209
|
agent_id=self.AGENT_ID,
|
|
@@ -223,22 +216,14 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
223
216
|
self.thought_chain.update_harness(goal_node.id, "BASELINE")
|
|
224
217
|
self._parent_node_id = goal_node.id
|
|
225
218
|
|
|
226
|
-
# 【深审修复·移除惰性共享写】原先 `self.workspace.active_goal = goal` 把每任务目标
|
|
227
|
-
# 写到 per-Agent **共享**的 HMR workspace 上,同一 Agent 实例并发多任务会互相覆盖。
|
|
228
|
-
# 审计确认此写**完全惰性**:foxclaw 召回走显式 query(见 _recall_memory,从不读
|
|
229
|
-
# workspace.active_goal),save_workspace 从未被调用(不持久化),无测试依赖它——
|
|
230
|
-
# 故 race 零可观测影响。移除该误导性共享写:**每任务目标的权威来源**是会话隔离的
|
|
231
|
-
# ThoughtChain(goal 节点,session_id 经 contextvar 隔离)+ 事件驱动的 CognitiveState
|
|
232
|
-
# (execution.completed 的 active_goal)。共享单值 workspace 本就无法忠实承载 N 个并发目标。
|
|
233
219
|
return self._session_id
|
|
234
220
|
|
|
235
|
-
# ── ThoughtChain + Harness 集成推理步骤 ───────────────
|
|
236
221
|
|
|
237
222
|
def _reasoning_result(self, node):
|
|
238
223
|
"""经统一 PolicyGate 做推理准入,返回 HarnessResult(与直接 harness.check 逐字一致)。
|
|
239
224
|
gate 经能力中枢惰性解析并缓存;无 gate(standalone/治理场景)则直接 harness.check。"""
|
|
240
|
-
gate = getattr(self, "_gate_cache",
|
|
241
|
-
if gate
|
|
225
|
+
gate: Any = getattr(self, "_gate_cache", _GATE_UNSET)
|
|
226
|
+
if gate is _GATE_UNSET:
|
|
242
227
|
caps = getattr(self, "_caps", None)
|
|
243
228
|
gate = caps.get("policy_gate") if caps is not None else None
|
|
244
229
|
self._gate_cache = gate
|
|
@@ -251,10 +236,10 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
251
236
|
content: str,
|
|
252
237
|
node_type="reasoning",
|
|
253
238
|
conf: float = 0.9,
|
|
254
|
-
mem_refs: list[str] = None,
|
|
255
|
-
tool_name: str = None,
|
|
256
|
-
entities: list[str] = None,
|
|
257
|
-
metadata: dict = None,
|
|
239
|
+
mem_refs: list[str] | None = None,
|
|
240
|
+
tool_name: str | None = None,
|
|
241
|
+
entities: list[str] | None = None,
|
|
242
|
+
metadata: dict | None = None,
|
|
258
243
|
) -> tuple[ThoughtNode, HarnessResult]:
|
|
259
244
|
"""
|
|
260
245
|
记录一个推理步骤,经过 Harness 校验
|
|
@@ -272,17 +257,14 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
272
257
|
entities=entities or [],
|
|
273
258
|
metadata=metadata,
|
|
274
259
|
)
|
|
275
|
-
# 锁只护「链插入 + 父指针挪动」这两步共享状态;Harness 判定在锁外,避免钩子阻塞并发工具。
|
|
276
260
|
with _THINK_LOCK:
|
|
277
261
|
self.thought_chain.add_node(node)
|
|
278
262
|
|
|
279
|
-
# Harness 校验(经统一 PolicyGate 做推理准入;拿到的仍是同一 HarnessResult,行为一致)
|
|
280
263
|
result = self._reasoning_result(node)
|
|
281
264
|
|
|
282
265
|
if result.is_blocked:
|
|
283
266
|
raise HarnessBlockedError(result, node)
|
|
284
267
|
|
|
285
|
-
# WARN:降低置信度但继续
|
|
286
268
|
if result.is_warned:
|
|
287
269
|
node.conf = max(node.conf - 0.15, 0.1)
|
|
288
270
|
|
|
@@ -364,8 +346,8 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
364
346
|
f"但无法提取为可读文本;若需要其内容,请说明并提示用户改用文本/PDF 等可读格式。")
|
|
365
347
|
return "\n\n" + "\n\n".join(parts) if parts else ""
|
|
366
348
|
|
|
367
|
-
def stream_reply(self, task: str, stop_event=None, images: list = None, history: list = None,
|
|
368
|
-
attachments: list = None, on_thinking=None, on_evidence=None):
|
|
349
|
+
def stream_reply(self, task: str, stop_event=None, images: list | None = None, history: list | None = None,
|
|
350
|
+
attachments: list | None = None, on_thinking=None, on_evidence=None):
|
|
369
351
|
"""流式直答路径(方案A):对话/问答/分析,不做多步编排(那走 run_task)。同步生成器,
|
|
370
352
|
由端点在线程池迭代成 SSE。stop_event 置位(客户端断连)则提前终止,省 token。
|
|
371
353
|
images(可选)= 多模态图片,随问题发给视觉模型(模型无关,非视觉模型 router 层降级)。
|
|
@@ -386,7 +368,7 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
386
368
|
from ..core import injection_guard as _ig
|
|
387
369
|
from ..core.model_router import _chunk_text
|
|
388
370
|
task = str(task or "")
|
|
389
|
-
_evidence_reset(self)
|
|
371
|
+
_evidence_reset(self)
|
|
390
372
|
if attachments:
|
|
391
373
|
ctx = self._attachments_context(attachments)
|
|
392
374
|
if ctx:
|
|
@@ -395,7 +377,7 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
395
377
|
messages = hist + [{"role": "user", "content": task}]
|
|
396
378
|
parts = []
|
|
397
379
|
used_tools = False
|
|
398
|
-
guarded_out = False
|
|
380
|
+
guarded_out = False
|
|
399
381
|
|
|
400
382
|
def _emit_thinking(delta: str) -> None:
|
|
401
383
|
"""推理内容的最后一道关:思考也不许带密钥/系统提示原文(与正文同等级净化)。"""
|
|
@@ -411,26 +393,23 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
411
393
|
try:
|
|
412
394
|
on_thinking(delta)
|
|
413
395
|
except Exception:
|
|
414
|
-
pass
|
|
396
|
+
pass
|
|
415
397
|
|
|
416
398
|
if _tools.tool_use_enabled() and self.router.supports_tools() and not images:
|
|
417
|
-
# 与 fallback 一致地注入偏好/技能/能力(否则开工具化会静默丢掉"总是注入偏好"的保证、
|
|
418
|
-
# 丢掉 SKILL.md 与能力知识)。**唯独不做 web_augment**——联网交给 LLM 自主调 web_search 工具。
|
|
419
399
|
try:
|
|
420
400
|
tmsgs = self._ability_augment(task, messages)
|
|
421
401
|
tmsgs = self._skill_augment(task, tmsgs)
|
|
422
402
|
tmsgs = self._pref_augment(tmsgs)
|
|
423
403
|
except Exception:
|
|
424
404
|
tmsgs = messages
|
|
425
|
-
_compact = getattr(self, "_compact_context", None)
|
|
405
|
+
_compact = getattr(self, "_compact_context", None)
|
|
426
406
|
if _compact is not None:
|
|
427
407
|
tmsgs = _compact(tmsgs, "tools")
|
|
428
408
|
try:
|
|
429
409
|
final = self._reply_with_tools(task, tmsgs, stop_event=stop_event)
|
|
430
410
|
except Exception:
|
|
431
411
|
final = None
|
|
432
|
-
if final is not None:
|
|
433
|
-
# 工具路径有完整文本 → 流出前兜底(密钥脱敏/拦系统提示 dump);传 kernel 用逐字串比对更准
|
|
412
|
+
if final is not None:
|
|
434
413
|
final, _oiss = _ig.guard_output(final, kernel=KERNEL_PROMPT)
|
|
435
414
|
guarded_out = bool(_oiss)
|
|
436
415
|
used_tools = True
|
|
@@ -439,25 +418,23 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
439
418
|
break
|
|
440
419
|
parts.append(chunk)
|
|
441
420
|
yield chunk
|
|
442
|
-
if not used_tools:
|
|
421
|
+
if not used_tools:
|
|
443
422
|
try:
|
|
444
|
-
messages = self._web_augment(task, messages)
|
|
423
|
+
messages = self._web_augment(task, messages)
|
|
445
424
|
messages = self._ability_augment(task, messages)
|
|
446
425
|
messages = self._skill_augment(task, messages)
|
|
447
426
|
messages = self._pref_augment(messages)
|
|
448
427
|
except Exception:
|
|
449
428
|
messages = hist + [{"role": "user", "content": task}]
|
|
450
|
-
_compact = getattr(self, "_compact_context", None)
|
|
429
|
+
_compact = getattr(self, "_compact_context", None)
|
|
451
430
|
if _compact is not None:
|
|
452
431
|
messages = _compact(messages, "chat")
|
|
453
|
-
_tail = ""
|
|
454
|
-
_WIN = 256
|
|
432
|
+
_tail = ""
|
|
433
|
+
_WIN = 256
|
|
455
434
|
for chunk in self.router.stream(self.system_prompt, messages,
|
|
456
435
|
agent_id=self.AGENT_ID, stop_event=stop_event,
|
|
457
436
|
images=images,
|
|
458
437
|
on_thinking=_emit_thinking if on_thinking else None):
|
|
459
|
-
# 流出前检测(而非"yield 后才检测"):密钥/泄露所在 chunk 不再已发给用户才中断。
|
|
460
|
-
# 只扫"尾缓冲 + 新 chunk"(增量滑窗),命中则本 chunk 不发、整段 dump 止于此。
|
|
461
438
|
_probe = _tail + chunk
|
|
462
439
|
if _ig.scan_output(_probe):
|
|
463
440
|
guarded_out = True
|
|
@@ -471,26 +448,19 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
471
448
|
yield _notice
|
|
472
449
|
reply = "".join(parts)
|
|
473
450
|
stopped = stop_event is not None and stop_event.is_set()
|
|
474
|
-
# 执行证据在正文之后一次性上报(晚于 delta、早于 done):前端拿到时答案已完整,
|
|
475
|
-
# 可以挂在答案下方供人核对;没有工具步的纯对话不产生任何证据事件。
|
|
476
451
|
if on_evidence is not None:
|
|
477
452
|
try:
|
|
478
453
|
_evs = _evidence_take(self)
|
|
479
454
|
if _evs:
|
|
480
455
|
on_evidence(_evs)
|
|
481
456
|
except Exception:
|
|
482
|
-
pass
|
|
483
|
-
# 落库前最终兜底(仅 fallback 路径需要):fallback 增量检查(无 kernel、只抓明显 dump)可能漏过
|
|
484
|
-
# 逐字复现的系统提示,这里用 kernel 逐字串比对再判一次,命中则不落库(防污染回流),不影响已流给
|
|
485
|
-
# 用户的内容。工具路径在流出前已 guard_output(kernel) 完整防护,无需重复扫(消冗余)。
|
|
457
|
+
pass
|
|
486
458
|
if reply and not guarded_out and not used_tools and _ig.scan_output(reply, kernel=KERNEL_PROMPT):
|
|
487
459
|
guarded_out = True
|
|
488
460
|
try:
|
|
489
461
|
self._think(content=("流式直答:" + reply)[:500], node_type="reflection", conf=0.8)
|
|
490
462
|
except Exception:
|
|
491
463
|
pass
|
|
492
|
-
# 活动可观测:对话路径此前**不发任何事件** → /bus/history 恒空、前端"活动"页永远没内容
|
|
493
|
-
# (run_task 走 _emit_execution 会发,但直答/流式对话不经那条路)。这里补一条同源事件。
|
|
494
464
|
try:
|
|
495
465
|
_caps = getattr(self, "_caps", None)
|
|
496
466
|
_spine = _caps.get("event_spine") if _caps is not None else None
|
|
@@ -503,12 +473,6 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
503
473
|
}, source=self.AGENT_ID)
|
|
504
474
|
except Exception:
|
|
505
475
|
pass
|
|
506
|
-
# 写入记忆(与 /api/task 一致;MCP 场景由 _suppress_memory 兜住);断连中止/触发输出护栏则不写
|
|
507
|
-
#
|
|
508
|
-
# A 类修复:故障降级回复**禁止写入长期记忆** —— 把「模型失败/未执行」当成
|
|
509
|
-
# 「对话结论」沉淀,会让 HMR 后续召回持续复读故障文案(一次故障 → 长期污染)。
|
|
510
|
-
# 判据用 take_call_degraded()(per-call)而非全局 is_stub:is_stub=True 是
|
|
511
|
-
# 「从未配置模型」的离线态,那个场景下记录任务/占位仍有诊断价值,不该一刀切。
|
|
512
476
|
_degraded = False
|
|
513
477
|
try:
|
|
514
478
|
_degraded = bool(self.router.take_call_degraded())
|
|
@@ -516,13 +480,12 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
516
480
|
_degraded = False
|
|
517
481
|
if reply and not stopped and not guarded_out and not _degraded:
|
|
518
482
|
try:
|
|
519
|
-
safe_reply = _ig.redact_secrets(reply)
|
|
483
|
+
safe_reply = _ig.redact_secrets(reply)
|
|
520
484
|
self._store_memory(f"对话:{task} → {safe_reply}"[:800], mem_type="conversation")
|
|
521
485
|
except Exception:
|
|
522
486
|
pass
|
|
523
|
-
self._maybe_store_preference(task)
|
|
487
|
+
self._maybe_store_preference(task)
|
|
524
488
|
elif _degraded and reply and not stopped:
|
|
525
|
-
# 降级:只留事件痕迹(可观测、可归因),不写记忆内容
|
|
526
489
|
try:
|
|
527
490
|
_spine = _caps.get("event_spine") if _caps is not None else None
|
|
528
491
|
if _spine is not None:
|
|
@@ -534,12 +497,8 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
534
497
|
except Exception:
|
|
535
498
|
pass
|
|
536
499
|
|
|
537
|
-
# ── 统一任务生命周期(模板方法,P1:5 个 run_task 收敛为 1)──────
|
|
538
|
-
# 领域 Agent 不再各自重写 run_task,只实现 _execute()(召回后的领域步骤)。
|
|
539
|
-
# 会话开启、记忆召回、返回体结构、Harness 拦截处理都在此**唯一**定义,
|
|
540
|
-
# 保证所有 Agent 生命周期一致(修复"每 Agent 各写一份、行为漂移")。
|
|
541
500
|
|
|
542
|
-
_recall_topk: int = 5
|
|
501
|
+
_recall_topk: int = 5
|
|
543
502
|
|
|
544
503
|
def _task_entities(self, task: str) -> list:
|
|
545
504
|
"""本任务的基线实体(用于 Harness 目标基线)。领域 Agent 覆盖。"""
|
|
@@ -549,12 +508,6 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
549
508
|
"""记忆召回查询(领域 Agent 可加前缀,如财务/行程)。默认用原任务。"""
|
|
550
509
|
return task
|
|
551
510
|
|
|
552
|
-
# ── 【P1-1】任务生命周期钩子 ────────────────────────────────
|
|
553
|
-
# 崩溃/重启恢复记账(RestartRecovery)。会话开启后、管线执行前触发 started;任务终结时
|
|
554
|
-
# 触发 settled(done=正常终态含 Harness 兜底/中断;failed=未兜底异常)。run_task 与
|
|
555
|
-
# run_task_stream 共用 _run_task_core,故两条路径都会触发。
|
|
556
|
-
# 实现放在基类(而不是只让主管覆盖):P1-1 时只有主管接了线,领域 Agent 跑一半崩掉
|
|
557
|
-
# 在恢复扫描里根本不可见。没有 _recovery 的 standalone/测试 Agent → 直接跳过。
|
|
558
511
|
def _on_session_started(self, session_id: str, task: str, context: dict) -> None:
|
|
559
512
|
"""会话已开、管线未跑:落 task_started(崩溃后才认得出"上次跑到一半")。"""
|
|
560
513
|
rec = getattr(self, "_recovery", None)
|
|
@@ -618,7 +571,7 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
618
571
|
pipe = self._pipeline_cache = CognitivePipeline(DEFAULT_STAGES)
|
|
619
572
|
return pipe
|
|
620
573
|
|
|
621
|
-
async def run_task(self, task: str, context: dict = None, stop_event=None) -> dict:
|
|
574
|
+
async def run_task(self, task: str, context: dict | None = None, stop_event=None) -> dict:
|
|
622
575
|
"""统一任务生命周期:开会话 → [认知管线:recall → act] → 组装返回。
|
|
623
576
|
Harness BLOCK 统一在此兜底为结构化错误。返回
|
|
624
577
|
{"result",...,"session_id","agent","thought_summary", **extra}。
|
|
@@ -633,7 +586,7 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
633
586
|
说明续了哪一次、复用了多少步。"""
|
|
634
587
|
return await self._run_task_core(task, context, stop_event=stop_event, on_stage=None)
|
|
635
588
|
|
|
636
|
-
async def _run_task_core(self, task: str, context: dict = None,
|
|
589
|
+
async def _run_task_core(self, task: str, context: dict | None = None,
|
|
637
590
|
stop_event=None, on_stage=None) -> dict:
|
|
638
591
|
"""run_task 的执行核心 —— 供同步 `run_task` 与流式 `run_task_stream` **共用**,避免两份
|
|
639
592
|
生命周期逻辑漂移。`on_stage`(D1):逐 stage 进度回调,透传给管线;None=同步路径不回调,
|
|
@@ -641,29 +594,18 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
641
594
|
from ..pipeline import PipelineContext
|
|
642
595
|
context = context or {}
|
|
643
596
|
session_id = self.start_session(goal=task, entities=self._task_entities(task))
|
|
644
|
-
# 来源可信性:渠道入口会带 untrusted_source=True(见 channels/dispatcher)。本任务里的
|
|
645
|
-
# 写/发送/外发动作据此强制 HITL;本地 HTTP 对话不带此键 → 行为逐字不变。
|
|
646
597
|
_untrusted_set(self, bool(context.get("untrusted_source")))
|
|
647
|
-
# 【P1-1】生命周期钩子:会话已开、管线未跑。默认 no-op;主管借此做崩溃/重启
|
|
648
|
-
# 恢复记账(task_started)——恢复记账需先于执行落日志,故必须在管线之前。
|
|
649
598
|
self._on_session_started(session_id, task, context)
|
|
650
|
-
# E2:pre_reasoning(agent_start)钩子——推理开始前触发,可注入 additionalContext(前插进 act
|
|
651
|
-
# 的 LLM 上下文,见 _call_llm)。默认无钩子/无注入 → 行为逐字不变。hooks 由 Foxclaw 装配注入;
|
|
652
|
-
# standalone/测试 Agent 无 hooks 属性 → getattr None → 整个钩子逻辑跳过。
|
|
653
599
|
_hooks = getattr(self, "hooks", None)
|
|
654
600
|
_inject_parts = []
|
|
655
601
|
if _hooks is not None:
|
|
656
602
|
_hr = _hooks.fire("agent_start", "", {"task": task, "agent_id": self.AGENT_ID})
|
|
657
603
|
if _hr.additional_context:
|
|
658
604
|
_inject_parts.append(_hr.additional_context)
|
|
659
|
-
_pref = self._prefs_context()
|
|
605
|
+
_pref = self._prefs_context()
|
|
660
606
|
if _pref:
|
|
661
607
|
_inject_parts.append(_pref)
|
|
662
|
-
# 偏好 + agent_start 钩子上下文合并进同一 _reasoning_inject → _call_llm 前插(复用 E2 机制)
|
|
663
608
|
_inject_tok = _reasoning_inject.set("\n\n".join(_inject_parts)) if _inject_parts else None
|
|
664
|
-
# 【P1 断点续跑】context={"_resume_task_id": <上次的 task_id/session_id>} 时,把上次
|
|
665
|
-
# 已完成的工具步骤装进 contextvar(root_key→结果):工具轮命中同指纹步骤直接复用结果、
|
|
666
|
-
# 不再重复执行。opt-in——不带该键 → 不装载、行为逐字不变。
|
|
667
609
|
_resume_tid = str(context.get("_resume_task_id") or "").strip()
|
|
668
610
|
_resume_cache = self._load_resume_cache(_resume_tid) if _resume_tid else {}
|
|
669
611
|
_resume_tok = _resume_steps.set(_resume_cache) if _resume_cache else None
|
|
@@ -685,19 +627,14 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
685
627
|
"thought_summary": self.thought_chain.get_summary(self.AGENT_ID),
|
|
686
628
|
**(ctx.extra or {}),
|
|
687
629
|
}
|
|
688
|
-
_plan = _plan_payload(ctx)
|
|
630
|
+
_plan = _plan_payload(ctx)
|
|
689
631
|
if _plan:
|
|
690
632
|
resp["plan"] = _plan
|
|
691
|
-
if _resume_cache:
|
|
633
|
+
if _resume_cache:
|
|
692
634
|
resp["resumed_from"] = _resume_tid
|
|
693
635
|
resp["resumed_steps"] = len(_resume_cache)
|
|
694
|
-
if ctx.interrupted:
|
|
636
|
+
if ctx.interrupted:
|
|
695
637
|
resp["interrupted"] = True
|
|
696
|
-
# 【A·Step3·确认闸提升】act 若经 skill dispatch 且未成功(needs_confirmation/blocked/
|
|
697
|
-
# circuit_open/error),把该态提升为 run_task **顶层 status**,让调用方无需深挖 result
|
|
698
|
-
# 即可识别"待确认/未完成动作";needs_confirmation 附 pending_action(供确认后带
|
|
699
|
-
# _confirmed 重跑恢复执行)。**未完成 ⇒ 不作为已完成真值沉淀**(blocked=True,呼应
|
|
700
|
-
# "被拦截任务不沉淀记忆");成功/纯 LLM 路径行为逐字不变。
|
|
701
638
|
skill_status = (ctx.extra or {}).get("skill_status")
|
|
702
639
|
skill_incomplete = bool(skill_status) and skill_status != "done"
|
|
703
640
|
if skill_incomplete:
|
|
@@ -711,41 +648,35 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
711
648
|
"hint": "确认后带 context={'_confirmed': <skill_id>, ...} 重跑本任务以执行"
|
|
712
649
|
"(绑定到 skill_id 更安全;_confirmed=True 为概括确认)",
|
|
713
650
|
}
|
|
714
|
-
# 「未完成 ⇒ 不作为已完成真值沉淀」的 blocked 判定,须同时覆盖两类未完成:
|
|
715
|
-
# ① skill 未成功(needs_confirmation/blocked/circuit_open/error);
|
|
716
|
-
# ② 被 stop_event **中断**(B3 深审 G2):流式任务客户端断连 → 中断,ctx.result 只是
|
|
717
|
-
# 部分/空产物,绝不能当"已完成真值"沉淀进 HMR 共享记忆(呼应 P5/Principle07)。
|
|
718
|
-
# P3 引入 interrupted 时未联动此处;B3 的 /api/task/stream 是首个真正会 set stop_event
|
|
719
|
-
# 的生产调用方,才使这条路径可达。非中断路径 blocked 值与改前逐字一致。
|
|
720
651
|
blocked = skill_incomplete or ctx.interrupted
|
|
721
652
|
self._emit_execution(task, ctx.result, blocked=blocked, context=context)
|
|
722
|
-
self._maybe_store_preference(task)
|
|
723
|
-
if _hooks is not None:
|
|
653
|
+
self._maybe_store_preference(task)
|
|
654
|
+
if _hooks is not None:
|
|
724
655
|
_hooks.fire("agent_end", "", {"task": task, "agent_id": self.AGENT_ID,
|
|
725
656
|
"result": resp.get("result"),
|
|
726
657
|
"interrupted": bool(ctx.interrupted)})
|
|
727
|
-
self._on_task_settled(session_id, "done")
|
|
658
|
+
self._on_task_settled(session_id, "done")
|
|
728
659
|
return resp
|
|
729
660
|
except HarnessBlockedError as e:
|
|
730
661
|
self._emit_execution(
|
|
731
662
|
task, {"error": "harness_blocked", "reason": e.result.reason},
|
|
732
663
|
blocked=True, context=context)
|
|
733
|
-
if _hooks is not None:
|
|
664
|
+
if _hooks is not None:
|
|
734
665
|
_hooks.fire("agent_end", "", {"task": task, "agent_id": self.AGENT_ID,
|
|
735
666
|
"error": "harness_blocked"})
|
|
736
|
-
self._on_task_settled(session_id, "done")
|
|
667
|
+
self._on_task_settled(session_id, "done")
|
|
737
668
|
return {"error": "harness_blocked", "reason": e.result.reason,
|
|
738
669
|
"session_id": session_id}
|
|
739
670
|
except Exception:
|
|
740
|
-
self._on_task_settled(session_id, "failed")
|
|
671
|
+
self._on_task_settled(session_id, "failed")
|
|
741
672
|
raise
|
|
742
673
|
finally:
|
|
743
674
|
if _inject_tok is not None:
|
|
744
|
-
_reasoning_inject.reset(_inject_tok)
|
|
675
|
+
_reasoning_inject.reset(_inject_tok)
|
|
745
676
|
if _resume_tok is not None:
|
|
746
|
-
_resume_steps.reset(_resume_tok)
|
|
677
|
+
_resume_steps.reset(_resume_tok)
|
|
747
678
|
|
|
748
|
-
def run_task_stream(self, task: str, context: dict = None, stop_event=None):
|
|
679
|
+
def run_task_stream(self, task: str, context: dict | None = None, stop_event=None):
|
|
749
680
|
"""流式任务执行(D1):**同步 generator**,逐 stage 吐进度 + act 逐 token 出字 + 最终结果。
|
|
750
681
|
产出事件(dict,逐条给 SSE):
|
|
751
682
|
{"stage": name} 每完成一个认知 stage(perceive→…→reflect)
|
|
@@ -769,7 +700,7 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
769
700
|
|
|
770
701
|
def _on_stage(name, _ctx):
|
|
771
702
|
bridge.put({"stage": name})
|
|
772
|
-
if name == "plan":
|
|
703
|
+
if name == "plan":
|
|
773
704
|
_plan = _plan_payload(_ctx)
|
|
774
705
|
if _plan:
|
|
775
706
|
bridge.put({"plan": _plan})
|
|
@@ -783,11 +714,11 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
783
714
|
resp = _asyncio.run(self._run_task_core(
|
|
784
715
|
task, context, stop_event=stop_event, on_stage=_on_stage))
|
|
785
716
|
bridge.put({"result": resp})
|
|
786
|
-
except Exception as e:
|
|
717
|
+
except Exception as e:
|
|
787
718
|
bridge.put({"error": str(e)})
|
|
788
719
|
finally:
|
|
789
720
|
_act_stream.reset(token)
|
|
790
|
-
bridge.put(_SENTINEL)
|
|
721
|
+
bridge.put(_SENTINEL)
|
|
791
722
|
|
|
792
723
|
_threading.Thread(target=_worker, daemon=True, name="task-stream").start()
|
|
793
724
|
while True:
|
|
@@ -824,7 +755,6 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
824
755
|
(路由分发由 supervisor_stages 的 dispatch stage 承担),不会走到本钩子。"""
|
|
825
756
|
raise NotImplementedError(f"{self.AGENT_ID} 未实现 _execute()")
|
|
826
757
|
|
|
827
|
-
# ── 编排协作接口(P3:领域知识归属领域 Agent,编排层只做通用聚合/路由)──────
|
|
828
758
|
def situation(self) -> dict:
|
|
829
759
|
"""本 Agent 的领域态势:声明自己的 domain + 状态 + 告警。默认无领域态势。
|
|
830
760
|
领域 Agent 覆盖。编排层据此**通用聚合**,不需懂任何领域内部 API。
|
|
@@ -849,7 +779,6 @@ class CognitiveAgent(AugmentMixin, LlmMixin, MemoryMixin,
|
|
|
849
779
|
req.status = "approved"
|
|
850
780
|
req.verdict = "无需特殊审批"
|
|
851
781
|
|
|
852
|
-
# ── 通用状态查询 ───────────────────────────────────────
|
|
853
782
|
|
|
854
783
|
def get_status(self) -> dict:
|
|
855
784
|
return {
|
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from ...core.dispatch import dispatch_failed as _dispatch_failed # noqa: F401 (re-export)
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
PRIOR_MAX_STEPS = 3 # 只带最近 N 步(更早的通常已被后续步骤消化)
|
|
8
|
-
PRIOR_MAX_CHARS = 300 # 单步输出截断
|
|
5
|
+
PRIOR_MAX_STEPS = 3
|
|
6
|
+
PRIOR_MAX_CHARS = 300
|
|
9
7
|
|
|
10
8
|
|
|
11
9
|
def _with_prior(subtask: str, prior: list) -> str:
|
|
@@ -49,12 +47,6 @@ def _fmt_mem(m: dict, max_chars: int | None = None) -> str:
|
|
|
49
47
|
return f"{head}:{content}" if head else content
|
|
50
48
|
|
|
51
49
|
|
|
52
|
-
# ── P0-1 反思重规划:执行结果判定 ────────────────────────────
|
|
53
|
-
# 实现已下沉到 core.dispatch(见文件头 import):pipeline 的 replan stage 也要用它,
|
|
54
|
-
# 而 pipeline 不能反向 import agents —— 两处都只依赖 core,依赖方向单一。
|
|
55
|
-
# `_dispatch_failed` 在此保留为 re-export,历史导入路径(agents.agents / impl.supervisor)不变。
|
|
56
|
-
|
|
57
|
-
|
|
58
50
|
def _extract_step_results(dr) -> list:
|
|
59
51
|
"""从 dispatch_result 提取各步结果(供 LLM 反思:含成功/失败状态与输出)。"""
|
|
60
52
|
if not isinstance(dr, dict):
|