from __future__ import annotations

import os
import asyncio
from typing import Literal
from dotenv import load_dotenv 
load_dotenv(override=True)
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from langchain.tools.retriever import create_retriever_tool
from langgraph.graph import MessagesState, StateGraph, START, END
from langgraph.prebuilt import ToolNode, tools_condition
from pydantic import BaseModel, Field
from dotenv import load_dotenv 
load_dotenv(override=True)
from langchain_deepseek import ChatDeepSeek
from langgraph.prebuilt import create_react_agent
from langchain_core.tools import tool
from langgraph_supervisor import create_supervisor
from pydantic import BaseModel, Field
import matplotlib
from enhanced_rag_agent import enhanced_rag_agent
from enhanced_python_agent import enhanced_python_agent
from enhanced_amap_agent import enhanced_amap_agent
from safe_file_agent import safe_file_agent
from terminal_command_agent import terminal_command_agent
from web_search_agent import web_search_agent
import json
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import pymysql
from langchain_tavily import TavilySearch
import pathlib

# ---------------------------------------------------------------------------
# LLM & Embeddings
# ---------------------------------------------------------------------------
model = ChatDeepSeek(model="deepseek-chat")
grader_model = ChatDeepSeek(model="deepseek-chat")

embed = OpenAIEmbeddings(
    api_key=os.getenv("OPENAI_API_KEY"),
    base_url="https://ai.devtool.tech/proxy/v1",
    model="text-embedding-3-small",
)

# ---------------------------------------------------------------------------
# Vector store & Retriever tool
# ---------------------------------------------------------------------------
VS_PATH = "telco_customer_churn_analytics_handbook"

# 使用本地文本检索器替代FAISS向量数据库
from langchain_core.tools import BaseTool
from langchain_core.callbacks import CallbackManagerForToolRun
import json

class LocalTextRetriever(BaseTool):
    """本地文本检索工具"""
    name: str = "retrieve_telco_handbook"
    description: str = "Search and return relevant sections from the Telco Customer Churn Analytics Handbook."
    
    def _run(self, query: str, run_manager: CallbackManagerForToolRun = None) -> str:
        """检索相关文档内容"""
        try:
            # 加载本地文本数据库
            db_file = os.path.join(VS_PATH, "text_database.json")
            
            if not os.path.exists(db_file):
                return "本地文本数据库未找到，请先运行 create_local_vector_db.py 创建数据库。"
            
            with open(db_file, 'r', encoding='utf-8') as f:
                sections = json.load(f)
            
            # 执行搜索
            results = self._simple_search(sections, query, top_k=3)
            
            if not results:
                return f"未找到与'{query}'相关的信息。"
            
            # 格式化结果
            formatted_results = []
            for i, result in enumerate(results, 1):
                formatted_results.append(f"【相关内容 {i}】\n{result['title']}\n\n{result['content'][:400]}...")
            
            return "\n\n".join(formatted_results)
            
        except Exception as e:
            return f"检索时发生错误：{str(e)}"
    
    def _simple_search(self, sections, query: str, top_k: int = 3):
        """简单的文本搜索"""
        results = []
        query_lower = query.lower()
        
        # 扩展查询词
        query_terms = self._expand_query(query_lower)
        
        for section in sections:
            score = 0
            content_lower = section['content'].lower()
            title_lower = section['title'].lower()
            
            # 计算匹配得分
            for term in query_terms:
                # 标题匹配得分更高
                if term in title_lower:
                    score += 10
                
                # 内容匹配
                score += content_lower.count(term) * 2
                
                # 关键词匹配
                for keyword in section.get('keywords', []):
                    if term in keyword.lower():
                        score += 5
            
            if score > 0:
                results.append({
                    'section': section,
                    'score': score,
                    'title': section['title'],
                    'content': section['content']
                })
        
        # 按得分排序
        results.sort(key=lambda x: x['score'], reverse=True)
        return results[:top_k]
    
    def _expand_query(self, query: str):
        """扩展查询词"""
        terms = [query]
        
        # 添加相关术语映射
        term_mapping = {
            '流失': ['churn', '客户流失', '用户流失', '离网'],
            '合同': ['contract', '合约', '签约'],
            '账单': ['charges', '费用', '付费', '缴费'],
            '服务': ['service', '业务', '功能'],
            '预测': ['prediction', '建模', '模型'],
            '特征': ['feature', '字段', '变量'],
            '数据': ['data', '信息', '记录'],
            '客户': ['customer', '用户', '客人'],
            '分析': ['analysis', '统计', '分析']
        }
        
        for key, synonyms in term_mapping.items():
            if key in query:
                terms.extend(synonyms)
            for syn in synonyms:
                if syn in query:
                    terms.append(key)
                    terms.extend([s for s in synonyms if s != syn])
                    break
        
        return list(set(terms))

