同一モデル設定を共有するデプロイ構成(compose.yml)

order-servicemenu-service は、いずれも同じ環境変数名のモデル設定を受け取ります。

# food-delivery-demo/compose.yml(抜粋)
order-service:
  environment:
    ARACHNE_STRANDS_MODEL_ID: ${ARACHNE_STRANDS_MODEL_ID:-}
    ARACHNE_STRANDS_MODEL_REGION: ${ARACHNE_STRANDS_MODEL_REGION:-}

menu-service:
  environment:
    ARACHNE_STRANDS_MODEL_PROVIDER: ${ARACHNE_STRANDS_MODEL_PROVIDER:-}
    ARACHNE_STRANDS_MODEL_ID: ${ARACHNE_STRANDS_MODEL_ID:-}
    ARACHNE_STRANDS_MODEL_REGION: ${ARACHNE_STRANDS_MODEL_REGION:-}

この構成で同じモデル設定を共有できますが、サービス境界の有無は呼び出し経路で確認します。

order-service 側の LLM 呼び出しは service-local(ArachneOrderIntentPlanner.java)

order-serviceorder-intake-agent の system prompt と structured output 型を自サービス内で定義し、独立して LLM を呼び出します。

// ArachneOrderIntentPlanner.java(抜粋)
AgentResult result = observationSupport.observe(
        "order-service",
        AGENT_NAME,
        sessionId,
        prompt.render(),
        () -> agentFactory.builder()
                .systemPrompt(systemPrompt(locale))
                .build()
                .run(prompt.render(), NormalizedOrderIntent.class));

この呼び出しは NormalizedOrderIntent.class の範囲で完結し、menu-service の型や tool 定義に依存しません。

サービス間は HTTP ペイロードで接続(RegistryBackedMenuGateway.java)

order-service から menu-service への接続は、POST /internal/menu/suggest の REST 呼び出しです。

// RegistryBackedMenuGateway.java(抜粋)
Objects.requireNonNull(restClient.post()
    .uri(endpointResolver.resolveUrl(menuCapabilityQuery, "/internal/menu/suggest"))
    .header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken)
    .contentType(MediaType.APPLICATION_JSON)
    .body(request)
    .retrieve()
    .body(MenuSuggestionResponse.class));

ここでの境界は LLM ではなく HTTP 契約です。order-serviceMenuSuggestionRequest を送り、MenuSuggestionResponse を受け取ります。

menu-service は別の system prompt と別の tool セットで、独立して LLM を呼び出します。

// MenuApplicationService.java(抜粋)
AgentResult result = agentObservationSupport.observe(
        "menu-service", "menu-agent", request.sessionId(), userPrompt.render(), agentState,
        () -> agentFactory.builder()
            .sessionId(request.sessionId())
            .state(agentState)
            .systemPrompt(buildSuggestionSystemPrompt())
            .tools(catalogLookupTool, calculateTotalTool)
            .build()
            .run(userPrompt.render(), MenuSelectionDecision.class));

出力型は MenuSelectionDecision.class で、order-service の NormalizedOrderIntent とは別の契約です。

観察された振る舞い

  • order-service の責務は「注文意図の正規化」で、menu-service の責務は「catalog grounding と候補選定」に分離されていた。
  • 両サービスは同じモデル設定を共有できるが、system prompt・tool・structured output 型はサービスごとに独立して定義されていた。
    • サービス間で渡るのは MenuSuggestionRequest の HTTP ペイロードのみで、LLM の内部状態を共有する経路はコード上に現れなかった。
  • obs-01 の trace 記録でも order-intake-agentmenu-agent は別サービスの別イベントとして記録されていた。

確認できる場所

  • food-delivery-demo/compose.yml(モデル設定環境変数の共有)
  • order-service/.../ArachneOrderIntentPlanner.java(order-service 内の LLM 呼び出し)
  • order-service/.../RegistryBackedMenuGateway.java(service 間の HTTP 契約)
  • menu-service/.../MenuApplicationService.java(menu-service 内の LLM 呼び出し)
  • site/src/content/observations/01-natural-language-front-door.mdx(両サービスの trace 例)