Przeglądaj źródła

生产装配:把 Context Broker 接入真实脚本构建依赖图

在 compose_host 中以同一个 MissionWorkbench 注入 Planner TaskContextProvider、Worker/Validator RoleContextProvider 和工具 Gateway。连接 RawArtifactStore、Root闭包、Trace receipt 与语义缓存目录,移除旧图片适配依赖,并同步生产依赖和启动测试。
SamLee 10 godzin temu
rodzic
commit
a7dfdc9171

+ 17 - 3
script_build_host/src/script_build_host/composition.py

@@ -25,6 +25,7 @@ from script_build_host.agents.prompt_catalog import (
     script_prompt_requests,
 )
 from script_build_host.api import ApiSecurity, UploadedTopicGateway, create_app
+from script_build_host.application.context_semantics import AdaptiveSemanticSelector
 from script_build_host.application.input_snapshot_service import ScriptInputSnapshotService
 from script_build_host.application.mission_factory import ScriptMissionFactory
 from script_build_host.application.mission_service import (
@@ -63,7 +64,7 @@ from script_build_host.domain.task_contracts import PhaseTwoLimits
 from script_build_host.infrastructure.outbound import OutboundPolicy
 from script_build_host.infrastructure.task_contract_store import FileScriptTaskContractStore
 from script_build_host.tools import LegacyScriptToolGateway, register_script_tools
-from script_build_host.tools.gateway import ImageAdapter, RetrievalAdapter
+from script_build_host.tools.gateway import RetrievalAdapter
 
 
 @dataclass(frozen=True)
@@ -80,7 +81,6 @@ class HostDependencies:
     principal_provider: PrincipalProvider
     build_authorizer: BuildAuthorizer
     retrieval_adapters: Mapping[str, RetrievalAdapter] = field(default_factory=dict)
-    image_adapter: ImageAdapter | None = None
     uploaded_topics: UploadedTopicGateway | None = None
     orchestration_config: Any = None
     event_sink: Any = None
@@ -186,6 +186,14 @@ def compose_host(dependencies: HostDependencies) -> HostComposition:
         task_store=dependencies.task_store,
         artifacts=dependencies.business_artifacts,
     )
