ソースを参照

Merge branch 'feature/20250604-version-control' of Server/AgentCoreService into master

fengzhoutian 1 週間 前
コミット
4cd89f8ffb

+ 30 - 0
pqai_agent/data_models/agent_configuration.py

@@ -0,0 +1,30 @@
+from enum import Enum
+
+from sqlalchemy import Column, Integer, Text, BigInteger, String, SmallInteger, Boolean, TIMESTAMP
+from sqlalchemy.ext.declarative import declarative_base
+
+Base = declarative_base()
+
+
+class AgentType(int, Enum):
+    REACTIVE = 0  # 响应式
+    PLANNING = 1  # 自主规划式
+
+class AgentConfiguration(Base):
+    __tablename__ = "agent_configuration"
+
+    id = Column(BigInteger, primary_key=True, autoincrement=True, comment="主键id")
+    name = Column(String(64), nullable=False, comment="唯一名称")
+    display_name = Column(String(64), nullable=True, comment="可选,显示名")
+    type = Column(SmallInteger, nullable=False, default=0, comment="Agent类型,0-响应式,1-自主规划式")
+    execution_model = Column(String(64), nullable=True, comment="执行LLM")
+    system_prompt = Column(Text, nullable=True, comment="系统设定prompt模板")
+    task_prompt = Column(Text, nullable=True, comment="执行任务prompt模板")
+    tools = Column(Text, nullable=True, comment="JSON数组,tool name")
+    sub_agents = Column(Text, nullable=True, comment="JSON数组,agent ID")
+    extra_params = Column(Text, nullable=True, comment="JSON KV对象")
+    is_delete = Column(Boolean, nullable=False, default=False, comment="逻辑删除标识")
+    create_user = Column(String(32), nullable=True, comment="创建用户")
+    update_user = Column(String(32), nullable=True, comment="更新用户")
+    create_time = Column(TIMESTAMP, nullable=True, server_default="CURRENT_TIMESTAMP", comment="创建时间")
+    update_time = Column(TIMESTAMP, nullable=True, server_default="CURRENT_TIMESTAMP", onupdate="CURRENT_TIMESTAMP", comment="更新时间")

+ 22 - 0
pqai_agent/data_models/service_module.py

@@ -0,0 +1,22 @@
+from enum import Enum
+
+from sqlalchemy import Column, Integer, Text, BigInteger, String, SmallInteger, Boolean, TIMESTAMP
+from sqlalchemy.ext.declarative import declarative_base
+
+Base = declarative_base()
+
+class ModuleAgentType(int, Enum):
+    NATIVE = 0  # 原生Agent
+    COZE = 1    # Coze Agent
+
+
+class ServiceModule(Base):
+    __tablename__ = "service_module"
+    id = Column(BigInteger, primary_key=True, autoincrement=True, comment="主键id")
+    name = Column(String(64), nullable=False, comment="唯一名称")
+    display_name = Column(String(64), nullable=True, comment="显示名")
+    default_agent_type = Column(SmallInteger, nullable=True, comment="默认Agent类型,0-原生,1-Coze")
+    default_agent_id = Column(BigInteger, nullable=True, comment="默认Agent ID")
+    is_delete = Column(Boolean, nullable=False, default=False, comment="逻辑删除标识")
+    create_time = Column(TIMESTAMP, nullable=False, server_default="CURRENT_TIMESTAMP", comment="创建时间")
+    update_time = Column(TIMESTAMP, nullable=False, server_default="CURRENT_TIMESTAMP", onupdate="CURRENT_TIMESTAMP", comment="更新时间")

+ 61 - 0
pqai_agent/toolkit/coze_function_tools.py

@@ -0,0 +1,61 @@
+import types
+from typing import List, Dict
+import textwrap
+from pqai_agent.toolkit.function_tool import FunctionTool
+from pqai_agent.chat_service import Coze, TokenAuth, Message, CozeChat
+
+
+class FunctionToolFactory:
+    @staticmethod
+    def create_tool(bot_id: str, coze_client: CozeChat, func_name: str, func_desc: str) -> FunctionTool:
+        """
+        Create a FunctionTool for a specific Coze Bot.
+
+        Args:
+            bot_id (str): The ID of the Coze Bot.
+            coze_client (CozeChat): The Coze client instance to interact with the bot.
+            func_name (str): The name of the function to be used in the FunctionTool.
+            func_desc (str): A description of the function to be used in the FunctionTool.
+        Returns:
+            FunctionTool: A FunctionTool wrapping the Coze Bot interaction.
+        """
+
+        func_doc = f"""
+            {func_desc}
+
+            Args:
+                messages (List[Dict]): A list of messages to send to the bot.
+                custom_variables (Dict): Custom variables for the bot.
+            Returns:
+                str: The final response from the bot.
+            """
+        func_doc = textwrap.dedent(func_doc).strip()
+
+        def coze_func(messages: List[Dict], custom_variables: Dict = None) -> str:
+            # ATTENTION:
+            # custom_variables (Dict): Custom variables for the bot. THIS IS A TRICK.
+            # THIS PARAMETER SHOULD NOT BE VISIBLE TO THE AGENT AND FILLED BY THE SYSTEM.
+
+            # FIXME: Coze bot can return multimodal content.
+
+            response = coze_client.create(
+                bot_id=bot_id,
+                user_id='agent_tool_call',
+                messages=[Message.build_user_question_text(msg["text"]) for msg in messages],
+                custom_variables=custom_variables
+            )
+            if not response:
+                return 'Error in calling the function.'
+            return response
+
+        dynamic_function = types.FunctionType(
+            coze_func.__code__,
+            globals(),
+            name=func_name,
+            argdefs=coze_func.__defaults__,
+            closure=coze_func.__closure__
+        )
+        dynamic_function.__doc__ = func_doc
+
+        # Wrap the function in a FunctionTool
+        return FunctionTool(dynamic_function)