# 创建本地文本检索工具实例    
retriever_tool = LocalTextRetriever()

# ---------------------------------------------------------------------------
# Prompts
# ---------------------------------------------------------------------------
SYSTEM_INSTRUCTION = (
    "You are a telecom data analysis assistant. You can answer ONLY questions related to the "
    "Telco Customer Churn Analytics Handbook, including topics such as customer churn prediction, "
    "data dictionary, modeling strategies, visualization analysis, business insights, and feature engineering. "
    "If the user's question is NOT related to this document, reply exactly: '我只能回答与电信客户流失分析手册相关的问题。' "
    "You may call the provided tool `retrieve_telco_handbook` when you need more context from the document."
)

GRADE_PROMPT = (
    "You are a grader assessing relevance of a retrieved document to a user question.\n"
    "Retrieved document:\n{context}\n\nUser question: {question}\n"
    "Return 'yes' if relevant, otherwise 'no'."
)

REWRITE_PROMPT = (
    "You are a telecom domain expert assistant.\n"
    "Given a user's question about the Telco Customer Churn Analytics Handbook, "
    "your task is to rewrite or expand it in a way that captures the underlying intent clearly, "
    "while improving its recall ability for document-based retrieval. Use formal and unambiguous wording.\n\n"
    "Original question:\n{question}\n\n"
    "Improved version:"
)


ANSWER_PROMPT = (
    "You are an expert assistant trained on the Telco Customer Churn Analytics Handbook.\n"
    "Please use the provided context to answer the question in a detailed, structured manner.\n"
    "Your answer should ideally include:\n"
    "- A concise summary of the relevant concept or finding\n"
    "- An explanation of its background or rationale\n"
    "- Practical implications or usage suggestions\n"
    "- Any supporting statistics or examples from the document\n\n"
    "If the context lacks relevant information, reply with: '很抱歉，我无法从手册中找到相关信息。'\n\n"
    "Question:\n{question}\n\n"
    "Context:\n{context}\n\n"
    "Answer:"
)

# ---------------------------------------------------------------------------
# LangGraph Nodes
# ---------------------------------------------------------------------------
async def generate_query_or_respond(state: MessagesState):
    """LLM decides to answer directly or call retriever tool."""
    response = await model.bind_tools([retriever_tool]).ainvoke(
        [
            {"role": "system", "content": SYSTEM_INSTRUCTION},
            *state["messages"],
        ]
    )
    return {"messages": [response]}


class GradeDoc(BaseModel):
    binary_score: str = Field(description="Relevance score 'yes' or 'no'.")


async def grade_documents(state: MessagesState) -> Literal["generate_answer", "rewrite_question"]:
    question = state["messages"][0].content  # original user question
    ctx = state["messages"][-1].content      # retriever output
    prompt = GRADE_PROMPT.format(question=question, context=ctx)
    result = await grader_model.with_structured_output(GradeDoc).ainvoke([
        {"role": "user", "content": prompt}
    ])
    return "generate_answer" if result.binary_score.lower().startswith("y") else "rewrite_question"


async def rewrite_question(state: MessagesState):
    question = state["messages"][0].content
    prompt = REWRITE_PROMPT.format(question=question)
    resp = await model.ainvoke([{"role": "user", "content": prompt}])
    return {"messages": [{"role": "user", "content": resp.content}]}


async def generate_answer(state: MessagesState):
    question = state["messages"][0].content
    ctx = state["messages"][-1].content
    prompt = ANSWER_PROMPT.format(question=question, context=ctx)
    resp = await model.ainvoke([{"role": "user", "content": prompt}])
    return {"messages": [resp]}

