File size: 3,244 Bytes
9548d3d
 
 
 
 
ca2ba49
 
 
 
 
 
 
 
9548d3d
ca2ba49
 
dd47faf
ca2ba49
 
552e239
 
9548d3d
 
 
 
 
 
ca2ba49
 
9548d3d
 
ca2ba49
9548d3d
 
 
dd47faf
 
 
 
ca2ba49
552e239
ca2ba49
 
 
 
 
 
9548d3d
 
 
 
ca2ba49
 
 
 
 
552e239
 
 
 
 
 
9548d3d
 
ca2ba49
 
 
 
 
 
 
 
 
 
 
 
9548d3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
"""
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()