""" Basic agent loop using the Anthropic Claude API. Receives user input, calls tools as needed, and returns results. """ import os import sys # Thêm thư mục gốc của dự án vào sys.path để hỗ trợ chạy file trực tiếp project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) if project_root not in sys.path: sys.path.append(project_root) import logging from typing import Any from langchain_core.messages import HumanMessage, AIMessage, ToolMessage, SystemMessage from datetime import datetime from src.llm import llm from src.config import DEFAULT_MODEL, LOG_LEVEL from src.prompts import AGENT_SYSTEM_PROMPT from src.tools import get_tool_schemas, execute_tool, get_langchain_tools logging.basicConfig(level=LOG_LEVEL, format="%(asctime)s [%(levelname)s] %(message)s") logger = logging.getLogger(__name__) def create_agent(): """Returns the LLM instance configured from src/llm.py.""" return llm def run_agent_loop(client: Any, user_input: str, max_turns: int = 10) -> str: """ Run the agent loop: send message -> receive response -> call tool -> repeat. """ # Dynamic Date Injection today = datetime.now() time_context = f"\n[CURRENT TIME CONTEXT]\nToday is {today.strftime('%A, %B %d, %Y')}.\n" messages = [ SystemMessage(content=AGENT_SYSTEM_PROMPT + time_context), HumanMessage(content=user_input) ] # Bind tools to the LLM lc_tools = get_langchain_tools() llm_with_tools = client.bind_tools(lc_tools) for turn in range(max_turns): logger.info(f"Turn {turn + 1}/{max_turns}") response = llm_with_tools.invoke(messages) messages.append(response) # If no tool calls, return the final response if not response.tool_calls: content = response.content if isinstance(content, str): return content if isinstance(content, list): return "".join(b.get("text", "") if isinstance(b, dict) else str(b) for b in content) return str(content) # Handle tool calls for tc in response.tool_calls: tool_name = tc["name"] logger.info(f"Calling tool: {tool_name}({tc['args']})") result = execute_tool(tool_name, tc["args"]) logger.info(f"Result: {str(result)[:200]}") messages.append(ToolMessage( content=str(result), tool_call_id=tc["id"], name=tool_name )) return "Agent reached the maximum number of processing turns." def main(): """Interactive loop - enter a prompt and receive results.""" client = create_agent() print("Agentic App (type 'quit' to exit)") print("-" * 50) while True: user_input = input("\nYou: ").strip() if not user_input or user_input.lower() in ("quit", "exit", "q"): print("Bye!") break try: response = run_agent_loop(client, user_input) print(f"\nAgent: {response}") except Exception as e: logger.error(f"Error: {e}") print(f"\nError: {e}") if __name__ == "__main__": main()