# ---------------------------------------------------------------------------
# Build graph
# ---------------------------------------------------------------------------
workflow = StateGraph(MessagesState)
workflow.add_node("generate_query_or_respond", generate_query_or_respond)
# 创建本地检索节点
async def retrieve_node(state: MessagesState):
    """本地检索节点"""
    # 获取最后一条消息中的工具调用
    last_message = state["messages"][-1]
    
    if hasattr(last_message, 'tool_calls') and last_message.tool_calls:
        tool_call = last_message.tool_calls[0]
        query = tool_call['args'].get('query', '')
        
        # 调用本地检索工具
        result = retriever_tool._run(query)
        
        return {"messages": [{"role": "tool", "content": result, "tool_call_id": tool_call['id']}]}
    else:
        return {"messages": [{"role": "tool", "content": "未找到有效的检索查询"}]}

workflow.add_node("retrieve", retrieve_node)
workflow.add_node("rewrite_question", rewrite_question)
workflow.add_node("generate_answer", generate_answer)

workflow.add_edge(START, "generate_query_or_respond")
# 工具条件判断函数
def tools_condition(state: MessagesState):
    """工具条件判断"""
    last_message = state["messages"][-1]
    
    if hasattr(last_message, 'tool_calls') and last_message.tool_calls:
        return "tools"
    else:
        return END

workflow.add_conditional_edges(
    "generate_query_or_respond", tools_condition, {"tools": "retrieve", END: END}
)
workflow.add_conditional_edges("retrieve", grade_documents)
workflow.add_edge("generate_answer", END)
workflow.add_edge("rewrite_question", "generate_query_or_respond")

rag_agent = workflow.compile(name="rag_agent")

# ✅ 创建SQL查询工具
description = """
当用户需要进行数据库查询工作时，请调用该函数。
该函数用于在指定MySQL服务器上运行一段SQL代码，完成数据查询相关工作，
并且当前函数是使用pymsql连接MySQL数据库。
本函数只负责运行SQL代码并进行数据查询，若要进行数据提取，则使用另一个extract_data函数。
"""

# 定义结构化参数模型
class SQLQuerySchema(BaseModel):
    sql_query: str = Field(description=description)

# 封装为 LangGraph 工具
@tool(args_schema=SQLQuerySchema)
def sql_inter(sql_query: str) -> str:
    """
    当用户需要进行数据库查询工作时，请调用该函数。
    该函数用于在指定MySQL服务器上运行一段SQL代码，完成数据查询相关工作，
    并且当前函数是使用pymsql连接MySQL数据库。
    本函数只负责运行SQL代码并进行数据查询，若要进行数据提取，则使用另一个extract_data函数。
    :param sql_query: 字符串形式的SQL查询语句，用于执行对MySQL中telco_data数据库中各张表进行查询，并获得各表中的各类相关信息
    :return：sql_query在MySQL中的运行结果。   
    """
    # print("正在调用 sql_inter 工具运行 SQL 查询...")
    
    # 加载环境变量
    load_dotenv(override=True)
    host = os.getenv('HOST')
    user = os.getenv('USER')
    mysql_pw = os.getenv('MYSQL_PW')
    db = os.getenv('DB_NAME')
    port = os.getenv('PORT')
    
    # 创建连接
    connection = pymysql.connect(
        host=host,
        user=user,
        passwd=mysql_pw,
        db=db,
        port=int(port),
        charset='utf8'
    )
    
    try:
        with connection.cursor() as cursor:
            cursor.execute(sql_query)
            results = cursor.fetchall()
            # print("SQL 查询已成功执行，正在整理结果...")
    finally:
        connection.close()

    # 将结果以 JSON 字符串形式返回
    return json.dumps(results, ensure_ascii=False)

# ✅ 创建数据提取工具
# 定义结构化参数
class ExtractQuerySchema(BaseModel):
    sql_query: str = Field(description="用于从 MySQL 提取数据的 SQL 查询语句。")
    df_name: str = Field(description="指定用于保存结果的 pandas 变量名称（字符串形式）。")