+ 214 - 0
pqai_agent_server/api_server.py

@@ -7,12 +7,17 @@ import werkzeug.exceptions
 from flask import Flask, request, jsonify
 from argparse import ArgumentParser
 
+from sqlalchemy.orm import sessionmaker
+
 from pqai_agent import configs
 
 from pqai_agent import logging_service, chat_service, prompt_templates
 from pqai_agent.agents.message_reply_agent import MessageReplyAgent
+from pqai_agent.data_models.agent_configuration import AgentConfiguration
+from pqai_agent.data_models.service_module import ServiceModule
 from pqai_agent.history_dialogue_service import HistoryDialogueService
 from pqai_agent.user_manager import MySQLUserManager, MySQLUserRelationManager
+from pqai_agent.utils.db_utils import create_sql_engine
 from pqai_agent.utils.prompt_utils import format_agent_profile, format_user_profile
 from pqai_agent_server.const import AgentApiConst
 from pqai_agent_server.models import MySQLSessionManager
@@ -307,6 +312,213 @@ def quit_human_interventions_status():
 
     return wrap_response(200, data=response)
 
+## Agent管理接口
+@app.route("/api/getNativeAgentList", methods=["GET"])
+def get_native_agent_list():
+    """
+    获取所有的Agent列表
+    :return:
+    """
+    page = request.args.get('page', 1)
+    page_size = request.args.get('page_size', 50)
+    create_user = request.args.get('create_user', None)
+    update_user = request.args.get('update_user', None)
+
+    offset = (int(page) - 1) * int(page_size)
+    with app.session_maker() as session:
+        query = session.query(AgentConfiguration) \
+            .filter(AgentConfiguration.is_delete == 0)
+        if create_user:
+            query = query.filter(AgentConfiguration.create_user == create_user)
+        if update_user:
+            query = query.filter(AgentConfiguration.update_user == update_user)
+        query = query.offset(offset).limit(int(page_size))
+        data = query.all()
+    ret_data = [
+        {
+            'id': agent.id,
+            'name': agent.name,
+            'display_name': agent.display_name,
+            'type': agent.type,
+            'execution_model': agent.execution_model,
+            'create_time': agent.create_time.strftime('%Y-%m-%d %H:%M:%S'),
+            'update_time': agent.update_time.strftime('%Y-%m-%d %H:%M:%S')
+        }
+        for agent in data
+    ]
+    return wrap_response(200, data=ret_data)
+
+@app.route("/api/getNativeAgentConfiguration", methods=["GET"])
+def get_native_agent_configuration():
+    """
+    获取指定Agent的配置
+    :return:
+    """
+    agent_id = request.args.get('agent_id')
+    if not agent_id:
+        return wrap_response(404, msg='agent_id is required')
+
+    with app.session_maker() as session:
+        agent = session.query(AgentConfiguration).filter(AgentConfiguration.id == agent_id).first()
+        if not agent:
+            return wrap_response(404, msg='Agent not found')
+
+        data = {
+            'id': agent.id,
+            'name': agent.name,
+            'display_name': agent.display_name,
+            'type': agent.type,
+            'execution_model': agent.execution_model,
+            'system_prompt': agent.system_prompt,
+            'task_prompt': agent.task_prompt,
+            'tools': agent.tools,
+            'sub_agents': agent.sub_agents,
+            'extra_params': agent.extra_params,
+            'create_time': agent.create_time.strftime('%Y-%m-%d %H:%M:%S'),
+            'update_time': agent.update_time.strftime('%Y-%m-%d %H:%M:%S')
+        }
+        return wrap_response(200, data=data)
+
+@app.route("/api/saveNativeAgentConfiguration", methods=["POST"])
+def save_native_agent_configuration():
+    """
+    保存Agent配置
+    :return:
+    """
+    req_data = request.json
+    agent_id = req_data.get('agent_id', None)
+    name = req_data.get('name')
+    display_name = req_data.get('display_name', None)
+    type_ = req_data.get('type', 0)
+    execution_model = req_data.get('execution_model', None)
+    system_prompt = req_data.get('system_prompt', None)
+    task_prompt = req_data.get('task_prompt', None)
+    tools = req_data.get('tools', [])
+    sub_agents = req_data.get('sub_agents', [])
+    extra_params = req_data.get('extra_params', {})
+
+    if not name:
+        return wrap_response(400, msg='name is required')
+
+    with app.session_maker() as session:
+        if agent_id:
+            agent_id = int(agent_id)
+            agent = session.query(AgentConfiguration).filter(AgentConfiguration.id == agent_id).first()
+            if not agent:
+                return wrap_response(404, msg='Agent not found')
+            agent.name = name
+            agent.display_name = display_name
+            agent.type = type_
+            agent.execution_model = execution_model
+            agent.system_prompt = system_prompt
+            agent.task_prompt = task_prompt
+            agent.tools = tools
+            agent.sub_agents = sub_agents
+            agent.extra_params = extra_params
+        else:
+            agent = AgentConfiguration(
+                name=name,
+                display_name=display_name,
+                type=type_,
+                execution_model=execution_model,
+                system_prompt=system_prompt,
+                task_prompt=task_prompt,
+                tools=tools,
+                sub_agents=sub_agents,
+                extra_params=extra_params
+            )
+            session.add(agent)
+
+        session.commit()
+        return wrap_response(200, msg='Agent configuration saved successfully', data={'id': agent.id})
+
+@app.route("/api/getModuleList", methods=["GET"])
+def get_module_list():
+    """
+    获取所有的模块列表
+    :return:
+    """
+    with app.session_maker() as session:
+        query = session.query(ServiceModule) \
+            .filter(ServiceModule.is_delete == 0)
+        data = query.all()
+    ret_data = [
+        {
+            'id': module.id,
+            'name': module.name,
+            'display_name': module.display_name,
+            'default_agent_type': module.default_agent_type,
+            'default_agent_id': module.default_agent_id,
+            'create_time': module.create_time.strftime('%Y-%m-%d %H:%M:%S'),
+            'update_time': module.update_time.strftime('%Y-%m-%d %H:%M:%S')
+        }
+        for module in data
+    ]
+    return wrap_response(200, data=ret_data)
+
+@app.route("/api/getModuleConfiguration", methods=["GET"])
+def get_module_configuration():
+    """
+    获取指定模块的配置
+    :return:
+    """
+    module_id = request.args.get('module_id')
+    if not module_id:
+        return wrap_response(404, msg='module_id is required')
+
+    with app.session_maker() as session:
+        module = session.query(ServiceModule).filter(ServiceModule.id == module_id).first()
+        if not module:
+            return wrap_response(404, msg='Module not found')
+
+        data = {
+            'id': module.id,
+            'name': module.name,
+            'display_name': module.display_name,
+            'default_agent_type': module.default_agent_type,
+            'default_agent_id': module.default_agent_id,
+            'create_time': module.create_time.strftime('%Y-%m-%d %H:%M:%S'),
+            'updated_time': module.updated_time.strftime('%Y-%m-%d %H:%M:%S')
+        }
+        return wrap_response(200, data=data)
+
+@app.route("/api/saveModuleConfiguration", methods=["POST"])
+def save_module_configuration():
+    """
+    保存模块配置
+    :return:
+    """
+    req_data = request.json
+    module_id = req_data.get('module_id', None)
+    name = req_data.get('name')
+    display_name = req_data.get('display_name', None)
+    default_agent_type = req_data.get('default_agent_type', 0)
+    default_agent_id = req_data.get('default_agent_id', None)
+
+    if not name:
+        return wrap_response(400, msg='name is required')
+
+    with app.session_maker() as session:
+        if module_id:
+            module_id = int(module_id)
+            module = session.query(ServiceModule).filter(ServiceModule.id == module_id).first()
+            if not module:
+                return wrap_response(404, msg='Module not found')
+            module.name = name
+            module.display_name = display_name
+            module.default_agent_type = default_agent_type
+            module.default_agent_id = default_agent_id
+        else:
+            module = ServiceModule(
+                name=name,
+                display_name=display_name,
+                default_agent_type=default_agent_type,
+                default_agent_id=default_agent_id
+            )
+            session.add(module)
+
+        session.commit()
+        return wrap_response(200, msg='Module configuration saved successfully', data={'id': module.id})
 
 @app.errorhandler(werkzeug.exceptions.BadRequest)
 def handle_bad_request(e):
@@ -345,6 +557,8 @@ if __name__ == '__main__':
         chat_history_table=chat_history_db_config['table']
     )
     app.session_manager = session_manager
+    agent_db_engine = create_sql_engine(config['storage']['agent_state']['mysql'])
+    app.session_maker = sessionmaker(bind=agent_db_engine)
 
     wecom_db_config = config['storage']['user_relation']
     user_relation_manager = MySQLUserRelationManager(