# Сесия 5: Бързо изграждане на AI-задвижвани агенти с Foundry Local Забележка: Възможностите на агентите в Foundry Local се развиват—потвърдете поддръжката в последните бележки за изданието преди да приложите сложни модели. ## Преглед Използвайте Foundry Local за бързо прототипиране на приложения с агенти: системни подсказки, основи и модели за оркестрация. Когато има поддръжка за агенти, можете да стандартизирате на OpenAI-съвместимо извикване на функции или да използвате Azure AI Agents в облачни хибридни дизайни. > **🔄 Актуализирано за модерния SDK**: Този модул е съобразен с последните модели на Microsoft Foundry-Local и съответства на цялостната имплементация в `samples/05/`. Примерите вече използват модерния `foundry-local-sdk` и клиента на `OpenAI` вместо ръчни заявки. **🏗️ Основни моменти от архитектурата:** - **Специализирани агенти**: Агенти за извличане, разсъждение и изпълнение с различни възможности - **Модел на координатор**: Оркестрира работни потоци с множество агенти с обратна връзка - **Интеграция с модерен SDK**: Използва `FoundryLocalManager` и клиента на OpenAI - **Готово за производство**: Включва обработка на грешки, мониторинг на производителността и проверки на състоянието - **Цялостни примери**: Интерактивен Jupyter notebook с разширени функции **📁 Локална имплементация:** - `samples/05/multi_agent_orchestration.ipynb` - Интерактивни примери и бенчмаркове - `samples/05/agents/specialists.py` - Имплементации на агенти - `samples/05/agents/coordinator.py` - Логика за оркестрация Референции: - Документация за Foundry Local: https://learn.microsoft.com/en-us/azure/ai-foundry/foundry-local/ - Azure AI Foundry Agents: https://learn.microsoft.com/en-us/azure/ai-services/agents/overview - Пример за извикване на функции (Foundry Local samples): https://github.com/microsoft/Foundry-Local/tree/main/samples/python/functioncalling ## Цели на обучението - Проектиране на системни подсказки и стратегии за основи за надеждно поведение - Имплементиране на модели за извикване на функции (използване на инструменти) - Оркестриране на работни потоци с множество агенти (локални и хибридни) - Планиране за наблюдаемост и безопасност ## Част 1: Системни подсказки и основи - Дефинирайте строги роли, ограничения и схеми за изход - Основавайте отговорите с локални или корпоративни данни - Налагайте JSON изходи за автоматизация надолу по веригата ## Част 2: Извикване на функции (Модерен SDK подход) ```python # tools.py import json from typing import List, Dict, Any def get_weather(city: str) -> str: return f"Weather in {city}: Sunny, 25C" # Modern tools format for OpenAI API TOOLS = [ { "type": "function", "function": { "name": "get_weather", "description": "Get current weather for a city", "parameters": { "type": "object", "properties": { "city": {"type": "string", "description": "City name"} }, "required": ["city"] } } } ] ``` ```python # agent.py from foundry_local import FoundryLocalManager from openai import OpenAI import json from tools import TOOLS, get_weather # Initialize Foundry Local Manager alias = "phi-4-mini" manager = FoundryLocalManager(alias) # Create OpenAI client using Foundry Local endpoint client = OpenAI( base_url=manager.endpoint, api_key=manager.api_key ) SYSTEM_PROMPT = "You are a helpful assistant. Use tools when needed." def process_function_call(messages: List[Dict], tools: List[Dict]) -> str: """Process function calling with modern OpenAI API.""" try: response = client.chat.completions.create( model=manager.get_model_info(alias).id, messages=messages, tools=tools, tool_choice="auto" ) message = response.choices[0].message if message.tool_calls: # Handle function calls messages.append(message) for tool_call in message.tool_calls: if tool_call.function.name == "get_weather": args = json.loads(tool_call.function.arguments) result = get_weather(args["city"]) # Add function result to messages messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": result }) # Get final response final_response = client.chat.completions.create( model=manager.get_model_info(alias).id, messages=messages ) return final_response.choices[0].message.content else: return message.content except Exception as e: return f"Error: {str(e)}" # Example usage messages = [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": "What's the weather in Paris?"} ] result = process_function_call(messages, TOOLS) print(result) ``` Изпълнение: ```powershell # Ensure Foundry Local is running with a model foundry model run phi-4-mini python agent.py ``` ## Част 3: Оркестрация с множество агенти (Модел) Проектирайте координатор, който насочва задачи към специализирани агенти (извличане, разсъждение, изпълнение) с помощта на OpenAI-съвместимия край на Foundry Local. Стъпка 1) Дефинирайте специализирани агенти с модерен SDK (вижте `samples/05/agents/specialists.py`) ```python # agents/specialists.py from foundry_local import FoundryLocalManager from openai import OpenAI from typing import List, Dict, Any class FoundryClient: """Shared client for all specialist agents.""" def __init__(self, model_alias: str = "phi-4-mini"): self.client = None self.model_name = None self.model_alias = model_alias self._initialize_client() def _initialize_client(self): """Initialize OpenAI client with Foundry Local.""" try: manager = FoundryLocalManager(self.model_alias) model_info = manager.get_model_info(self.model_alias) self.client = OpenAI( base_url=manager.endpoint, api_key=manager.api_key ) self.model_name = model_info.id print(f"✅ Foundry Local initialized with model: {self.model_name}") except Exception as e: print(f"❌ Error initializing Foundry Local: {e}") raise def chat(self, messages: List[Dict[str, str]], max_tokens: int = 300, temperature: float = 0.4) -> str: """Send chat completion request to the model.""" try: response = self.client.chat.completions.create( model=self.model_name, messages=messages, max_tokens=max_tokens, temperature=temperature ) return response.choices[0].message.content except Exception as e: return f"Error generating response: {str(e)}" # Global client instance _client = FoundryClient() class RetrievalAgent: """Agent specialized in retrieving relevant information from knowledge sources.""" SYSTEM = """You are a specialized retrieval agent. Your job is to extract and retrieve the most relevant information from knowledge sources based on a given query. Focus on key facts, data points, and contextual information that would be useful for decision-making.""" def run(self, query: str) -> str: """Retrieve relevant information based on the query.""" messages = [ {"role": "system", "content": self.SYSTEM}, {"role": "user", "content": f"Query: {query}\n\nRetrieve the most relevant key facts, data points, and contextual information that would help answer this query or support decision-making around it."} ] return _client.chat(messages) class ReasoningAgent: """Agent specialized in step-by-step analysis and reasoning.""" SYSTEM = """You are a specialized reasoning agent. Your job is to analyze inputs step-by-step and produce structured, logical conclusions. Break down complex problems into manageable parts and provide clear reasoning for your conclusions.""" def run(self, context: str, question: str) -> str: """Analyze context and question to produce structured conclusions.""" messages = [ {"role": "system", "content": self.SYSTEM}, {"role": "user", "content": f"Context:\n{context}\n\nQuestion: {question}\n\nAnalyze this step-by-step and provide a structured, logical conclusion with clear reasoning."} ] return _client.chat(messages, max_tokens=400) class ExecutionAgent: """Agent specialized in creating actionable execution plans.""" SYSTEM = """You are a specialized execution agent. Your job is to transform decisions and conclusions into concrete, actionable steps. Always format your response as valid JSON with an array of action items. Each action should be specific, measurable, and achievable.""" def run(self, decision: str) -> str: """Transform decision into actionable steps in JSON format.""" messages = [ {"role": "system", "content": self.SYSTEM}, {"role": "user", "content": f"Decision/Conclusion:\n{decision}\n\nCreate 3-5 specific, actionable steps to implement this decision. Format as JSON with this structure:\n{{\"actions\": [{{\"step\": 1, \"description\": \"...\", \"priority\": \"high/medium/low\", \"timeline\": \"...\"}}]}}"} ] return _client.chat(messages, max_tokens=400, temperature=0.3) ``` Стъпка 2) Създайте координатор с разширени функции ```python # agents/coordinator.py from .specialists import RetrievalAgent, ReasoningAgent, ExecutionAgent from typing import Dict, Any import time import json class Coordinator: """Multi-agent coordinator that orchestrates specialist agents to handle complex tasks.""" def __init__(self): """Initialize the coordinator with specialist agents.""" self.retrieval = RetrievalAgent() self.reasoning = ReasoningAgent() self.execution = ExecutionAgent() def handle(self, user_goal: str) -> Dict[str, Any]: """ Orchestrate multiple agents to handle a complex user goal. Args: user_goal: The user's high-level goal or request Returns: Dictionary containing the goal, context, decision, and actions """ print(f"🎯 **Coordinator:** Processing goal: {user_goal}") print("=" * 60) start_time = time.time() # Step 1: Retrieve relevant context print("📚 **Step 1:** Retrieving context...") context = self.retrieval.run(user_goal) print(f" ✅ Context retrieved ({len(context)} chars)") # Step 2: Analyze and reason about the context print("🧠 **Step 2:** Analyzing and reasoning...") decision = self.reasoning.run(context, user_goal) print(f" ✅ Analysis completed ({len(decision)} chars)") # Step 3: Create actionable execution plan print("⚡ **Step 3:** Creating execution plan...") actions = self.execution.run(decision) print(f" ✅ Execution plan created ({len(actions)} chars)") end_time = time.time() processing_time = end_time - start_time result = { "goal": user_goal, "context": context, "decision": decision, "actions": actions, "agent_flow": ["retrieval", "reasoning", "execution"], "processing_time": processing_time, "timestamp": time.strftime("%Y-%m-%d %H:%M:%S") } print(f"✅ **Coordination Complete** (⏱️ {processing_time:.2f}s)") return result def handle_with_feedback(self, user_goal: str, feedback_rounds: int = 1) -> Dict[str, Any]: """ Handle a goal with multiple feedback rounds for refinement. Args: user_goal: The user's high-level goal or request feedback_rounds: Number of feedback rounds to perform Returns: Dictionary containing the refined result """ result = self.handle(user_goal) for round_num in range(feedback_rounds): print(f"\n🔄 **Feedback Round {round_num + 1}:**") print("-" * 40) # Use reasoning agent to refine the execution plan refinement_prompt = f""" Original Goal: {user_goal} Current Decision: {result['decision']} Current Actions: {result['actions']} Review the above and suggest improvements or refinements to make the execution plan more effective. """ refined_decision = self.reasoning.run(result['context'], refinement_prompt) refined_actions = self.execution.run(refined_decision) result['decision'] = refined_decision result['actions'] = refined_actions result['refinement_rounds'] = round_num + 1 print(f" ✅ Round {round_num + 1} refinement completed") return result def main(): """Main function demonstrating the multi-agent coordinator.""" print("🤖 **Multi-Agent Coordinator Demo**") print("=" * 50) # Create coordinator coord = Coordinator() # Example goals example_goals = [ "Create a plan to onboard 5 new customers this month", "Develop a strategy to improve team productivity by 20%", "Design a customer feedback collection system" ] # Process example with feedback goal = example_goals[0] print(f"🎯 **Processing Goal:** {goal}") print("-" * 50) try: # Basic processing result = coord.handle(goal) # With feedback refinement refined_result = coord.handle_with_feedback(goal, feedback_rounds=1) print("\n📊 **Final Result:**") print("=" * 50) print(f"**Goal:** {refined_result['goal']}") print(f"**Processing Time:** {refined_result['processing_time']:.2f}s") # Try to parse actions as JSON try: actions_json = json.loads(refined_result['actions']) print(f"\n**Formatted Actions:**") print(json.dumps(actions_json, indent=2)) except (json.JSONDecodeError, TypeError): print(f"\n**Actions:** {refined_result['actions']}") except Exception as e: print(f"❌ **Error:** {e}") print("\nPlease ensure Foundry Local is running with a model loaded.") if __name__ == "__main__": main() ``` Стъпка 3) Валидирайте срещу Foundry Local и изпълнете примерите ```powershell REM Confirm the local endpoint and model are available foundry model list foundry model run phi-4-mini curl http://localhost:8000/v1/models REM Run the coordinator from Module08 directory cd Module08 python -m samples.05.agents.coordinator REM Or explore the comprehensive Jupyter notebook jupyter notebook samples/05/multi_agent_orchestration.ipynb ``` > **📚 Референции за локални примери:** > - **Основна имплементация**: `samples/05/agents/specialists.py` и `samples/05/agents/coordinator.py` > - **Цялостни примери**: `samples/05/multi_agent_orchestration.ipynb` > - **Инструкции за настройка**: `samples/05/README.md` > > **🔗 Свързани примери от Foundry Local:** > - [Пример за извикване на функции](https://github.com/microsoft/Foundry-Local/tree/main/samples/python/functioncalling) > - [Hello Foundry Local](https://github.com/microsoft/Foundry-Local/tree/main/samples/python/hello-foundry-local) Насоки: - Имплементирайте повторения и таймаути между агентите - Добавете малък in-memory store (dict) за състояние на разговор/нишка - Въведете ограничаване на скоростта при свързване на множество повиквания ## Част 4: Наблюдаемост и безопасност Проследявайте подсказки, отговори и грешки локално, като същевременно налагате хигиена на данните във вашия агентен стек. Стъпка 1) Лек запис на заявки (по избор) Забележка: Следният помощник не е включен по подразбиране. Създайте `infra/obs.py`, ако искате локално JSON логване за експерименти. ```python # infra/obs.py import time, json, os from datetime import datetime LOG_DIR = os.getenv("FOUNDRY_AGENT_LOG_DIR", "./agent_logs") os.makedirs(LOG_DIR, exist_ok=True) def log_event(kind: str, payload: dict): ts = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ") path = os.path.join(LOG_DIR, f"{ts}_{kind}.json") with open(path, "w", encoding="utf-8") as f: json.dump(payload, f, ensure_ascii=False, indent=2) ``` Интегрирайте логването в агенти (по избор): ```python # in agents/specialists.py after receiving content from infra.obs import log_event # ... inside chat(...) resp = r.json() log_event("chat_request", {"endpoint": f"{BASE_URL}/v1/chat/completions"}) log_event("chat_response", resp) return resp["choices"][0]["message"]["content"] ``` Стъпка 2) Валидирайте наличността и основното състояние чрез CLI ```powershell REM Ensure Foundry Local is running a model foundry model list foundry model run phi-4-mini REM Validate the OpenAI-compatible endpoint curl http://localhost:8000/v1/models ``` Стъпка 3) Редакция и хигиена на PII - Преди изпращане на съобщения към модела, премахнете или хеширайте чувствителни полета (имейли, телефонни номера, идентификатори) - Съхранявайте суровите изходни данни на устройството, предавайте само необходимите контекстуални низове Пример за помощник за редакция: ```python # infra/redact.py import re EMAIL_RE = re.compile(r"[\w\.-]+@[\w\.-]+") PHONE_RE = re.compile(r"\+?\d[\d\s\-]{7,}\d") def sanitize(text: str) -> str: text = EMAIL_RE.sub("[REDACTED_EMAIL]", text) text = PHONE_RE.sub("[REDACTED_PHONE]", text) return text ``` Използване в агенти: ```python from infra.redact import sanitize # user_goal = sanitize(user_goal) # context = sanitize(context) ``` Стъпка 4) Circuit breakers и обработка на грешки - Обгърнете всяко повикване на агент с try/except и експоненциално обратно изчакване - Прекъснете процеса при повтарящи се неуспехи ```python import time def with_retry(func, retries=3, base_delay=0.5): for i in range(retries): try: return func() except Exception as e: if i == retries - 1: raise time.sleep(base_delay * (2 ** i)) ``` Стъпка 5) Локален одитен след и експорт - Съхранявайте JSON логове под `./agent_logs` - Периодично компресирайте и завъртайте логовете - Експортирайте обобщения за прегледи (брой, средно време за отговор, процент на грешки) Стъпка 6) Кръстосана проверка с документацията на Microsoft Learn - Foundry Local предоставя OpenAI-съвместим API (валидирано с `curl /v1/models`) - Използвайте `foundry model run ` за потвърждение на наличността на модела - Следвайте официалните указания за клиентска интеграция и примерни приложения (Open WebUI/how-tos) Референции - **Документация за Foundry Local**: https://learn.microsoft.com/en-us/azure/ai-foundry/foundry-local/ - **Azure AI Agents**: https://learn.microsoft.com/en-us/azure/ai-services/agents/overview - **Локални примери**: - Оркестрация с множество агенти: `Module08/samples/05/multi_agent_orchestration.ipynb` - Имплементация на агенти: `Module08/samples/05/agents/` - README за примери: `Module08/samples/05/README.md` - **Официални примери от Microsoft**: - [Извикване на функции](https://github.com/microsoft/Foundry-Local/tree/main/samples/python/functioncalling) - [Hello Foundry Local](https://github.com/microsoft/Foundry-Local/tree/main/samples/python/hello-foundry-local) - [Foundry Local Python SDK](https://github.com/microsoft/Foundry-Local/tree/main/sdk/python) - **Примери за интеграция**: https://learn.microsoft.com/en-us/azure/ai-foundry/foundry-local/how-to/how-to-chat-application-with-open-web-ui ## Следващи стъпки - Изследвайте Azure AI Agents за оркестрация в облака - Добавете корпоративни конектори (Microsoft Graph, Search, бази данни) ---