# 注册为 Agent 工具
@tool(args_schema=ExtractQuerySchema)
def extract_data(sql_query: str, df_name: str) -> str:
    """
    用于在MySQL数据库中提取一张表到当前Python环境中，注意，本函数只负责数据表的提取，
    并不负责数据查询，若需要在MySQL中进行数据查询，请使用sql_inter函数。
    同时需要注意，编写外部函数的参数消息时，必须是满足json格式的字符串，
    :param sql_query: 字符串形式的SQL查询语句，用于提取MySQL中的某张表。
    :param df_name: 将MySQL数据库中提取的表格进行本地保存时的变量名，以字符串形式表示。
    :return：表格读取和保存结果
    """
    print("正在调用 extract_data 工具运行 SQL 查询...")
    
    load_dotenv(override=True)
    host = os.getenv('HOST')
    user = os.getenv('USER')
    mysql_pw = os.getenv('MYSQL_PW')
    db = os.getenv('DB_NAME')
    port = os.getenv('PORT')

    # 创建数据库连接
    connection = pymysql.connect(
        host=host,
        user=user,
        passwd=mysql_pw,
        db=db,
        port=int(port),
        charset='utf8'
    )

    try:
        # 执行 SQL 并保存为全局变量
        df = pd.read_sql(sql_query, connection)
        globals()[df_name] = df
        # print("数据成功提取并保存为全局变量：", df_name)
        return f"✅ 成功创建 pandas 对象 `{df_name}`，包含从 MySQL 提取的数据。"
    except Exception as e:
        return f"❌ 执行失败：{e}"
    finally:
        connection.close()

SQL_Agent_prompt = """
你是一名经验丰富的智能数据分析助手，擅长帮助用户高效完成以下任务：

1. **数据库查询：**
   - 当用户需要获取数据库中某些数据或进行SQL查询时，请调用`sql_inter`工具，该工具已经内置了pymysql连接MySQL数据库的全部参数，包括数据库名称、用户名、密码、端口等，你只需要根据用户需求生成SQL语句即可。
   - 你需要准确根据用户请求生成SQL语句，例如 `SELECT * FROM 表名` 或包含条件的查询。

2. **数据表提取：**
   - 当用户希望将数据库中的表格导入Python环境进行后续分析时，请调用`extract_data`工具。
   - 你需要根据用户提供的表名或查询条件生成SQL查询语句，并将数据保存到指定的pandas变量中。
"""

sql_tool = [sql_inter, extract_data]
sql_agent = create_react_agent(model=model, tools=sql_tool, prompt=SQL_Agent_prompt, name="sql_agent")


# ✅创建Python代码执行工具
# Python代码执行工具结构化参数说明
class PythonCodeInput(BaseModel):
    py_code: str = Field(description="一段合法的 Python 代码字符串，例如 '2 + 2' 或 'x = 3\\ny = x * 2'")

@tool(args_schema=PythonCodeInput)
def python_inter(py_code):
    """
    当用户需要编写Python程序并执行时，请调用该函数。
    该函数可以执行一段Python代码并返回最终结果，需要注意，本函数只能执行非绘图类的代码，若是绘图相关代码，则需要调用fig_inter函数运行。
    """    
    g = globals()
    try:
        # 尝试如果是表达式，则返回表达式运行结果
        return str(eval(py_code, g))
    # 若报错，则先测试是否是对相同变量重复赋值
    except Exception as e:
        global_vars_before = set(g.keys())
        try:            
            exec(py_code, g)
        except Exception as e:
            return f"代码执行时报错{e}"
        global_vars_after = set(g.keys())
        new_vars = global_vars_after - global_vars_before
        # 若存在新变量
        if new_vars:
            result = {var: g[var] for var in new_vars}
            # print("代码已顺利执行，正在进行结果梳理...")
            return str(result)
        else:
            # print("代码已顺利执行，正在进行结果梳理...")
            return "已经顺利执行代码"

# ✅ 创建绘图工具
# 绘图工具结构化参数说明
class FigCodeInput(BaseModel):
    py_code: str = Field(description="要执行的 Python 绘图代码，必须使用 matplotlib/seaborn 创建图像并赋值给变量")
    fname: str = Field(description="图像对象的变量名，例如 'fig'，用于从代码中提取并保存为图片")

