"""Kompilacja prymitywów APM do obiektów agno. To jest sedno rozwiązania: definicja agenta (rola, model, narzędzia, skille, schemat wyjścia) jest wersjonowanym artefaktem APM, a nie kodem. Zmiana zachowania sieci agentowej nie wymaga zmiany Pythona - wymaga podbicia wersji pakietu kontekstowego. """ from __future__ import annotations from collections.abc import Callable from typing import Any from agno.agent import Agent from agno.skills import LocalSkills, Skills from ..config import Settings from ..llm import ModelFactory from .loader import ApmContext from .primitives import AgentPrimitive class ToolResolutionError(RuntimeError): pass def _resolve_tools(names: list[str], registry: dict[str, Callable[..., Any]]) -> list[Callable[..., Any]]: unknown = [n for n in names if n not in registry] if unknown: raise ToolResolutionError( f"Definicja agenta odwołuje się do nieistniejących narzędzi: {', '.join(unknown)}. " f"Dostępne: {', '.join(sorted(registry))}" ) return [registry[n] for n in names] def build_skills(ctx: ApmContext, names: list[str]) -> Skills | None: """Buduje natywny obiekt `Skills` agno z katalogów skilli dostarczonych przez APM. agno waliduje katalogi względem specyfikacji Agent Skills - niepoprawny skill wysadza przebieg na starcie, a nie w połowie modyfikacji kodu. """ if not names: return None paths = ctx.skill_paths(names) return Skills(loaders=[LocalSkills(str(p)) for p in paths]) def build_agent( definition: AgentPrimitive, ctx: ApmContext, settings: Settings, model_factory: ModelFactory, tool_registry: dict[str, Callable[..., Any]], schema_registry: dict[str, type] | None = None, ) -> Agent: schema_registry = schema_registry or {} instructions: list[str] = [] instructions.extend(ctx.context_bodies(definition.context_names)) instructions.append(definition.body) instructions.extend(ctx.instruction_bodies(definition.instruction_names)) output_schema = None if definition.output_schema_name: if definition.output_schema_name not in schema_registry: raise KeyError( f"Agent '{definition.name}' deklaruje output_schema='{definition.output_schema_name}', " f"którego nie ma w rejestrze schematów: {sorted(schema_registry)}" ) output_schema = schema_registry[definition.output_schema_name] return Agent( name=definition.name, description=definition.description or None, model=model_factory.for_profile(definition.model_profile, definition.temperature), instructions=instructions, tools=_resolve_tools(definition.tool_names, tool_registry) or None, skills=build_skills(ctx, definition.skill_names), output_schema=output_schema, tool_call_limit=definition.tool_call_limit or settings.tool_call_limit, markdown=False, telemetry=False, add_datetime_to_context=True, # Nazwa roli w kontekście systemowym: pomaga modelowi trzymać się swojego zadania, # a atrapie LLM (llm/mock_server.py) rozpoznać, który agent pyta. add_name_to_context=True, retries=2, )