|
| 1 | +"""L1.5 — Automated Design of Agentic Systems (ADAS). |
| 2 | +
|
| 3 | +Self-Evolving Architecture: If the static experts (Hegelian, Rigor Sentinel) |
| 4 | +are insufficient for a highly specific query, this layer dynamically writes |
| 5 | +a custom Pydantic Schema and instantiates a new Expert Node on the fly. |
| 6 | +""" |
| 7 | +from typing import Dict, Any, Type |
| 8 | +from pydantic import BaseModel, create_model, Field |
| 9 | +from loguru import logger |
| 10 | + |
| 11 | +from epistemic_forge.models import ProjectSpec, DynamicExpertSchema |
| 12 | +from epistemic_forge.llm import generate_structured |
| 13 | +from epistemic_forge.experts.base import EpistemicExpert |
| 14 | + |
| 15 | +def generate_dynamic_expert(spec: ProjectSpec) -> EpistemicExpert: |
| 16 | + """Uses LLM to design a custom expert class and Pydantic schema.""" |
| 17 | + |
| 18 | + logger.info("🧬 L1.5 ADAS: Generating a custom Self-Evolving Expert tailored to this query...") |
| 19 | + |
| 20 | + messages = [ |
| 21 | + {"role": "system", "content": "You are a Meta-Architect (ADAS). Your job is to design a highly specialized 'AI Expert Node' that is perfectly tailored to solve the user's specific problem. Define its output schema and its system prompt."}, |
| 22 | + {"role": "user", "content": f"Problem: {spec.question}\nKeywords: {spec.keywords}\n\nDesign the perfect expert to analyze this."} |
| 23 | + ] |
| 24 | + |
| 25 | + blueprint: DynamicExpertSchema = generate_structured( |
| 26 | + messages=messages, |
| 27 | + response_model=DynamicExpertSchema, |
| 28 | + model=spec.target_model, |
| 29 | + api_base=spec.api_base |
| 30 | + ) |
| 31 | + |
| 32 | + logger.debug(f"🧬 Blueprint acquired: {blueprint.expert_class_name}") |
| 33 | + |
| 34 | + # Dynamically create the Pydantic Model based on the LLM's design |
| 35 | + field_definitions = {} |
| 36 | + for f in blueprint.fields_to_extract: |
| 37 | + for fname, fdesc in f.items(): |
| 38 | + # Clean field name to be a valid python identifier |
| 39 | + safe_fname = "".join(c for c in fname if c.isalnum() or c == "_").lower() |
| 40 | + if safe_fname: |
| 41 | + field_definitions[safe_fname] = (str, Field(description=fdesc)) |
| 42 | + |
| 43 | + DynamicModel = create_model(f"{blueprint.expert_class_name}Output", **field_definitions) |
| 44 | + |
| 45 | + # Create the Expert Class dynamically |
| 46 | + class DynamicallyGeneratedExpert(EpistemicExpert): |
| 47 | + @property |
| 48 | + def expert_name(self) -> str: |
| 49 | + return blueprint.expert_class_name |
| 50 | + |
| 51 | + def analyze(self, spec: ProjectSpec, context: Dict[str, Any]) -> BaseModel: |
| 52 | + logger.debug(f"Activating dynamically generated expert: {self.expert_name}") |
| 53 | + msgs = [ |
| 54 | + {"role": "system", "content": blueprint.system_prompt}, |
| 55 | + {"role": "user", "content": f"Problem: {spec.question}\nContext: {context}\nAnalyze this."} |
| 56 | + ] |
| 57 | + return generate_structured( |
| 58 | + messages=msgs, |
| 59 | + response_model=DynamicModel, |
| 60 | + model=spec.target_model, |
| 61 | + api_base=spec.api_base |
| 62 | + ) |
| 63 | + |
| 64 | + return DynamicallyGeneratedExpert() |
0 commit comments