@tool(args_schema=FigCodeInput)
def fig_inter(py_code: str, fname: str) -> str:
    """
    当用户需要使用 Python 进行可视化绘图任务时，请调用该函数。

    注意：
    1. 所有绘图代码必须创建一个图像对象，并将其赋值为指定变量名（例如 `fig`）。
    2. 必须使用 `fig = plt.figure()` 或 `fig = plt.subplots()`。
    3. 不要使用 `plt.show()`。
    4. 请确保代码最后调用 `fig.tight_layout()`。
    5. 所有绘图代码中，坐标轴标签（xlabel、ylabel）、标题（title）、图例（legend）等文本内容，必须使用英文描述。

    示例代码：
    fig = plt.figure(figsize=(10,6))
    plt.plot([1,2,3], [4,5,6])
    fig.tight_layout()
    """
    # print("正在调用fig_inter工具运行Python代码...")

    current_backend = matplotlib.get_backend()
    matplotlib.use('Agg')

    local_vars = {"plt": plt, "pd": pd, "sns": sns}
    
    # ✅ 设置图像保存路径（你自己的绝对路径）
    base_dir = r"C:\Users\Administrator\Desktop\Data_Agent_Pro\agent-chat-ui\public"
    images_dir = os.path.join(base_dir, "images")
    os.makedirs(images_dir, exist_ok=True)  # ✅ 自动创建 images 文件夹（如不存在）
    try:
        g = globals()
        exec(py_code, g, local_vars)
        g.update(local_vars)

        fig = local_vars.get(fname, None)
        if fig:
            image_filename = f"{fname}.png"
            abs_path = os.path.join(images_dir, image_filename)  # ✅ 绝对路径
            rel_path = os.path.join("images", image_filename)    # ✅ 返回相对路径（给前端用）

            fig.savefig(abs_path, bbox_inches='tight')
            return f"✅ 图片已保存，路径为: {rel_path}"
        else:
            return "⚠️ 图像对象未找到，请确认变量名正确并为 matplotlib 图对象。"
    except Exception as e:
        return f"❌ 执行失败：{e}"
    finally:
        plt.close('all')
        matplotlib.use(current_backend)

Python_Agent_prompt = """
你是一名经验丰富的智能数据分析助手，擅长帮助用户高效完成以下任务：

1. **非绘图类任务的Python代码执行：**
   - 当用户需要执行Python脚本或进行数据处理、统计计算时，请调用`python_inter`工具。
   - 仅限执行非绘图类代码，例如变量定义、数据分析等。

2. **绘图类Python代码执行：**
   - 当用户需要进行可视化展示（如生成图表、绘制分布等）时，请调用`fig_inter`工具。
   - 你可以直接读取数据并进行绘图，不需要借助`python_inter`工具读取图片。
   - 你应根据用户需求编写绘图代码，并正确指定绘图对象变量名（如 `fig`）。
   - 当你生成Python绘图代码时必须指明图像的名称，如fig = plt.figure()或fig = plt.subplots()创建图像对象，并赋值为fig。
   - 不要调用plt.show()，否则图像将无法保存。

**回答要求：**
- 所有回答均使用**简体中文**，清晰、礼貌、简洁。
- 若需要用户提供更多信息，请主动提出明确的问题。
- 如果有生成的图片文件，不要仅输出图片路径文字，或者仅申明已经绘制完图像，请务必在回答中使用Markdown格式插入图片，如：![Categorical Features vs Churn](images/fig.png)
"""

python_tool = [python_inter, fig_inter]
python_agent = create_react_agent(model=model, tools=python_tool, prompt=Python_Agent_prompt, name="python_agent")