+    semantic_call = getattr(dependencies.runner, "utility_llm_call", None) or getattr(
+        dependencies.runner, "llm_call", None
+    )
+    semantic_cache_root = (
+        dependencies.agent_data_root / "context-broker" / "semantic-cache"
+        if dependencies.agent_data_root is not None
+        else None
+    )
     workbench = MissionWorkbench(
         task_store=dependencies.task_store,
         bindings=dependencies.bindings,
@@ -193,6 +201,13 @@ def compose_host(dependencies: HostDependencies) -> HostComposition:
         artifacts=dependencies.business_artifacts,
         contracts=contract_store,
         candidates=candidate_service,
+        root_tools=root_delivery,
+        raw_artifacts=dependencies.raw_artifact_store,
+        semantic_selector=AdaptiveSemanticSelector(
+            llm_call=semantic_call,
+            cache_root=semantic_cache_root,
+        ),
+        receipt_store=getattr(dependencies.runner, "trace_store", None),
     )
     deterministic_worker = ScriptBuildDeterministicWorker(
         candidate_service, bindings=dependencies.bindings
@@ -262,7 +277,6 @@ def compose_host(dependencies: HostDependencies) -> HostComposition:
         artifacts=dependencies.business_artifacts,
         coordinator=coordinator,
         retrieval_adapters=dependencies.retrieval_adapters,
-        image_adapter=dependencies.image_adapter,
         dispatch_guard=mission_service,
         planner_tools=planning_service,
         candidate_tools=candidate_service,

+ 5 - 10
script_build_host/src/script_build_host/production.py

@@ -24,7 +24,6 @@ from script_build_host.adapters import (
     OpenAICompatibleEmbeddingClient,
     PatternRetrievalAdapter,
     SafeHttpClient,
-    SafeImageAdapter,
     SqlAlchemyStrategySource,
     SqlUploadedTopicGateway,
 )
@@ -185,6 +184,8 @@ def compose_production_host(
         timeout_seconds=settings.request_timeout_seconds,
         max_response_bytes=settings.max_response_bytes,
     )
+    data_root = settings.agent_data_root.resolve()
+    raw_artifacts = FileRawArtifactStore(data_root)
     embedding_client: OpenAICompatibleEmbeddingClient | None = None
     if settings.embedding_api_key is not None and settings.embedding_endpoint:
         embedding_client = OpenAICompatibleEmbeddingClient(
@@ -208,6 +209,7 @@ def compose_production_host(
         retrieval_adapters["pattern"] = PatternRetrievalAdapter(
             settings.pattern_endpoint,
             safe_http,
+            raw_artifacts,
             poll_interval_seconds=settings.poll_interval_seconds,
             poll_timeout_seconds=settings.request_timeout_seconds,
         )
@@ -215,6 +217,7 @@ def compose_production_host(
         retrieval_adapters["decode"] = HttpDecodeRetrievalAdapter(
             settings.decode_endpoint,
             safe_http,
+            raw_artifacts,
         )
     elif vector_decode and settings.decode_raw_root is not None:
         retrieval_adapters["decode"] = LegacyVectorDecodeRetrievalAdapter(
@@ -244,6 +247,7 @@ def compose_production_host(
         retrieval_adapters["external"] = ExternalRetrievalAdapter(
             settings.external_endpoint,
             safe_http,
+            raw_artifacts,
         )
     retrieval_adapters["knowledge"] = (
         LegacyLlmKnowledgeRetrievalAdapter(
@@ -285,10 +289,8 @@ def compose_production_host(
         decode_path=decode_path,
         knowledge_path=knowledge_path,
     )
-    data_root = settings.agent_data_root.resolve()
     fresh_local_outputs = settings.environment.lower() == "test"
     output_reads = database.write if fresh_local_outputs else database.read
-    raw_artifacts = FileRawArtifactStore(data_root)
     candidate_workspaces = SqlAlchemyCandidateWorkspaceRepository(database.write, artifacts)
     legacy_projection = LegacyDetailProjectionService(
         output_reads,
@@ -325,13 +327,6 @@ def compose_production_host(
             principal_provider=runtime.principal_provider,
             build_authorizer=runtime.build_authorizer,
             retrieval_adapters=retrieval_adapters,
-            image_adapter=SafeImageAdapter(
-                safe_http,
-                raw_artifacts,
-                max_images=settings.max_images,
-                max_image_bytes=settings.max_image_bytes,
-                max_total_image_bytes=settings.max_total_image_bytes,
-            ),
             uploaded_topics=SqlUploadedTopicGateway(
                 database.write,
                 max_payload_bytes=settings.max_upload_bytes,

+ 2 - 1
script_build_host/tests/test_internal_runtime.py

@@ -45,7 +45,8 @@ def test_role_model_manifest_covers_all_sixteen_presets_and_keeps_overrides() ->
     )
     presets = result["presets"]
     assert isinstance(presets, dict)
-    assert len(presets) == 16
+    assert len(presets) == 17
+    assert presets["script_context_broker"]["temperature"] == 0.0
     assert {value["model"] for value in presets.values()} == {"qwen3.7-max"}
     assert presets["script_root_validator"]["temperature"] == 0.0
     assert presets["script_planner"]["max_iterations"] == 100

+ 1 - 1
script_build_host/tests/test_production_composition.py

@@ -122,7 +122,7 @@ async def test_production_expands_one_internal_model_to_all_script_roles(tmp_pat
     manifest = host.composition.mission_service.phase_two_model_manifest
     presets = manifest["presets"]
     assert isinstance(presets, dict)
-    assert len(presets) == 16
+    assert len(presets) == 17
     assert presets["script_root_worker"]["model"] == "qwen3.7-max"
     assert presets["script_root_validator"]["max_iterations"] == 25
     await host.close()