SUPERVISOR_PROMPT = """你是一个智能任务调度系统，负责分析用户需求并将任务分配给最合适的专业Agent。

## 🎯 核心职责
- 分析用户请求的任务类型和复杂度
- 根据Agent能力选择最合适的执行者
- 确保任务按正确的依赖顺序执行
- 监控任务完成度并决定下一步行动

## 🤖 可用Agent及其专业能力

### 📚 `enhanced_rag_agent` - 电信业务知识专家
**专用工具**: retrieve_telco_handbook
**核心能力**: 电信客户流失分析手册查询
**使用场景**:
- 需要解释电信业务术语(如"高价值客户"、"流失率"等)
- 查询数据字段含义和业务背景
- 获取分析方法和建模策略指导

### 💾 `sql_agent` - 数据库操作专家  
**专用工具**: sql_inter(查询), extract_data(提取到DataFrame)
**核心能力**: MySQL数据库操作
**使用场景**:
- 查询数据库中的记录数量、统计信息
- 为Python分析准备数据(必须先提取数据)
- 执行复杂SQL查询

### 🐍 `enhanced_python_agent` - 数据科学专家
**专用工具**: enhanced_python_exec, advanced_visualization, data_analysis_tool, ml_modeling_tool, save_chart_to_desktop
**核心能力**: Python数据分析、可视化、机器学习
**使用场景**:
- 数据统计分析和计算
- 创建图表和可视化
- 机器学习建模
- 保存图表到桌面
**重要**: 无法直接访问数据库，需要sql_agent先提取数据

### 🗺️ `enhanced_amap_agent` - 地理服务专家
**专用工具**: weather_query, geocode_address, poi_search, route_planning等
**核心能力**: 高德地图API服务
**使用场景**:
- 天气查询
- 地址坐标转换
- 兴趣点搜索
- 路径规划

### 📁 `safe_file_agent` - 文件管理专家
**专用工具**: safe_read_file, safe_write_file, write_to_desktop等
**核心能力**: 安全文件操作
**使用场景**:
- 读写工作区文件
- 创建文档到桌面
- 文件管理操作

### 💻 `terminal_command_agent` - 系统命令专家
**专用工具**: execute_terminal_command, execute_batch_commands等
**核心能力**: 终端命令执行
**使用场景**:
- 系统命令执行
- 开发工具调用
- 批量操作

### 🔍 `web_search_agent` - 网络搜索专家
**专用工具**: web_search, news_search, finance_search等
**核心能力**: 实时网络信息检索
**使用场景**:
- 搜索最新信息
- 获取新闻资讯
- 查找技术文档

## 🔄 任务分配决策流程

### 1️⃣ 需求分析
```
用户输入 → 识别任务类型 → 确定所需能力 → 选择Agent
```

### 2️⃣ 依赖关系处理
**数据分析流程**: 
```
enhanced_rag_agent(获取业务知识) → sql_agent(提取数据) → enhanced_python_agent(分析/可视化)
```

**文件输出流程**:
```
enhanced_python_agent(生成图表) → safe_file_agent(保存到桌面)
```

### 3️⃣ 任务分类模板

| 任务类型 | 关键词示例 | 推荐Agent | 依赖关系 |
|---------|-----------|----------|---------|
| 业务概念查询 | "什么是"、"解释"、"定义" | enhanced_rag_agent | 无 |
| 数据查询 | "多少个"、"统计"、"查询数据库" | sql_agent | 可能需要rag_agent解释字段 |
| 数据分析 | "分析"、"计算"、"统计" | sql_agent → enhanced_python_agent | 先提取后分析 |
| 图表制作 | "绘制"、"图表"、"可视化" | sql_agent → enhanced_python_agent | 先提取后绘图 |
| 文件操作 | "保存"、"创建文件"、"写入" | safe_file_agent | 无 |
| 地理服务 | "天气"、"地址"、"导航" | enhanced_amap_agent | 无 |
| 网络搜索 | "搜索"、"最新"、"查找" | web_search_agent | 无 |
| 系统操作 | "命令"、"执行"、"安装" | terminal_command_agent | 无 |

## ⚡ 决策规则

1. **单一职责**: 每次只分配给一个Agent
2. **依赖优先**: 有依赖关系时，必须按顺序执行
3. **能力匹配**: 选择最专业的Agent处理对应任务
4. **完成检查**: 所有子任务完成后输出`FINISH`

## 📤 输出格式
从以下选项中选择一个: 
`['enhanced_rag_agent', 'sql_agent', 'enhanced_python_agent', 'enhanced_amap_agent', 'safe_file_agent', 'terminal_command_agent', 'web_search_agent', 'FINISH']`

## 🔍 决策示例

**用户**: "分析客户流失数据并生成图表保存到桌面"
**分析**: 需要业务知识→数据提取→分析可视化→文件保存
**执行顺序**: 
1. enhanced_rag_agent (理解流失分析方法)
2. sql_agent (提取客户数据) 
3. enhanced_python_agent (分析并生成图表到桌面)
4. FINISH

立即开始分析用户需求并选择合适的Agent!"""

from langchain.chat_models import init_chat_model
supervisor = create_supervisor(
    model=init_chat_model(model="deepseek-chat", model_provider="deepseek", temperature=0),
    agents=[
        enhanced_rag_agent,
        enhanced_python_agent,
        sql_agent, 
        enhanced_amap_agent,
        safe_file_agent,
        terminal_command_agent,
        web_search_agent
    ],
    prompt=SUPERVISOR_PROMPT,
    add_handoff_back_messages=True
).compile()