From 82b32b36ca8cb718bf602cb43bf8ecae75740003 Mon Sep 17 00:00:00 2001 From: zhizhi <928570418@qq.com> Date: Thu, 20 Nov 2025 10:44:30 +0800 Subject: [PATCH 01/83] =?UTF-8?q?=E2=9C=A8=20image=20to=20text=20tool?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../services/tool_configuration_service.py | 37 +++++++++++++++++++ sdk/nexent/core/tools/__init__.py | 4 +- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/backend/services/tool_configuration_service.py b/backend/services/tool_configuration_service.py index ba1ff5628..9953f13c2 100644 --- a/backend/services/tool_configuration_service.py +++ b/backend/services/tool_configuration_service.py @@ -6,6 +6,7 @@ from typing import Any, List, Optional, Dict from urllib.parse import urljoin +from jinja2 import Template, StrictUndefined from pydantic_core import PydanticUndefined from fastmcp import Client import jsonref @@ -26,6 +27,13 @@ from services.elasticsearch_service import get_embedding_model, elastic_core from services.tenant_config_service import get_selected_knowledge_list +from backend.consts.const import MODEL_CONFIG_MAPPING +from backend.database.client import minio_client, MinioClient +from backend.utils.config_utils import tenant_config_manager, get_model_name_from_config +from backend.utils.prompt_template_utils import get_analyze_file_prompt_template +from sdk.nexent import MessageObserver +from sdk.nexent.core.models import OpenAIVLModel + logger = logging.getLogger("tool_configuration_service") @@ -612,6 +620,35 @@ def _validate_local_tool( 'embedding_model': embedding_model } tool_instance = tool_class(**params) + elif tool_name == "image_text_understanding_tool": + if not tenant_id or not user_id: + raise ToolExecutionException(f"Tenant ID and User ID are required for {tool_name} validation") + vlm_model_config = tenant_config_manager.get_model_config( + key=MODEL_CONFIG_MAPPING["vlm"], tenant_id=tenant_id) + image_to_text_model = OpenAIVLModel( + observer=MessageObserver(), + model_id=get_model_name_from_config( + vlm_model_config) if vlm_model_config else "", + api_base=vlm_model_config.get("base_url", ""), + api_key=vlm_model_config.get("api_key", ""), + temperature=0.7, + top_p=0.7, + frequency_penalty=0.5, + max_tokens=512 + ) + # Load prompts from yaml file + language = 'zh' + prompts = get_analyze_file_prompt_template(language) + system_prompt_template = Template(prompts['image_analysis']['system_prompt'], + undefined=StrictUndefined) + + params = { + **instantiation_params, + 'vlm_model': image_to_text_model, + 'storage_client': minio_client, + 'system_prompt_template': system_prompt_template + } + tool_instance = tool_class(**params) else: tool_instance = tool_class(**instantiation_params) diff --git a/sdk/nexent/core/tools/__init__.py b/sdk/nexent/core/tools/__init__.py index 078594bcc..51a8967a9 100644 --- a/sdk/nexent/core/tools/__init__.py +++ b/sdk/nexent/core/tools/__init__.py @@ -12,6 +12,7 @@ from .move_item_tool import MoveItemTool from .list_directory_tool import ListDirectoryTool from .terminal_tool import TerminalTool +from .image_text_understanding_tool import ImageTextUnderstandingTool __all__ = [ "ExaSearchTool", @@ -27,5 +28,6 @@ "DeleteDirectoryTool", "MoveItemTool", "ListDirectoryTool", - "TerminalTool" + "TerminalTool", + "ImageTextUnderstandingTool" ] From 1064d382d8a5b2f4e07bf259924ad969fdfb3fdb Mon Sep 17 00:00:00 2001 From: zhizhi <928570418@qq.com> Date: Thu, 20 Nov 2025 15:35:36 +0800 Subject: [PATCH 02/83] =?UTF-8?q?=E2=9C=A8=20image=20to=20text=20tool?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/agents/create_agent_info.py | 16 +++ backend/mcp_service.py | 5 + backend/services/image_service.py | 21 ++++ .../services/tool_configuration_service.py | 21 +--- sdk/nexent/core/agents/nexent_agent.py | 6 + sdk/nexent/core/tools/__init__.py | 4 +- .../core/tools/image_understanding_tool.py | 106 ++++++++++++++++++ 7 files changed, 159 insertions(+), 20 deletions(-) create mode 100644 sdk/nexent/core/tools/image_understanding_tool.py diff --git a/backend/agents/create_agent_info.py b/backend/agents/create_agent_info.py index 3cae4f1c0..d6acc07b6 100644 --- a/backend/agents/create_agent_info.py +++ b/backend/agents/create_agent_info.py @@ -25,6 +25,10 @@ from utils.config_utils import tenant_config_manager, get_model_name_from_config from consts.const import LOCAL_MCP_SERVER, MODEL_CONFIG_MAPPING, LANGUAGE +from backend.database.client import minio_client +from backend.services.image_service import get_vlm_model +from backend.utils.prompt_template_utils import get_analyze_file_prompt_template + logger = logging.getLogger("create_agent_info") logger.setLevel(logging.DEBUG) @@ -236,6 +240,18 @@ async def create_tool_config_list(agent_id, tenant_id, user_id): "vdb_core": get_vector_db_core(), "embedding_model": get_embedding_model(tenant_id=tenant_id), } + elif tool_config.class_name == "ImageUnderstandingTool": + # Load prompts from yaml file + language = 'zh' + prompts = get_analyze_file_prompt_template(language) + system_prompt_template = Template(prompts['image_analysis']['system_prompt'], + undefined=StrictUndefined) + tool_config.metadata = { + "vlm_model": get_vlm_model(tenant_id=tenant_id), + "storage_client": minio_client, + "system_prompt_template": system_prompt_template, + } + tool_config_list.append(tool_config) return tool_config_list diff --git a/backend/mcp_service.py b/backend/mcp_service.py index c36d476ca..9aaf527f6 100644 --- a/backend/mcp_service.py +++ b/backend/mcp_service.py @@ -1,4 +1,7 @@ import logging + +from tool_collection.mcp.blur_image_tool import local_blur_image +from tool_collection.mcp.get_image_by_s3_url_tool import local_get_image_by_s3_url_tool from utils.logging_utils import configure_logging from fastmcp import FastMCP from tool_collection.mcp.local_mcp_service import local_mcp_service @@ -17,6 +20,8 @@ # mount local service (stable, not affected by remote proxy) nexent_mcp.mount(local_mcp_service.name, local_mcp_service) +nexent_mcp.mount(local_blur_image.name, local_blur_image) +nexent_mcp.mount(local_get_image_by_s3_url_tool.name, local_get_image_by_s3_url_tool) if __name__ == "__main__": nexent_mcp.run(transport="sse", host="0.0.0.0", port=5011) diff --git a/backend/services/image_service.py b/backend/services/image_service.py index 939e87315..080d23d8d 100644 --- a/backend/services/image_service.py +++ b/backend/services/image_service.py @@ -4,6 +4,11 @@ import aiohttp from consts.const import DATA_PROCESS_SERVICE +from nexent import MessageObserver +from nexent.core.models import OpenAIVLModel + +from backend.consts.const import MODEL_CONFIG_MAPPING +from backend.utils.config_utils import tenant_config_manager, get_model_name_from_config logger = logging.getLogger("image_service") @@ -23,3 +28,19 @@ async def proxy_image_impl(decoded_url: str): result = await response.json() return result + +def get_vlm_model(tenant_id: str): + # Get the tenant config + vlm_model_config = tenant_config_manager.get_model_config( + key=MODEL_CONFIG_MAPPING["vlm"], tenant_id=tenant_id) + return OpenAIVLModel( + observer=MessageObserver(), + model_id=get_model_name_from_config( + vlm_model_config) if vlm_model_config else "", + api_base=vlm_model_config.get("base_url", ""), + api_key=vlm_model_config.get("api_key", ""), + temperature=0.7, + top_p=0.7, + frequency_penalty=0.5, + max_tokens=512 + ) diff --git a/backend/services/tool_configuration_service.py b/backend/services/tool_configuration_service.py index f5d8ce31c..31c6b3c59 100644 --- a/backend/services/tool_configuration_service.py +++ b/backend/services/tool_configuration_service.py @@ -27,12 +27,9 @@ from services.vectordatabase_service import get_embedding_model, get_vector_db_core from services.tenant_config_service import get_selected_knowledge_list -from backend.consts.const import MODEL_CONFIG_MAPPING from backend.database.client import minio_client, MinioClient -from backend.utils.config_utils import tenant_config_manager, get_model_name_from_config +from backend.services.image_service import get_vlm_model from backend.utils.prompt_template_utils import get_analyze_file_prompt_template -from sdk.nexent import MessageObserver -from sdk.nexent.core.models import OpenAIVLModel logger = logging.getLogger("tool_configuration_service") @@ -621,22 +618,10 @@ def _validate_local_tool( 'embedding_model': embedding_model, } tool_instance = tool_class(**params) - elif tool_name == "image_text_understanding_tool": + elif tool_name == "image_text_understanding": if not tenant_id or not user_id: raise ToolExecutionException(f"Tenant ID and User ID are required for {tool_name} validation") - vlm_model_config = tenant_config_manager.get_model_config( - key=MODEL_CONFIG_MAPPING["vlm"], tenant_id=tenant_id) - image_to_text_model = OpenAIVLModel( - observer=MessageObserver(), - model_id=get_model_name_from_config( - vlm_model_config) if vlm_model_config else "", - api_base=vlm_model_config.get("base_url", ""), - api_key=vlm_model_config.get("api_key", ""), - temperature=0.7, - top_p=0.7, - frequency_penalty=0.5, - max_tokens=512 - ) + image_to_text_model = get_vlm_model(tenant_id=tenant_id) # Load prompts from yaml file language = 'zh' prompts = get_analyze_file_prompt_template(language) diff --git a/sdk/nexent/core/agents/nexent_agent.py b/sdk/nexent/core/agents/nexent_agent.py index a44310c9c..ea035e957 100644 --- a/sdk/nexent/core/agents/nexent_agent.py +++ b/sdk/nexent/core/agents/nexent_agent.py @@ -71,6 +71,12 @@ def create_local_tool(self, tool_config: ToolConfig): vdb_core=tool_config.metadata.get("vdb_core", []), embedding_model=tool_config.metadata.get("embedding_model", []), **params) + elif class_name == "ImageUnderstandingTool": + tools_obj = tool_class(observer=self.observer, + vlm_model=tool_config.metadata.get("vlm_model", []), + storage_client=tool_config.metadata.get("storage_client", []), + system_prompt_template=tool_config.metadata.get("system_prompt_template", []), + **params) else: tools_obj = tool_class(**params) if hasattr(tools_obj, 'observer'): diff --git a/sdk/nexent/core/tools/__init__.py b/sdk/nexent/core/tools/__init__.py index 51a8967a9..2b467b3a5 100644 --- a/sdk/nexent/core/tools/__init__.py +++ b/sdk/nexent/core/tools/__init__.py @@ -12,7 +12,7 @@ from .move_item_tool import MoveItemTool from .list_directory_tool import ListDirectoryTool from .terminal_tool import TerminalTool -from .image_text_understanding_tool import ImageTextUnderstandingTool +from .image_understanding_tool import ImageUnderstandingTool __all__ = [ "ExaSearchTool", @@ -29,5 +29,5 @@ "MoveItemTool", "ListDirectoryTool", "TerminalTool", - "ImageTextUnderstandingTool" + "ImageUnderstandingTool" ] diff --git a/sdk/nexent/core/tools/image_understanding_tool.py b/sdk/nexent/core/tools/image_understanding_tool.py new file mode 100644 index 000000000..7c0f4088c --- /dev/null +++ b/sdk/nexent/core/tools/image_understanding_tool.py @@ -0,0 +1,106 @@ +import json +import logging +from io import BytesIO + +from jinja2 import Template +from pydantic import Field +from smolagents.tools import Tool + +from ..models.openai_vlm import OpenAIVLModel +from ..utils.observer import MessageObserver, ProcessType +from ..utils.tools_common_message import ToolCategory, ToolSign +from ... import MinIOStorageClient +from ...multi_modal.load_save_object import LoadSaveObjectManager + +logger = logging.getLogger("image_understanding_tool") + + +class ImageUnderstandingTool(Tool): + """Tool for extracting text from images stored in S3-compatible storage.""" + + name = "image_understanding" + description = ( + "Understand an image stored in S3-compatible storage or HTTP and return the text content inside the image. " + "Provide the object location via an s3:// URL or http:// URL or https:// URL." + ) + inputs = { + "image_url": { + "type": "string", + "description": "URL of the image to analyze (e.g., 's3://bucket/path/to/image.png'," + "'http://image.png', 'https://image.png')." + }, + "query": { + "type": "string", + "description": "The user query to perform." + } + } + output_type = "string" + # todo + category = ToolCategory.FILE.value + tool_sign = ToolSign.FILE_OPERATION.value + + def __init__( + self, + observer: MessageObserver = Field(description="Message observer", default=None, exclude=True), + vlm_model: OpenAIVLModel = Field(description="The VLM model to use", default=None, exclude=True), + storage_client: MinIOStorageClient = Field(description="Storage client to use", default=None, exclude=True), + # todo 这么写对不对 + system_prompt_template: Template = Field(description="System prompt template to use", default=None, exclude=True), + ): + super().__init__() + self.observer = observer + self.vlm_model = vlm_model + # Use provided storage_client or create a default one + # if storage_client is None: + # storage_client = create_storage_client_from_config() + self.storage_client = storage_client + self.system_prompt_template = system_prompt_template + + + # Create LoadSaveObjectManager with the storage client + self.mm = LoadSaveObjectManager(storage_client=self.storage_client) + + # Dynamically apply the load_object decorator to forward method + self.forward = self.mm.load_object(input_names=["image_url"])(self._forward_impl) + + self.running_prompt_zh = "正在分析图片文字..." + self.running_prompt_en = "Analyzing image text..." + + def _forward_impl(self, image_url: bytes, query: str) -> str: + """ + Analyze the image specified by the S3 URL and return recognized text. + + Note: This method is wrapped by load_object decorator which downloads + the image from S3 URL and passes bytes to this method. + + Args: + image_url: Image bytes (converted from S3 URL by decorator). + + Returns: + JSON string containing the recognized text. + + Raises: + Exception: If the image cannot be downloaded or analyzed. + """ + # Note: image_url is now bytes after decorator processing + image_stream = BytesIO(image_url) + + # Send tool run message + if self.observer: + running_prompt = self.running_prompt_zh if self.observer.lang == "zh" else self.running_prompt_en + self.observer.add_message("", ProcessType.TOOL, running_prompt) + card_content = [{"icon": "image", "text": "Processing image..."}] + self.observer.add_message("", ProcessType.CARD, json.dumps(card_content, ensure_ascii=False)) + + # # Load messages based on language + # messages = get_file_processing_messages_template(language) + + try: + text = self.vlm_model.analyze_image( + image_input=image_stream, + system_prompt=self.system_prompt_template.render({'query': query})).content + return text + # return messages["IMAGE_CONTENT_SUCCESS"].format(filename=filename, content=text) + except Exception as e: + raise e + From e6734eb60f0643f083b404c7f0541da4d279505b Mon Sep 17 00:00:00 2001 From: panyehong <2655992392@qq.com> Date: Thu, 20 Nov 2025 15:47:10 +0800 Subject: [PATCH 03/83] =?UTF-8?q?=E2=9C=A8Agent=20duplicate=20name=20handl?= =?UTF-8?q?ing=20logic=20improvement=20#1622?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/prompts/utils/prompt_generate.yaml | 40 ++ backend/prompts/utils/prompt_generate_en.yaml | 40 ++ backend/services/agent_service.py | 366 +++++++++++++++++- backend/services/prompt_service.py | 93 ++++- 4 files changed, 534 insertions(+), 5 deletions(-) diff --git a/backend/prompts/utils/prompt_generate.yaml b/backend/prompts/utils/prompt_generate.yaml index fc102788b..bde7d84bb 100644 --- a/backend/prompts/utils/prompt_generate.yaml +++ b/backend/prompts/utils/prompt_generate.yaml @@ -214,6 +214,26 @@ AGENT_VARIABLE_NAME_SYSTEM_PROMPT: |- web_search_assistant +AGENT_VARIABLE_NAME_REGENERATE_SYSTEM_PROMPT: |- + ### 去重约束: + 1. 保持与“{original_name}”语义一致,体现同样的职责定位。 + 2. 禁止生成以下已存在的变量名:{existing_names}。 + 3. 可参考任务描述提炼核心能力,但仍需符合变量命名规范。 + + +AGENT_VARIABLE_NAME_REGENERATE_USER_PROMPT: |- + ### 任务描述: + {task_description} + + ### 原始变量名: + {original_name} + + ### 已存在的变量名: + {existing_names} + + 请生成一个新的 Agent 变量名,保持语义一致但避免与已存在的变量名重复。 + + AGENT_DESCRIPTION_SYSTEM_PROMPT: |- ### 你是【Agent应用描述生成专家】,用于帮助用户生成应用描述。 现在正在构建一个Agent应用,用户的输入包含三个部分:任务描述、使用工具、使用到的助手。 @@ -231,6 +251,26 @@ AGENT_DESCRIPTION_SYSTEM_PROMPT: |- 你是一个网络搜索助手,可以根据用户问题搜索出对应的信息,并给出搜索结果。 +AGENT_DISPLAY_NAME_REGENERATE_SYSTEM_PROMPT: |- + ### 去重约束: + 1. 新展示名称需要延续“{original_display_name}”的语义与定位。 + 2. 禁止输出以下已存在的展示名称:{existing_display_names}。 + 3. 需保持展示名称简洁易懂,并符合语言要求。 + + +AGENT_DISPLAY_NAME_REGENERATE_USER_PROMPT: |- + ### 任务描述: + {task_description} + + ### 原始展示名称: + {original_display_name} + + ### 已存在的展示名称: + {existing_display_names} + + 请生成一个新的 Agent 展示名称,语义与原名称一致但不与现有展示名称重复。 + + USER_PROMPT: |- ### 任务描述: {{task_description}} diff --git a/backend/prompts/utils/prompt_generate_en.yaml b/backend/prompts/utils/prompt_generate_en.yaml index 8ddf8899c..ad46c1ac4 100644 --- a/backend/prompts/utils/prompt_generate_en.yaml +++ b/backend/prompts/utils/prompt_generate_en.yaml @@ -218,6 +218,26 @@ AGENT_VARIABLE_NAME_SYSTEM_PROMPT: |- web_search_assistant +AGENT_VARIABLE_NAME_REGENERATE_SYSTEM_PROMPT: |- + ### De-duplication Constraints: + 1. Keep the meaning aligned with "{original_name}" so that the responsibilities remain clear. + 2. Never output any of the existing variable names: {existing_names}. + 3. Follow the variable naming rules and leverage the task description when helpful. + + +AGENT_VARIABLE_NAME_REGENERATE_USER_PROMPT: |- + ### Task Description: + {task_description} + + ### Original Variable Name: + {original_name} + + ### Existing Variable Names: + {existing_names} + + Please generate a new agent variable name that keeps the same meaning but does not duplicate the existing names. + + AGENT_DESCRIPTION_SYSTEM_PROMPT: |- ### You are an [Agent Description Generation Expert], used to help users generate agent descriptions. You are currently building an Agent application. User input contains three parts: task description, tools used, and assistants used. @@ -235,6 +255,26 @@ AGENT_DESCRIPTION_SYSTEM_PROMPT: |- You are a web search assistant that can search for corresponding information based on user questions and provide search results. +AGENT_DISPLAY_NAME_REGENERATE_SYSTEM_PROMPT: |- + ### De-duplication Constraints: + 1. Keep the semantics consistent with "{original_display_name}". + 2. Do not output any of the existing display names: {existing_display_names}. + 3. Ensure the display name is concise, readable, and language-compliant. + + +AGENT_DISPLAY_NAME_REGENERATE_USER_PROMPT: |- + ### Task Description: + {task_description} + + ### Original Display Name: + {original_display_name} + + ### Existing Display Names: + {existing_display_names} + + Please generate a new agent display name with the same meaning that does not duplicate any existing display names. + + USER_PROMPT: |- ### Task Description: {{task_description}} diff --git a/backend/services/agent_service.py b/backend/services/agent_service.py index b1e9ff813..32c45aa90 100644 --- a/backend/services/agent_service.py +++ b/backend/services/agent_service.py @@ -58,6 +58,7 @@ from utils.config_utils import tenant_config_manager from utils.memory_utils import build_memory_config from utils.thread_utils import submit +from utils.prompt_template_utils import get_prompt_generate_prompt_template # Import monitoring utilities from utils.monitoring import monitoring_manager @@ -137,6 +138,283 @@ def _resolve_model_with_fallback( return None +def _check_agent_param_duplicate( + name: str, + check_param: str = "name", + existing_agents: list[dict] | None = None, + exclude_agent_id: int | None = None +) -> bool: + """ + Check if a specific agent field already exists in the provided list. + + Args: + name: Agent name or display_name to check + check_param: Field key to inspect (e.g., "name", "display_name") + existing_agents: Optional pre-fetched agent list to avoid extra DB calls + exclude_agent_id: Optional agent ID to exclude from check (for updates) + + Returns: + True if duplicate exists, False otherwise + """ + if not existing_agents: + return False + + for agent in existing_agents: + if exclude_agent_id and agent.get("agent_id") == exclude_agent_id: + continue + if agent.get(check_param) == name: + return True + return False + + +def _generate_unique_value_with_suffix( + base_name: str, + check_param: str, + *, + existing_values: set[str] | None = None, + existing_agents: list[dict] | None = None, + exclude_agent_id: int | None = None, + max_suffix_attempts: int = 100, + error_message: str +) -> str: + counter = 1 + while counter <= max_suffix_attempts: + candidate = f"{base_name}_{counter}" + if existing_values is not None: + if candidate not in existing_values: + existing_values.add(candidate) + return candidate + else: + if not _check_agent_param_duplicate( + candidate, + check_param=check_param, + existing_agents=existing_agents, + exclude_agent_id=exclude_agent_id + ): + return candidate + counter += 1 + raise ValueError(error_message) + + +def _generate_unique_agent_name_with_suffix( + base_name: str, + existing_names: set[str] | None = None, + existing_agents: list[dict] | None = None, + exclude_agent_id: int | None = None, + max_suffix_attempts: int = 100 +) -> str: + return _generate_unique_value_with_suffix( + base_name, + "name", + existing_values=existing_names, + existing_agents=existing_agents, + exclude_agent_id=exclude_agent_id, + max_suffix_attempts=max_suffix_attempts, + error_message="Failed to generate unique agent name after max attempts" + ) + + +def _generate_unique_display_name_with_suffix( + base_name: str, + existing_display_names: set[str] | None = None, + existing_agents: list[dict] | None = None, + exclude_agent_id: int | None = None, + max_suffix_attempts: int = 100 +) -> str: + return _generate_unique_value_with_suffix( + base_name, + "display_name", + existing_values=existing_display_names, + existing_agents=existing_agents, + exclude_agent_id=exclude_agent_id, + max_suffix_attempts=max_suffix_attempts, + error_message="Failed to generate unique agent display name after max attempts" + ) + + +def _regenerate_agent_name_with_llm( + original_name: str, + existing_names: set[str] | None, + task_description: str, + model_id: int, + tenant_id: str, + language: str = LANGUAGE["ZH"] +) -> str: + """ + Regenerate agent name using LLM to ensure it's similar but not duplicate. + + Args: + original_name: Original agent name + existing_names: List of existing agent names to avoid + task_description: Task description for context + model_id: Model ID to use for generation + tenant_id: Tenant ID + language: Language code + + Returns: + Regenerated agent name + """ + prompt_template = get_prompt_generate_prompt_template(language) + base_system_prompt = prompt_template.get("AGENT_VARIABLE_NAME_SYSTEM_PROMPT", "") + constraint_system_prompt = prompt_template.get("AGENT_VARIABLE_NAME_REGENERATE_SYSTEM_PROMPT", "") + user_prompt_template = prompt_template.get("AGENT_VARIABLE_NAME_REGENERATE_USER_PROMPT", "") + + existing_name_set = existing_names if existing_names is not None else set() + existing_names_str = ", ".join(sorted(existing_name_set)) if existing_name_set else "None" + + if constraint_system_prompt: + system_prompt = ( + f"{base_system_prompt}\n\n" + f"{constraint_system_prompt.format(original_name=original_name, existing_names=existing_names_str)}" + ) + else: + system_prompt = f"{base_system_prompt}" + + if user_prompt_template: + user_prompt = user_prompt_template.format( + task_description=task_description or "", + original_name=original_name, + existing_names=existing_names_str + ) + else: + user_prompt = ( + f"### Task Description:\n{task_description}\n\n" + f"### Original Name (for reference):\n{original_name}\n\n" + f"### Existing Names to Avoid:\n{existing_names_str}\n\n" + f"Please generate a new agent variable name that is similar in meaning to " + f"\"{original_name}\" but different from all existing names." + ) + + max_attempts = 5 + last_error = None + + for attempt in range(1, max_attempts + 1): + try: + from services.prompt_service import call_llm_for_system_prompt + regenerated_name = call_llm_for_system_prompt( + model_id=model_id, + user_prompt=user_prompt, + system_prompt=system_prompt, + callback=None, + tenant_id=tenant_id + ) + regenerated_name = (regenerated_name or "").strip().replace('\n', '').strip() + + if not regenerated_name: + raise ValueError("Generated empty agent name") + if not regenerated_name.isidentifier(): + raise ValueError(f"Generated invalid agent name '{regenerated_name}'") + if regenerated_name in existing_name_set: + raise ValueError(f"Generated duplicate agent name '{regenerated_name}'") + + existing_name_set.add(regenerated_name) + return regenerated_name + except Exception as exc: + last_error = exc + logger.warning( + f"Attempt {attempt}/{max_attempts} to regenerate agent name failed: {exc}" + ) + + logger.error( + "Failed to regenerate agent name with LLM after maximum retries", + exc_info=last_error + ) + return _generate_unique_agent_name_with_suffix( + original_name, + existing_names=existing_name_set + ) + + +def _regenerate_agent_display_name_with_llm( + original_display_name: str, + existing_display_names: set[str] | None, + task_description: str, + model_id: int, + tenant_id: str, + language: str = LANGUAGE["ZH"] +) -> str: + """ + Regenerate agent display_name using LLM to ensure it's similar but not duplicate. + + Args: + original_display_name: Original agent display_name + existing_display_names: List of existing agent display_names to avoid + task_description: Task description for context + model_id: Model ID to use for generation + tenant_id: Tenant ID + language: Language code + + Returns: + Regenerated agent display_name + """ + prompt_template = get_prompt_generate_prompt_template(language) + base_system_prompt = prompt_template.get("AGENT_DISPLAY_NAME_SYSTEM_PROMPT", "") + constraint_system_prompt = prompt_template.get("AGENT_DISPLAY_NAME_REGENERATE_SYSTEM_PROMPT", "") + user_prompt_template = prompt_template.get("AGENT_DISPLAY_NAME_REGENERATE_USER_PROMPT", "") + + existing_display_name_set = existing_display_names if existing_display_names is not None else set() + existing_names_str = ", ".join(sorted(existing_display_name_set)) if existing_display_name_set else "None" + + if constraint_system_prompt: + system_prompt = ( + f"{base_system_prompt}\n\n" + f"{constraint_system_prompt.format(original_display_name=original_display_name, existing_display_names=existing_names_str)}" + ) + else: + system_prompt = f"{base_system_prompt}" + + if user_prompt_template: + user_prompt = user_prompt_template.format( + task_description=task_description or "", + original_display_name=original_display_name, + existing_display_names=existing_names_str + ) + else: + user_prompt = ( + f"### Task Description:\n{task_description}\n\n" + f"### Original Display Name (for reference):\n{original_display_name}\n\n" + f"### Existing Display Names to Avoid:\n{existing_names_str}\n\n" + f"Please generate a new agent display name that is similar in meaning to " + f"\"{original_display_name}\" but different from all existing names." + ) + + max_attempts = 5 + last_error = None + + for attempt in range(1, max_attempts + 1): + try: + from services.prompt_service import call_llm_for_system_prompt + regenerated_name = call_llm_for_system_prompt( + model_id=model_id, + user_prompt=user_prompt, + system_prompt=system_prompt, + callback=None, + tenant_id=tenant_id + ) + regenerated_name = (regenerated_name or "").strip().replace('\n', '').strip() + if not regenerated_name: + raise ValueError("Generated empty display name") + if regenerated_name in existing_display_name_set: + raise ValueError(f"Generated duplicate display name '{regenerated_name}'") + + existing_display_name_set.add(regenerated_name) + return regenerated_name + except Exception as exc: + last_error = exc + logger.warning( + f"Attempt {attempt}/{max_attempts} to regenerate agent display name failed: {exc}" + ) + + logger.error( + "Failed to regenerate agent display name with LLM after maximum retries", + exc_info=last_error + ) + return _generate_unique_display_name_with_suffix( + original_display_name, + existing_display_names=existing_display_name_set + ) + + async def _stream_agent_chunks( agent_request: "AgentRequest", user_id: str, @@ -744,9 +1022,93 @@ async def import_agent_by_agent_id(import_agent_info: ExportAndImportAgentInfo, tenant_id=tenant_id ) + # Check for duplicate names and regenerate if needed + agent_name = import_agent_info.name + agent_display_name = import_agent_info.display_name + + # Get all existing agent names and display names for duplicate checking + all_agents = query_all_agent_info_by_tenant_id(tenant_id) + existing_names = {agent.get("name") for agent in all_agents if agent.get("name")} + existing_display_names = {agent.get("display_name") for agent in all_agents if agent.get("display_name")} + + # Check and regenerate name if duplicate + if _check_agent_param_duplicate(agent_name, existing_agents=all_agents): + logger.info(f"Agent name '{agent_name}' already exists, regenerating with LLM") + # Get model for regeneration (use business_logic_model_id if available, otherwise use model_id) + regeneration_model_id = business_logic_model_id or model_id + if regeneration_model_id: + try: + agent_name = _regenerate_agent_name_with_llm( + original_name=agent_name, + existing_names=existing_names, + task_description=import_agent_info.business_description or import_agent_info.description or "", + model_id=regeneration_model_id, + tenant_id=tenant_id, + language=LANGUAGE["ZH"] # Default to Chinese, can be enhanced later + ) + logger.info(f"Regenerated agent name: '{agent_name}'") + except Exception as e: + logger.error(f"Failed to regenerate agent name with LLM: {str(e)}, using fallback") + # Fallback: add suffix + agent_name = _generate_unique_agent_name_with_suffix( + agent_name, + existing_names=existing_names, + existing_agents=all_agents + ) + else: + logger.warning("No model available for regeneration, using fallback") + # Fallback: add suffix + agent_name = _generate_unique_agent_name_with_suffix( + agent_name, + existing_names=existing_names, + existing_agents=all_agents + ) + + # Check and regenerate display_name if duplicate + if _check_agent_param_duplicate( + agent_display_name, + check_param="display_name", + existing_agents=all_agents + ): + logger.info(f"Agent display_name '{agent_display_name}' already exists, regenerating with LLM") + # Get model for regeneration (use business_logic_model_id if available, otherwise use model_id) + regeneration_model_id = business_logic_model_id or model_id + if regeneration_model_id: + try: + agent_display_name = _regenerate_agent_display_name_with_llm( + original_display_name=agent_display_name, + existing_display_names=existing_display_names, + task_description=import_agent_info.business_description or import_agent_info.description or "", + model_id=regeneration_model_id, + tenant_id=tenant_id, + language=LANGUAGE["ZH"] # Default to Chinese, can be enhanced later + ) + logger.info(f"Regenerated agent display_name: '{agent_display_name}'") + except Exception as e: + logger.error(f"Failed to regenerate agent display_name with LLM: {str(e)}, using fallback") + # Fallback: add suffix + agent_display_name = _generate_unique_display_name_with_suffix( + agent_display_name, + existing_display_names=existing_display_names, + existing_agents=all_agents + ) + else: + logger.warning("No model available for regeneration, using fallback") + # Fallback: add suffix + agent_display_name = _generate_unique_display_name_with_suffix( + agent_display_name, + existing_display_names=existing_display_names, + existing_agents=all_agents + ) + + if agent_name: + existing_names.add(agent_name) + if agent_display_name: + existing_display_names.add(agent_display_name) + # create a new agent - new_agent = create_agent(agent_info={"name": import_agent_info.name, - "display_name": import_agent_info.display_name, + new_agent = create_agent(agent_info={"name": agent_name, + "display_name": agent_display_name, "description": import_agent_info.description, "business_description": import_agent_info.business_description, "model_id": model_id, diff --git a/backend/services/prompt_service.py b/backend/services/prompt_service.py index e0bae7870..4abc47e15 100644 --- a/backend/services/prompt_service.py +++ b/backend/services/prompt_service.py @@ -8,7 +8,7 @@ from consts.const import LANGUAGE, MODEL_CONFIG_MAPPING, MESSAGE_ROLE, THINK_END_PATTERN, THINK_START_PATTERN from consts.model import AgentInfoRequest -from database.agent_db import update_agent, query_sub_agents_id_list, search_agent_info_by_agent_id +from database.agent_db import update_agent, query_sub_agents_id_list, search_agent_info_by_agent_id, query_all_agent_info_by_tenant_id from database.model_management_db import get_model_by_model_id from database.tool_db import query_tools_by_ids from services.agent_service import get_enable_tool_id_by_agent_id @@ -143,14 +143,101 @@ def generate_and_save_system_prompt_impl(agent_id: int, else: logger.info( "Updating agent with business_description and prompt segments") + + # Check for duplicate names and regenerate if needed + agent_name = final_results["agent_var_name"] + agent_display_name = final_results["agent_display_name"] + + # Import functions locally to avoid circular import + from services.agent_service import ( + _check_agent_param_duplicate, + _regenerate_agent_name_with_llm, + _regenerate_agent_display_name_with_llm, + _generate_unique_agent_name_with_suffix, + _generate_unique_display_name_with_suffix + ) + + # Get all existing agent names and display names for duplicate checking + all_agents = query_all_agent_info_by_tenant_id(tenant_id) + existing_names = { + agent.get("name") + for agent in all_agents + if agent.get("name") and agent.get("agent_id") != agent_id + } + existing_display_names = { + agent.get("display_name") + for agent in all_agents + if agent.get("display_name") and agent.get("agent_id") != agent_id + } + + # Check and regenerate name if duplicate + if _check_agent_param_duplicate( + agent_name, + existing_agents=all_agents, + exclude_agent_id=agent_id + ): + logger.info(f"Agent name '{agent_name}' already exists, regenerating with LLM") + try: + agent_name = _regenerate_agent_name_with_llm( + original_name=agent_name, + existing_names=existing_names, + task_description=task_description, + model_id=model_id, + tenant_id=tenant_id, + language=language + ) + logger.info(f"Regenerated agent name: '{agent_name}'") + except Exception as e: + logger.error(f"Failed to regenerate agent name with LLM: {str(e)}, using fallback") + # Fallback: add suffix + agent_name = _generate_unique_agent_name_with_suffix( + agent_name, + existing_names=existing_names, + existing_agents=all_agents, + exclude_agent_id=agent_id + ) + + # Check and regenerate display_name if duplicate + if _check_agent_param_duplicate( + agent_display_name, + check_param="display_name", + existing_agents=all_agents, + exclude_agent_id=agent_id + ): + logger.info(f"Agent display_name '{agent_display_name}' already exists, regenerating with LLM") + try: + agent_display_name = _regenerate_agent_display_name_with_llm( + original_display_name=agent_display_name, + existing_display_names=existing_display_names, + task_description=task_description, + model_id=model_id, + tenant_id=tenant_id, + language=language + ) + logger.info(f"Regenerated agent display_name: '{agent_display_name}'") + except Exception as e: + logger.error(f"Failed to regenerate agent display_name with LLM: {str(e)}, using fallback") + # Fallback: add suffix + agent_display_name = _generate_unique_display_name_with_suffix( + agent_display_name, + existing_display_names=existing_display_names, + existing_agents=all_agents, + exclude_agent_id=agent_id + ) + + if agent_name: + existing_names.add(agent_name) + if agent_display_name: + existing_display_names.add(agent_display_name) + agent_info = AgentInfoRequest( agent_id=agent_id, business_description=task_description, duty_prompt=final_results["duty"], constraint_prompt=final_results["constraint"], few_shots_prompt=final_results["few_shots"], - name=final_results["agent_var_name"], - display_name=final_results["agent_display_name"], + name=agent_name, + display_name=agent_display_name, description=final_results["agent_description"] ) update_agent( From 906ca053d85a78469a3c4f395f6925a5c8a9567f Mon Sep 17 00:00:00 2001 From: zhizhi <928570418@qq.com> Date: Fri, 21 Nov 2025 11:10:12 +0800 Subject: [PATCH 04/83] =?UTF-8?q?=E2=9C=A8=20image=20to=20text=20tool?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/agents/create_agent_info.py | 6 ---- .../services/tool_configuration_service.py | 11 ++----- sdk/nexent/core/agents/nexent_agent.py | 1 - sdk/nexent/core/prompts/understand_image.yaml | 14 ++++++++ .../core/prompts/understand_image_en.yaml | 13 ++++++++ .../core/tools/image_understanding_tool.py | 33 ++++++++++--------- sdk/nexent/multi_modal/load_save_object.py | 4 +-- 7 files changed, 48 insertions(+), 34 deletions(-) create mode 100644 sdk/nexent/core/prompts/understand_image.yaml create mode 100644 sdk/nexent/core/prompts/understand_image_en.yaml diff --git a/backend/agents/create_agent_info.py b/backend/agents/create_agent_info.py index d6acc07b6..033e9df3f 100644 --- a/backend/agents/create_agent_info.py +++ b/backend/agents/create_agent_info.py @@ -241,15 +241,9 @@ async def create_tool_config_list(agent_id, tenant_id, user_id): "embedding_model": get_embedding_model(tenant_id=tenant_id), } elif tool_config.class_name == "ImageUnderstandingTool": - # Load prompts from yaml file - language = 'zh' - prompts = get_analyze_file_prompt_template(language) - system_prompt_template = Template(prompts['image_analysis']['system_prompt'], - undefined=StrictUndefined) tool_config.metadata = { "vlm_model": get_vlm_model(tenant_id=tenant_id), "storage_client": minio_client, - "system_prompt_template": system_prompt_template, } tool_config_list.append(tool_config) diff --git a/backend/services/tool_configuration_service.py b/backend/services/tool_configuration_service.py index 31c6b3c59..473804bfa 100644 --- a/backend/services/tool_configuration_service.py +++ b/backend/services/tool_configuration_service.py @@ -618,21 +618,14 @@ def _validate_local_tool( 'embedding_model': embedding_model, } tool_instance = tool_class(**params) - elif tool_name == "image_text_understanding": + elif tool_name == "image_understanding": if not tenant_id or not user_id: raise ToolExecutionException(f"Tenant ID and User ID are required for {tool_name} validation") image_to_text_model = get_vlm_model(tenant_id=tenant_id) - # Load prompts from yaml file - language = 'zh' - prompts = get_analyze_file_prompt_template(language) - system_prompt_template = Template(prompts['image_analysis']['system_prompt'], - undefined=StrictUndefined) - params = { **instantiation_params, 'vlm_model': image_to_text_model, - 'storage_client': minio_client, - 'system_prompt_template': system_prompt_template + 'storage_client': minio_client } tool_instance = tool_class(**params) else: diff --git a/sdk/nexent/core/agents/nexent_agent.py b/sdk/nexent/core/agents/nexent_agent.py index ea035e957..8c84e4cac 100644 --- a/sdk/nexent/core/agents/nexent_agent.py +++ b/sdk/nexent/core/agents/nexent_agent.py @@ -75,7 +75,6 @@ def create_local_tool(self, tool_config: ToolConfig): tools_obj = tool_class(observer=self.observer, vlm_model=tool_config.metadata.get("vlm_model", []), storage_client=tool_config.metadata.get("storage_client", []), - system_prompt_template=tool_config.metadata.get("system_prompt_template", []), **params) else: tools_obj = tool_class(**params) diff --git a/sdk/nexent/core/prompts/understand_image.yaml b/sdk/nexent/core/prompts/understand_image.yaml new file mode 100644 index 000000000..c78826fa1 --- /dev/null +++ b/sdk/nexent/core/prompts/understand_image.yaml @@ -0,0 +1,14 @@ +# 图片分析 Prompt 模板 +# 用于图片分析 + +system_prompt: |- + 用户提出了一个问题:{{ query }},请从回答这个问题的角度精简、仔细描述一下这个图片,200字以内。 + + **图片分析要求:** + 1. 重点关注与用户问题相关的图片内容 + 2. 描述要精简明了,突出关键信息 + 3. 避免无关细节,专注于能帮助回答问题的内容 + 4. 保持客观描述,不要过度解读 + +user_prompt: | + 请仔细观察这张图片,并从回答用户问题的角度进行描述。 \ No newline at end of file diff --git a/sdk/nexent/core/prompts/understand_image_en.yaml b/sdk/nexent/core/prompts/understand_image_en.yaml new file mode 100644 index 000000000..7bc81daa2 --- /dev/null +++ b/sdk/nexent/core/prompts/understand_image_en.yaml @@ -0,0 +1,13 @@ +# Image Understanding Prompt Templates + +system_prompt: |- + The user has asked a question: {{ query }}. Please provide a concise and careful description of this image from the perspective of answering this question, within 200 words. + + **Image Analysis Requirements:** + 1. Focus on image content relevant to the user's question + 2. Keep descriptions concise and clear, highlighting key information + 3. Avoid irrelevant details, focus on content that helps answer the question + 4. Maintain objective description, avoid over-interpretation + +user_prompt: | + Please carefully observe this image and describe it from the perspective of answering the user's question. \ No newline at end of file diff --git a/sdk/nexent/core/tools/image_understanding_tool.py b/sdk/nexent/core/tools/image_understanding_tool.py index 7c0f4088c..62970f2cf 100644 --- a/sdk/nexent/core/tools/image_understanding_tool.py +++ b/sdk/nexent/core/tools/image_understanding_tool.py @@ -2,12 +2,13 @@ import logging from io import BytesIO -from jinja2 import Template +from jinja2 import Template, StrictUndefined from pydantic import Field from smolagents.tools import Tool from ..models.openai_vlm import OpenAIVLModel from ..utils.observer import MessageObserver, ProcessType +from ..utils.prompt_template_utils import get_prompt_template from ..utils.tools_common_message import ToolCategory, ToolSign from ... import MinIOStorageClient from ...multi_modal.load_save_object import LoadSaveObjectManager @@ -50,21 +51,16 @@ def __init__( super().__init__() self.observer = observer self.vlm_model = vlm_model - # Use provided storage_client or create a default one - # if storage_client is None: - # storage_client = create_storage_client_from_config() self.storage_client = storage_client self.system_prompt_template = system_prompt_template - - # Create LoadSaveObjectManager with the storage client self.mm = LoadSaveObjectManager(storage_client=self.storage_client) # Dynamically apply the load_object decorator to forward method self.forward = self.mm.load_object(input_names=["image_url"])(self._forward_impl) - self.running_prompt_zh = "正在分析图片文字..." - self.running_prompt_en = "Analyzing image text..." + self.running_prompt_zh = "正在理解图片..." + self.running_prompt_en = "Understanding image..." def _forward_impl(self, image_url: bytes, query: str) -> str: """ @@ -92,15 +88,20 @@ def _forward_impl(self, image_url: bytes, query: str) -> str: card_content = [{"icon": "image", "text": "Processing image..."}] self.observer.add_message("", ProcessType.CARD, json.dumps(card_content, ensure_ascii=False)) - # # Load messages based on language - # messages = get_file_processing_messages_template(language) + # Load prompts from yaml file + prompts = get_prompt_template(template_type='understand_image',language = self.observer.lang) try: - text = self.vlm_model.analyze_image( + + response = self.vlm_model.analyze_image( image_input=image_stream, - system_prompt=self.system_prompt_template.render({'query': query})).content - return text - # return messages["IMAGE_CONTENT_SUCCESS"].format(filename=filename, content=text) + system_prompt=Template(prompts['system_prompt'],undefined=StrictUndefined).render({'query': query})) except Exception as e: - raise e - + raise Exception(f"Error understanding image: {str(e)}") + text = response.content + # Record the detailed content of this search + search_results_data = {'text':text} + if self.observer: + search_results_data = json.dumps(search_results_data, ensure_ascii=False) + self.observer.add_message("", ProcessType.SEARCH_CONTENT, search_results_data) + return json.dumps(search_results_data, ensure_ascii=False) diff --git a/sdk/nexent/multi_modal/load_save_object.py b/sdk/nexent/multi_modal/load_save_object.py index 9e85f4880..4bc391036 100644 --- a/sdk/nexent/multi_modal/load_save_object.py +++ b/sdk/nexent/multi_modal/load_save_object.py @@ -87,7 +87,7 @@ def _upload_bytes_to_minio( self, bytes_data: bytes, object_name: Optional[str] = None, - bucket: str = "multi-modal", + bucket: str = "nexent", content_type: str = "application/octet-stream", ) -> str: """ @@ -194,7 +194,7 @@ def save_object( self, output_names: List[str], output_transformers: Optional[List[Callable[[Any], bytes]]] = None, - bucket: str = "multi-modal", + bucket: str = "nexent", ): """ Decorator factory that uploads outputs to storage after function execution. From e4166e143ff97bc7f3ebaf49341761aa81e4f25a Mon Sep 17 00:00:00 2001 From: panyehong <2655992392@qq.com> Date: Fri, 21 Nov 2025 12:33:45 +0800 Subject: [PATCH 05/83] =?UTF-8?q?=E2=9C=A8Agent=20duplicate=20name=20handl?= =?UTF-8?q?ing=20logic=20improvement=20#1622?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/prompts/utils/prompt_generate.yaml | 97 ++-- backend/prompts/utils/prompt_generate_en.yaml | 98 ++-- backend/services/agent_service.py | 457 +++++++++--------- backend/services/prompt_service.py | 5 - 4 files changed, 325 insertions(+), 332 deletions(-) diff --git a/backend/prompts/utils/prompt_generate.yaml b/backend/prompts/utils/prompt_generate.yaml index 87edef2f2..cdcc8cedb 100644 --- a/backend/prompts/utils/prompt_generate.yaml +++ b/backend/prompts/utils/prompt_generate.yaml @@ -52,8 +52,8 @@ FEW_SHOTS_SYSTEM_PROMPT: |- - 用简单的Python编写代码 - 遵循python代码规范和python语法 - 根据格式规范正确调用工具/助手 - - 考虑到代码执行与展示用户代码的区别,使用'代码:\n```\n'开头,并以'```'表达运行代码,使用'代码:\n```\n'开头,并以'```'表达展示代码 - - 注意运行的代码不会被用户看到,所以如果用户需要看到代码,你需要使用'代码:\n```\n'开头,并以'```'表达展示代码。 + - 考虑到代码执行与展示用户代码的区别,使用'代码:\n```\n'开头,并以'```'表达运行代码,使用'代码:\n```\n'开头,并以'```'表达展示代码 + - 注意运行的代码不会被用户看到,所以如果用户需要看到代码,你需要使用'代码:\n```\n'开头,并以'```'表达展示代码。 3. 观察结果: - 查看代码执行结果 @@ -61,7 +61,7 @@ FEW_SHOTS_SYSTEM_PROMPT: |- 在思考结束后,当Agent认为可以回答用户问题,那么可以不生成代码,直接生成最终回答给到用户并停止循环。 ### python代码规范 - 1. 如果认为是需要执行的代码,代码内容以'代码:\n```\n'开头,并以'```'标识符结尾。如果是不需要执行仅用于展示的代码,代码内容以'代码:\n```\n'开头,并以'```'标识符结尾,其中语言类型例如python、java、javascript等; + 1. 如果认为是需要执行的代码,代码内容以'代码:\n```\n'开头,并以'```'标识符结尾。如果是不需要执行仅用于展示的代码,代码内容以'代码:\n```\n'开头,并以'```'标识符结尾,其中语言类型例如python、java、javascript等; 2. 只使用已定义的变量,变量将在多次调用之间持续保持; 3. 使用“print()”函数让下一次的模型调用看到对应变量信息; 4. 正确使用工具/助手的入参,使用关键字参数,不要用字典形式; @@ -160,12 +160,11 @@ FEW_SHOTS_SYSTEM_PROMPT: |- middle = [x for x in arr if x == pivot] right = [x for x in arr if x > pivot] return quick_sort(left) + middle + quick_sort(right) - ``` + ``` 观察结果:快速排序的python代码。 思考:我已经获得了快速排序的python代码,现在我将生成最终回答。 快速排序的python代码如下: - 代码: ``` def quick_sort(arr): if len(arr) <= 1: @@ -175,7 +174,7 @@ FEW_SHOTS_SYSTEM_PROMPT: |- middle = [x for x in arr if x == pivot] right = [x for x in arr if x > pivot] return quick_sort(left) + middle + quick_sort(right) - ``` + ``` --- @@ -215,26 +214,6 @@ AGENT_VARIABLE_NAME_SYSTEM_PROMPT: |- web_search_assistant -AGENT_VARIABLE_NAME_REGENERATE_SYSTEM_PROMPT: |- - ### 去重约束: - 1. 保持与“{original_name}”语义一致,体现同样的职责定位。 - 2. 禁止生成以下已存在的变量名:{existing_names}。 - 3. 可参考任务描述提炼核心能力,但仍需符合变量命名规范。 - - -AGENT_VARIABLE_NAME_REGENERATE_USER_PROMPT: |- - ### 任务描述: - {task_description} - - ### 原始变量名: - {original_name} - - ### 已存在的变量名: - {existing_names} - - 请生成一个新的 Agent 变量名,保持语义一致但避免与已存在的变量名重复。 - - AGENT_DESCRIPTION_SYSTEM_PROMPT: |- ### 你是【Agent应用描述生成专家】,用于帮助用户生成应用描述。 现在正在构建一个Agent应用,用户的输入包含三个部分:任务描述、使用工具、使用到的助手。 @@ -252,26 +231,6 @@ AGENT_DESCRIPTION_SYSTEM_PROMPT: |- 你是一个网络搜索助手,可以根据用户问题搜索出对应的信息,并给出搜索结果。 -AGENT_DISPLAY_NAME_REGENERATE_SYSTEM_PROMPT: |- - ### 去重约束: - 1. 新展示名称需要延续“{original_display_name}”的语义与定位。 - 2. 禁止输出以下已存在的展示名称:{existing_display_names}。 - 3. 需保持展示名称简洁易懂,并符合语言要求。 - - -AGENT_DISPLAY_NAME_REGENERATE_USER_PROMPT: |- - ### 任务描述: - {task_description} - - ### 原始展示名称: - {original_display_name} - - ### 已存在的展示名称: - {existing_display_names} - - 请生成一个新的 Agent 展示名称,语义与原名称一致但不与现有展示名称重复。 - - USER_PROMPT: |- ### 任务描述: {{task_description}} @@ -289,3 +248,49 @@ USER_PROMPT: |- {% else %} 你没有可用的助手 {% endif %} + + +AGENT_NAME_REGENERATE_SYSTEM_PROMPT: |- + ### 你是【Agent变量名调整专家】 + 你的工作是根据任务描述以及已有变量名,生成一个语义一致但不重复的 Python 变量名。 + + #### 约束 + 1. 变量名只能包含字母、数字和下划线,并且以字母或下划线开头,长度不超过 30 个字符。 + 2. 避免与已有变量名重复,同时保持与原始名称相近的语义。 + 3. 仅输出变量名本身,不要附加额外解释或标点。 + + +AGENT_NAME_REGENERATE_USER_PROMPT: |- + ### 任务描述 + {{ task_description }} + + ### 原始变量名 + {{ original_value }} + + ### 已有变量名 + {{ existing_values }} + + 请在满足约束的前提下,生成一个新的变量名,使其语义接近原始变量名但不与已有变量名重复。只输出变量名本身。 + + +AGENT_DISPLAY_NAME_REGENERATE_SYSTEM_PROMPT: |- + ### 你是【Agent展示名称调整专家】 + 你的任务是结合业务背景和已有展示名称,生成一个语义一致但不重复的 Agent 展示名称。 + + #### 约束 + 1. 展示名称需自然、易读,能够概括 Agent 的核心能力,长度不超过 30 个字符。 + 2. 不得包含具体工具名称或多余符号。 + 3. 仅输出展示名称本身。 + + +AGENT_DISPLAY_NAME_REGENERATE_USER_PROMPT: |- + ### 任务描述 + {{ task_description }} + + ### 原始展示名称 + {{ original_value }} + + ### 已有展示名称 + {{ existing_values }} + + 请输出一个新的展示名称,语义接近原始名称但不与已存在的展示名称重复,并满足上述约束。只输出展示名称本身。 diff --git a/backend/prompts/utils/prompt_generate_en.yaml b/backend/prompts/utils/prompt_generate_en.yaml index 1c3bfd1cc..e8026df2a 100644 --- a/backend/prompts/utils/prompt_generate_en.yaml +++ b/backend/prompts/utils/prompt_generate_en.yaml @@ -53,8 +53,8 @@ FEW_SHOTS_SYSTEM_PROMPT: |- - Write code in simple Python - Follow Python coding standards and Python syntax - Call tools/assistants correctly according to format specifications - - To distinguish between code execution and displaying user code, use 'Code: \n```\n' to start executing code and '```' to indicate its completion. Use 'Code: \n```\n' to start displaying code and '```' to indicate its completion. - - Note that executed code is not visible to users. If users need to see the code, use 'Code: \n```\n' as the start and '```' to denote displayed code. + - To distinguish between code execution and displaying user code, use 'Code: \n```\n' to start executing code and '```' to indicate its completion. Use 'Code: \n```\n' to start displaying code and '```' to indicate its completion. + - Note that executed code is not visible to users. If users need to see the code, use 'Code: \n```\n' as the start and '```' to denote displayed code. 3. Observe Results: - View code execution results @@ -62,7 +62,7 @@ FEW_SHOTS_SYSTEM_PROMPT: |- After thinking, when you believe you can answer the user's question, you can generate a final answer directly to the user without generating code and stop the loop. ### Python Code Specifications - 1. If it is considered to be code that needs to be executed, the code content begins with 'Code:\n```\n' and ends with '```'. If the code does not need to be executed for display only, the code content begins with 'Code:\n```\n', and ends with '```', where language_type can be python, java, javascript, etc.; + 1. If it is considered to be code that needs to be executed, the code content begins with 'Code:\n```\n' and ends with '```'. If the code does not need to be executed for display only, the code content begins with 'Code:\n```\n', and ends with '```', where language_type can be python, java, javascript, etc.; 2. Only use defined variables, variables will persist between multiple calls; 3. Use "print()" function to let the next model call see corresponding variable information; 4. Use tool/assistant input parameters correctly, use keyword arguments, not dictionary format; @@ -158,7 +158,7 @@ FEW_SHOTS_SYSTEM_PROMPT: |- middle = [x for x in arr if x == pivot] right = [x for x in arr if x > pivot] return quick_sort(left) + middle + quick_sort(right) - ``` + ``` Observe Results: The Python quick sort code. Think: I have obtained the Python quick sort code, now I will generate the final answer. @@ -172,7 +172,7 @@ FEW_SHOTS_SYSTEM_PROMPT: |- middle = [x for x in arr if x == pivot] right = [x for x in arr if x > pivot] return quick_sort(left) + middle + quick_sort(right) - ``` + ``` --- @@ -218,26 +218,6 @@ AGENT_VARIABLE_NAME_SYSTEM_PROMPT: |- web_search_assistant -AGENT_VARIABLE_NAME_REGENERATE_SYSTEM_PROMPT: |- - ### De-duplication Constraints: - 1. Keep the meaning aligned with "{original_name}" so that the responsibilities remain clear. - 2. Never output any of the existing variable names: {existing_names}. - 3. Follow the variable naming rules and leverage the task description when helpful. - - -AGENT_VARIABLE_NAME_REGENERATE_USER_PROMPT: |- - ### Task Description: - {task_description} - - ### Original Variable Name: - {original_name} - - ### Existing Variable Names: - {existing_names} - - Please generate a new agent variable name that keeps the same meaning but does not duplicate the existing names. - - AGENT_DESCRIPTION_SYSTEM_PROMPT: |- ### You are an [Agent Description Generation Expert], used to help users generate agent descriptions. You are currently building an Agent application. User input contains three parts: task description, tools used, and assistants used. @@ -255,26 +235,6 @@ AGENT_DESCRIPTION_SYSTEM_PROMPT: |- You are a web search assistant that can search for corresponding information based on user questions and provide search results. -AGENT_DISPLAY_NAME_REGENERATE_SYSTEM_PROMPT: |- - ### De-duplication Constraints: - 1. Keep the semantics consistent with "{original_display_name}". - 2. Do not output any of the existing display names: {existing_display_names}. - 3. Ensure the display name is concise, readable, and language-compliant. - - -AGENT_DISPLAY_NAME_REGENERATE_USER_PROMPT: |- - ### Task Description: - {task_description} - - ### Original Display Name: - {original_display_name} - - ### Existing Display Names: - {existing_display_names} - - Please generate a new agent display name with the same meaning that does not duplicate any existing display names. - - USER_PROMPT: |- ### Task Description: {{task_description}} @@ -291,4 +251,50 @@ USER_PROMPT: |- {{assistant_description}} {% else %} You have no available assistants - {% endif %} \ No newline at end of file + {% endif %} + + +AGENT_NAME_REGENERATE_SYSTEM_PROMPT: |- + ### You are an [Agent Variable Name Refinement Expert] + Your job is to generate a Python-friendly variable name that keeps the same intent while avoiding duplicates. + + #### Constraints + 1. The name may contain only letters, numbers, and underscores, must start with a letter or underscore, and stay within 30 characters. + 2. Keep the new name semantically close to the original one while ensuring it does not duplicate existing names. + 3. Return the variable name only—no explanations or extra symbols. + + +AGENT_NAME_REGENERATE_USER_PROMPT: |- + ### Task Description + {{ task_description }} + + ### Original Variable Name + {{ original_value }} + + ### Existing Variable Names + {{ existing_values }} + + Generate a new variable name that satisfies the constraints, keeps the original meaning, and does not duplicate any existing names. Output only the variable name. + + +AGENT_DISPLAY_NAME_REGENERATE_SYSTEM_PROMPT: |- + ### You are an [Agent Display Name Refinement Expert] + You summarize the agent's capability and generate a readable display name that remains unique within the workspace. + + #### Constraints + 1. The name must be concise, easy to read, summarize the agent's responsibility, and stay within 30 characters. + 2. Avoid mentioning specific tool names or adding extra punctuation. + 3. Return only the display name. + + +AGENT_DISPLAY_NAME_REGENERATE_USER_PROMPT: |- + ### Task Description + {{ task_description }} + + ### Original Display Name + {{ original_value }} + + ### Existing Display Names + {{ existing_values }} + + Output a new display name that is semantically aligned with the original while remaining unique and honoring the constraints. Return only the display name. \ No newline at end of file diff --git a/backend/services/agent_service.py b/backend/services/agent_service.py index 250a3c785..1ae6680b3 100644 --- a/backend/services/agent_service.py +++ b/backend/services/agent_service.py @@ -4,12 +4,13 @@ import os import uuid from collections import deque -from typing import Optional, Dict +from typing import Callable, Optional, Dict from fastapi import Header, Request from fastapi.responses import JSONResponse, StreamingResponse from nexent.core.agents.run_agent import agent_run from nexent.memory.memory_service import clear_memory, add_memory_in_levels +from jinja2 import Template from agents.agent_run_manager import agent_run_manager from agents.create_agent_info import create_agent_run_info, create_tool_config_list @@ -55,10 +56,11 @@ from services.remote_mcp_service import add_remote_mcp_server_list from services.tool_configuration_service import update_tool_list from utils.auth_utils import get_current_user_info, get_user_language -from utils.config_utils import tenant_config_manager +from utils.config_utils import tenant_config_manager, get_model_name_from_config from utils.memory_utils import build_memory_config from utils.thread_utils import submit from utils.prompt_template_utils import get_prompt_generate_prompt_template +from smolagents import OpenAIServerModel # Import monitoring utilities from utils.monitoring import monitoring_manager @@ -138,283 +140,279 @@ def _resolve_model_with_fallback( return None -def _check_agent_param_duplicate( - name: str, - check_param: str = "name", - existing_agents: list[dict] | None = None, - exclude_agent_id: int | None = None +def _normalize_language_key(language: str) -> str: + normalized = (language or "").lower() + if normalized.startswith(LANGUAGE["ZH"]): + return LANGUAGE["ZH"] + return LANGUAGE["EN"] + + +def _render_prompt_template(template_str: str, **context) -> str: + if not template_str: + return "" + try: + return Template(template_str).render(**context).strip() + except Exception as exc: + logger.warning(f"Failed to render prompt template: {exc}") + return template_str + + +def _format_existing_values(values: set[str], language: str) -> str: + if not values: + return "无" if _normalize_language_key(language) == LANGUAGE["ZH"] else "None" + return ", ".join(sorted(values)) + + +def _check_agent_value_duplicate( + field_key: str, + value: str, + tenant_id: str, + exclude_agent_id: int | None = None, + agents_cache: list[dict] | None = None ) -> bool: - """ - Check if a specific agent field already exists in the provided list. - - Args: - name: Agent name or display_name to check - check_param: Field key to inspect (e.g., "name", "display_name") - existing_agents: Optional pre-fetched agent list to avoid extra DB calls - exclude_agent_id: Optional agent ID to exclude from check (for updates) - - Returns: - True if duplicate exists, False otherwise - """ - if not existing_agents: + if not value: return False - - for agent in existing_agents: + if agents_cache is None: + agents_cache = query_all_agent_info_by_tenant_id(tenant_id) + for agent in agents_cache: if exclude_agent_id and agent.get("agent_id") == exclude_agent_id: continue - if agent.get(check_param) == name: + if agent.get(field_key) == value: return True return False +def _check_agent_name_duplicate( + name: str, + tenant_id: str, + exclude_agent_id: int | None = None, + agents_cache: list[dict] | None = None +) -> bool: + return _check_agent_value_duplicate( + "name", + name, + tenant_id=tenant_id, + exclude_agent_id=exclude_agent_id, + agents_cache=agents_cache + ) + + +def _check_agent_display_name_duplicate( + display_name: str, + tenant_id: str, + exclude_agent_id: int | None = None, + agents_cache: list[dict] | None = None +) -> bool: + return _check_agent_value_duplicate( + "display_name", + display_name, + tenant_id=tenant_id, + exclude_agent_id=exclude_agent_id, + agents_cache=agents_cache + ) + + def _generate_unique_value_with_suffix( - base_name: str, - check_param: str, + base_value: str, *, - existing_values: set[str] | None = None, - existing_agents: list[dict] | None = None, + tenant_id: str, + duplicate_check_fn: Callable[..., bool], + agents_cache: list[dict] | None = None, exclude_agent_id: int | None = None, - max_suffix_attempts: int = 100, - error_message: str + max_suffix_attempts: int = 100 ) -> str: counter = 1 while counter <= max_suffix_attempts: - candidate = f"{base_name}_{counter}" - if existing_values is not None: - if candidate not in existing_values: - existing_values.add(candidate) - return candidate - else: - if not _check_agent_param_duplicate( - candidate, - check_param=check_param, - existing_agents=existing_agents, - exclude_agent_id=exclude_agent_id - ): - return candidate + candidate = f"{base_value}_{counter}" + if not duplicate_check_fn( + candidate, + tenant_id=tenant_id, + exclude_agent_id=exclude_agent_id, + agents_cache=agents_cache + ): + return candidate counter += 1 - raise ValueError(error_message) + raise ValueError("Failed to generate unique value after max attempts") def _generate_unique_agent_name_with_suffix( - base_name: str, - existing_names: set[str] | None = None, - existing_agents: list[dict] | None = None, - exclude_agent_id: int | None = None, - max_suffix_attempts: int = 100 + base_value: str, + tenant_id: str, + agents_cache: list[dict] | None = None, + exclude_agent_id: int | None = None ) -> str: return _generate_unique_value_with_suffix( - base_name, - "name", - existing_values=existing_names, - existing_agents=existing_agents, - exclude_agent_id=exclude_agent_id, - max_suffix_attempts=max_suffix_attempts, - error_message="Failed to generate unique agent name after max attempts" + base_value, + tenant_id=tenant_id, + duplicate_check_fn=_check_agent_name_duplicate, + agents_cache=agents_cache, + exclude_agent_id=exclude_agent_id ) def _generate_unique_display_name_with_suffix( - base_name: str, - existing_display_names: set[str] | None = None, - existing_agents: list[dict] | None = None, - exclude_agent_id: int | None = None, - max_suffix_attempts: int = 100 + base_value: str, + tenant_id: str, + agents_cache: list[dict] | None = None, + exclude_agent_id: int | None = None ) -> str: return _generate_unique_value_with_suffix( - base_name, - "display_name", - existing_values=existing_display_names, - existing_agents=existing_agents, - exclude_agent_id=exclude_agent_id, - max_suffix_attempts=max_suffix_attempts, - error_message="Failed to generate unique agent display name after max attempts" + base_value, + tenant_id=tenant_id, + duplicate_check_fn=_check_agent_display_name_duplicate, + agents_cache=agents_cache, + exclude_agent_id=exclude_agent_id ) -def _regenerate_agent_name_with_llm( - original_name: str, - existing_names: set[str] | None, +def _regenerate_agent_value_with_llm( + *, + original_value: str, + existing_values: list[str], task_description: str, model_id: int, tenant_id: str, - language: str = LANGUAGE["ZH"] + language: str, + system_prompt_key: str, + user_prompt_key: str, + default_system_prompt: str, + default_user_prompt_builder: Callable[[dict], str], + fallback_fn: Callable[[str], str] ) -> str: """ - Regenerate agent name using LLM to ensure it's similar but not duplicate. - - Args: - original_name: Original agent name - existing_names: List of existing agent names to avoid - task_description: Task description for context - model_id: Model ID to use for generation - tenant_id: Tenant ID - language: Language code - - Returns: - Regenerated agent name + Shared helper to regenerate agent-related values with an LLM. """ prompt_template = get_prompt_generate_prompt_template(language) - base_system_prompt = prompt_template.get("AGENT_VARIABLE_NAME_SYSTEM_PROMPT", "") - constraint_system_prompt = prompt_template.get("AGENT_VARIABLE_NAME_REGENERATE_SYSTEM_PROMPT", "") - user_prompt_template = prompt_template.get("AGENT_VARIABLE_NAME_REGENERATE_USER_PROMPT", "") - - existing_name_set = existing_names if existing_names is not None else set() - existing_names_str = ", ".join(sorted(existing_name_set)) if existing_name_set else "None" + system_prompt = _render_prompt_template( + prompt_template.get(system_prompt_key, ""), + original_value=original_value + ) + user_prompt_template = prompt_template.get(user_prompt_key, "") - if constraint_system_prompt: - system_prompt = ( - f"{base_system_prompt}\n\n" - f"{constraint_system_prompt.format(original_name=original_name, existing_names=existing_names_str)}" - ) - else: - system_prompt = f"{base_system_prompt}" + value_set = {value for value in existing_values if value} + context = { + "task_description": task_description or "", + "original_value": original_value, + "existing_values": _format_existing_values(value_set, language) + } + user_prompt = _render_prompt_template(user_prompt_template, **context) - if user_prompt_template: - user_prompt = user_prompt_template.format( - task_description=task_description or "", - original_name=original_name, - existing_names=existing_names_str - ) - else: - user_prompt = ( - f"### Task Description:\n{task_description}\n\n" - f"### Original Name (for reference):\n{original_name}\n\n" - f"### Existing Names to Avoid:\n{existing_names_str}\n\n" - f"Please generate a new agent variable name that is similar in meaning to " - f"\"{original_name}\" but different from all existing names." - ) + if not system_prompt: + system_prompt = default_system_prompt + if not user_prompt: + user_prompt = default_user_prompt_builder(context) max_attempts = 5 - last_error = None + last_error: Exception | None = None for attempt in range(1, max_attempts + 1): try: from services.prompt_service import call_llm_for_system_prompt - regenerated_name = call_llm_for_system_prompt( + regenerated_value = call_llm_for_system_prompt( model_id=model_id, user_prompt=user_prompt, system_prompt=system_prompt, callback=None, tenant_id=tenant_id ) - regenerated_name = (regenerated_name or "").strip().replace('\n', '').strip() - - if not regenerated_name: - raise ValueError("Generated empty agent name") - if not regenerated_name.isidentifier(): - raise ValueError(f"Generated invalid agent name '{regenerated_name}'") - if regenerated_name in existing_name_set: - raise ValueError(f"Generated duplicate agent name '{regenerated_name}'") - - existing_name_set.add(regenerated_name) - return regenerated_name + candidate = (regenerated_value or "").strip().splitlines()[0].strip() + if candidate in value_set: + raise ValueError(f"Generated duplicate value '{candidate}'") + return candidate except Exception as exc: last_error = exc logger.warning( - f"Attempt {attempt}/{max_attempts} to regenerate agent name failed: {exc}" + f"Attempt {attempt}/{max_attempts} to regenerate value failed: {exc}" ) logger.error( - "Failed to regenerate agent name with LLM after maximum retries", + "Failed to regenerate agent value with LLM after maximum retries", exc_info=last_error ) - return _generate_unique_agent_name_with_suffix( - original_name, - existing_names=existing_name_set - ) + return fallback_fn(original_value) -def _regenerate_agent_display_name_with_llm( - original_display_name: str, - existing_display_names: set[str] | None, +def _regenerate_agent_name_with_llm( + original_name: str, + existing_names: list[str], task_description: str, model_id: int, tenant_id: str, - language: str = LANGUAGE["ZH"] + language: str = LANGUAGE["ZH"], + agents_cache: list[dict] | None = None, + exclude_agent_id: int | None = None ) -> str: - """ - Regenerate agent display_name using LLM to ensure it's similar but not duplicate. - - Args: - original_display_name: Original agent display_name - existing_display_names: List of existing agent display_names to avoid - task_description: Task description for context - model_id: Model ID to use for generation - tenant_id: Tenant ID - language: Language code - - Returns: - Regenerated agent display_name - """ - prompt_template = get_prompt_generate_prompt_template(language) - base_system_prompt = prompt_template.get("AGENT_DISPLAY_NAME_SYSTEM_PROMPT", "") - constraint_system_prompt = prompt_template.get("AGENT_DISPLAY_NAME_REGENERATE_SYSTEM_PROMPT", "") - user_prompt_template = prompt_template.get("AGENT_DISPLAY_NAME_REGENERATE_USER_PROMPT", "") - - existing_display_name_set = existing_display_names if existing_display_names is not None else set() - existing_names_str = ", ".join(sorted(existing_display_name_set)) if existing_display_name_set else "None" - - if constraint_system_prompt: - system_prompt = ( - f"{base_system_prompt}\n\n" - f"{constraint_system_prompt.format(original_display_name=original_display_name, existing_display_names=existing_names_str)}" + return _regenerate_agent_value_with_llm( + original_value=original_name, + existing_values=existing_names, + task_description=task_description, + model_id=model_id, + tenant_id=tenant_id, + language=language, + system_prompt_key="AGENT_NAME_REGENERATE_SYSTEM_PROMPT", + user_prompt_key="AGENT_NAME_REGENERATE_USER_PROMPT", + default_system_prompt=( + "You refine agent variable names so that they stay close to the " + "original meaning and remain unique within the tenant." + ), + default_user_prompt_builder=lambda ctx: ( + f"### Task Description:\n{ctx['task_description']}\n\n" + f"### Original Name:\n{ctx['original_value']}\n\n" + f"### Existing Names:\n{ctx['existing_values']}\n\n" + "Generate a concise Python variable name that keeps the same " + "meaning and does not duplicate the existing names. Return only " + "the variable name." + ), + fallback_fn=lambda base_value: _generate_unique_agent_name_with_suffix( + base_value, + tenant_id=tenant_id, + agents_cache=agents_cache, + exclude_agent_id=exclude_agent_id ) - else: - system_prompt = f"{base_system_prompt}" + ) - if user_prompt_template: - user_prompt = user_prompt_template.format( - task_description=task_description or "", - original_display_name=original_display_name, - existing_display_names=existing_names_str - ) - else: - user_prompt = ( - f"### Task Description:\n{task_description}\n\n" - f"### Original Display Name (for reference):\n{original_display_name}\n\n" - f"### Existing Display Names to Avoid:\n{existing_names_str}\n\n" - f"Please generate a new agent display name that is similar in meaning to " - f"\"{original_display_name}\" but different from all existing names." - ) - max_attempts = 5 - last_error = None - for attempt in range(1, max_attempts + 1): - try: - from services.prompt_service import call_llm_for_system_prompt - regenerated_name = call_llm_for_system_prompt( - model_id=model_id, - user_prompt=user_prompt, - system_prompt=system_prompt, - callback=None, - tenant_id=tenant_id - ) - regenerated_name = (regenerated_name or "").strip().replace('\n', '').strip() - if not regenerated_name: - raise ValueError("Generated empty display name") - if regenerated_name in existing_display_name_set: - raise ValueError(f"Generated duplicate display name '{regenerated_name}'") - - existing_display_name_set.add(regenerated_name) - return regenerated_name - except Exception as exc: - last_error = exc - logger.warning( - f"Attempt {attempt}/{max_attempts} to regenerate agent display name failed: {exc}" - ) - - logger.error( - "Failed to regenerate agent display name with LLM after maximum retries", - exc_info=last_error - ) - return _generate_unique_display_name_with_suffix( - original_display_name, - existing_display_names=existing_display_name_set +def _regenerate_agent_display_name_with_llm( + original_display_name: str, + existing_display_names: list[str], + task_description: str, + model_id: int, + tenant_id: str, + language: str = LANGUAGE["ZH"], + agents_cache: list[dict] | None = None, + exclude_agent_id: int | None = None +) -> str: + return _regenerate_agent_value_with_llm( + original_value=original_display_name, + existing_values=existing_display_names, + task_description=task_description, + model_id=model_id, + tenant_id=tenant_id, + language=language, + system_prompt_key="AGENT_DISPLAY_NAME_REGENERATE_SYSTEM_PROMPT", + user_prompt_key="AGENT_DISPLAY_NAME_REGENERATE_USER_PROMPT", + default_system_prompt=( + "You refine agent display names so they remain unique, concise, " + "and aligned with the agent's capability." + ), + default_user_prompt_builder=lambda ctx: ( + f"### Task Description:\n{ctx['task_description']}\n\n" + f"### Original Display Name:\n{ctx['original_value']}\n\n" + f"### Existing Display Names:\n{ctx['existing_values']}\n\n" + "Generate a new display name that keeps the same meaning but does " + "not duplicate existing names. Return only the display name." + ), + fallback_fn=lambda base_value: _generate_unique_display_name_with_suffix( + base_value, + tenant_id=tenant_id, + agents_cache=agents_cache, + exclude_agent_id=exclude_agent_id + ) ) + async def _stream_agent_chunks( agent_request: "AgentRequest", user_id: str, @@ -1028,11 +1026,11 @@ async def import_agent_by_agent_id(import_agent_info: ExportAndImportAgentInfo, # Get all existing agent names and display names for duplicate checking all_agents = query_all_agent_info_by_tenant_id(tenant_id) - existing_names = {agent.get("name") for agent in all_agents if agent.get("name")} - existing_display_names = {agent.get("display_name") for agent in all_agents if agent.get("display_name")} + existing_names = [agent.get("name") for agent in all_agents if agent.get("name")] + existing_display_names = [agent.get("display_name") for agent in all_agents if agent.get("display_name")] # Check and regenerate name if duplicate - if _check_agent_param_duplicate(agent_name, existing_agents=all_agents): + if _check_agent_name_duplicate(agent_name, tenant_id, agents_cache=all_agents): logger.info(f"Agent name '{agent_name}' already exists, regenerating with LLM") # Get model for regeneration (use business_logic_model_id if available, otherwise use model_id) regeneration_model_id = business_logic_model_id or model_id @@ -1044,32 +1042,27 @@ async def import_agent_by_agent_id(import_agent_info: ExportAndImportAgentInfo, task_description=import_agent_info.business_description or import_agent_info.description or "", model_id=regeneration_model_id, tenant_id=tenant_id, - language=LANGUAGE["ZH"] # Default to Chinese, can be enhanced later + language=LANGUAGE["ZH"], # Default to Chinese, can be enhanced later + agents_cache=all_agents ) logger.info(f"Regenerated agent name: '{agent_name}'") except Exception as e: logger.error(f"Failed to regenerate agent name with LLM: {str(e)}, using fallback") - # Fallback: add suffix agent_name = _generate_unique_agent_name_with_suffix( agent_name, - existing_names=existing_names, - existing_agents=all_agents + tenant_id=tenant_id, + agents_cache=all_agents ) else: logger.warning("No model available for regeneration, using fallback") - # Fallback: add suffix agent_name = _generate_unique_agent_name_with_suffix( agent_name, - existing_names=existing_names, - existing_agents=all_agents + tenant_id=tenant_id, + agents_cache=all_agents ) # Check and regenerate display_name if duplicate - if _check_agent_param_duplicate( - agent_display_name, - check_param="display_name", - existing_agents=all_agents - ): + if _check_agent_display_name_duplicate(agent_display_name, tenant_id, agents_cache=all_agents): logger.info(f"Agent display_name '{agent_display_name}' already exists, regenerating with LLM") # Get model for regeneration (use business_logic_model_id if available, otherwise use model_id) regeneration_model_id = business_logic_model_id or model_id @@ -1081,31 +1074,25 @@ async def import_agent_by_agent_id(import_agent_info: ExportAndImportAgentInfo, task_description=import_agent_info.business_description or import_agent_info.description or "", model_id=regeneration_model_id, tenant_id=tenant_id, - language=LANGUAGE["ZH"] # Default to Chinese, can be enhanced later + language=LANGUAGE["ZH"], # Default to Chinese, can be enhanced later + agents_cache=all_agents ) logger.info(f"Regenerated agent display_name: '{agent_display_name}'") except Exception as e: logger.error(f"Failed to regenerate agent display_name with LLM: {str(e)}, using fallback") - # Fallback: add suffix agent_display_name = _generate_unique_display_name_with_suffix( agent_display_name, - existing_display_names=existing_display_names, - existing_agents=all_agents + tenant_id=tenant_id, + agents_cache=all_agents ) else: logger.warning("No model available for regeneration, using fallback") - # Fallback: add suffix agent_display_name = _generate_unique_display_name_with_suffix( agent_display_name, - existing_display_names=existing_display_names, - existing_agents=all_agents + tenant_id=tenant_id, + agents_cache=all_agents ) - if agent_name: - existing_names.add(agent_name) - if agent_display_name: - existing_display_names.add(agent_display_name) - # create a new agent new_agent = create_agent(agent_info={"name": agent_name, "display_name": agent_display_name, diff --git a/backend/services/prompt_service.py b/backend/services/prompt_service.py index 892c492e7..4c331ceb3 100644 --- a/backend/services/prompt_service.py +++ b/backend/services/prompt_service.py @@ -250,11 +250,6 @@ def generate_and_save_system_prompt_impl(agent_id: int, exclude_agent_id=agent_id ) - if agent_name: - existing_names.add(agent_name) - if agent_display_name: - existing_display_names.add(agent_display_name) - agent_info = AgentInfoRequest( agent_id=agent_id, business_description=task_description, From 15fe9c5fe6ed9b392042c89c8774bb9b065e618b Mon Sep 17 00:00:00 2001 From: biansimeng Date: Fri, 21 Nov 2025 20:33:38 +0800 Subject: [PATCH 06/83] Update docs --- doc/docs/.vitepress/config.mts | 49 ++-- doc/docs/en/deployment/upgrade-guide.md | 2 +- .../en/getting-started/development-guide.md | 2 +- doc/docs/en/getting-started/faq.md | 34 ++- doc/docs/en/getting-started/installation.md | 2 +- ...-configuration.md => agent-development.md} | 35 +-- doc/docs/en/user-guide/agent-market.md | 37 +++ doc/docs/en/user-guide/agent-space.md | 61 +++++ doc/docs/en/user-guide/app-configuration.md | 44 ---- doc/docs/en/user-guide/home-page.md | 49 ++++ doc/docs/en/user-guide/index.md | 42 ---- .../knowledge-base-configuration.md | 79 ------- doc/docs/en/user-guide/knowledge-base.md | 77 ++++++ doc/docs/en/user-guide/local-tools/index.md | 75 +++--- .../user-guide/local-tools/terminal-tool.md | 11 +- doc/docs/en/user-guide/memory-management.md | 140 +++++++++++ doc/docs/en/user-guide/memory.md | 103 -------- doc/docs/en/user-guide/model-configuration.md | 190 --------------- doc/docs/en/user-guide/model-management.md | 220 ++++++++++++++++++ doc/docs/en/user-guide/quick-setup.md | 53 +++++ .../{chat-interface.md => start-chat.md} | 30 +-- doc/docs/en/user-guide/user-management.md | 37 +++ doc/docs/zh/deployment/upgrade-guide.md | 2 +- .../zh/getting-started/development-guide.md | 2 +- doc/docs/zh/getting-started/faq.md | 14 +- doc/docs/zh/getting-started/installation.md | 2 +- doc/docs/zh/opensource-memorial-wall.md | 10 - ...-configuration.md => agent-development.md} | 25 +- doc/docs/zh/user-guide/agent-market.md | 39 ++++ doc/docs/zh/user-guide/agent-space.md | 66 ++++++ doc/docs/zh/user-guide/app-configuration.md | 44 ---- doc/docs/zh/user-guide/home-page.md | 49 ++++ doc/docs/zh/user-guide/index.md | 45 ---- ...ase-configuration.md => knowledge-base.md} | 15 +- doc/docs/zh/user-guide/local-tools/index.md | 53 +++-- .../user-guide/local-tools/terminal-tool.md | 11 +- doc/docs/zh/user-guide/memory-management.md | 160 +++++++++++++ doc/docs/zh/user-guide/memory.md | 103 -------- ...l-configuration.md => model-management.md} | 68 +++++- doc/docs/zh/user-guide/quick-setup.md | 53 +++++ .../{chat-interface.md => start-chat.md} | 20 +- doc/docs/zh/user-guide/user-management.md | 39 ++++ 42 files changed, 1359 insertions(+), 833 deletions(-) rename doc/docs/en/user-guide/{agent-configuration.md => agent-development.md} (66%) create mode 100644 doc/docs/en/user-guide/agent-market.md create mode 100644 doc/docs/en/user-guide/agent-space.md delete mode 100644 doc/docs/en/user-guide/app-configuration.md create mode 100644 doc/docs/en/user-guide/home-page.md delete mode 100644 doc/docs/en/user-guide/index.md delete mode 100644 doc/docs/en/user-guide/knowledge-base-configuration.md create mode 100644 doc/docs/en/user-guide/knowledge-base.md create mode 100644 doc/docs/en/user-guide/memory-management.md delete mode 100644 doc/docs/en/user-guide/memory.md delete mode 100644 doc/docs/en/user-guide/model-configuration.md create mode 100644 doc/docs/en/user-guide/model-management.md create mode 100644 doc/docs/en/user-guide/quick-setup.md rename doc/docs/en/user-guide/{chat-interface.md => start-chat.md} (86%) create mode 100644 doc/docs/en/user-guide/user-management.md rename doc/docs/zh/user-guide/{agent-configuration.md => agent-development.md} (83%) create mode 100644 doc/docs/zh/user-guide/agent-market.md create mode 100644 doc/docs/zh/user-guide/agent-space.md delete mode 100644 doc/docs/zh/user-guide/app-configuration.md create mode 100644 doc/docs/zh/user-guide/home-page.md delete mode 100644 doc/docs/zh/user-guide/index.md rename doc/docs/zh/user-guide/{knowledge-base-configuration.md => knowledge-base.md} (82%) create mode 100644 doc/docs/zh/user-guide/memory-management.md delete mode 100644 doc/docs/zh/user-guide/memory.md rename doc/docs/zh/user-guide/{model-configuration.md => model-management.md} (78%) create mode 100644 doc/docs/zh/user-guide/quick-setup.md rename doc/docs/zh/user-guide/{chat-interface.md => start-chat.md} (90%) create mode 100644 doc/docs/zh/user-guide/user-management.md diff --git a/doc/docs/.vitepress/config.mts b/doc/docs/.vitepress/config.mts index ddf8d1dba..a2bd9b867 100644 --- a/doc/docs/.vitepress/config.mts +++ b/doc/docs/.vitepress/config.mts @@ -55,25 +55,31 @@ export default defineConfig({ { text: "User Guide", items: [ - { text: "Quick Start", link: "/en/user-guide/" }, + { text: "Home Page", link: "/en/user-guide/home-page" }, + { text: "Start Chat", link: "/en/user-guide/start-chat" }, { - text: "App Configuration", - link: "/en/user-guide/app-configuration", + text: "Quick Setup", + link: "/en/user-guide/quick-setup", }, + { text: "Agent Space", link: "/en/user-guide/agent-space" }, + { text: "Agent Market", link: "/en/user-guide/agent-market" }, { - text: "Model Configuration", - link: "/en/user-guide/model-configuration", + text: "Agent Development", + link: "/en/user-guide/agent-development", }, { - text: "Knowledge Base Configuration", - link: "/en/user-guide/knowledge-base-configuration", + text: "Knowledge Base", + link: "/en/user-guide/knowledge-base", }, { - text: "Agent Configuration", - link: "/en/user-guide/agent-configuration", + text: "Model Management", + link: "/en/user-guide/model-management", }, - { text: "Chat Interface", link: "/en/user-guide/chat-interface" }, - { text: "Memory Configuration", link: "/en/user-guide/memory" }, + { + text: "Memory Management", + link: "/en/user-guide/memory-management", + }, + { text: "User Management", link: "/en/user-guide/user-management" }, { text: "Local Tools", items: [ @@ -231,19 +237,22 @@ export default defineConfig({ { text: "用户指南", items: [ - { text: "快速开始", link: "/zh/user-guide/" }, - { text: "应用配置", link: "/zh/user-guide/app-configuration" }, - { text: "模型配置", link: "/zh/user-guide/model-configuration" }, + { text: "首页", link: "/zh/user-guide/home-page" }, + { text: "开始问答", link: "/zh/user-guide/start-chat" }, + { text: "快速设置", link: "/zh/user-guide/quick-setup" }, + { text: "智能体空间", link: "/zh/user-guide/agent-space" }, + { text: "智能体市场", link: "/zh/user-guide/agent-market" }, { - text: "知识库配置", - link: "/zh/user-guide/knowledge-base-configuration", + text: "智能体开发", + link: "/zh/user-guide/agent-development", }, { - text: "智能体配置", - link: "/zh/user-guide/agent-configuration", + text: "知识库", + link: "/zh/user-guide/knowledge-base", }, - { text: "对话页面", link: "/zh/user-guide/chat-interface" }, - { text: "记忆配置", link: "/zh/user-guide/memory" }, + { text: "模型管理", link: "/zh/user-guide/model-management" }, + { text: "记忆管理", link: "/zh/user-guide/memory-management" }, + { text: "用户管理", link: "/zh/user-guide/user-management" }, { text: "本地工具", items: [ diff --git a/doc/docs/en/deployment/upgrade-guide.md b/doc/docs/en/deployment/upgrade-guide.md index 34f5b3818..055ed3769 100644 --- a/doc/docs/en/deployment/upgrade-guide.md +++ b/doc/docs/en/deployment/upgrade-guide.md @@ -134,6 +134,6 @@ Run the SQL scripts shipped with each release to keep your schema up to date. After deployment: 1. Open `http://localhost:3000` in your browser. -2. Review the [User Guide](https://doc.nexent.tech/en/user-guide/) to validate agent functionality. +2. Review the [User Guide](https://doc.nexent.tech/en/user-guide/home-page) to validate agent functionality. diff --git a/doc/docs/en/getting-started/development-guide.md b/doc/docs/en/getting-started/development-guide.md index d781d4e28..3721a8813 100644 --- a/doc/docs/en/getting-started/development-guide.md +++ b/doc/docs/en/getting-started/development-guide.md @@ -210,7 +210,7 @@ For detailed build instructions, see [Docker Build Guide](../deployment/docker-b ### Documentation Resources - [Installation Guide](./installation.md) - Environment setup and deployment - [FAQ](./faq) - Frequently asked questions -- [User Guide](../user-guide/) - Nexent user guide +- [User Guide](../user-guide/home-page) - Nexent user guide ### Community Support - [Discord Community](https://discord.gg/tb5H3S3wyv) - Real-time communication and support diff --git a/doc/docs/en/getting-started/faq.md b/doc/docs/en/getting-started/faq.md index bccfe8901..4311385f1 100644 --- a/doc/docs/en/getting-started/faq.md +++ b/doc/docs/en/getting-started/faq.md @@ -1,36 +1,44 @@ # Nexent FAQ -This FAQ addresses common questions and issues you might encounter while installing and using Nexent. For the basic installation steps, please refer to the [Installation & Development](./installation). For basic using instructions, please refer to the [User Guide](../user-guide/). +This FAQ addresses common questions and issues you might encounter while installing and using Nexent. For the basic installation steps, please refer to the [Installation & Development](./installation). For basic using instructions, please refer to the [User Guide](../user-guide/home-page). ## 🚫 Common Errors & Operations ### 🌐 Network Connection Issues + - **Q: How can a Docker container access models deployed on the host machine (e.g., Ollama)?** - A: Since `localhost` inside the container refers to the container itself, use one of these methods to connect to host services: - **Option 1: Use Docker's special DNS name `host.docker.internal`** - Supported environments: Mac/Windows and newer Docker Desktop versions (Linux version also supported) + **Option 1: Use Docker's special DNS name `host.docker.internal`** + + Supported environments: Mac/Windows and newer Docker Desktop versions (Linux version also supported) + ```bash http://host.docker.internal:11434/v1 ``` **Option 2: Use host machine's actual IP (ensure firewall allows access)** + ```bash http://[HOST_IP]:11434/v1 ``` - **Option 3: Modify Docker Compose configuration** + **Option 3: Modify Docker Compose configuration** + Add to your docker-compose.yaml file: + ```yaml extra_hosts: - "host.docker.internal:host-gateway" ``` ### 🔌 Port Conflicts + - **Q: Port 3000 is already in use. How can I change it?** - A: You can modify the port in the Docker Compose configuration file. ### 📦 Container Issues + - **Q: How do I check container logs?** - A: Use `docker logs ` to view logs for specific containers. @@ -44,11 +52,23 @@ This FAQ addresses common questions and issues you might encounter while install 2. **Valid API key**: Verify your API key has proper permissions 3. **Model name**: Confirm the model identifier is correct 4. **Network access**: Ensure your deployment can reach the provider's servers - - For model setup instruction, see [Model Configuration](../user-guide/model-configuration) in User Guide. + + For model setup instruction, see [Model Management](../user-guide/model-management) in User Guide. + +- **Q: Multi-turn chats fail when using the official DeepSeek API. How can I resolve this?** + - A: The official DeepSeek API only accepts text payloads, but Nexent sends multimodal payloads, so multi-turn calls are rejected. Use a provider such as SiliconFlow that exposes DeepSeek models with multimodal compatibility. Our requests look like: + ```python + { "role":"user", "content":[ { "type":"text", "text":"prompt" } ] } + ``` + whereas DeepSeek expects: + ```python + { "role":"user", "content":"prompt" } + ``` ## 💡 Need Help If your question isn't answered here: + - Join our [Discord community](https://discord.gg/tb5H3S3wyv) for real-time support -- Check our [GitHub Issues](https://github.com/ModelEngine-Group/nexent/issues) for similar problems \ No newline at end of file +- Check our [GitHub Issues](https://github.com/ModelEngine-Group/nexent/issues) for similar problems +- Open a thread in [GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions). diff --git a/doc/docs/en/getting-started/installation.md b/doc/docs/en/getting-started/installation.md index 9935ff6bb..8026ecf47 100644 --- a/doc/docs/en/getting-started/installation.md +++ b/doc/docs/en/getting-started/installation.md @@ -48,7 +48,7 @@ After executing this command, the system will provide two different versions for When deployment completes successfully: 1. Open **http://localhost:3000** in your browser -2. Refer to the [User Guide](../user-guide/) to develop agents +2. Refer to the [User Guide](../user-guide/home-page) to develop agents ## 🏗️ Service Architecture diff --git a/doc/docs/en/user-guide/agent-configuration.md b/doc/docs/en/user-guide/agent-development.md similarity index 66% rename from doc/docs/en/user-guide/agent-configuration.md rename to doc/docs/en/user-guide/agent-development.md index 348cc60d7..8c48b286f 100644 --- a/doc/docs/en/user-guide/agent-configuration.md +++ b/doc/docs/en/user-guide/agent-development.md @@ -1,6 +1,6 @@ -# Agent Configuration +# Agent Development -In the Agent Configuration module, you can create, configure, and manage agents. Agents are the core feature of Nexent—they can understand your needs and perform corresponding tasks. +In the Agent Development page, you can create, configure, and manage agents. Agents are the core feature of Nexent—they can understand your needs and perform corresponding tasks. ## 🛠️ Agent Management @@ -9,6 +9,7 @@ In the Agent Configuration module, you can create, configure, and manage agents. On the Agent Management tab, click "Create Agent" to create a new blank agent. Click "Exit Create" to leave creation mode. If you have an existing agent configuration, you can also import it: + 1. Click "Import Agent" 2. In the popup, select the agent configuration file (JSON format) 3. Click "Open"; the system will validate the file format and content, and display the imported agent information @@ -49,6 +50,8 @@ Agents can use various tools to complete tasks, such as knowledge base search, e +> 📚 Want to learn about all the built-in local tools in Nexent? Please see [Local Tools Overview](./local-tools/index.md). + ### Add MCP Tools Nexent allows you to quickly use third-party MCP tools to enrich agent capabilities. @@ -61,7 +64,7 @@ Nexent allows you to quickly use third-party MCP tools to enrich agent capabilit -Many third-party services such as ModelScope provide MCP services, which you can quickly integrate and use. +Many third-party services such as [ModelScope](https://www.modelscope.cn/mcp) provide MCP services, which you can quickly integrate and use. ### Custom Tools @@ -70,15 +73,15 @@ You can refer to the following guides to develop your own tools and integrate th - [MCP Tool Development](../backend/tools/mcp) - [SDK Tool Documentation](../sdk/core/tools) -## 📝 Describe Business Logic +## 📝 Describe the Agent’s Logic -### Describe How should this Agent Work +### Describe How the Agent Should Work -Based on the selected collaborative agents and tools, you can now use concise language to describe how you want this agent to work. Nexent will automatically generate the agent’s name, description, and prompt suggestions based on your configuration and description. +Once the collaborators and tools are ready, describe—in plain language—how you expect the agent to behave. Nexent uses this description plus your configuration to auto-generate the agent name, description, and prompts. -1. In the "Describe should this agent work" editor, enter a brief description -2. Click "Generate"; Nexent will generate detailed agent content for you -3. You can further edit the generated content for fine-tuning +1. Enter the description under **Describe how this agent should work** +2. Click **Generate Agent** and wait for the detailed content +3. Review the generated role prompt, constraints, examples, and tweak anything you want
@@ -94,14 +97,16 @@ After completing the initial agent configuration, you can debug the agent and op ### Manage Agents -- **Save:** After successful debugging, click the "Save" button in the lower right corner to save the agent. The agent will then be available for selection in the chat page. -- **Export:** Export the successfully debugged agent configuration as a JSON file. When creating an agent, you can use this JSON file to create a copy via import. -- **Delete:** Delete the agent (use with caution) +- **Save:** After you finish debugging, click **Save** in the lower-right corner so the agent becomes available in Start Chat. +- **Export:** Download a JSON file of any agent you like, then re-import it later to create copies. +- **Delete:** Remove an agent you no longer need (this cannot be undone). ## 🚀 Next Steps -After completing agent configuration, you can click "Complete Setup" to proceed to: +After shipping an agent you can: -1. **[Chat Interface](./chat-interface)** – Interact with your agent +1. Manage every build inside **[Agent Space](./agent-space)**. +2. Start working with your agent on the **[Start Chat](./start-chat)** page. +3. Configure **[Memory Management](./memory-management)** to give agents persistent memory. -If you encounter any issues during agent configuration, please refer to our **[FAQ](../getting-started/faq)** or join our [Discord community](https://discord.gg/tb5H3S3wyv) for support. \ No newline at end of file +Need help? Check the **[FAQ](../getting-started/faq)** or open a thread in [GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions). \ No newline at end of file diff --git a/doc/docs/en/user-guide/agent-market.md b/doc/docs/en/user-guide/agent-market.md new file mode 100644 index 000000000..5f9e229cf --- /dev/null +++ b/doc/docs/en/user-guide/agent-market.md @@ -0,0 +1,37 @@ +# Agent Market + +Agent Market is an upcoming Nexent module that will provide a curated catalog of ready-to-use agents. + +## 🎯 Coming Features + +Agent Market will let you: + +- **Browse agents** – Explore featured and community-built agents. +- **Install instantly** – Add high-quality agents to your workspace with one click. +- **Share your work** – Publish the agents you build. +- **Rate & give feedback** – Help the community discover great agents. + +## ⏳ Stay Tuned + +Agent Market is currently under development. We’re building an ecosystem where you can: + +- Quickly access verified agents. +- Share your creations with the community. +- Discover new use cases and inspiration. + +## 📢 Follow Updates + +Want to know when Agent Market launches? + +- Join the [Discord community](https://discord.gg/tb5H3S3wyv) for announcements. +- Track project updates in the repository. + +## 🚀 Related Features + +While Agent Market is being built, you can: + +1. Manage your agents in **[Agent Space](./agent-space)**. +2. Create new agents in **[Agent Development](./agent-development)**. +3. Test agents in **[Start Chat](./start-chat)**. + +Need help? Check the **[FAQ](../getting-started/faq)** or open a thread in [GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions). diff --git a/doc/docs/en/user-guide/agent-space.md b/doc/docs/en/user-guide/agent-space.md new file mode 100644 index 000000000..57ddc1a75 --- /dev/null +++ b/doc/docs/en/user-guide/agent-space.md @@ -0,0 +1,61 @@ +# Agent Space + +Agent Space is the central dashboard for every agent you have built. View agents in card form, inspect their configurations, delete or export them, and jump straight into chats. + +## 📦 Agent Cards + +Each agent appears as a card showing: + +- **Icon** – The agent’s avatar. +- **Name** – The display name. +- **Description** – A quick summary of what it does. +- **Status** – Whether the agent is available. +- **Actions** – Shortcuts for editing, exporting, deleting, and more. + +## 🔧 Manage Agents + +### View Agent Details + +Click a card to open its details: + +- **Basic info:** ID, name, description, and status. +- **Model configuration:** Model name, max tokens, business logic model, etc. +- **Prompts:** Role, constraints, examples, and the original description. +- **Tools:** Every tool the agent can use. +- **Sub-agents:** Any collaborative agents that are configured. + +### Edit an Agent + +1. Click **Edit** on the card. +2. You’ll be taken to the Agent Development page. +3. Adjust the settings and save—updates sync back to Agent Space automatically. + +### Delete an Agent + +1. Click **Delete** on the card. +2. Confirm the deletion (this cannot be undone). + +> ⚠️ **Note:** Deleting an agent permanently removes it. Export a backup first if you might need it later. + +### Export an Agent + +1. Click **Export** on the card. +2. Nexent downloads a JSON configuration file you can import later. + +### View Relationships + +1. Click **View Relationships** to see how the agent interacts with tools and other agents. + +### Jump to Chat + +1. Click **Chat** to open Start Chat with the agent already selected. + +## 🚀 Next Steps + +Once you finish reviewing agents you can: + +1. Talk to them in **[Start Chat](./start-chat)**. +2. Continue iterating in **[Agent Development](./agent-development)**. +3. Enhance retention with **[Memory Management](./memory-management)**. + +Need help? Check the **[FAQ](../getting-started/faq)** or open a thread in [GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions). diff --git a/doc/docs/en/user-guide/app-configuration.md b/doc/docs/en/user-guide/app-configuration.md deleted file mode 100644 index 8776863b3..000000000 --- a/doc/docs/en/user-guide/app-configuration.md +++ /dev/null @@ -1,44 +0,0 @@ -# App Configuration - -In the App Configuration module, you can configure the basic information of your application, including the app icon, name, and description. Proper configuration helps improve the app’s recognition and user experience. - -- The app icon and name will be displayed in the upper left corner of the chat page, helping users quickly identify the current app. -- The app description is provided to the model as background information when generating agents, enhancing the model’s understanding of the application scenario. - -## 🖼️ App Icon Configuration - -Click the app icon to configure it. Nexent provides two ways to set the icon: - -- **Use a preset icon:** Select from the built-in icon library, choose an image and background color for quick setup. -- **Upload a custom image:** Supports PNG and JPG formats, with a file size limit of 2MB. - -
- - -
- -## 📝 App Name & Description Configuration - -### App Name - -- The app name is displayed in the upper left corner of the chat page, helping users quickly identify the current app. -- It is recommended to use a concise and clear name that reflects the app’s functionality. Avoid using special characters. - -### App Description - -- The app description is provided to the model as background information, helping it understand the application scenario. -- Highlight the core features of the app, and keep the description complete, fluent, and concise. - -
- -
- -## 🚀 Next Steps - -After completing the app configuration, we recommend you continue with: - -1. **[Model Configuration](./model-configuration)** – Connect the AI models you need -2. **[Knowledge Base Configuration](./knowledge-base-configuration)** – Create and manage knowledge bases -3. **[Agent Configuration](./agent-configuration)** – Create and configure agents - -If you encounter any issues during configuration, please refer to our **[FAQ](../getting-started/faq)** or join our [Discord community](https://discord.gg/tb5H3S3wyv) for support. \ No newline at end of file diff --git a/doc/docs/en/user-guide/home-page.md b/doc/docs/en/user-guide/home-page.md new file mode 100644 index 000000000..3b6ae86c6 --- /dev/null +++ b/doc/docs/en/user-guide/home-page.md @@ -0,0 +1,49 @@ +# User Guide + +Nexent is a future-oriented zero-code agent development platform that helps anyone build and deploy custom AI agents without writing code or handling complex workflows. + +This guide walks you through Nexent’s major features and daily workflows so you can get productive in minutes. + +By the end, you’ll know how to turn your ideas into production-ready agents that deliver real value at work and in life. + +## 🏠 Homepage Overview + +The Nexent homepage highlights the core entry points of the platform: + +### Primary actions + +1. **Start Chatting** – Jump straight into the chat interface and talk to an agent. +2. **Quick Setup** – Follow the guided flow to finish model, knowledge base, and agent setup in sequence. +3. **Agent Space** – Browse and manage every agent you have built. + +### Left navigation + +The left sidebar exposes every major module: + +- **Home** – Return to the homepage. +- **Start Chat** – Open the chat interface. +- **Quick Setup** – Complete the recommended setup flow (Models → Knowledge Base → Agent). +- **Agent Space** – Manage all existing agents. +- **Agent Market** – Discover and install published agents (coming soon). +- **Agent Development** – Create and configure agents. +- **Knowledge Base** – Upload and curate documents. +- **Model Management** – Configure app info and connect models. +- **Memory Management** – Enable and tune the multi-layer memory system. +- **User Management** – Manage platform users (coming soon). + +Use the language switcher in the top-right corner to toggle between Simplified Chinese and English. The lower-left corner shows the running Nexent version to simplify troubleshooting when asking for help. + +## 🚀 Quick Start + +We recommend configuring the platform in this order: + +1. Set up **[Model Management](./model-management)** to define app details and connect AI models. +2. Create your **[Knowledge Base](./knowledge-base)** and upload documents. +3. Build your **[Agents](./agent-development)** on top of the models and knowledge base. +4. When everything is ready, chat with your agents via **[Start Chatting](./start-chat)**. + +Prefer a guided flow? Click the “Quick Setup” button on the homepage and follow the prompts. + +## 💡 Get Help + +Need help? Check the **[FAQ](../getting-started/faq)** or open a thread in [GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions). \ No newline at end of file diff --git a/doc/docs/en/user-guide/index.md b/doc/docs/en/user-guide/index.md deleted file mode 100644 index 177a5b83d..000000000 --- a/doc/docs/en/user-guide/index.md +++ /dev/null @@ -1,42 +0,0 @@ -# User Guide - -Nexent is a future-oriented zero-code intelligent agent development platform, dedicated to enabling everyone to easily build and deploy their own AI agents—no programming background or complex operations required! - -This user guide will help you fully understand Nexent’s powerful features and usage, so you can quickly get started with all kinds of operations. - -By studying this guide, you’ll be able to efficiently leverage Nexent, turning your creativity into reality and bringing true value and delight to your work and life! - -## 🚀 Quick Start - -On the Nexent homepage, you’ll see two main action buttons: - -1. **Start Chatting:** Click to directly enter the chat page and interact with your agent. -2. **Quick Setup:** Click to enter the agent configuration page and set up your own agent. - -![Homepage](./assets/app/homepage.png) - -Additionally, you can switch languages (Simplified Chinese or English) in the upper right corner of the page as needed. The current Nexent version number is displayed in the lower-left corner of the page, which can help you seek assistance when you encounter problems. - -## 💡 Next Steps - -Ready to get started? We recommend configuring in the following order: - -1. First, complete **[App Configuration](./app-configuration)** to set up your app’s basic information. -2. Then, proceed to **[Model Configuration](./model-configuration)** to connect the AI models you need. -3. Next, go to **[Knowledge Base Configuration](./knowledge-base-configuration)** to upload your documents and data. -4. After that, configure **[Agent Configuration](./agent-configuration)** to create your own agent. -5. Once everything is set, you can interact with your agent on the **[Chat Interface](./chat-interface)**. - -## 🛠️ Local Tools - -Nexent provides a rich set of local tools to help agents complete various complex tasks. The following introduces some tools that have certain operational difficulty: - -- **[Terminal Tool](./local-tools/terminal-tool)**: Execute commands on remote servers through SSH connections, enabling server management and system monitoring - -## 🧠 Advanced Features - -Beyond basic configuration, Nexent also provides powerful advanced features: - -- **[Memory Configuration](./memory)**: Learn about Nexent's four-layer memory architecture for cross-conversation knowledge accumulation and retrieval - -If you encounter any issues during configuration, please refer to our **[FAQ](../getting-started/faq)** or join our [Discord community](https://discord.gg/tb5H3S3wyv) for support. \ No newline at end of file diff --git a/doc/docs/en/user-guide/knowledge-base-configuration.md b/doc/docs/en/user-guide/knowledge-base-configuration.md deleted file mode 100644 index 04f1c08f5..000000000 --- a/doc/docs/en/user-guide/knowledge-base-configuration.md +++ /dev/null @@ -1,79 +0,0 @@ -# Knowledge Base Configuration - -In the Knowledge Base Configuration module, you can create and manage knowledge bases, upload files in various formats, and generate content summaries. The knowledge base is a key information source for agents, allowing them to access your private data and documents. - -## 🛠️ Create a Knowledge Base - -1. Click the "Create" button to create a knowledge base -2. Set an easy-to-identify name for the knowledge base (note: names must be unique) - -## 📁 Upload Files - -### Upload Files - -1. In the knowledge base list, select the knowledge base to upload files to -2. Click the file upload area, select the files to upload (multiple selection supported), or drag files directly into the upload area -3. The system will automatically process the uploaded files, extract text content, and perform vectorization -4. You can view the processing status in the list (Processing/Forwarding/Ready) - -![File Upload](./assets/knowledge/create-knowledge-base.png) - -### Supported File Formats - -Nexent supports multiple file formats, including: -- **Text:** .txt, .md -- **PDF:** .pdf -- **Word:** .docx -- **PowerPoint:** .pptx -- **Excel:** .xlsx -- **Data files:** .csv - -## 📊 Knowledge Base Summary - -It is recommended to configure an accurate and complete summary for each knowledge base. This helps agents accurately select the appropriate knowledge base for search. - -1. Click the "Details" button to view detailed content of the knowledge base -2. Select one LLM and click the "Auto Summary" button to automatically generate a summary -3. You can edit the generated summary for greater accuracy -4. Don’t forget to click "Save" to save your changes - -![Content Summary](./assets/knowledge/summary-knowledge-base.png) - -> Note: The auto summary feature uses the system’s secondary model as configured in Model Configuration. If not configured, this feature will be unavailable. You can manually enter a summary for the knowledge base. - -## 🔍 Knowledge Base Management - -### View Knowledge Bases - -1. **Knowledge Base List** - - The left sidebar displays all created knowledge bases - - Shows name, file count, creation time, etc. -2. **Knowledge Base Details** - - Click a knowledge base name to view all documents in it - - Click "Details" to view the knowledge base summary - -
- - -
- -### Edit Knowledge Bases - -1. **Delete Knowledge Base** - - Click the "Delete" button next to the knowledge base name in the right sidebar - - Confirm the deletion (this action is irreversible) - -![Delete Knowledge Base](./assets/knowledge/delete-knowledge-base.png) - -2. **Delete/Add Files** - - Click the knowledge base name, then click "Delete" in the file list to remove files - - Or use the upload area below the file list to add new files - -## 🚀 Next Steps - -After completing knowledge base configuration, we recommend you continue with: - -1. **[Agent Configuration](./agent-configuration)** – Create and configure agents -2. **[Chat Interface](./chat-interface)** – Interact with your agent - -If you encounter any issues during knowledge base configuration, please refer to our **[FAQ](../getting-started/faq)** or join our [Discord community](https://discord.gg/tb5H3S3wyv) for support. \ No newline at end of file diff --git a/doc/docs/en/user-guide/knowledge-base.md b/doc/docs/en/user-guide/knowledge-base.md new file mode 100644 index 000000000..2d9e92b26 --- /dev/null +++ b/doc/docs/en/user-guide/knowledge-base.md @@ -0,0 +1,77 @@ +# Knowledge Base + +Create and manage knowledge bases, upload documents, and generate summaries. Knowledge bases are critical information sources that let agents securely use your private data. + +## 🔧 Create a Knowledge Base + +1. Click **Create Knowledge Base** +2. Enter a descriptive, unique name + +## 📁 Upload Files + +### Upload Files + +1. Select a knowledge base from the list +2. Click the upload area to pick files (multi-select supported) or drag them in directly +3. Nexent automatically parses files, extracts text, and vectorizes the content +4. Track the processing status in the list (Parsing/Ingesting/Ready) + +![File Upload](./assets/knowledge/create-knowledge-base.png) + +### Supported File Formats + +Nexent supports multiple file formats, including: +- **Text:** .txt, .md +- **PDF:** .pdf +- **Word:** .docx +- **PowerPoint:** .pptx +- **Excel:** .xlsx +- **Data files:** .csv + +## 📊 Knowledge Base Summary + +Give every knowledge base a clear summary so agents can pick the right source during retrieval. + +1. Click **Details** to open the detailed view +2. Choose a model and click **Auto Summary** to generate a description +3. Edit the generated text to improve accuracy +4. Click **Save** to store your changes + +![Content Summary](./assets/knowledge/summary-knowledge-base.png) + +## 🔍 Knowledge Base Management + +### View Knowledge Bases + +1. **Knowledge Base List** + - The left column lists every created knowledge base + - Shows the name, file count, creation time, and more +2. **Knowledge Base Details** + - Click a knowledge base to see all documents + - Click **Details** to view or edit the summary + +
+ + +
+ +### Edit Knowledge Bases + +1. **Delete Knowledge Base** + - Click **Delete** to the right of the knowledge base row + - Confirm the deletion (irreversible) + +![Delete Knowledge Base](./assets/knowledge/delete-knowledge-base.png) + +2. **Delete/Add Files** + - Inside the file list, click **Delete** to remove a document + - Use the upload area under the list to add new files + +## 🚀 Next Steps + +After completing knowledge base configuration, we recommend you continue with: + +1. **[Agent Development](./agent-development)** – Create and configure agents +2. **[Start Chat](./start-chat)** – Interact with your agent + +Need help? Check the **[FAQ](../getting-started/faq)** or open a thread in [GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions). \ No newline at end of file diff --git a/doc/docs/en/user-guide/local-tools/index.md b/doc/docs/en/user-guide/local-tools/index.md index af8ffeef6..159efff58 100644 --- a/doc/docs/en/user-guide/local-tools/index.md +++ b/doc/docs/en/user-guide/local-tools/index.md @@ -1,53 +1,58 @@ # Local Tools -The Nexent platform provides a rich set of local tools that help agents complete various system-level tasks and local operations. These tools provide agents with powerful execution capabilities through direct interaction with local systems or remote servers. +The Nexent platform provides a rich set of local tools that help agents complete various system-level tasks and local operations. These tools offer powerful execution capabilities through direct interaction with local systems or remote servers. ## 🛠️ Available Tools -### Terminal Tool +Nexent preloads a set of reusable local tools grouped by capability: email, file, and search. The Terminal tool is offered separately to provide remote shell capabilities. The following sections list each tool alongside its core features so agents can quickly pick the right capability. -**[Terminal Tool](./terminal-tool)** is one of the core local tools of the Nexent platform, allowing agents to execute commands on remote servers through SSH connections. +### 📧 Email Tools -**Key Features**: -- Remote server management -- System monitoring and status checking -- File operations and directory management -- Service start/stop and configuration management -- Log viewing and analysis +- **get_email**: Fetches mailbox content through IMAP. Supports restricting by time range (days), filtering by sender, and limiting the number of returned messages. The tool automatically decodes multilingual subjects and bodies, and returns subject, timestamp, sender, and body summary to simplify downstream analysis. +- **send_email**: Sends HTML emails via SMTP. Supports multiple recipients, CC, and BCC, as well as custom sender display names. All connections use SSL/TLS. The result reports delivery status and subject for easy tracking. -**Use Cases**: -- Server operations automation -- System monitoring and alerting -- Batch file processing -- Deployment and release management -- Troubleshooting and diagnostics +### 📂 File Tools -## 🔧 Tool Configuration +- **create_directory**: Creates nested directories at the specified relative path, skipping existing levels and returning the result together with the final absolute path. +- **create_file**: Creates a file and writes content. Automatically creates parent directories if needed, supports custom encoding (default UTF-8), and allows empty files. +- **read_file**: Reads a text file and returns metadata such as size, line count, and encoding. Warns when the file is large (10 MB safety threshold). +- **list_directory**: Lists directory contents in a tree view. Supports maximum recursion depth, hidden file display, and file sizes. Output includes both visual text and structured JSON to clearly present project structure. +- **move_item**: Moves files or folders within the workspace. Automatically creates destination directories, avoids overwriting existing targets, and reports how many items were moved and their total size. +- **delete_file**: Deletes a single file with permission and existence checks. Provides clear error messages on failure. +- **delete_directory**: Recursively deletes a directory and its contents with existence, permission, and safety checks. Returns the deleted relative path. -All local tools need to be configured in the agent configuration: +> All file paths must be relative to the workspace (default `/mnt/nexent`). The system automatically validates paths to prevent escaping the workspace boundary. -1. Navigate to the **[Agent Configuration](../agent-configuration)** page -2. Select the agent to configure -3. Find the corresponding local tool in the "Select Agent Tools" tab -4. Click the configuration button and fill in the necessary connection parameters -5. Test the connection to ensure configuration is correct -6. Save the configuration and enable the tool +### 🔍 Search Tools -## ⚠️ Security Considerations +- **knowledge_base_search**: Queries the local knowledge-base index with `hybrid`, `accurate`, or `semantic` modes. Can filter by index name and returns sources, scores, and citation indices, ideal for answering questions from internal documents or industry references. +- **exa_search**: Calls the EXA API for real-time web search. Supports configuring the number of results and optionally returns image links (with additional filtering performed server-side). Requires an EXA API key in the tool configuration, which you can obtain for free at [exa.ai](https://exa.ai/). +- **tavily_search**: Uses the Tavily API to retrieve webpages, particularly strong for news and current events. Returns both text results and related image URLs, with optional image filtering. Request a free API key from [tavily.com](https://www.tavily.com/). +- **linkup_search**: Uses the Linkup API to fetch text and images. In addition to regular webpages, it can return image-only results, making it useful when mixed media references are required. Register at [linkup.so](https://www.linkup.so/) to obtain a free API key. -When using local tools, please pay attention to the following security considerations: +### 🖥️ Terminal Tool -- **Permission Control**: Create dedicated users for tools, follow the principle of least privilege -- **Network Security**: Use VPN or IP whitelist to restrict access -- **Authentication Security**: Prefer key-based authentication, regularly rotate keys -- **Command Restrictions**: Configure command whitelist in production environments -- **Audit Logging**: Enable detailed operation logging +The **Terminal Tool** is one of Nexent's core local capabilities that provides a persistent SSH session. Agents can execute remote commands, perform system inspections, read logs, or deploy services. Refer to the dedicated [Terminal Tool guide](./terminal-tool) for detailed setup, parameters, and security guidance. + +## 🔧 Tool Configuration -## 🚀 Next Steps +All local tools need to be configured inside Agent Development: + +1. Navigate to the **[Agent Development](../agent-development)** page +2. Select the agent you want to configure +3. In the "Select Agent Tools" tab, locate the desired local tool +4. Click the configuration button and fill in the required connection parameters +5. Test the connection to ensure the configuration is correct +6. Save the configuration and enable the tool + +## ⚠️ Security Considerations -After configuring local tools, you can: +When using local tools, keep the following security practices in mind: -1. **[Agent Configuration](../agent-configuration)** - Add tools to agents -2. **[Chat Interface](../chat-interface)** - Use tools through agents to execute tasks +- **Permission Control**: Create dedicated users for each tool and follow least privilege +- **Network Security**: Use VPN or IP allowlists to restrict access +- **Authentication Security**: Favor key-based authentication and rotate keys regularly +- **Command Restrictions**: Configure command whitelists in production environments +- **Audit Logging**: Enable detailed logging for all operations -If you encounter any issues while using local tools, please refer to the detailed documentation of the corresponding tool or join our [Discord Community](https://discord.gg/tb5H3S3wyv) for support. +Need help? Please open a thread in [GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions). diff --git a/doc/docs/en/user-guide/local-tools/terminal-tool.md b/doc/docs/en/user-guide/local-tools/terminal-tool.md index 4ed9a80ee..45cfa67df 100644 --- a/doc/docs/en/user-guide/local-tools/terminal-tool.md +++ b/doc/docs/en/user-guide/local-tools/terminal-tool.md @@ -207,7 +207,7 @@ The tool returns results in JSON format, including: ### Configure Terminal Tool in Nexent 1. Log in to the Nexent platform -2. Navigate to the **[Agent Configuration](../agent-configuration)** page +2. Navigate to the **[Agent Development](../agent-development)** page 3. Select the agent to configure 4. Find "Terminal Tool" in the "Select Agent Tools" tab @@ -421,11 +421,4 @@ maxretry = 3 bantime = 3600 ``` -## 🚀 Next Steps - -After completing Terminal tool configuration, you can: - -1. **[Agent Configuration](../agent-configuration)** - Add Terminal tool to agents -2. **[Chat Interface](../chat-interface)** - Use Terminal tool through agents to execute server management tasks - -If you encounter any issues during configuration, please refer to our **[FAQ](../../getting-started/faq)** or join our [Discord Community](https://discord.gg/tb5H3S3wyv) for support. +Need help? Please open a thread in [GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions). diff --git a/doc/docs/en/user-guide/memory-management.md b/doc/docs/en/user-guide/memory-management.md new file mode 100644 index 000000000..6a802818b --- /dev/null +++ b/doc/docs/en/user-guide/memory-management.md @@ -0,0 +1,140 @@ +# Memory Management + +Nexent’s intelligent memory system gives agents persistent context. With multi-level memories, agents can remember key facts across conversations, retrieve them automatically, and deliver more personalized answers. + +## 🎯 What the Memory System Does + +The memory system lets agents “remember” important information and reuse it later without you repeating yourself. + +### Core Benefits + +- **Cross-conversation memory** – Agents keep track of important facts from earlier chats. +- **Automatic retrieval** – Relevant memories are pulled in automatically. +- **Personalized service** – Responses adapt to user preferences and habits. +- **Knowledge accumulation** – Agents keep getting smarter the more you use them. + +## ⚙️ System Configuration + +### Access Memory Management + +1. Click **Memory Management** in the left navigation. +2. Open the **System Configuration** section. + +### Base Settings + +| Setting | Options | Default | Description | +| --- | --- | --- | --- | +| Memory Service Status | Enable / Disable | Enable | Controls whether the memory system runs. | +| Agent Memory Sharing Strategy | Always Share / Ask Every Time / Never Share | Always Share | Defines if agents can share memories without user confirmation. | + +
+ Memory configuration +
+ +**Setting Tips** + +- **Memory service status** – Disable it if you want a completely stateless experience; enable it to unlock all memory features. +- **Sharing strategy** + - *Always Share* – Agents exchange memories automatically. + - *Ask Every Time* – You approve each sharing request. + - *Never Share* – Agents stay isolated. + +## 📚 Memory Levels + +Nexent uses four storage levels so you can keep global knowledge and private facts separate. + +### Tenant-Level + +- **Scope:** Entire organization, shared by all users and agents. +- **Stores:** SOPs, compliance policies, org charts, long-term facts. +- **Best for:** Company-wide knowledge and governance. +- **Managed by:** Tenant administrators. + +### Agent-Level + +- **Scope:** A specific agent, shared by everyone using it. +- **Stores:** Domain knowledge, skill templates, historical summaries. +- **Best for:** Letting an agent accumulate expertise over time. +- **Managed by:** Tenant administrators. + +### User-Level + +- **Scope:** A single user account. +- **Stores:** Personal preferences, habits, favorite commands, personal info. +- **Best for:** Tailoring the platform to a specific user. +- **Managed by:** That user. + +### User-Agent Level + +- **Scope:** A specific agent used by a specific user (most granular). +- **Stores:** Collaboration history, personal facts, task context. +- **Best for:** Deep personalization and long-running projects. +- **Managed by:** That user. + +### Retrieval Priority + +When an agent retrieves memory it follows this order (high ➝ low): + +1. Tenant Level – shared facts and policies. +2. User-Agent Level – very specific context for that pairing. +3. User Level – general personal preferences. +4. Agent Level – the agent’s professional knowledge. + +## 🤖 Automated Memory Management + +The system takes care of most work for you: + +- **Smart extraction:** Detects important facts in conversations and stores them automatically. +- **Context injection:** Retrieves the most relevant memories and adds them to prompts silently. +- **Incremental updates:** Refreshes or removes outdated memories so the store stays clean. + +## ✋ Manual Memory Operations + +Need full control? Manage entries manually. + +### Add a Memory + +1. Choose the level (tenant / agent / user / user-agent) and target agent. +2. Click the green **+** button. +3. Enter up to 500 characters describing the fact. +4. Click the check mark to save. + +
+ Add memory +
+ +### Delete Memories + +- **Delete group:** Click the red ✕ icon to remove every entry under that agent group (confirm in the dialog). +- **Delete single entry:** Click the red eraser icon to remove one entry. + +
+ Delete memory +
+ +## 💡 Usage Tips + +### Memory Content Guidelines + +1. **Keep entries atomic:** Each memory should contain *one* clear fact. + - ✅ Good: “The user prefers dark mode.” + - ❌ Not good: “The user prefers dark mode, works nights, and loves coffee.” +2. **Maintain freshness:** Review and remove outdated entries regularly. +3. **Protect privacy:** Store sensitive info at the user or user-agent level instead of tenant level. + +### Best Practices + +- Pick the memory level that matches the sharing needs. +- Let automation handle routine facts; manually add critical knowledge. +- Review the memory list periodically to keep everything relevant. +- Keep personal or sensitive data scoped tightly to the right user. + +## 🚀 Next Steps + +With memory configured you can: + +1. Experience the new continuity in **[Start Chat](./start-chat)**. +2. Manage all agents in **[Agent Space](./agent-space)**. +3. Build more agents inside **[Agent Development](./agent-development)**. + +Need help? Check the **[FAQ](../getting-started/faq)** or open a thread in [GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions). \ No newline at end of file diff --git a/doc/docs/en/user-guide/memory.md b/doc/docs/en/user-guide/memory.md deleted file mode 100644 index 5c70487c1..000000000 --- a/doc/docs/en/user-guide/memory.md +++ /dev/null @@ -1,103 +0,0 @@ -# 🧠Nexent Intelligent Memory System Technical Specification - -## 1. System Architecture Overview - -The Nexent Intelligent Memory System is built on an advanced memory storage architecture that provides intelligent agents with persistent context-aware capabilities. Through a multi-layered memory management mechanism, the system achieves cross-conversation knowledge accumulation and retrieval, significantly enhancing the coherence and personalization of human-machine interactions. - -### Core Technical Features -- **Layered Memory Architecture**: Four-level memory storage system built on the mem0 framework -- **Adaptive Memory Management**: Supports both automated and manual memory operation modes -- **Cross-Session Persistence**: Ensures continuity of knowledge and context across multiple conversations -- **Fine-Grained Permission Control**: Provides flexible memory sharing strategy configuration - - -## 2. Configuration and Initialization - -### 2.1 System Activation -1. Access the memory management interface: Click the **Memory Management Icon** in the upper right corner of the conversation interface -2. Enter the **System Configuration** module for initialization settings - -### 2.2 Core Configuration Parameters - -| Configuration Item | Options | Default Value | Description | -|-------------------|---------|---------------|-------------| -| Memory Service Status | Enable/Disable | Enable | Controls the operational status of the entire memory system | -| Agent Memory Sharing Strategy | Always Share/Ask Me Each Time/Prohibit Sharing | Always Share | Defines whether user authorization consent is required for memory sharing between agents | - -
- Select Agent -
- - -## 3. Layered Memory Architecture - -Nexent adopts a four-layer memory storage architecture based on **mem0**, achieving precise memory classification and retrieval through different scopes and lifecycle management: - -### 3.1 Architecture Layer Details - -#### Tenant Level Memory - - **Scope**: Organization-wide - - **Storage Content**: Enterprise-level standard operating procedures, compliance policies, organizational structure, factual information - - **Lifecycle**: Long-term storage - - **Config Role**: Tenant Administrator - - **Applications**: Enterprise knowledge management, standardized process execution, compliance checking - -#### Agent Level Memory - - **Scope**: Specific Agent - - **Storage Content**: Professional domain knowledge, skill templates, historical conversation summaries, learning accumulation - - **Lifecycle**: Consistent with agent lifecycle - - **Config Role**: Tenant Administrator - - **Applications**: Professional skill accumulation, domain knowledge sedimentation, experiential learning - -#### User Level Memory - - **Scope**: Specific User Account - - **Storage Content**: Personal preference settings, usage habits, common instruction templates, personal information - - **Lifecycle**: Long-term storage - - **Config Role**: All Users - - **Applications**: Personalized services, user experience optimization, preference management - -#### User-Agent Level Memory - - **Scope**: Specific Agent under Specific User Account - - **Storage Content**: Collaboration history, personalized factual information, specific task context, relationship models - - **Lifecycle**: Consistent with agent lifecycle - - **Config Role**: All Users - - **Applications**: Deep collaboration scenarios, personalized tuning, task continuity maintenance - -### 3.2 Memory Priority and Retrieval Strategy - -Memory retrieval follows the following priority order (from high to low): -1. **Tenant Level** → Basic facts -2. **User-Agent Level** → Most specific context information -3. **User Level** → Personal preferences and habits -4. **Agent Level** → Professional knowledge and skills - - -## 4. Operation Modes and Functional Interfaces - -### 4.1 Automated Memory Management -- **Intelligent Extraction**: Automatically identifies key factual information in conversations and generates memory entries -- **Automatic Context Embedding**: Agents automatically retrieve the most relevant memory entries and implicitly embed them in conversation context -- **Incremental Updates**: Supports progressive updates, supplementation, and automatic cleanup of memory content - -### 4.2 Manual Memory Operations - -#### Adding Memory -- Click the green plus button, input text, then click the checkmark to add a memory entry (maximum 500 characters) - -
- Select Agent -
- -#### Deleting Memory -- Click the red cross button, then click confirm in the popup confirmation dialog to delete all memory entries under a specific Agent group -- Click the red eraser button to delete a specific memory entry - -
- Select Agent -
- -### 4.3 Memory Management Best Practices - -1. **Atomicity Principle**: Each memory entry should contain **concise**, **single**, **clear** factual information -2. **Temporal Management**: Regularly clean up outdated or no longer relevant memory entries to maintain the timeliness and accuracy of the memory database -3. **Privacy Protection**: Sensitive information should be avoided from being shared at the tenant level or agent level \ No newline at end of file diff --git a/doc/docs/en/user-guide/model-configuration.md b/doc/docs/en/user-guide/model-configuration.md deleted file mode 100644 index e2982dd28..000000000 --- a/doc/docs/en/user-guide/model-configuration.md +++ /dev/null @@ -1,190 +0,0 @@ -# Model Configuration - -In the Model Configuration module, you can connect various types of AI models, including large language models, vector models, and vision language models. Nexent supports multiple model providers, allowing you to flexibly choose the most suitable models for your needs. - -## 🔄 Sync ModelEngine Models - -Nexent will soon support seamless integration with the ModelEngine platform, enabling automatic synchronization and use of all models you have deployed on ModelEngine. Stay tuned! - -## 🛠️ Add Custom Models - -### Add a Single Model - -1. **Add a custom model** - - Click the "Add Custom Model" button to open the add model dialog. -2. **Select model type** - - Click the model type dropdown and select the type you want to add (Large Language Model/Embedding Model/Vision Language Model). -3. **Configure model parameters** - - **Model Name (required):** Enter the model name as used in requests. - - **Display Name:** Optionally set a display name for the model (defaults to the model name). - - **Model URL (required):** Enter the API endpoint provided by the model provider. - - **API Key:** Enter your API key. - -> ⚠️ **Notes**: -> 1. Obtain the model name from the model provider, typically in the format `model series/model name`. For example, if the model series is `Qwen` and the model name is `Qwen3-8B`, the model name is `Qwen/Qwen3-8B`. -> 2. Obtain the model URL from the model provider's API documentation. For example, if the model provider is Silicon Flow, the URL for the large language model is `https://api.siliconflow.cn/v1`, the URL for the vector model is `https://api.siliconflow.cn/v1/embeddings`, and the URL for the visual language model is `https://api.siliconflow.cn/v1`. -> 3. Create and obtain an API key from the model provider's API key management page. - -4. **Connectivity Verification** - - Click the "Verify" button. The system will send a test request and return the result. -5. **Save Model** - - After configuration, click "Add" to add the model to the available models list. -
- -
- -### Batch Add Models - -To improve import efficiency, Nexent provides a batch model import feature. - -1. **Batch Add Models** - - In the add model dialog, enable the batch add switch. -2. **Select Model Provider** - - Click the model provider dropdown and select a provider. -3. **Select Model Type** - - Click the model type dropdown and select the type you want to add (LLM/Vector/Visual). -4. **Enter API Key (required)** - - Enter your API key. -5. **Get Models** - - Click the "Get Models" button to retrieve a list of models. -6. **Select Models** - - The fetched models are disabled by default. You need to manually enable the models you want to use. -7. **Save Models** - - After configuration, click "add" to add all selected models to the available models list. - -
- -
- -## 🔧 Edit Custom Models - -When you need to edit model configurations or delete models you no longer use, follow these steps: - -1. Click the "Edit Custom Models" button. -2. Select the model type to edit or delete (LLM/Vector/Visual). -3. Choose whether to batch edit models or edit a single-instance custom model. -4. For batch edits, you can toggle model switches and add or remove models. Click the "Edit Configuration" button in the top-right to apply configuration edits to the selected models in bulk. -5. If you are editing a single-instance custom model, click the delete button 🗑️ to remove the target model. To edit its configuration, click the model name to open the edit dialog. - -
- - -
-
-
- - -
-
-
- - -
- -## ⚙️ Configure System Models - -After adding models, you need to configure the system base model, which will be used for basic functions such as title generation and real-time file reading. When agents are running, you can specify specific models for each agent. - -### Base Model Configuration -The system base model is used for core platform functions, including: -- Title generation -- Real-time file reading -- Basic text processing - -**Configuration Steps:** -- Click the base model dropdown to select a model from the added large language models as the system base model. - -### Agent Model Configuration -When creating and configuring agents, you can specify specific models for each agent: -- Each agent can independently select the large language model to use -- Support configuring different models for different agents to meet various business needs -- Agent model configuration will be set in the agent configuration page - -### Vector Model -Vector models are primarily used for vectorizing text, images, and other data in knowledge bases, serving as the foundation for efficient retrieval and semantic understanding. Configuring appropriate vector models can significantly improve knowledge base search accuracy and multimodal data processing capabilities. -- Click the vector model dropdown to select from the added vector models. - -### Multimodal Model -Multimodal models combine visual and language capabilities, enabling handling of complex scenarios involving text, images, and other information types. For example, when uploading image files in the chat interface, the system automatically calls multimodal models for content analysis and intelligent conversation. -- Click the vision language model dropdown to select from the added vision language models. - -
- - - -
- -## ✅ Check Model Connectivity - -Regularly checking model connectivity is important for stable system operation. With the connectivity check feature, you can promptly discover and resolve model connection issues, ensuring service continuity and reliability. - -**Check Process:** -- Click the "Check Model Connectivity" button -- The system will automatically test the connection status of all configured system models - -**Status Indicators:** -- 🔵 **Blue dot:** Checking, please wait -- 🔴 **Red dot:** Connection failed, check configuration or network -- 🟢 **Green dot:** Connection normal, model is available - -**Troubleshooting Suggestions:** -- Check if the network connection is stable -- Verify that the API key is valid and not expired -- Confirm the service status of the model provider -- Check firewall and security policy settings - -## 🤖 Supported Model Providers - -### 🤖 Large Language Models (LLM) -Nexent supports any **OpenAI API-compatible** large language model provider, including: -- [SiliconFlow](https://siliconflow.cn/) -- [Ali Bailian](https://bailian.console.aliyun.com/) -- [TokenPony](https://www.tokenpony.cn/) -- [DeepSeek](https://platform.deepseek.com/) -- [OpenAI](https://platform.openai.com/) -- [Anthropic](https://console.anthropic.com/) -- [Moonshot](https://platform.moonshot.cn/) - -You can follow these steps to integrate models: -1. Visit the model provider's official website and register an account; -2. Create and copy the API Key; -3. Check the API endpoint in the documentation (i.e., model URL, usually ending with `/v1`); -4. Click "Add Custom Model" in the Nexent model configuration page, fill in the required information, and you're ready to go. - -### 🎭 Multimodal Vision Models - -Use the same API Key and model URL as large language models, but specify multimodal model names, such as **Qwen/Qwen2.5-VL-32B-Instruct** provided by SiliconFlow. - -### 🔤 Vector Models - -Use the same API Key as large language models, but the model URL is usually different, typically ending with `/v1/embeddings`, and specify vector model names, such as **BAAI/bge-m3** provided by SiliconFlow. - -### 🎤 Speech Models - -Currently only supports VolcEngine Voice, and needs to be configured in `.env` -- **Website**: [volcengine.com/product/voice-tech](https://www.volcengine.com/product/voice-tech) -- **Free Tier**: Available for personal use -- **Features**: High-quality Chinese and English speech synthesis - -**Getting Started**: -1. Register a VolcEngine account -2. Access Voice Technology services -3. Create an application and get API Key -4. Configure TTS/STT settings in environment - -## 💡 Need Help - -If you encounter issues with model providers: -1. Check provider-specific documentation -2. Verify API key permissions and quotas -3. Test with provider's official examples -4. Join our [Discord community](https://discord.gg/tb5H3S3wyv) for support - -## 🚀 Next Steps - -After completing model configuration, we recommend you click "Next" to continue with: - -1. **[Knowledge Base Configuration](./knowledge-base-configuration)** – Create and manage knowledge bases -2. **[Agent Configuration](./agent-configuration)** – Create and configure agents - -If you encounter any issues during model configuration, please refer to our **[FAQ](../getting-started/faq)** or join our [Discord community](https://discord.gg/tb5H3S3wyv) for support. \ No newline at end of file diff --git a/doc/docs/en/user-guide/model-management.md b/doc/docs/en/user-guide/model-management.md new file mode 100644 index 000000000..77f09fe67 --- /dev/null +++ b/doc/docs/en/user-guide/model-management.md @@ -0,0 +1,220 @@ +# Model Management + +In the Model Management module, you can configure your app’s basic information and connect every model the platform needs, including large language models, embedding models, and vision-language models. Nexent supports multiple providers so you can pick the best option for each scenario. + +## 🖼️ App Configuration + +App configuration is the first step of model management. Configure the icon, name, and description so users can instantly recognize the app and the platform can pass the proper context to models. + +- The icon and name appear in the upper-left corner of the chat page. +- The description is used as background information when generating agents to improve the model’s understanding of your use case. + +### App Icon Configuration + +Click the app icon to open the configuration panel. Nexent provides two options: + +- **Use a preset icon**: Pick an icon from the built-in gallery and optionally change the background color for fast setup. +- **Upload a custom image**: Supports PNG and JPG (≤2 MB). + +
+ + +
+ +### App Name & Description + +#### App Name + +- Displayed on the chat page, helping users recognize the current app. +- Keep it short, descriptive, and free of special characters. + +#### App Description + +- Passed to the model as background context. +- Highlight the core capabilities and keep the text fluent and concise. + +
+ +
+ +## 🤖 Model Configuration + +### 🔄 Sync ModelEngine Models + +Nexent will soon support seamless integration with the ModelEngine platform so you can automatically sync and reuse every model you deploy there. Stay tuned! + +### 🛠️ Add Custom Models + +#### Add a Single Model + +1. **Add a custom model** + - Click **Add Custom Model** to open the dialog. +2. **Select model type** + - Choose Large Language Model, Embedding Model, or Vision Language Model. +3. **Configure model parameters** + - **Model Name (required):** The name you send in API requests. + - **Display Name:** Optional label shown in the UI (defaults to the model name). + - **Model URL (required):** API endpoint from the provider. + - **API Key:** Your provider key. + +> ⚠️ **Notes** +> 1. Model names usually follow `series/model`. Example: `Qwen/Qwen3-8B`. +> 2. API endpoints come from the provider docs. For SiliconFlow, examples include `https://api.siliconflow.cn/v1` (LLM, VLM) and `https://api.siliconflow.cn/v1/embeddings` (embedding). +> 3. Generate API keys from the provider’s key management console. + +4. **Connectivity verification** + - Click **Verify** to send a test request and confirm connectivity. +5. **Save model** + - Click **Add** to place the model in the available list. + +
+ +
+ +#### Batch Add Models + +Use batch import to speed up onboarding: + +1. Enable the **Batch Add Models** toggle in the dialog. +2. Select a **model provider**. +3. Choose the **model type** (LLM/Embedding/Vision). +4. Enter the **API Key** (required). +5. Click **Fetch Models** to retrieve the provider list. +6. Toggle on the models you need (disabled by default). +7. Click **Add** to save every selected model at once. + +
+ +
+ +### 🔧 Edit Custom Models + +Modify or delete models anytime: + +1. Click **Edit Custom Models**. +2. Select the model type (LLM/Embedding/Vision). +3. Choose between batch editing or single-model editing. +4. For batch edits, toggle models on/off or click **Edit Config** in the upper-right to change settings in bulk. +5. For single models, click the trash icon 🗑️ to delete, or click the model name to open the edit dialog. + +
+ + +
+
+
+ + +
+
+
+ + +
+ +## ⚙️ Configure System Models + +After adding models, assign the platform-level defaults. These models handle system tasks such as title generation, real-time file reading, and multimodal parsing. Individual agents can still choose their own run-time models. + +### Base Model + +- Used for core platform features (title generation, real-time file access, basic text processing). +- Choose any added large language model from the dropdown. + +### Embedding Model + +- Powers semantic search for text, images, and other knowledge-base content. +- Select one of the added embedding models. + +### Vision-Language Model + +- Required for multimodal chat scenarios (for example, when users upload images). +- Pick one of the added vision-language models. + +
+ + + +
+ +## ✅ Check Model Connectivity + +Run regular connectivity checks to keep the platform healthy: + +1. Click **Check Model Connectivity**. +2. Nexent tests every configured system model automatically. + +Status indicators: + +- 🔵 **Blue dot** – Checking in progress. +- 🔴 **Red dot** – Connection failed; review configuration or network. +- 🟢 **Green dot** – Connection is healthy. + +Troubleshooting tips: + +- Confirm network stability. +- Ensure the API key is valid and not expired. +- Check the provider’s service status. +- Review firewall and security policies. + +## 🤖 Supported Providers + +### Large Language Models + +Nexent supports any **OpenAI-compatible** provider, including: + +- [SiliconFlow](https://siliconflow.cn/) +- [Ali Bailian](https://bailian.console.aliyun.com/) +- [TokenPony](https://www.tokenpony.cn/) +- [DeepSeek](https://platform.deepseek.com/) +- [OpenAI](https://platform.openai.com/) +- [Anthropic](https://console.anthropic.com/) +- [Moonshot](https://platform.moonshot.cn/) + +Getting started: + +1. Sign up at the provider’s portal. +2. Create and copy an API key. +3. Locate the API endpoint (usually ending with `/v1`). +4. Click **Add Custom Model** in Nexent and fill in the required fields. + +### Multimodal Vision Models + +Use the same API key and URL as LLMs but specify a multimodal model name, for example **Qwen/Qwen2.5-VL-32B-Instruct** on SiliconFlow. + +### Embedding Models + +Use the same API key as LLMs but typically a different endpoint (often `/v1/embeddings`), for example **BAAI/bge-m3** from SiliconFlow. + +### Speech Models + +Currently only **VolcEngine Voice** is supported and must be configured via `.env`: + +- **Website:** [volcengine.com/product/voice-tech](https://www.volcengine.com/product/voice-tech) +- **Free tier:** Available for individual use +- **Highlights:** High-quality Chinese/English TTS + +Steps: + +1. Register a VolcEngine account. +2. Enable the Voice Technology service. +3. Create an app and generate an API key. +4. Configure the TTS/STT settings in your environment. + +## 💡 Need Help + +If you run into provider issues: + +1. Review the provider’s documentation. +2. Check API key permissions and quotas. +3. Test with the provider’s official samples. +4. Ask the community in our [Discord server](https://discord.gg/tb5H3S3wyv). + +## 🚀 Next Steps + +After closing the Model Management flow, continue with: + +1. **[Knowledge Base](./knowledge-base)** – Create and manage knowledge bases. +2. **[Agent Development](./agent-development)** – Build and configure agents. + +Need help? Check the **[FAQ](../getting-started/faq)** or open a thread in [GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions). \ No newline at end of file diff --git a/doc/docs/en/user-guide/quick-setup.md b/doc/docs/en/user-guide/quick-setup.md new file mode 100644 index 000000000..ef4e43bab --- /dev/null +++ b/doc/docs/en/user-guide/quick-setup.md @@ -0,0 +1,53 @@ +# Quick Setup + +Quick Setup provides a guided path that walks you through the recommended setup order. Follow the three steps—model management, knowledge bases, and agent development—to get the platform ready fast. + +## 📋 Configuration Flow + +Quick Setup is organized in the following order: + +### Step 1: Model Management + +Configure basic app information and connect AI models: + +- **App configuration:** Set the icon, name, and description. +- **Model configuration:** Add large language models, embedding models, and vision-language models. + +Learn more: [Model Management](./model-management) + +### Step 2: Knowledge Base + +Create knowledge bases and upload documents: + +- **Create a knowledge base:** Give agents a place to search. +- **Upload files:** Support for multiple file formats. +- **Generate summaries:** Create concise knowledge-base descriptions. + +Learn more: [Knowledge Base](./knowledge-base) + +### Step 3: Agent Development + +Create and configure agents: + +- **Create an agent:** Start from scratch or import an existing configuration. +- **Configure capabilities:** Add collaborative agents and tools. +- **Describe logic:** Tell Nexent how the agent should work. + +Learn more: [Agent Development](./agent-development) + +## 🎯 Tips + +1. **Follow the order:** Completing each step in sequence guarantees every dependency is ready. +2. **Save progress:** Click save after finishing each step. +3. **Enable knowledge search:** Add the `knowledge_base_search` tool when you want agents to use local knowledge. +4. **Test early:** After setup, run a few conversations on the chat page to confirm everything works. + +## 🚀 Next Steps + +After finishing Quick Setup: + +1. Visit **[Agent Space](./agent-space)** to review and manage agents. +2. Use **[Start Chat](./start-chat)** to talk to your agents. +3. Configure **[Memory Management](./memory-management)** to give agents persistent memory. + +Need help? Check the **[FAQ](../getting-started/faq)** or open a thread in [GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions). \ No newline at end of file diff --git a/doc/docs/en/user-guide/chat-interface.md b/doc/docs/en/user-guide/start-chat.md similarity index 86% rename from doc/docs/en/user-guide/chat-interface.md rename to doc/docs/en/user-guide/start-chat.md index 2db2d39d6..68a8d6f25 100644 --- a/doc/docs/en/user-guide/chat-interface.md +++ b/doc/docs/en/user-guide/start-chat.md @@ -1,6 +1,6 @@ -# Chat Interface +# Start Chat -The Chat Interface is the core area for interacting with your agents. Here, you can chat with different agents, upload files, use voice input, and manage your chat history. +The Start Chat is the core area for interacting with your agents. Here, you can talk to different agents, upload files, use voice input, and manage your chat history. ## 🤖 Start Chatting @@ -27,11 +27,12 @@ Before starting a chat, you need to select an agent. After selecting an agent, you can send text messages in the following ways: 1. **Enter your question** - - Type your question or command in the input box at the bottom of the chat box + - Type your question or command in the input box at the bottom + - Press `Shift+Enter` to insert a line break 2. **Send Message** - Click the send button on the right side of the input box - - Or simply press Enter on your keyboard + - Or press `Enter` on your keyboard - The agent will start processing your request and generate a reply 3. **View Replies** @@ -44,7 +45,7 @@ After selecting an agent, you can send text messages in the following ways: ### Use Voice Input -Nexent supports voice input, allowing you to interact with agents by speaking: +Nexent supports voice input (make sure you have configured the speech model under [Model Management](./model-management) beforehand) so you can interact by speaking: 1. **Enable Voice Input** - Find the microphone icon in the lower right corner of the input box @@ -61,7 +62,7 @@ Nexent supports voice input, allowing you to interact with agents by speaking: - You can also manually edit the recognized text before sending - Both Chinese and English speech recognition are supported -> 💡 **Tip:** For better speech recognition, use it in a quiet environment and speak clearly. +> 💡 **Tip:** For better recognition results, use it in a quiet environment and articulate clearly. ### Upload Files for Chat @@ -120,10 +121,15 @@ The left sidebar provides complete chat history management: Chat Edit
-### Access Settings Page +### Access Other Modules -- In the lower left corner of the sidebar, find the ⚙️ settings icon -- Click the icon to enter the settings page, where you can modify agent and system settings +Use the left navigation bar to jump to other modules at any time: + +- **Agent Space** – Review and manage all agents you have built. +- **Agent Studio** – Continue creating or editing agents. +- **Model Management** – Update app information and model credentials. +- **Knowledge Base** – Upload, summarize, and organize documents. +- **Memory Management** – Configure multi-layer memory and sharing rules. ## 🔍 View Knowledge References @@ -198,8 +204,4 @@ Congratulations! You now master all the core features of Nexent. We look forward ### Get Help -If you encounter any issues while using Nexent: - -- 📖 Check our **[FAQ](../getting-started/faq)** for detailed answers -- 💬 Join our [Discord community](https://discord.gg/tb5H3S3wyv) to communicate with other users -- 🆘 Contact technical support for professional help \ No newline at end of file +Need help? Check the **[FAQ](../getting-started/faq)** or open a thread in [GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions). \ No newline at end of file diff --git a/doc/docs/en/user-guide/user-management.md b/doc/docs/en/user-guide/user-management.md new file mode 100644 index 000000000..5f16ed179 --- /dev/null +++ b/doc/docs/en/user-guide/user-management.md @@ -0,0 +1,37 @@ +# User Management + +User Management is an upcoming Nexent module that will add full user and permission controls. + +## 🎯 Coming Features + +User Management will include: + +- **User directory** – View and manage every platform user. +- **Permission controls** – Assign features and resource access per user. +- **Role management** – Create role bundles and apply them quickly. +- **Usage insights** – Monitor activity and adoption metrics. + +## ⏳ Stay Tuned + +We are building a flexible user-management system so you can: + +- Apply fine-grained permission policies. +- Configure roles that match your organization. +- Understand how users collaborate with agents. + +## 📢 Follow Updates + +Want to know when User Management ships? + +- Join our [Discord community](https://discord.gg/tb5H3S3wyv) for announcements. +- Follow project updates in the repository. + +## 🚀 Related Features + +While waiting for User Management you can: + +1. Manage agents in **[Agent Space](./agent-space)**. +2. Configure models in **[Model Management](./model-management)**. +3. Chat with agents via **[Start Chat](./start-chat)**. + +Need help? Check the **[FAQ](../getting-started/faq)** or open a thread in [GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions). \ No newline at end of file diff --git a/doc/docs/zh/deployment/upgrade-guide.md b/doc/docs/zh/deployment/upgrade-guide.md index 5a11dc0cf..b7a2e12ad 100644 --- a/doc/docs/zh/deployment/upgrade-guide.md +++ b/doc/docs/zh/deployment/upgrade-guide.md @@ -135,4 +135,4 @@ bash deploy.sh 部署完成后: 1. 在浏览器打开 `http://localhost:3000` -2. 参考 [用户指南](https://doc.nexent.tech/zh/user-guide/) 完成智能体配置与验证 +2. 参考 [用户指南](https://doc.nexent.tech/zh/user-guide/home-page) 完成智能体配置与验证 diff --git a/doc/docs/zh/getting-started/development-guide.md b/doc/docs/zh/getting-started/development-guide.md index 333f41536..2790a44bb 100644 --- a/doc/docs/zh/getting-started/development-guide.md +++ b/doc/docs/zh/getting-started/development-guide.md @@ -210,7 +210,7 @@ source .env && python backend/runtime_service.py # 运行态服务 ### 文档资源 - [安装部署](./installation.md) - 环境搭建和部署 - [常见问题](./faq) - 常见问题解答 -- [用户指南](../user-guide/) - Nexent使用指南 +- [用户指南](../user-guide/home-page) - Nexent使用指南 ### 社区支持 - [Discord 社区](https://discord.gg/tb5H3S3wyv) - 实时交流和支持 diff --git a/doc/docs/zh/getting-started/faq.md b/doc/docs/zh/getting-started/faq.md index 992c076c4..eb48b2565 100644 --- a/doc/docs/zh/getting-started/faq.md +++ b/doc/docs/zh/getting-started/faq.md @@ -1,6 +1,6 @@ # Nexent 常见问题 -本常见问题解答主要针对安装和使用 Nexent 过程中可能遇到的问题。如需了解基本安装步骤,请参考[安装部署](./installation)。如需了解基本使用指导,请参考[用户指南](../user-guide/)。 +本常见问题解答主要针对安装和使用 Nexent 过程中可能遇到的问题。如需了解基本安装步骤,请参考[安装部署](./installation)。如需了解基本使用指导,请参考[用户指南](../user-guide/home-page)。 ## 🚫 常见错误与运维方式 @@ -42,8 +42,16 @@ 2. **有效的 API 密钥**: 验证您的 API 密钥具有适当权限 3. **模型名称**: 确认模型标识符正确 4. **网络访问**: 确保您的部署可以访问提供商的服务器 - - 关于如何配置模型,请参阅用户指南中的 [模型配置](../user-guide/model-configuration)。 + 关于如何配置模型,请参阅用户指南中的 [模型管理](../user-guide/model-management)。 + +- **Q: 接入 DeepSeek 官方 API 时多轮对话会报错,如何解决?** + - A: DeepSeek 官方当前仅支持文本对话接口,而 Nexent 的推理流程面向多模态设计。在多轮对话中,官方 API 无法正确接收多模态格式数据,因此会触发错误。建议改用硅基流动等已对 DeepSeek 系列模型完成多模态适配的供应商,既保持 DeepSeek 模型的体验,又能兼容 Nexent 的多模态调用链。具体来说,我们使用的消息体形如: + ```python + { "role":"user", "content":[ { "type":"text", "text":"prompt" } ] } + ``` + 而DeepSeek只接收: + ```python + { "role":"user", "content":"prompt" } ## 💡 需要帮助 diff --git a/doc/docs/zh/getting-started/installation.md b/doc/docs/zh/getting-started/installation.md index 15caa7391..2a2760d22 100644 --- a/doc/docs/zh/getting-started/installation.md +++ b/doc/docs/zh/getting-started/installation.md @@ -48,7 +48,7 @@ bash deploy.sh 部署成功完成后: 1. 在浏览器中打开 **http://localhost:3000** -2. 参考 [用户指南](../user-guide/) 进行智能体的开发 +2. 参考 [用户指南](../user-guide/home-page) 进行智能体的开发 ## 📦 服务架构 diff --git a/doc/docs/zh/opensource-memorial-wall.md b/doc/docs/zh/opensource-memorial-wall.md index 6592039ff..e48999550 100644 --- a/doc/docs/zh/opensource-memorial-wall.md +++ b/doc/docs/zh/opensource-memorial-wall.md @@ -15,9 +15,6 @@ 每条消息应包含您的姓名/昵称和日期。 请保持消息的礼貌和尊重,符合我们的行为准则。 --> -::: china-king-hs - 2025-11-20 -希望能正常使用nexent -::: ::: info happyzhang - 2025-11-13 也许我们正见证着未来的“后起之秀”😀 @@ -437,10 +434,3 @@ Nexent功能如此之强大,给我很多帮助,感谢开发者!厉害 感谢 Nexent 让我踏上了开源之旅!给我一个机会制作智能体 ::: -::: info chengyudan - 2025-10-20 -感谢 Nexent 让我踏上了开源之旅! -::: - -::: info user - 2025-11-20 -学习ai - agent非常好的项目,后面会持续输出贡献! -::: diff --git a/doc/docs/zh/user-guide/agent-configuration.md b/doc/docs/zh/user-guide/agent-development.md similarity index 83% rename from doc/docs/zh/user-guide/agent-configuration.md rename to doc/docs/zh/user-guide/agent-development.md index e6fabebde..a339e6fe2 100644 --- a/doc/docs/zh/user-guide/agent-configuration.md +++ b/doc/docs/zh/user-guide/agent-development.md @@ -1,6 +1,6 @@ -# 智能体配置 +# 智能体开发 -在智能体配置模块中,您可以创建、配置和管理智能体。智能体是Nexent的核心功能,它们能够理解您的需求并执行相应的任务。 +在智能体开发页面中,您可以创建、配置和管理智能体。智能体是Nexent的核心功能,它们能够理解您的需求并执行相应的任务。 ## 🔧 Agent管理 @@ -8,6 +8,7 @@ 在Agent管理页签下,点击“创建Agent”即可创建一个空白智能体,点击“退出创建”即可退出创建模式。 如果您有现成的智能体配置,也可以导入使用: + 1. 点击“导入Agent” 2. 在弹出的文件中选择智能体配置文件(JSON 格式) 3. 点击“打开”按钮,系统会验证配置文件的格式和内容,并显示导入的智能体信息 @@ -17,6 +18,7 @@ ### 智能体列表 + 在Agent管理页签下,您可以看到已创建的所有智能体列表,点击智能体会选中它以进行详细配置,再次点击则会取消选中。 ## 👥 配置Agent能力 @@ -34,8 +36,8 @@ - ### 选择Agent的工具 + 智能体可以使用各种工具来完成任务,如知识库检索、收发邮件、文件管理等本地工具,也可接入第三方MCP工具,或自定义工具。 1. 在“选择Agent的工具”页签右侧,点击“刷新工具”来刷新可用工具列表 @@ -47,7 +49,10 @@ +> 📚 想了解系统已经内置的所有本地工具能力?请参阅 [本地工具概览](./local-tools/index.md)。 + ### 添加MCP工具 + Nexent支持您快速便捷地使用第三方MCP工具,丰富Agent能力。 1. 在“选择Agent的工具”页签右侧,点击“MCP配置”,可在弹窗中进行MCP服务器的配置,查看已配置的MCP服务器 @@ -58,15 +63,16 @@ Nexent支持您快速便捷地使用第三方MCP工具,丰富Agent能力。 -有许多三方服务如modelscope提供了MCP服务,您可以快速接入使用。 +有许多三方服务如 [ModelScope](https://www.modelscope.cn/mcp) 提供了MCP服务,您可以快速接入使用。 ### 自定义工具 + 您可参考以下指导文档,开发自己的工具,并接入Nexent使用,丰富Agent能力。 + - [LangChain 工具指南](../backend/tools/langchain) - [MCP 工具开发](../backend/tools/mcp) - [SDK 工具文档](../sdk/core/tools) - ## 📝 描述业务逻辑 ### 描述Agent应该如何工作 @@ -82,6 +88,7 @@ Nexent支持您快速便捷地使用第三方MCP工具,丰富Agent能力。 ### 调试Agent + 在完成初步Agent配置后,您可以对Agent进行调试,根据调试结果微调配置,持续提升Agent表现。 1. 在页面右下角点击"调试"按钮,弹出智能体调试页面 @@ -96,8 +103,10 @@ Nexent支持您快速便捷地使用第三方MCP工具,丰富Agent能力。 ## 🚀 下一步 -完成智能体配置后,您可以点击“完成配置”按钮,进入: +完成智能体开发后,您可以: -1. **[对话页面](./chat-interface)** - 与智能体进行交互 +1. 在 **[智能体空间](./agent-space)** 中查看和管理所有智能体 +2. 在 **[开始问答](./start-chat)** 中与智能体进行交互 +3. 在 **[记忆管理](./memory-management)** 配置记忆以提升智能体的个性化能力 -如果您在智能体配置过程中遇到任何问题,请参考我们的 **[常见问题](../getting-started/faq)** 或加入我们的 [Discord 社区](https://discord.gg/tb5H3S3wyv) 获取支持。 \ No newline at end of file +如果您在智能体开发过程中遇到任何问题,请参考我们的 **[常见问题](../getting-started/faq)** 或在[GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions)中进行提问获取支持。 diff --git a/doc/docs/zh/user-guide/agent-market.md b/doc/docs/zh/user-guide/agent-market.md new file mode 100644 index 000000000..ca68c9400 --- /dev/null +++ b/doc/docs/zh/user-guide/agent-market.md @@ -0,0 +1,39 @@ +# 智能体市场 + +智能体市场是Nexent平台即将推出的功能模块,将为您提供丰富的智能体资源。 + +## 🎯 功能预告 + +智能体市场将提供以下功能: + +- **浏览智能体**:浏览平台提供的各类智能体 +- **获取智能体**:一键获取并使用优质智能体 +- **分享智能体**:分享您开发的智能体到市场 +- **评价和反馈**:对智能体进行评价和反馈 + +## ⏳ 敬请期待 + +智能体市场功能正在开发中,敬请期待! + +我们正在努力为您打造一个丰富、便捷的智能体生态,让您能够: + +- 快速获取经过验证的优质智能体 +- 分享您的创作,与社区共同成长 +- 发现更多创新应用场景 + +## 📢 获取最新动态 + +想要第一时间了解智能体市场的上线信息? + +- 加入我们的 [Discord 社区](https://discord.gg/tb5H3S3wyv) 获取最新动态 +- 关注项目更新,了解开发进展 + +## 🚀 相关功能 + +在等待智能体市场上线期间,您可以: + +1. 在 **[智能体空间](./agent-space)** 中管理您自己的智能体 +2. 通过 **[智能体开发](./agent-development)** 创建专属智能体 +3. 在 **[开始问答](./start-chat)** 中体验智能体的强大功能 + +如果您使用过程中遇到任何问题,请参考我们的 **[常见问题](../getting-started/faq)** 或在[GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions)中进行提问获取支持。 diff --git a/doc/docs/zh/user-guide/agent-space.md b/doc/docs/zh/user-guide/agent-space.md new file mode 100644 index 000000000..cb8380cd8 --- /dev/null +++ b/doc/docs/zh/user-guide/agent-space.md @@ -0,0 +1,66 @@ +# 智能体空间 + +智能体空间是您管理所有已开发智能体的中心。在这里,您可以以卡片形式查看所有智能体及智能体详细配置,进行智能体删除、导出等管理操作。 + +## 📦 智能体卡片展示 + +智能体空间以卡片形式展示所有已开发好的智能体,每个卡片包含: + +- **智能体图标**:智能体的标识图标 +- **智能体名称**:智能体的显示名称 +- **智能体描述**:智能体的功能描述 +- **智能体状态**:智能体是否可用的状态 +- **操作按钮**:快速操作入口 + +## 🔧 管理智能体 + +在智能体空间中,您可以对每个智能体进行以下操作: + +### 查看智能体详细信息 + +点击智能体卡片,即可查看智能体详细信息: + +- **基础信息**:智能体ID、名称、描述、状态等 +- **模型配置**:模型名称、最大部署、业务逻辑模型名称等 +- **提示词**:包含角色提示词、约束提示词、示例提示词、以及原始业务描述 +- **工具**:配置的工具 +- **子智能体**:配置的子智能体 + +### 编辑智能体 + +1. 点击智能体卡片上的"编辑"按钮 +2. 跳转到智能体开发页面进行修改 +3. 保存后更新会同步到智能体空间 + +### 删除智能体 + +1. 点击智能体卡片上的"删除"按钮 +2. 确认删除操作(此操作不可撤销) +3. 删除后智能体将从列表中移除 + +> ⚠️ **注意事项**:删除智能体是不可撤销的操作,请谨慎操作。 + +### 导出智能体 + +1. 点击智能体卡片上的"导出"按钮 +2. 系统会下载智能体配置文件(JSON格式),可用于后续导入或备份 + +### 查看调用关系 + +1. 点击智能体卡片上的"查看关系"按钮 +2. 查看该智能体与工具/其他智能体的协作关系 + +### 跳转到对话 + +1. 点击智能体卡片上的"对话"按钮 +2. 直接跳转到对话页面,使用该智能体进行交互 + +## 🚀 下一步 + +在智能体空间中完成管理后,您可以: + +1. 在 **[开始问答](./start-chat)** 中与智能体进行交互 +2. 继续 **[智能体开发](./agent-development)** 创建更多智能体 +3. 配置 **[记忆管理](./memory-management)** 以提升智能体的记忆能力 + +如果您在配置过程中遇到任何问题,请参考我们的 **[常见问题](../getting-started/faq)** 或在[GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions)中进行提问获取支持。 diff --git a/doc/docs/zh/user-guide/app-configuration.md b/doc/docs/zh/user-guide/app-configuration.md deleted file mode 100644 index cb4906657..000000000 --- a/doc/docs/zh/user-guide/app-configuration.md +++ /dev/null @@ -1,44 +0,0 @@ -# 应用配置 - -在应用配置模块中,您可以配置应用的基本信息,包括应用图标、名称和描述。合理配置有助于提升应用的辨识度和用户体验。 - -- 应用图标和名称会展示在对话页面的左上角,帮助用户快速识别当前应用。 -- 应用的描述在生成智能体时会作为背景信息提供给模型,提升模型对应用场景的理解。 - -## 🖼️ 应用图标配置 - -点击应用图标可进行图标的配置。Nexent 提供了两种图标配置方式: - -- **使用预设图标**:从预设的图标库中选择,可选择图像及背景颜色,适合快速配置。 -- **上传自定义图片**:支持PNG、JPG图片格式,文件大小不超过2MB。 -
- - -
- - -## 📝 应用名称及描述配置 - -### 应用名称 - -- 应用名称会展示在对话页面的左上角,帮助用户快速识别当前应用。 -- 建议使用简洁明了、能体现应用功能的名称,避免使用特殊字符。 - -### 应用描述 - -- 应用描述会作为背景信息提供给模型,帮助理解应用场景。 -- 建议突出应用核心功能,完整流畅且简洁明了。 - -
- -
- -## 🚀 下一步 - -完成应用配置后,建议您继续配置: - -1. **[模型配置](./model-configuration)** - 接入您需要的AI模型 -2. **[知识库配置](./knowledge-base-configuration)** - 创建和管理知识库 -3. **[智能体配置](./agent-configuration)** - 创建和配置智能体 - -如果您在配置过程中遇到任何问题,请参考我们的 **[常见问题](../getting-started/faq)** 或加入我们的 [Discord 社区](https://discord.gg/tb5H3S3wyv) 获取支持。 \ No newline at end of file diff --git a/doc/docs/zh/user-guide/home-page.md b/doc/docs/zh/user-guide/home-page.md new file mode 100644 index 000000000..1d8269432 --- /dev/null +++ b/doc/docs/zh/user-guide/home-page.md @@ -0,0 +1,49 @@ +# 用户指南 + +Nexent是一款面向未来的零代码智能体开发平台,致力于让每个人都能轻松构建和部署专属的AI智能体,无需编程基础,也无需繁琐操作! + +本用户指南将带您全面了解Nexent的强大功能和使用方法,助您快速上手各类操作~ + +通过学习本指南,您将能够高效利用Nexent,把创意变为现实,让智能体为您的工作和生活带来真正的价值与惊喜! + +## 🏠 首页概览 + +Nexent首页展示了平台的核心功能,为您提供快速入口: + +### 主要功能按钮 + +1. **开始问答**:点击后直接进入对话页面,与智能体进行交互 +2. **快速配置**:点击后进入快速配置流程,按照顺序完成模型管理、知识库和智能体开发配置 +3. **智能体空间**:点击后进入智能体空间,查看和管理所有已开发的智能体 + +### 左侧导航栏 + +页面左侧提供了完整的导航栏,包含以下模块: + +- **首页**:返回平台首页 +- **开始问答**:进入对话页面,与智能体进行交互 +- **快速配置**:快速完成模型->知识库->智能体配置 +- **智能体空间**:管理所有已开发的智能体 +- **智能体市场**:浏览和获取智能体(敬请期待) +- **智能体开发**:创建和配置智能体 +- **知识库**:创建和管理知识库 +- **模型管理**:配置应用信息和模型 +- **记忆管理**:配置和管理智能记忆系统 +- **用户管理**:管理系统用户(敬请期待) + +此外,页面右上角还支持切换语言(简体中文或English),您可以根据需要自由切换语言。页面左下角展示了当前Nexent的版本号,有助于帮助您在遇到问题时寻求帮助。 + +## 🚀 快速开始 + +建议您按照以下顺序进行配置: + +1. 首先进行 **[模型管理](./model-management)**,配置应用信息和接入模型 +2. 然后进行 **[知识库](./knowledge-base)**,上传您的文档和资料 +3. 接着进行 **[智能体开发](./agent-development)**,创建您的专属智能体 +4. 一切就绪后,就可以在 **[开始问答](./start-chat)** 中与智能体进行交互了 + +或者,您也可以直接点击首页的"快速配置"按钮,按照引导完成配置。 + +## 💡 获取帮助 + +如果您在配置过程中遇到任何问题,请参考我们的 **[常见问题](../getting-started/faq)** 或在[GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions)中进行提问获取支持。 \ No newline at end of file diff --git a/doc/docs/zh/user-guide/index.md b/doc/docs/zh/user-guide/index.md deleted file mode 100644 index e27a4217c..000000000 --- a/doc/docs/zh/user-guide/index.md +++ /dev/null @@ -1,45 +0,0 @@ -# 用户指南 - -Nexent是一款面向未来的零代码智能体开发平台,致力于让每个人都能轻松构建和部署专属的AI智能体,无需编程基础,也无需繁琐操作! - -本用户指南将带您全面了解Nexent的强大功能和使用方法,助您快速上手各类操作~ - -通过学习本指南,您将能够高效利用Nexent,把创意变为现实,让智能体为您的工作和生活带来真正的价值与惊喜! - -## 🚀 快速开始 - -在Nexent首页,您可以看到两个主要操作按钮: - -1. **开始问答:** 点击后直接进入对话页面,与智能体进行交互 -2. **快速配置:** 点击后进入智能体配置页面,配置您的专属智能体 - -![首页页面](./assets/app/homepage.png) - -此外,页面右上角还支持切换语言(简体中文或English),您可以根据需要自由切换语言。页面左下角展示了当前Nexent的版本号,有助于帮助您在遇到问题时寻求帮助。 - - -## 💡 下一步 - -准备好开始了吗?建议您按照以下顺序进行配置: - -1. 首先进行 **[应用配置](./app-configuration)**,为您的应用配置基本信息 -2. 然后进行 **[模型配置](./model-configuration)**,接入您需要的AI模型 -3. 接着进行 **[知识库配置](./knowledge-base-configuration)**,上传您的文档和资料 -4. 最后进行 **[智能体配置](./agent-configuration)**,创建您的专属智能体 -5. 一切就绪后,就可以在 **[对话页面](./chat-interface)** 中与智能体进行交互了 - - -## 🛠️ 本地工具 - -Nexent提供了丰富的本地工具,帮助智能体完成各种复杂任务,以下将介绍部分有一定操作难度的工具: - -- **[Terminal工具](./local-tools/terminal-tool)**:通过SSH连接远程服务器执行命令,实现服务器管理和系统监控 - - -## 🧠 高级功能 - -除了基础配置外,Nexent还提供了强大的高级功能: - -- **[记忆配置](./memory)**:了解Nexent的四层记忆架构,实现跨对话的知识累积与检索 - -如果您在配置过程中遇到任何问题,请参考我们的 **[常见问题](../getting-started/faq)** 或加入我们的 [Discord 社区](https://discord.gg/tb5H3S3wyv) 获取支持。 \ No newline at end of file diff --git a/doc/docs/zh/user-guide/knowledge-base-configuration.md b/doc/docs/zh/user-guide/knowledge-base.md similarity index 82% rename from doc/docs/zh/user-guide/knowledge-base-configuration.md rename to doc/docs/zh/user-guide/knowledge-base.md index 7ae6f1af5..1302493cd 100644 --- a/doc/docs/zh/user-guide/knowledge-base-configuration.md +++ b/doc/docs/zh/user-guide/knowledge-base.md @@ -1,6 +1,6 @@ -# 知识库配置 +# 知识库 -在知识库配置模块中,您可以创建和管理知识库,上传各种格式的文件,并生成内容总结。知识库是智能体的重要信息来源,让智能体能够访问您的私有数据和文档。 +在知识库模块,您可以创建和管理知识库,上传各种格式的文件,并生成内容总结。知识库是智能体的重要信息来源,让智能体能够访问您的私有数据和文档。 ## 🔧 创建知识库 @@ -18,10 +18,10 @@ ![文件上传](./assets/knowledge/create-knowledge-base.png) - ### 支持的文件格式 Nexent支持多种文件格式,包括: + - **文本**: .txt, .md文件 - **PDF**: .pdf文件 - **Word**: .docx文件 @@ -29,7 +29,6 @@ Nexent支持多种文件格式,包括: - **Excel**: .xlsx文件 - **数据文件**: .csv文件 - ## 📊 知识库总结 建议您为每个知识库配置准确且完整的总结描述,这有助于后续智能体在进行知识库检索时,准确选择合适的知识库进行检索。 @@ -39,7 +38,6 @@ Nexent支持多种文件格式,包括: 3. 您可对生成的内容总结进行编辑修改,使其更准确 4. 最后记得点击“保存”将您的修改保存 - ![内容总结](./assets/knowledge/summary-knowledge-base.png) ## 🔍 知识库管理 @@ -66,7 +64,6 @@ Nexent支持多种文件格式,包括: - 确认删除操作(此操作不可恢复) ![删除知识库](./assets/knowledge/delete-knowledge-base.png) - 2. **删除/新增文件** - 点击知识库名称,在文件列表中点击“删除”按钮,可从知识库中删除文件 - 点击知识库名称,在文件列表下方文件上传区域,可新增文件到知识库中 @@ -75,7 +72,7 @@ Nexent支持多种文件格式,包括: 完成知识库配置后,建议您继续配置: -1. **[智能体配置](./agent-configuration)** - 创建和配置智能体 -2. **[对话页面](./chat-interface)** - 与智能体进行交互 +1. **[智能体开发](./agent-development)** - 创建和配置智能体 +2. **[开始问答](./start-chat)** - 与智能体进行交互 -如果您在知识库配置过程中遇到任何问题,请参考我们的 **[常见问题](../getting-started/faq)** 或加入我们的 [Discord 社区](https://discord.gg/tb5H3S3wyv) 获取支持。 \ No newline at end of file +如果您在知识库配置过程中遇到任何问题,请参考我们的 **[常见问题](../getting-started/faq)** 或在[GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions)中进行提问获取支持。 \ No newline at end of file diff --git a/doc/docs/zh/user-guide/local-tools/index.md b/doc/docs/zh/user-guide/local-tools/index.md index a8dcc6ff2..b8dd50e59 100644 --- a/doc/docs/zh/user-guide/local-tools/index.md +++ b/doc/docs/zh/user-guide/local-tools/index.md @@ -4,29 +4,41 @@ Nexent平台提供了丰富的本地工具,帮助智能体完成各种系统 ## 🛠️ 可用工具 -### Terminal工具 +Nexent预置了一组可以直接复用的本地工具。它们按照能力分为邮件、文件、搜索三大类,Terminal 工具则作为远程 Shell 能力单独提供。下方列出各工具的名称与核心特性,方便在 Agent 中快速定位所需能力。 -**[Terminal工具](./terminal-tool)** 是Nexent平台的核心本地工具之一,允许智能体通过SSH连接远程服务器执行命令。 +### 📧 邮件工具(Email) -**主要功能**: -- 远程服务器管理 -- 系统监控和状态检查 -- 文件操作和目录管理 -- 服务启停和配置管理 -- 日志查看和分析 +- **get_email**:通过 IMAP 协议拉取邮箱内容,支持按天数限定时间范围、按发件人精确过滤,并可限制一次返回的邮件数量。工具会自动处理多语言主题与正文解码,结果包含主题、时间、发件人、正文摘要等字段,便于 Agent 做进一步分析。 +- **send_email**:基于 SMTP 发送 HTML 格式邮件,支持同时指定多个收件人、抄送(CC)、密送(BCC),并可自定义发件人展示名。所有连接都走 SSL/TLS,发送结果会提示投递状态和主题,方便记录。 -**适用场景**: -- 服务器运维自动化 -- 系统监控和告警 -- 批量文件处理 -- 部署和发布管理 -- 故障排查和诊断 +### 📂 文件工具(File) + +- **create_directory**:在指定相对路径下创建多级目录,自动跳过已存在的层级,并返回创建结果与最终绝对路径。 +- **create_file**:新建文件并写入内容,如果父级目录不存在会自动创建;支持自定义编码(默认 UTF-8)和空内容文件。 +- **read_file**:读取文本文件内容并返回文件大小、行数、编码等元信息,在文件过大时会提醒(10MB 安全阈值)。 +- **list_directory**:以树状结构列出目录内容,可控制最大递归深度、是否展示隐藏文件和文件大小,输出同时包含可视化字符串和结构化 JSON,适合用于展示项目结构。 +- **move_item**:在工作空间内移动文件或文件夹,自动创建目标目录,避免目标已存在导致覆盖,并在结果中给出移动的项目数量和大小。 +- **delete_file**:删除单个文件,包含权限与存在性校验,失败时会给出明确错误信息。 +- **delete_directory**:递归删除目录及其内容,带有目录存在性、权限和安全校验,删除后返回被删除的相对路径。 + +> 所有文件路径都需要是工作空间内的相对路径(默认 `/mnt/nexent`),工具会自动校验防止越界访问。 + +### 🔍 搜索工具(Search) + +- **knowledge_base_search**:对接本地知识库索引,支持 `hybrid`、`accurate`、`semantic` 三种检索模式,可按索引名称筛选。返回结果附带来源、得分、引用序号,适合回答私有文档或行业资料问题。 +- **exa_search**:调用 EXA API 进行实时全网搜索,可配置返回条数并支持附带图片链接(默认在服务端进一步筛选)。需要在工具配置中填写 EXA API Key,前往 [exa.ai](https://exa.ai/) 注册即可免费获取。 +- **tavily_search**:基于 Tavily API 的网页搜索,擅长新闻、实时资讯查询,同时返回文本结果和相关图片 URL,同样支持可选的图片过滤能力,可在 [tavily.com](https://www.tavily.com/) 免费申请 API Key。 +- **linkup_search**:使用 Linkup API 获取文本与图片结果,除了普通网页内容,还能返回纯图片结果,适合需要图文混合参考的场景。访问 [linkup.so](https://www.linkup.so/) 注册获取免费的 API Key。 + +### 🖥️ Terminal工具 + +**Terminal工具** 是 Nexent 平台的核心本地工具之一,提供持久化 SSH 会话能力,可在 Agent 中执行远程命令、进行系统巡检、读取日志或部署服务。详细的部署、参数和安全指引请查看专门的 [Terminal 使用手册](./terminal-tool.md)。 ## 🔧 工具配置 -所有本地工具都需要在智能体配置中进行设置: +所有本地工具都需要在智能体开发中进行设置: -1. 进入 **[智能体配置](../agent-configuration)** 页面 +1. 进入 **[智能体开发](../agent-development)** 页面 2. 选择要配置的智能体 3. 在"选择Agent的工具"页签中找到相应的本地工具 4. 点击配置按钮,填写必要的连接参数 @@ -43,11 +55,4 @@ Nexent平台提供了丰富的本地工具,帮助智能体完成各种系统 - **命令限制**:在生产环境中配置命令白名单 - **审计日志**:启用详细的操作日志记录 -## 🚀 下一步 - -配置好本地工具后,您可以: - -1. **[智能体配置](../agent-configuration)** - 将工具添加到智能体中 -2. **[对话页面](../chat-interface)** - 通过智能体使用工具执行任务 - -如果您在使用本地工具过程中遇到任何问题,请参考相应工具的详细文档或加入我们的 [Discord 社区](https://discord.gg/tb5H3S3wyv) 获取支持。 +如果您使用本地工具过程中遇到任何问题,请在[GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions)中进行提问获取支持。 \ No newline at end of file diff --git a/doc/docs/zh/user-guide/local-tools/terminal-tool.md b/doc/docs/zh/user-guide/local-tools/terminal-tool.md index e1442bb57..4aab60e28 100644 --- a/doc/docs/zh/user-guide/local-tools/terminal-tool.md +++ b/doc/docs/zh/user-guide/local-tools/terminal-tool.md @@ -207,7 +207,7 @@ Terminal工具提供以下核心功能: ### 在Nexent中配置Terminal工具 1. 登录Nexent平台 -2. 进入 **[智能体配置](../agent-configuration)** 页面 +2. 进入 **[智能体开发](../agent-development)** 页面 3. 选择要配置的智能体 4. 在"选择Agent的工具"页签中找到"Terminal工具" @@ -421,11 +421,4 @@ maxretry = 3 bantime = 3600 ``` -## 🚀 下一步 - -完成Terminal工具配置后,您可以: - -1. **[智能体配置](../agent-configuration)** - 将Terminal工具添加到智能体中 -2. **[对话页面](../chat-interface)** - 通过智能体使用Terminal工具执行服务器管理任务 - -如果您在配置过程中遇到任何问题,请参考我们的 **[常见问题](../../getting-started/faq)** 或加入我们的 [Discord 社区](https://discord.gg/tb5H3S3wyv) 获取支持。 +如果您使用过程中遇到任何问题,请在[GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions)中进行提问获取支持。 diff --git a/doc/docs/zh/user-guide/memory-management.md b/doc/docs/zh/user-guide/memory-management.md new file mode 100644 index 000000000..f9ce88559 --- /dev/null +++ b/doc/docs/zh/user-guide/memory-management.md @@ -0,0 +1,160 @@ +# 记忆管理 + +Nexent的智能记忆系统为智能体提供持久化的上下文感知能力,通过多层级记忆管理机制,实现跨对话会话的知识累积与检索,显著提升人机交互的连贯性和个性化程度。 + +## 🎯 什么是智能记忆系统 + +智能记忆系统让智能体能够"记住"重要信息,并在后续对话中自动使用这些记忆,为您提供更连贯、更个性化的服务体验。 + +### 核心优势 + +- **跨对话记忆**:智能体可以记住之前对话中的重要信息 +- **自动检索**:智能体会自动检索相关记忆,无需您重复说明 +- **个性化服务**:根据您的偏好和习惯提供个性化服务 +- **知识积累**:智能体的知识会随着使用不断积累和优化 + +## ⚙️ 系统配置 + +### 访问记忆管理 + +1. 在左侧导航栏中点击"记忆管理" +2. 进入记忆管理页面进行配置 + +### 基础配置 + +在记忆管理页面的"系统配置"模块中,您可以进行以下设置: + +| 配置项 | 选项 | 默认值 | 说明 | +|--------|------|--------|------| +| 记忆服务状态 | 启用/禁用 | 启用 | 控制整个记忆系统的运行状态 | +| Agent 记忆共享策略 | 总是共享/每次询问我/禁止共享 | 总是共享 | 定义Agent间共享记忆生成是否需要用户授权同意 | + +
+ 记忆系统配置 +
+ +### 配置说明 + +- **记忆服务状态**:启用后,智能体将能够使用记忆功能;禁用后,所有记忆功能将暂停。 +- **Agent 记忆共享策略**: + - **总是共享**:智能体之间自动共享记忆,无需确认 + - **每次询问我**:智能体间共享记忆前会询问您的意见 + - **禁止共享**:智能体之间不共享记忆,保持独立 + +## 📚 记忆层级 + +Nexent采用四层记忆存储架构,不同层级的记忆有不同的作用域和用途: + +### 租户级记忆 + +- **作用域**:组织全局,所有用户和智能体共享 +- **存储内容**:企业级标准操作流程、合规政策、组织架构、事实信息 +- **适用场景**:企业知识管理、标准化流程执行、合规性检查 +- **管理权限**:租户管理员 + +### 智能体级记忆 + +- **作用域**:特定智能体,该智能体的所有用户共享 +- **存储内容**:专业领域知识、技能模板、历史对话摘要、学习积累 +- **适用场景**:专业技能积累、领域知识沉淀、经验学习 +- **管理权限**:租户管理员 + +### 用户级记忆 + +- **作用域**:特定用户账户,仅该用户可见 +- **存储内容**:个人偏好设置、使用习惯、常用指令模板、个人信息 +- **适用场景**:个性化服务、用户体验优化、偏好管理 +- **管理权限**:用户自己 + +### 用户-智能体级记忆 + +- **作用域**:特定用户账户下的特定智能体,最私密和个性化 +- **存储内容**:协作历史、个性化事实信息、特定任务上下文、关系模型 +- **适用场景**:深度协作场景、个性化调优、任务连续性维护 +- **管理权限**:用户自己 + +### 记忆优先级 + +当智能体需要检索记忆时,会按照以下优先级顺序(由高到低): + +1. **租户级** → 基础事实和通用知识 +2. **用户-智能体级** → 最具体的上下文信息 +3. **用户级** → 个人偏好和习惯 +4. **智能体级** → 专业知识和技能 + +## 🤖 自动化记忆管理 + +智能记忆系统支持自动化管理,让您无需手动操作即可享受记忆功能: + +### 智能提取 + +- 系统会自动识别对话中的关键事实信息 +- 自动生成记忆条目并存储到合适的层级 +- 无需您手动添加,系统会智能判断重要性 + +### 自动上下文嵌入 + +- 智能体会自动检索相关性最高的记忆条目 +- 将记忆隐式嵌入到对话上下文中 +- 让智能体能够基于历史记忆提供更准确的回答 + +### 增量更新 + +- 支持记忆内容的渐进式更新和补充 +- 自动清理过时或不再相关的记忆条目 +- 保持记忆库的时效性和准确性 + +## ✋ 手动记忆操作 + +除了自动化管理,您也可以手动管理记忆,确保重要信息被正确记录: + +### 添加记忆 + +1. 在记忆管理页面,选择要添加记忆的层级和智能体 +2. 点击绿色的"对话加号"按钮 +3. 输入要记录的内容(最多500字符) +4. 点击对钩确认添加 + +
+ 添加记忆 +
+ +### 删除记忆 + +您可以通过以下方式删除记忆: + +- **删除分组记忆**:点击红色叉号按钮,在确认弹框中点击确认,可删除某个智能体分组下所有的记忆条目 +- **删除单条记忆**:点击红色橡皮按钮,可删除特定的一条记忆条目 + +
+ 删除记忆 +
+ +## 💡 使用建议 + +### 记忆内容原则 + +1. **原子性原则**:每条记忆应包含**简洁**、**单一**、**明确**的事实信息 + - ✅ 好:用户喜欢使用深色主题 + - ❌ 不好:用户喜欢使用深色主题,并且经常在晚上工作,还喜欢喝咖啡 + +2. **时效性管理**:定期清理过时或不再相关的记忆条目,保持记忆库的时效性和准确性 + +3. **隐私保护**:敏感信息应尽量避免在租户层级或智能体层级进行共享,建议使用用户级或用户-智能体级记忆 + +### 最佳实践 + +- **合理选择层级**:根据信息的共享需求选择合适的记忆层级 +- **定期检查**:定期查看和清理记忆,确保信息的准确性 +- **利用自动化**:让系统自动管理常规记忆,手动管理重要信息 +- **保护隐私**:个人敏感信息使用用户级记忆,避免共享 + +## 🚀 下一步 + +配置好记忆管理后,您可以: + +1. 在 **[开始问答](./start-chat)** 中体验智能体的记忆能力 +2. 在 **[智能体空间](./agent-space)** 中管理您的智能体 +3. 继续 **[智能体开发](./agent-development)** 创建更多智能体 + +如果您在配置过程中遇到任何问题,请参考我们的 **[常见问题](../getting-started/faq)** 或在[GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions)中进行提问获取支持。 diff --git a/doc/docs/zh/user-guide/memory.md b/doc/docs/zh/user-guide/memory.md deleted file mode 100644 index d4c2cb5f6..000000000 --- a/doc/docs/zh/user-guide/memory.md +++ /dev/null @@ -1,103 +0,0 @@ -# 🧠Nexent 智能记忆系统技术规格说明 - -## 1. 系统架构概述 - -Nexent 智能记忆系统基于先进的记忆存储架构,为智能体提供持久化的上下文感知能力。该系统通过多层级记忆管理机制,实现了跨对话会话的知识累积与检索,显著提升了人机交互的连贯性和个性化程度。 - -### 核心技术特性 -- **分层记忆架构**:基于 mem0 框架构建的四级记忆存储体系 -- **自适应记忆管理**:支持自动化和手动化的记忆操作模式 -- **跨会话持久化**:确保知识和上下文在多次对话中的连续性 -- **细粒度权限控制**:提供灵活的记忆共享策略配置 - - -## 2. 配置与初始化 - -### 2.1 系统激活 -1. 访问记忆管理界面:点击对话界面右上角的**记忆管理图标** -2. 进入**系统配置**模块进行初始化设置 - -### 2.2 核心配置参数 - -| 配置项 | 选项 | 默认值 | 说明 | -|--------|-----------------|--------|--------------------------| -| 记忆服务状态 | 启用/禁用 | 启用 | 控制整个记忆系统的运行状态 | -| Agent 记忆共享策略 | 总是共享/每次询问我/禁止共享 | 总是共享 | 定义Agent间共享记忆生成是否需要用户授权同意 | - -
- 选择智能体 -
- - -## 3. 分层记忆架构 - -Nexent 采用基于 **mem0** 的四层记忆存储架构,通过不同的作用域和生命周期管理,实现精确的记忆分类与检索: - -### 3.1 架构层级详解 - -#### 租户级记忆(Tenant Level Memory) - - **作用域**: 组织全局 - - **存储内容**: 企业级标准操作流程、合规政策、组织架构、事实信息 - - **生命周期**: 长期存储 - - **配置角色**: 租户管理员 - - **典型应用**: 企业知识管理、标准化流程执行、合规性检查 - -#### 智能体级记忆(Agent Level Memory) - - **作用域**: 特定智能体 - - **存储内容**: 专业领域知识、技能模板、历史对话摘要、学习积累 - - **生命周期**: 与智能体生命周期一致 - - **配置角色**: 租户管理员 - - **典型应用**: 专业技能积累、领域知识沉淀、经验学习 - -#### 用户级记忆(User Level Memory) - - **作用域**: 特定用户账户 - - **存储内容**: 个人偏好设置、使用习惯、常用指令模板、个人信息 - - **生命周期**: 长期存储 - - **配置角色**: 全体用户 - - **典型应用**: 个性化服务、用户体验优化、偏好管理 - -#### 用户-智能体级记忆(User-Agent Level Memory) - - **作用域**: 特定用户账户下的特定智能体 - - **存储内容**: 协作历史、个性化事实信息、特定任务上下文、关系模型 - - **生命周期**: 与智能体生命周期一致 - - **配置角色**: 全体用户 - - **典型应用**: 深度协作场景、个性化调优、任务连续性维护 - -### 3.2 记忆优先级与检索策略 - -记忆检索遵循以下优先级顺序(由高到低): -1. **租户级** → 基础事实 -2. **用户-智能体级** → 最具体的上下文信息 -2. **用户级** → 个人偏好和习惯 -3. **智能体级** → 专业知识和技能 - - -## 4. 操作模式与功能接口 - -### 4.1 自动化记忆管理 -- **智能提取**:自动识别对话中的关键事实信息并生成记忆条目 -- **自动上下文嵌入**:智能体将自动检索相关性最高的记忆条目,隐式嵌入对话上下文中 -- **增量更新**:支持记忆内容的渐进式更新、补充和自动清理 - -### 4.2 手动记忆操作 - -#### 添加记忆 -- 点击绿色的“对话加号”按钮,输入文本,再点击对钩可添加一条记忆条目(最多500字符) - -
- 选择智能体 -
- -#### 删除记忆 -- 点击红色叉号按钮,在跳出的二次确认弹框中点击确认按钮,可删除某个Agent分组下所有的记忆条目 -- 点击红色橡皮按钮,可删除特定的一条记忆条目 - -
- Select Agent -
- -### 4.3 记忆管理最佳实践 - -1. **原子性原则**:每条记忆应包含 **简洁**、**单一**、**明确** 的事实信息 -2. **时效性管理**:定期清理过时或不再相关的记忆条目,保持记忆库的时效性和准确性 -3. **隐私保护**:敏感信息应尽量避免在租户层级或智能体层级进行共享 diff --git a/doc/docs/zh/user-guide/model-configuration.md b/doc/docs/zh/user-guide/model-management.md similarity index 78% rename from doc/docs/zh/user-guide/model-configuration.md rename to doc/docs/zh/user-guide/model-management.md index 6725695a8..b13d67c83 100644 --- a/doc/docs/zh/user-guide/model-configuration.md +++ b/doc/docs/zh/user-guide/model-management.md @@ -1,6 +1,43 @@ -# 模型配置 +# 模型管理 -在模型配置模块中,您可以接入各类AI模型,包括大语言模型、向量化模型和视觉语言模型。Nexent支持多种模型提供商,帮助您根据实际需求灵活选择最适合的模型。 +在模型管理模块中,您可以配置应用的基本信息,并接入各类AI模型,包括大语言模型、向量化模型和视觉语言模型。Nexent支持多种模型提供商,帮助您根据实际需求灵活选择最适合的模型。 + +## 🖼️ 应用配置 + +应用配置是模型管理的第一步,您可以配置应用的基本信息,包括应用图标、名称和描述。合理配置有助于提升应用的辨识度和用户体验。 + +- 应用图标和名称会展示在对话页面的左上角,帮助用户快速识别当前应用。 +- 应用的描述在生成智能体时会作为背景信息提供给模型,提升模型对应用场景的理解。 + +### 应用图标配置 + +点击应用图标可进行图标的配置。Nexent 提供了两种图标配置方式: + +- **使用预设图标**:从预设的图标库中选择,可选择图像及背景颜色,适合快速配置。 +- **上传自定义图片**:支持PNG、JPG图片格式,文件大小不超过2MB。 + +
+ + +
+ +### 应用名称及描述配置 + +#### 应用名称 + +- 应用名称会展示在对话页面的左上角,帮助用户快速识别当前应用。 +- 建议使用简洁明了、能体现应用功能的名称,避免使用特殊字符。 + +#### 应用描述 + +- 应用描述会作为背景信息提供给模型,帮助理解应用场景。 +- 建议突出应用核心功能,完整流畅且简洁明了。 + +
+ +
+ +## 🤖 模型配置 ## 🔄 同步ModelEngine模型 @@ -9,7 +46,9 @@ Nexent即将支持与ModelEngine平台的无缝对接,届时可自动同步并 ## 🛠️ 添加自定义模型 ### 添加单个模型 + #### 添加单个模型步骤 + 1. **添加自定义模型** - 点击"添加自定义模型"按钮,进入添加模型弹窗。 2. **选择模型类型** @@ -87,20 +126,27 @@ Nexent即将支持与ModelEngine平台的无缝对接,届时可自动同步并 添加模型后,您需要配置系统基础模型,该模型将用于标题生成、实时文件读取等基础功能。在智能体运行时,您可以为每个智能体指定特定的运行模型。 ### 基础模型配置 + 系统基础模型用于处理平台的核心功能,包括: + - 标题生成 - 实时文件读取 - 基础文本处理 **配置步骤**: + - 点击基础模型下拉框,从已添加的大语言模型中选择一个作为系统基础模型。 ### 向量化模型 + 向量化模型主要用于知识库的文本、图片等数据的向量化处理,是实现高效检索和语义理解的基础。配置合适的向量化模型,可以显著提升知识库的搜索准确率和多模态数据的处理能力。 + - 点击向量模型下拉框,从已添加的向量化模型中选择一个。 ### 多模态模型 + 多模态模型结合了视觉和语言能力,能够处理包含文本、图片等多种信息的复杂场景。例如,在对话页面上传图片文件时,系统会自动调用多模态模型进行内容解析和智能对话。 + - 点击视觉语言模型下拉框,从已添加的视觉语言模型中选择一个。
@@ -114,15 +160,18 @@ Nexent即将支持与ModelEngine平台的无缝对接,届时可自动同步并 定期检查模型连通性是确保系统稳定运行的重要环节。通过连通性检查功能,您可以及时发现和解决模型连接问题,保证服务的连续性和可靠性。 **检查流程**: + - 点击"检查模型连通性"按钮 - 系统将自动测试所有已配置的系统模型的连接状态 **状态指示**: + - 🔵 **蓝色圆点**:表示正在检测中,请耐心等待 - 🔴 **红色圆点**:表示连接失败,需要检查配置或网络状态 - 🟢 **绿色圆点**:表示连接正常,模型可以正常使用 **故障排查建议**: + - 检查网络连接是否稳定 - 验证API密钥是否有效且未过期 - 确认模型服务商的服务状态 @@ -131,7 +180,9 @@ Nexent即将支持与ModelEngine平台的无缝对接,届时可自动同步并 ## 🤖 支持的模型提供商 ### 🤖 大语言模型LLM + Nexent 支持任何 **遵循OpenAI API规范** 的大语言模型供应商,包括: + - [硅基流动](https://siliconflow.cn/) - [阿里云百炼](https://bailian.console.aliyun.com/) - [小马算力](https://www.tokenpony.cn/) @@ -141,6 +192,7 @@ Nexent 支持任何 **遵循OpenAI API规范** 的大语言模型供应商,包 - [月之暗面](https://platform.moonshot.cn/) 可参考以下步骤进行模型接入: + 1. 访问模型供应商官网,注册账户; 2. 创建并复制API Key; 3. 在文档中查看API端点(即模型URL,一般以`/v1`为结尾); @@ -157,11 +209,13 @@ Nexent 支持任何 **遵循OpenAI API规范** 的大语言模型供应商,包 ### 🎤 语音模型 目前仅支持火山引擎语音,且需要在`.env`中进行配置 + - **网站**: [volcengine.com/product/voice-tech](https://www.volcengine.com/product/voice-tech) - **免费额度**: 个人使用可用 - **特色**: 高质量中英文语音合成 **开始使用**: + 1. 注册火山引擎账户 2. 访问语音技术服务 3. 创建应用并获取 API Key @@ -170,6 +224,7 @@ Nexent 支持任何 **遵循OpenAI API规范** 的大语言模型供应商,包 ## 💡 需要帮助 如果您在模型提供商方面遇到问题: + 1. 查看提供商特定文档 2. 验证 API 密钥权限和配额 3. 使用提供商官方示例进行测试 @@ -177,8 +232,9 @@ Nexent 支持任何 **遵循OpenAI API规范** 的大语言模型供应商,包 ## 🚀 下一步 -完成模型配置后,建议您点击"下一步"按钮,继续配置: -1. **[知识库配置](./knowledge-base-configuration)** - 创建和管理知识库。 -2. **[智能体配置](./agent-configuration)** - 创建和配置智能体。 +完成模型管理配置后,建议您继续配置: + +1. **[知识库](./knowledge-base)** - 创建和管理知识库。 +2. **[智能体开发](./agent-development)** - 创建和配置智能体。 -如在模型配置过程中遇到任何问题,请参考我们的 **[常见问题](../getting-started/faq)** ,或加入我们的 [Discord社区](https://discord.gg/tb5H3S3wyv) 获取支持。 \ No newline at end of file +如果您在模型配置过程中遇到任何问题,请参考我们的 **[常见问题](../getting-started/faq)** 或在[GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions)中进行提问获取支持。 \ No newline at end of file diff --git a/doc/docs/zh/user-guide/quick-setup.md b/doc/docs/zh/user-guide/quick-setup.md new file mode 100644 index 000000000..dafd2afe5 --- /dev/null +++ b/doc/docs/zh/user-guide/quick-setup.md @@ -0,0 +1,53 @@ +# 快速配置 + +快速配置功能为您提供了一条便捷的配置路径,按照推荐的顺序引导您完成系统的基础配置。快速配置包含三个主要步骤:模型管理、知识库配置和智能体开发。 + +## 📋 配置流程 + +快速配置按照以下顺序组织,帮助您快速完成系统设置: + +### 第一步:模型管理 + +配置应用基本信息和接入AI模型: + +- **应用配置**:设置应用图标、名称和描述 +- **模型配置**:接入大语言模型、向量化模型和视觉语言模型 + +详细内容请参考:[模型管理](./model-management) + +### 第二步:知识库配置 + +创建知识库并上传文档: + +- **创建知识库**:为智能体准备知识来源 +- **上传文件**:支持多种文件格式 +- **生成总结**:为知识库生成内容总结 + +详细内容请参考:[知识库](./knowledge-base) + +### 第三步:智能体开发 + +创建和配置智能体: + +- **创建智能体**:创建新的智能体或导入已有智能体 +- **配置能力**:设置协作智能体和工具 +- **描述业务逻辑**:定义智能体的工作方式 + +详细内容请参考:[智能体开发](./agent-development) + +## 🎯 使用建议 + +1. **按顺序配置**:建议按照快速配置的顺序进行,确保每个步骤都正确完成 +2. **保存配置**:每个步骤完成后记得保存配置 +3. **启用知识库**:若要启用本地知识检索能力,需在智能体配置中选中knowledge_base_serch工具 +4. **测试验证**:配置完成后,建议在对话页面测试智能体的功能 + +## 🚀 下一步 + +完成快速配置后,您可以: + +1. 进入 **[智能体空间](./agent-space)** 查看和管理所有智能体 +2. 在 **[开始问答](./start-chat)** 中与智能体进行交互 +3. 配置 **[记忆管理](./memory-management)** 以提升智能体的记忆能力 + +如果您在配置过程中遇到任何问题,请参考我们的 **[常见问题](../getting-started/faq)** 或在[GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions)中进行提问获取支持。 diff --git a/doc/docs/zh/user-guide/chat-interface.md b/doc/docs/zh/user-guide/start-chat.md similarity index 90% rename from doc/docs/zh/user-guide/chat-interface.md rename to doc/docs/zh/user-guide/start-chat.md index b93d6581b..262161d02 100644 --- a/doc/docs/zh/user-guide/chat-interface.md +++ b/doc/docs/zh/user-guide/start-chat.md @@ -1,8 +1,8 @@ -# 对话页面 +# 开始问答 -对话页面是您与智能体进行交互的核心界面。在这里,您可以与不同的智能体进行对话,上传文件,使用语音输入,并管理您的对话历史。 +开始问答页面是您与智能体进行交互的核心界面。在这里,您可以与不同的智能体进行对话,上传文件,使用语音输入,并管理您的对话历史。 -## 🤖 开始对话 +## 🤖 开始问答 ### 选择智能体 @@ -28,6 +28,7 @@ 1. **输入您的问题** - 在对话框底部的输入框中输入您的问题或指令 + - Shift+Enter键可换行 2. **发送消息** - 点击输入框右侧的发送按钮 @@ -44,7 +45,7 @@ ### 使用语音输入 -Nexent支持语音输入功能,让您可以通过说话与智能体交互: +Nexent支持语音输入功能,让您可以通过语音与智能体交互。前提是您已经配置了语音模型,配置教程可参考[模型管理](./model-management)。 1. **启用语音输入** - 在输入框右下角找到麦克风图标 @@ -86,7 +87,6 @@ Nexent支持语音输入功能,让您可以通过说话与智能体交互: - 智能体可以分析、总结或处理文件中的信息 - 支持多文件同时上传和处理 - > ⚠️ **注意事项**:上传的文件大小有限制,建议单个文件不超过10MB。对于大型文档,建议分批上传。 ## 📚 管理您的对话历史 @@ -125,10 +125,14 @@ Nexent支持语音输入功能,让您可以通过说话与智能体交互: 对话编辑
-### 访问配置页面 +### 访问其他功能 -- 在左侧边栏左下角找到⚙️设置图标 -- 点击图标进入配置页面,可以修改智能体配置和系统设置 +你可通过左侧导航栏可以快速访问其他功能模块 +- **智能体空间**:管理所有已开发的智能体 +- **智能体开发**:创建和配置新的智能体 +- **模型管理**:配置AI模型和应用信息 +- **知识库**:管理知识库和文档 +- **记忆管理**:配置和管理智能记忆系统 ## 🔍 查看知识引用来源 diff --git a/doc/docs/zh/user-guide/user-management.md b/doc/docs/zh/user-guide/user-management.md new file mode 100644 index 000000000..24b45af00 --- /dev/null +++ b/doc/docs/zh/user-guide/user-management.md @@ -0,0 +1,39 @@ +# 用户管理 + +用户管理是Nexent平台即将推出的功能模块,将为您提供完整的用户管理能力。 + +## 🎯 功能预告 + +用户管理将提供以下功能: + +- **用户列表**:查看和管理所有系统用户 +- **用户权限**:配置用户的访问权限和功能权限 +- **用户角色**:管理用户角色和权限组 +- **用户统计**:查看用户使用情况和统计数据 + +## ⏳ 敬请期待 + +用户管理功能正在开发中,敬请期待! + +我们正在努力为您打造一个完善、灵活的用户管理体系,让您能够: + +- 精细化管理用户权限 +- 灵活配置用户角色 +- 全面了解用户使用情况 + +## 📢 获取最新动态 + +想要第一时间了解用户管理功能的上线信息? + +- 加入我们的 [Discord 社区](https://discord.gg/tb5H3S3wyv) 获取最新动态 +- 关注项目更新,了解开发进展 + +## 🚀 相关功能 + +在等待用户管理功能上线期间,您可以: + +1. 在 **[智能体空间](./agent-space)** 中管理您的智能体 +2. 通过 **[模型管理](./model-management)** 配置系统模型 +3. 在 **[开始问答](./start-chat)** 中体验平台功能 + +如果您有任何建议或想法,欢迎通过 [Discord 社区](https://discord.gg/tb5H3S3wyv) 与我们分享! From 694dc17a8e7933beb09881289d90551b7b4e5061 Mon Sep 17 00:00:00 2001 From: Lin-Wang-QHU <1016736136@qq.com> Date: Fri, 21 Nov 2025 20:49:38 +0800 Subject: [PATCH 07/83] Update opensource-memorial-wall.md --- doc/docs/zh/opensource-memorial-wall.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/docs/zh/opensource-memorial-wall.md b/doc/docs/zh/opensource-memorial-wall.md index 6592039ff..765afec0e 100644 --- a/doc/docs/zh/opensource-memorial-wall.md +++ b/doc/docs/zh/opensource-memorial-wall.md @@ -444,3 +444,7 @@ Nexent功能如此之强大,给我很多帮助,感谢开发者!厉害 ::: info user - 2025-11-20 学习ai - agent非常好的项目,后面会持续输出贡献! ::: + +::: info 开源小白 - 2025-11-19 +感谢 Nexent 让我踏上了开源之旅!这个项目的文档真的很棒,帮助我快速上手。 +::: From 74ab821287fa9044d39fafdb4def59216e488de8 Mon Sep 17 00:00:00 2001 From: Alfred Whitman <103871486+AgainsTurb@users.noreply.github.com> Date: Sat, 22 Nov 2025 13:26:18 +0800 Subject: [PATCH 08/83] Add user testimonials to memorial wall documentation --- doc/docs/zh/opensource-memorial-wall.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/docs/zh/opensource-memorial-wall.md b/doc/docs/zh/opensource-memorial-wall.md index 6592039ff..840f95864 100644 --- a/doc/docs/zh/opensource-memorial-wall.md +++ b/doc/docs/zh/opensource-memorial-wall.md @@ -444,3 +444,7 @@ Nexent功能如此之强大,给我很多帮助,感谢开发者!厉害 ::: info user - 2025-11-20 学习ai - agent非常好的项目,后面会持续输出贡献! ::: + +::: info user - 2025-11-22 +感谢nexent这个开源项目 +::: From 93d5b34267e5822b5c706566b0ebb6665852116d Mon Sep 17 00:00:00 2001 From: eric Liu Date: Sat, 22 Nov 2025 14:36:28 +0800 Subject: [PATCH 09/83] (Disable_generate_prompt_textbox_while_generating --- frontend/app/[locale]/agents/components/PromptManager.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/app/[locale]/agents/components/PromptManager.tsx b/frontend/app/[locale]/agents/components/PromptManager.tsx index 1677d01e4..a9f57dc14 100644 --- a/frontend/app/[locale]/agents/components/PromptManager.tsx +++ b/frontend/app/[locale]/agents/components/PromptManager.tsx @@ -615,7 +615,7 @@ export default function PromptManager({ overflowY: "auto", }} autoSize={false} - disabled={!isEditingMode} + disabled={!isEditingMode || isGeneratingAgent} /> From 3cfdec05b909e019566469d621e902f2bccb35bb Mon Sep 17 00:00:00 2001 From: fujin6399-bit Date: Sun, 23 Nov 2025 11:10:48 +0800 Subject: [PATCH 10/83] Update opensource-memorial-wall.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit : tip xiaofu-2025-11-23 xiaofu到此一游,感谢 Nexent 让我踏上了开源之旅! --- doc/docs/zh/opensource-memorial-wall.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/doc/docs/zh/opensource-memorial-wall.md b/doc/docs/zh/opensource-memorial-wall.md index 6592039ff..7b517414c 100644 --- a/doc/docs/zh/opensource-memorial-wall.md +++ b/doc/docs/zh/opensource-memorial-wall.md @@ -15,9 +15,10 @@ 每条消息应包含您的姓名/昵称和日期。 请保持消息的礼貌和尊重,符合我们的行为准则。 --> -::: china-king-hs - 2025-11-20 +:::china-king-hs - 2025-11-20 希望能正常使用nexent -::: +::: tip xiaofu-2025-11-23 +xiaofu到此一游,感谢 Nexent 让我踏上了开源之旅! ::: info happyzhang - 2025-11-13 也许我们正见证着未来的“后起之秀”😀 @@ -434,13 +435,15 @@ Nexent功能如此之强大,给我很多帮助,感谢开发者!厉害 ::: ::: tip qinjavermo - 2025-11-19 + 感谢 Nexent 让我踏上了开源之旅!给我一个机会制作智能体 ::: ::: info chengyudan - 2025-10-20 感谢 Nexent 让我踏上了开源之旅! -::: +:: ::: info user - 2025-11-20 学习ai - agent非常好的项目,后面会持续输出贡献! -::: + + From 7d74f3eb6dba5b33ec79f9f7e7686188b1e5dc49 Mon Sep 17 00:00:00 2001 From: DUTBenjamin <165814040+DUTBenjamin@users.noreply.github.com> Date: Sun, 23 Nov 2025 14:34:06 +0800 Subject: [PATCH 11/83] Add messages from DUTBenjamin --- doc/docs/zh/opensource-memorial-wall.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/docs/zh/opensource-memorial-wall.md b/doc/docs/zh/opensource-memorial-wall.md index 6592039ff..17703186a 100644 --- a/doc/docs/zh/opensource-memorial-wall.md +++ b/doc/docs/zh/opensource-memorial-wall.md @@ -15,6 +15,10 @@ 每条消息应包含您的姓名/昵称和日期。 请保持消息的礼貌和尊重,符合我们的行为准则。 --> +::: info DUTBenjamin - 2025-11-23 +来参加华为ICT大赛的,正好借着这个机会多捣鼓捣鼓,学到更多东西,加油! +::: + ::: china-king-hs - 2025-11-20 希望能正常使用nexent ::: From 820578d273b36664872407608e2d34e415f8805f Mon Sep 17 00:00:00 2001 From: dean-stack Date: Sun, 23 Nov 2025 16:38:48 +0800 Subject: [PATCH 12/83] Add user testimonials to memorial wall Added real reviews --- doc/docs/zh/opensource-memorial-wall.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/docs/zh/opensource-memorial-wall.md b/doc/docs/zh/opensource-memorial-wall.md index 6592039ff..63aeb57f2 100644 --- a/doc/docs/zh/opensource-memorial-wall.md +++ b/doc/docs/zh/opensource-memorial-wall.md @@ -444,3 +444,7 @@ Nexent功能如此之强大,给我很多帮助,感谢开发者!厉害 ::: info user - 2025-11-20 学习ai - agent非常好的项目,后面会持续输出贡献! ::: + +::: dean-stock - 2025-11-23 +感谢nexent让我第一次接触到了智能体,让我从使用到创作智能体的转变。 +::: From af377ed88ca30ca842c546fddd36eada6b42df69 Mon Sep 17 00:00:00 2001 From: adasibi Date: Sun, 23 Nov 2025 17:32:19 +0800 Subject: [PATCH 13/83] Add user messages to memorial wall Added user messages expressing appreciation for the project and its functionality. --- doc/docs/zh/opensource-memorial-wall.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/docs/zh/opensource-memorial-wall.md b/doc/docs/zh/opensource-memorial-wall.md index 6592039ff..35a61900f 100644 --- a/doc/docs/zh/opensource-memorial-wall.md +++ b/doc/docs/zh/opensource-memorial-wall.md @@ -15,6 +15,8 @@ 每条消息应包含您的姓名/昵称和日期。 请保持消息的礼貌和尊重,符合我们的行为准则。 --> + + ::: china-king-hs - 2025-11-20 希望能正常使用nexent ::: @@ -444,3 +446,6 @@ Nexent功能如此之强大,给我很多帮助,感谢开发者!厉害 ::: info user - 2025-11-20 学习ai - agent非常好的项目,后面会持续输出贡献! ::: + +学习到ai了,很好用 + From 16d0afdbe505a60a2ea5ce054f42cbac37ecd661 Mon Sep 17 00:00:00 2001 From: charleswang-de <2024213837@bupt.cn> Date: Sun, 23 Nov 2025 17:32:51 +0800 Subject: [PATCH 14/83] Update opensource-memorial-wall.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ::: info chao - 2025-11-23 使用 Nexent 开发了项目,MCP 工具集成特别强大,节省了大量开发时间! ::: --- doc/docs/zh/opensource-memorial-wall.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/docs/zh/opensource-memorial-wall.md b/doc/docs/zh/opensource-memorial-wall.md index 6592039ff..7be101ad2 100644 --- a/doc/docs/zh/opensource-memorial-wall.md +++ b/doc/docs/zh/opensource-memorial-wall.md @@ -444,3 +444,4 @@ Nexent功能如此之强大,给我很多帮助,感谢开发者!厉害 ::: info user - 2025-11-20 学习ai - agent非常好的项目,后面会持续输出贡献! ::: + From b247825f6949ed5737c340995b6a9aa2fdecdbd9 Mon Sep 17 00:00:00 2001 From: adasibi Date: Sun, 23 Nov 2025 17:38:59 +0800 Subject: [PATCH 15/83] Add user contributions to memorial wall --- doc/docs/zh/opensource-memorial-wall.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/docs/zh/opensource-memorial-wall.md b/doc/docs/zh/opensource-memorial-wall.md index 6592039ff..77ec65d73 100644 --- a/doc/docs/zh/opensource-memorial-wall.md +++ b/doc/docs/zh/opensource-memorial-wall.md @@ -443,4 +443,6 @@ Nexent功能如此之强大,给我很多帮助,感谢开发者!厉害 ::: info user - 2025-11-20 学习ai - agent非常好的项目,后面会持续输出贡献! -::: + +::: adasibi - 2025-11-23 +学习ai很好用,感谢 Nexent 让我踏上了开源之旅! From 88ed77ae5554fdb528d2f18a3f1a2f8dc8a86604 Mon Sep 17 00:00:00 2001 From: Chenxi Wu Date: Sun, 23 Nov 2025 21:39:12 +0800 Subject: [PATCH 16/83] Update opensource-memorial-wall.md --- doc/docs/zh/opensource-memorial-wall.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/docs/zh/opensource-memorial-wall.md b/doc/docs/zh/opensource-memorial-wall.md index 6592039ff..b8bde2e46 100644 --- a/doc/docs/zh/opensource-memorial-wall.md +++ b/doc/docs/zh/opensource-memorial-wall.md @@ -15,6 +15,11 @@ 每条消息应包含您的姓名/昵称和日期。 请保持消息的礼貌和尊重,符合我们的行为准则。 --> + +::: info aurorahashcat - 2025-11-23 +nexent看起来超棒的自动化智能体构建平台,祝越来越好😀 +::: + ::: china-king-hs - 2025-11-20 希望能正常使用nexent ::: From 69cf667a6697be77a15d27324e6198b32f2f4358 Mon Sep 17 00:00:00 2001 From: williamllk Date: Sun, 23 Nov 2025 22:59:01 +0800 Subject: [PATCH 17/83] Add user contributions to memorial wall MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加了一个 williamllk from SJTU 的评论 --- doc/docs/zh/opensource-memorial-wall.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/docs/zh/opensource-memorial-wall.md b/doc/docs/zh/opensource-memorial-wall.md index 6592039ff..c3bdb4274 100644 --- a/doc/docs/zh/opensource-memorial-wall.md +++ b/doc/docs/zh/opensource-memorial-wall.md @@ -444,3 +444,7 @@ Nexent功能如此之强大,给我很多帮助,感谢开发者!厉害 ::: info user - 2025-11-20 学习ai - agent非常好的项目,后面会持续输出贡献! ::: + +::: williamllk from SJTU - 2025-11-23 +感谢 Nexent 让我第一次制作智能体,尝试将AI4Science的理念付诸实践 +::: From be133cd9aa9c6fd29fea791b53e9df889746b9e6 Mon Sep 17 00:00:00 2001 From: zhizhi <928570418@qq.com> Date: Mon, 24 Nov 2025 09:57:08 +0800 Subject: [PATCH 18/83] =?UTF-8?q?=E2=9C=A8=20image=20to=20text=20tool?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/agents/create_agent_info.py | 2 +- .../services/tool_configuration_service.py | 2 +- sdk/nexent/core/agents/nexent_agent.py | 2 +- ...derstand_image.yaml => analyze_image.yaml} | 0 ...nd_image_en.yaml => analyze_image_en.yaml} | 0 sdk/nexent/core/tools/__init__.py | 4 +- ...standing_tool.py => analyze_image_tool.py} | 38 +++++++------- .../core/utils/prompt_template_utils.py | 49 +++++++++++++++++++ 8 files changed, 71 insertions(+), 26 deletions(-) rename sdk/nexent/core/prompts/{understand_image.yaml => analyze_image.yaml} (100%) rename sdk/nexent/core/prompts/{understand_image_en.yaml => analyze_image_en.yaml} (100%) rename sdk/nexent/core/tools/{image_understanding_tool.py => analyze_image_tool.py} (68%) create mode 100644 sdk/nexent/core/utils/prompt_template_utils.py diff --git a/backend/agents/create_agent_info.py b/backend/agents/create_agent_info.py index 033e9df3f..285a73806 100644 --- a/backend/agents/create_agent_info.py +++ b/backend/agents/create_agent_info.py @@ -240,7 +240,7 @@ async def create_tool_config_list(agent_id, tenant_id, user_id): "vdb_core": get_vector_db_core(), "embedding_model": get_embedding_model(tenant_id=tenant_id), } - elif tool_config.class_name == "ImageUnderstandingTool": + elif tool_config.class_name == "AnalyzeImageTool": tool_config.metadata = { "vlm_model": get_vlm_model(tenant_id=tenant_id), "storage_client": minio_client, diff --git a/backend/services/tool_configuration_service.py b/backend/services/tool_configuration_service.py index 473804bfa..be149037e 100644 --- a/backend/services/tool_configuration_service.py +++ b/backend/services/tool_configuration_service.py @@ -618,7 +618,7 @@ def _validate_local_tool( 'embedding_model': embedding_model, } tool_instance = tool_class(**params) - elif tool_name == "image_understanding": + elif tool_name == "analyze_image": if not tenant_id or not user_id: raise ToolExecutionException(f"Tenant ID and User ID are required for {tool_name} validation") image_to_text_model = get_vlm_model(tenant_id=tenant_id) diff --git a/sdk/nexent/core/agents/nexent_agent.py b/sdk/nexent/core/agents/nexent_agent.py index 8c84e4cac..ed97d383f 100644 --- a/sdk/nexent/core/agents/nexent_agent.py +++ b/sdk/nexent/core/agents/nexent_agent.py @@ -71,7 +71,7 @@ def create_local_tool(self, tool_config: ToolConfig): vdb_core=tool_config.metadata.get("vdb_core", []), embedding_model=tool_config.metadata.get("embedding_model", []), **params) - elif class_name == "ImageUnderstandingTool": + elif class_name == "AnalyzeImageTool": tools_obj = tool_class(observer=self.observer, vlm_model=tool_config.metadata.get("vlm_model", []), storage_client=tool_config.metadata.get("storage_client", []), diff --git a/sdk/nexent/core/prompts/understand_image.yaml b/sdk/nexent/core/prompts/analyze_image.yaml similarity index 100% rename from sdk/nexent/core/prompts/understand_image.yaml rename to sdk/nexent/core/prompts/analyze_image.yaml diff --git a/sdk/nexent/core/prompts/understand_image_en.yaml b/sdk/nexent/core/prompts/analyze_image_en.yaml similarity index 100% rename from sdk/nexent/core/prompts/understand_image_en.yaml rename to sdk/nexent/core/prompts/analyze_image_en.yaml diff --git a/sdk/nexent/core/tools/__init__.py b/sdk/nexent/core/tools/__init__.py index 2b467b3a5..429043d1f 100644 --- a/sdk/nexent/core/tools/__init__.py +++ b/sdk/nexent/core/tools/__init__.py @@ -12,7 +12,7 @@ from .move_item_tool import MoveItemTool from .list_directory_tool import ListDirectoryTool from .terminal_tool import TerminalTool -from .image_understanding_tool import ImageUnderstandingTool +from .analyze_image_tool import AnalyzeImageTool __all__ = [ "ExaSearchTool", @@ -29,5 +29,5 @@ "MoveItemTool", "ListDirectoryTool", "TerminalTool", - "ImageUnderstandingTool" + "AnalyzeImageTool" ] diff --git a/sdk/nexent/core/tools/image_understanding_tool.py b/sdk/nexent/core/tools/analyze_image_tool.py similarity index 68% rename from sdk/nexent/core/tools/image_understanding_tool.py rename to sdk/nexent/core/tools/analyze_image_tool.py index 62970f2cf..c26bf6a6a 100644 --- a/sdk/nexent/core/tools/image_understanding_tool.py +++ b/sdk/nexent/core/tools/analyze_image_tool.py @@ -13,16 +13,17 @@ from ... import MinIOStorageClient from ...multi_modal.load_save_object import LoadSaveObjectManager -logger = logging.getLogger("image_understanding_tool") +logger = logging.getLogger("analyze_image_tool") -class ImageUnderstandingTool(Tool): - """Tool for extracting text from images stored in S3-compatible storage.""" +class AnalyzeImageTool(Tool): + """Tool for understanding and analyzing image""" - name = "image_understanding" + name = "analyze_image" description = ( - "Understand an image stored in S3-compatible storage or HTTP and return the text content inside the image. " - "Provide the object location via an s3:// URL or http:// URL or https:// URL." + "This tool uses a visual language model to understand images based on your query and then returns a description of the image." + "It's used to understand and analyze images stored in S3 buckets, via HTTP and HTTPS." + "Use this tool when you want to retrieve information contained in an image and provide the image's URL and your query." ) inputs = { "image_url": { @@ -45,32 +46,29 @@ def __init__( observer: MessageObserver = Field(description="Message observer", default=None, exclude=True), vlm_model: OpenAIVLModel = Field(description="The VLM model to use", default=None, exclude=True), storage_client: MinIOStorageClient = Field(description="Storage client to use", default=None, exclude=True), - # todo 这么写对不对 - system_prompt_template: Template = Field(description="System prompt template to use", default=None, exclude=True), ): super().__init__() self.observer = observer self.vlm_model = vlm_model self.storage_client = storage_client - self.system_prompt_template = system_prompt_template # Create LoadSaveObjectManager with the storage client self.mm = LoadSaveObjectManager(storage_client=self.storage_client) # Dynamically apply the load_object decorator to forward method self.forward = self.mm.load_object(input_names=["image_url"])(self._forward_impl) - self.running_prompt_zh = "正在理解图片..." - self.running_prompt_en = "Understanding image..." + self.running_prompt_zh = "正在分析图片..." + self.running_prompt_en = "Analyzing image..." def _forward_impl(self, image_url: bytes, query: str) -> str: """ - Analyze the image specified by the S3 URL and return recognized text. + Analyze images of S3 URL, HTTP URL, or HTTPS URL and return the identified text. Note: This method is wrapped by load_object decorator which downloads - the image from S3 URL and passes bytes to this method. + the image from S3 URL, HTTP URL, or HTTPS URL and passes bytes to this method. Args: - image_url: Image bytes (converted from S3 URL by decorator). + image_url: Image bytes (converted from S3 URL, HTTP URL, or HTTPS URL by decorator). Returns: JSON string containing the recognized text. @@ -85,23 +83,21 @@ def _forward_impl(self, image_url: bytes, query: str) -> str: if self.observer: running_prompt = self.running_prompt_zh if self.observer.lang == "zh" else self.running_prompt_en self.observer.add_message("", ProcessType.TOOL, running_prompt) - card_content = [{"icon": "image", "text": "Processing image..."}] + card_content = [{"icon": "image", "text": "Analyzing image..."}] self.observer.add_message("", ProcessType.CARD, json.dumps(card_content, ensure_ascii=False)) # Load prompts from yaml file - prompts = get_prompt_template(template_type='understand_image',language = self.observer.lang) + prompts = get_prompt_template(template_type='analyze_image', language=self.observer.lang) try: response = self.vlm_model.analyze_image( image_input=image_stream, - system_prompt=Template(prompts['system_prompt'],undefined=StrictUndefined).render({'query': query})) + system_prompt=Template(prompts['system_prompt'], undefined=StrictUndefined).render({'query': query})) except Exception as e: raise Exception(f"Error understanding image: {str(e)}") text = response.content # Record the detailed content of this search - search_results_data = {'text':text} - if self.observer: - search_results_data = json.dumps(search_results_data, ensure_ascii=False) - self.observer.add_message("", ProcessType.SEARCH_CONTENT, search_results_data) + # todo 返回的结构体是什么? + search_results_data = {'text': text} return json.dumps(search_results_data, ensure_ascii=False) diff --git a/sdk/nexent/core/utils/prompt_template_utils.py b/sdk/nexent/core/utils/prompt_template_utils.py new file mode 100644 index 000000000..2196e44e9 --- /dev/null +++ b/sdk/nexent/core/utils/prompt_template_utils.py @@ -0,0 +1,49 @@ +import logging +import os +from typing import Dict, Any + +import yaml + +from consts.const import LANGUAGE + +logger = logging.getLogger("prompt_template_utils") + +# Define template path mapping +template_paths = { + 'analyze_image': { + LANGUAGE["ZH"]: 'core/prompts/analyze_image.yaml', + LANGUAGE["EN"]: 'core/prompts/analyze_image_en.yaml' + } +} + +def get_prompt_template(template_type: str, language: str = LANGUAGE["ZH"], **kwargs) -> Dict[str, Any]: + """ + Get prompt template + + Args: + template_type: Template type, supports the following values: + - 'analyze_image': Analyze image template + language: Language code ('zh' or 'en') + **kwargs: Additional parameters, for agent type need to pass is_manager parameter + + Returns: + dict: Loaded prompt template + """ + logger.info( + f"Getting prompt template for type: {template_type}, language: {language}, kwargs: {kwargs}") + + if template_type not in template_paths: + raise ValueError(f"Unsupported template type: {template_type}") + + # Get template path + template_path = template_paths[template_type][language] + + # Get the directory of this file and construct absolute path + current_dir = os.path.dirname(os.path.abspath(__file__)) + # Go up one level from utils to core, then use the template path + core_dir = os.path.dirname(current_dir) + absolute_template_path = os.path.join(core_dir, template_path.replace('core/', '')) + + # Read and return template content + with open(absolute_template_path, 'r', encoding='utf-8') as f: + return yaml.safe_load(f) From c206605be706302d76d6cf114be740500301abe7 Mon Sep 17 00:00:00 2001 From: lostlight530 <13957086206@163.com> Date: Mon, 24 Nov 2025 10:58:00 +0800 Subject: [PATCH 19/83] Add user messages to open source memorial wall Added new messages from users to the open source memorial wall, highlighting contributions and experiences. --- doc/docs/zh/opensource-memorial-wall.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/docs/zh/opensource-memorial-wall.md b/doc/docs/zh/opensource-memorial-wall.md index e48999550..8446f488b 100644 --- a/doc/docs/zh/opensource-memorial-wall.md +++ b/doc/docs/zh/opensource-memorial-wall.md @@ -15,6 +15,9 @@ 每条消息应包含您的姓名/昵称和日期。 请保持消息的礼貌和尊重,符合我们的行为准则。 --> +::: tip lostlight530 - 2025-11-24 +通过 Nexent 实现了 Router-Worker 架构的完美落地。无论是构建高情商的拟人化伴侣,还是处理严苛的结构化数据约束,这套框架都游刃有余。多智能体编排体验极佳! +::: ::: info happyzhang - 2025-11-13 也许我们正见证着未来的“后起之秀”😀 From ac6f54d87e2a3f75eec5457be9677d239a47ecc4 Mon Sep 17 00:00:00 2001 From: dantingli1 <19858731425@163.com> Date: Mon, 24 Nov 2025 16:00:19 +0800 Subject: [PATCH 20/83] Update memorial wall with new entry Added a new message to the memorial wall. --- doc/docs/zh/opensource-memorial-wall.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/docs/zh/opensource-memorial-wall.md b/doc/docs/zh/opensource-memorial-wall.md index 9f1a097e2..e01b8551e 100644 --- a/doc/docs/zh/opensource-memorial-wall.md +++ b/doc/docs/zh/opensource-memorial-wall.md @@ -472,8 +472,6 @@ xiaofu到此一游,感谢 Nexent 让我踏上了开源之旅! ::: info chao - 2025-11-23 使用 Nexent 开发了项目,MCP 工具集成特别强大,节省了大量开发时间! -::: +:::初学,加油!未来可期! + -::: adasibi - 2025-11-23 -学习ai很好用,感谢 Nexent 让我踏上了开源之旅! -::: \ No newline at end of file From e2688ee5ba9af2fc0555b08ef537f00e1a43f277 Mon Sep 17 00:00:00 2001 From: wuhu-wjd <664412717@qq.com> Date: Mon, 24 Nov 2025 20:17:16 +0800 Subject: [PATCH 21/83] Update opensource-memorial-wall.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 来参加华为ICT大赛的,正好借着这个机会多学到更多东西,加油! --- doc/docs/zh/opensource-memorial-wall.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/docs/zh/opensource-memorial-wall.md b/doc/docs/zh/opensource-memorial-wall.md index 9f1a097e2..93209b768 100644 --- a/doc/docs/zh/opensource-memorial-wall.md +++ b/doc/docs/zh/opensource-memorial-wall.md @@ -476,4 +476,8 @@ xiaofu到此一游,感谢 Nexent 让我踏上了开源之旅! ::: adasibi - 2025-11-23 学习ai很好用,感谢 Nexent 让我踏上了开源之旅! -::: \ No newline at end of file +::: + +::: info DUTBenjamin - 2025-11-23 +来参加华为ICT大赛的,正好借着这个机会学到更多东西,加油! +::: From b261cfaa1421cd5d3ec457084831bdd57bc867b8 Mon Sep 17 00:00:00 2001 From: czh1316 <1820531702@qq.com> Date: Mon, 24 Nov 2025 21:27:48 +0800 Subject: [PATCH 22/83] Update opensource-memorial-wall.md --- doc/docs/zh/opensource-memorial-wall.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/docs/zh/opensource-memorial-wall.md b/doc/docs/zh/opensource-memorial-wall.md index 9f1a097e2..a1e097407 100644 --- a/doc/docs/zh/opensource-memorial-wall.md +++ b/doc/docs/zh/opensource-memorial-wall.md @@ -476,4 +476,4 @@ xiaofu到此一游,感谢 Nexent 让我踏上了开源之旅! ::: adasibi - 2025-11-23 学习ai很好用,感谢 Nexent 让我踏上了开源之旅! -::: \ No newline at end of file +:::Nexent越来越好! From 59f3122360565b0225c066fac7d60b15f50a105b Mon Sep 17 00:00:00 2001 From: wmc1112 <759659013@qq.com> Date: Mon, 24 Nov 2025 14:53:22 +0800 Subject: [PATCH 23/83] =?UTF-8?q?=F0=9F=90=9B=20Bugfix:=20After=20redeploy?= =?UTF-8?q?ment,=20the=20contents=20of=20files=20in=20Minio=20were=20clear?= =?UTF-8?q?ed=20#1770?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose.prod.yml | 2 +- docker/docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index 5e3fc1a22..41b0f0c1c 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -250,7 +250,7 @@ services: MINIO_ROOT_USER: ${MINIO_ROOT_USER} MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD} volumes: - - ${ROOT_DIR}/minio/data:/data + - ${ROOT_DIR}/minio/data:/etc/minio/data networks: - nexent restart: always diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 8011b8f28..f59430d24 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -271,7 +271,7 @@ services: MINIO_ROOT_USER: ${MINIO_ROOT_USER} MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD} volumes: - - ${ROOT_DIR}/minio/data:/data + - ${ROOT_DIR}/minio/data:/etc/minio/data networks: - nexent restart: always From 94d893b897ffa8934d2badedbe98f76fb1d1b1aa Mon Sep 17 00:00:00 2001 From: Jasonxia007 Date: Tue, 25 Nov 2025 11:46:14 +0800 Subject: [PATCH 24/83] =?UTF-8?q?=E2=9C=A8=20Now=20documents=20and=20chunk?= =?UTF-8?q?s=20support=20semantic=20search?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/apps/vectordatabase_app.py | 31 +- backend/consts/model.py | 12 + backend/services/vectordatabase_service.py | 63 +++ .../components/document/DocumentChunk.tsx | 450 +++++++++++++++--- .../components/document/DocumentList.tsx | 4 +- frontend/lib/utils.ts | 45 ++ frontend/public/locales/en/common.json | 14 +- frontend/public/locales/zh/common.json | 16 +- frontend/services/api.ts | 1 + frontend/services/knowledgeBaseService.ts | 49 ++ frontend/styles/globals.css | 49 ++ test/backend/app/test_vectordatabase_app.py | 114 +++++ .../services/test_vectordatabase_service.py | 160 +++++-- 13 files changed, 908 insertions(+), 100 deletions(-) diff --git a/backend/apps/vectordatabase_app.py b/backend/apps/vectordatabase_app.py index b2b410264..c5e73dc97 100644 --- a/backend/apps/vectordatabase_app.py +++ b/backend/apps/vectordatabase_app.py @@ -5,7 +5,7 @@ from fastapi import APIRouter, Body, Depends, Header, HTTPException, Path, Query from fastapi.responses import JSONResponse -from consts.model import IndexingResponse +from consts.model import HybridSearchRequest, IndexingResponse from nexent.vector_database.base import VectorDatabaseCore from services.vectordatabase_service import ( ElasticSearchService, @@ -226,3 +226,32 @@ def get_index_chunks( f"Error getting chunks for index '{index_name}': {error_msg}") raise HTTPException( status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=f"Error getting chunks: {error_msg}") + + +@router.post("/search/hybrid") +async def hybrid_search( + payload: HybridSearchRequest, + vdb_core: VectorDatabaseCore = Depends(get_vector_db_core), + authorization: Optional[str] = Header(None), +): + """Run a hybrid (accurate + semantic) search across indices.""" + try: + _, tenant_id = get_current_user_id(authorization) + result = ElasticSearchService.search_hybrid( + index_names=payload.index_names, + query=payload.query, + tenant_id=tenant_id, + top_k=payload.top_k, + weight_accurate=payload.weight_accurate, + vdb_core=vdb_core, + ) + return JSONResponse(status_code=HTTPStatus.OK, content=result) + except ValueError as exc: + raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, + detail=str(exc)) + except Exception as exc: + logger.error(f"Hybrid search failed: {exc}", exc_info=True) + raise HTTPException( + status_code=HTTPStatus.INTERNAL_SERVER_ERROR, + detail=f"Error executing hybrid search: {str(exc)}", + ) diff --git a/backend/consts/model.py b/backend/consts/model.py index 2d2208a91..4b41a7134 100644 --- a/backend/consts/model.py +++ b/backend/consts/model.py @@ -175,6 +175,18 @@ class IndexingResponse(BaseModel): total_submitted: int +class HybridSearchRequest(BaseModel): + """Request payload for hybrid knowledge-base searches.""" + query: str = Field(..., min_length=1, + description="Search query text") + index_names: List[str] = Field(..., min_items=1, + description="List of index names to search") + top_k: int = Field(10, ge=1, le=100, + description="Number of results to return") + weight_accurate: float = Field(0.5, ge=0.0, le=1.0, + description="Weight applied to accurate search scores") + + # Request models class ProcessParams(BaseModel): chunking_strategy: Optional[str] = "basic" diff --git a/backend/services/vectordatabase_service.py b/backend/services/vectordatabase_service.py index bab6bd284..cfa70a267 100644 --- a/backend/services/vectordatabase_service.py +++ b/backend/services/vectordatabase_service.py @@ -996,3 +996,66 @@ def get_index_chunks( error_msg = f"Error retrieving chunks from index {index_name}: {str(e)}" logger.error(error_msg) raise Exception(error_msg) + + @staticmethod + def search_hybrid( + *, + index_names: List[str], + query: str, + tenant_id: str, + top_k: int = 10, + weight_accurate: float = 0.5, + vdb_core: VectorDatabaseCore = Depends(get_vector_db_core), + ): + """ + Execute a hybrid search that blends accurate and semantic scoring. + """ + try: + if not tenant_id: + raise ValueError("Tenant ID is required for hybrid search") + if not query or not query.strip(): + raise ValueError("Query text is required for hybrid search") + if not index_names: + raise ValueError("At least one index name is required") + if top_k <= 0: + raise ValueError("top_k must be greater than 0") + if weight_accurate < 0 or weight_accurate > 1: + raise ValueError("weight_accurate must be between 0 and 1") + + embedding_model = get_embedding_model(tenant_id) + if not embedding_model: + raise ValueError( + "No embedding model configured for the current tenant") + + start_time = time.perf_counter() + raw_results = vdb_core.hybrid_search( + index_names=index_names, + query_text=query, + embedding_model=embedding_model, + top_k=top_k, + weight_accurate=weight_accurate, + ) + elapsed_ms = int((time.perf_counter() - start_time) * 1000) + + formatted_results = [] + for item in raw_results: + document = dict(item.get("document", {})) + document["score"] = item.get("score") + document["index"] = item.get("index") + if "scores" in item: + document["score_details"] = item["scores"] + formatted_results.append(document) + + return { + "results": formatted_results, + "total": len(formatted_results), + "query_time_ms": elapsed_ms, + } + except ValueError: + raise + except Exception as exc: + logger.error( + f"Hybrid search failed for indices {index_names}: {exc}", + exc_info=True, + ) + raise Exception(f"Error executing hybrid search: {str(exc)}") \ No newline at end of file diff --git a/frontend/app/[locale]/knowledges/components/document/DocumentChunk.tsx b/frontend/app/[locale]/knowledges/components/document/DocumentChunk.tsx index 517042fc6..55a1674dc 100644 --- a/frontend/app/[locale]/knowledges/components/document/DocumentChunk.tsx +++ b/frontend/app/[locale]/knowledges/components/document/DocumentChunk.tsx @@ -10,14 +10,32 @@ import { App, Spin, Tag, - Tooltip, + Tooltip as AntdTooltip, Pagination, + Input, + Select, } from "antd"; -import { Download, ScanText } from "lucide-react"; +import { + Download, + ScanText, + Trash2, + SquarePen, + Search, + FilePlus2, + Goal, + X, +} from "lucide-react"; import { FieldNumberOutlined } from "@ant-design/icons"; import knowledgeBaseService from "@/services/knowledgeBaseService"; import { Document } from "@/types/knowledgeBase"; import log from "@/lib/logger"; +import { formatScoreAsPercentage, getScoreColor } from "@/lib/utils"; +import { + Tooltip as UITooltip, + TooltipTrigger, + TooltipContent, + TooltipProvider, +} from "@/components/ui/tooltip"; interface Chunk { id: string; @@ -25,12 +43,15 @@ interface Chunk { path_or_url?: string; filename?: string; create_time?: string; + score?: number; // Search score (0-1 range) - only present in search results } interface DocumentChunkProps { knowledgeBaseName: string; documents: Document[]; getFileIcon: (type: string) => string; + currentEmbeddingModel?: string | null; + knowledgeBaseEmbeddingModel?: string; } const PAGE_SIZE = 10; @@ -41,6 +62,8 @@ const DocumentChunk: React.FC = ({ knowledgeBaseName, documents, getFileIcon, + currentEmbeddingModel = null, + knowledgeBaseEmbeddingModel = "", }) => { const { t } = useTranslation(); const { message } = App.useApp(); @@ -58,15 +81,66 @@ const DocumentChunk: React.FC = ({ page: 1, pageSize: PAGE_SIZE, }); + const [searchType, setSearchType] = useState<"document" | "chunk">("chunk"); + const [searchValue, setSearchValue] = useState(""); + const [filteredDocumentIds, setFilteredDocumentIds] = useState< + string[] | null + >(null); + const [chunkSearchResult, setChunkSearchResult] = useState( + null + ); + const [chunkSearchTotal, setChunkSearchTotal] = useState(0); + const [chunkSearchLoading, setChunkSearchLoading] = useState(false); + + const resetChunkSearch = React.useCallback(() => { + setChunkSearchResult(null); + setChunkSearchTotal(0); + setChunkSearchLoading(false); + }, []); + + const displayedDocuments = React.useMemo(() => { + if (filteredDocumentIds === null) { + return documents; + } + return documents.filter((doc) => filteredDocumentIds.includes(doc.id)); + }, [documents, filteredDocumentIds]); + + const isChunkSearchActive = chunkSearchResult !== null; + + // Determine if in read-only mode + const isReadOnlyMode = React.useMemo(() => { + if (!currentEmbeddingModel || !knowledgeBaseEmbeddingModel) { + return false; + } + if (knowledgeBaseEmbeddingModel === "unknown") { + return false; + } + return currentEmbeddingModel !== knowledgeBaseEmbeddingModel; + }, [currentEmbeddingModel, knowledgeBaseEmbeddingModel]); // Set active document when documents change useEffect(() => { - if (documents.length > 0 && !activeDocumentKey) { - setActiveDocumentKey(documents[0].id); - // Reset pagination when document changes + const sourceDocuments = + filteredDocumentIds !== null ? displayedDocuments : documents; + + if (sourceDocuments.length === 0) { + if (activeDocumentKey) { + setActiveDocumentKey(""); + } + setChunks([]); + setTotal(0); + return; + } + + const hasActiveDocument = sourceDocuments.some( + (doc) => doc.id === activeDocumentKey + ); + + if (!hasActiveDocument) { + setActiveDocumentKey(sourceDocuments[0].id); setPagination((prev) => ({ ...prev, page: 1 })); } - }, [documents, activeDocumentKey]); + }, [documents, displayedDocuments, filteredDocumentIds, activeDocumentKey]); // Load chunks for active document with server-side pagination useEffect(() => { @@ -147,6 +221,7 @@ const DocumentChunk: React.FC = ({ setChunks([]); setTotal(documentChunkCounts[key] ?? 0); setPagination((prev) => ({ ...prev, page: 1 })); + resetChunkSearch(); }; // Handle pagination change @@ -154,6 +229,126 @@ const DocumentChunk: React.FC = ({ setPagination({ page, pageSize }); }; + const getDisplayName = React.useCallback((name: string): string => { + const lastDotIndex = name.lastIndexOf("."); + if (lastDotIndex <= 0) { + return name; + } + return name.substring(0, lastDotIndex); + }, []); + + // Clear search input and reset all search states + const handleClearSearch = React.useCallback(() => { + setSearchValue(""); + setFilteredDocumentIds(null); + resetChunkSearch(); + }, [resetChunkSearch]); + + const handleSearch = React.useCallback(async () => { + const trimmedValue = searchValue.trim(); + + if (!trimmedValue) { + setFilteredDocumentIds(null); + resetChunkSearch(); + return; + } + + if (searchType === "document") { + resetChunkSearch(); + const searchLower = trimmedValue.toLowerCase(); + const matchedDocs = documents.filter((doc) => { + const fullName = (doc.name || "").trim(); + const displayName = getDisplayName(fullName); + return ( + fullName.toLowerCase().includes(searchLower) || + displayName.toLowerCase().includes(searchLower) + ); + }); + + if (matchedDocs.length === 0) { + setFilteredDocumentIds([]); + setActiveDocumentKey(""); + setChunks([]); + setTotal(0); + message.warning(t("document.chunk.search.noDocument")); + return; + } + + setFilteredDocumentIds(matchedDocs.map((doc) => doc.id)); + + const hasActive = matchedDocs.some((doc) => doc.id === activeDocumentKey); + + if (!hasActive) { + setActiveDocumentKey(matchedDocs[0].id); + setPagination((prev) => ({ ...prev, page: 1 })); + } + return; + } + + if (!activeDocumentKey) { + message.warning(t("document.chunk.search.noActiveDocument")); + return; + } + + if (!knowledgeBaseName) { + message.error(t("document.chunk.error.searchFailed")); + return; + } + + setFilteredDocumentIds(null); + setChunkSearchResult([]); + setChunkSearchTotal(0); + setChunkSearchLoading(true); + + try { + const response = await knowledgeBaseService.hybridSearch( + knowledgeBaseName, + trimmedValue, + { + topK: pagination.pageSize, + } + ); + + const filteredChunks = (response.results || []) + .map((item) => { + // Backend returns document fields at the top level + return { + id: item.id || "", + content: item.content || "", + path_or_url: item.path_or_url, + filename: item.filename, + create_time: item.create_time, + score: item.score, // Preserve search score for display + }; + }) + .filter((chunk) => chunk.path_or_url === activeDocumentKey); + + setChunkSearchResult(filteredChunks); + setChunkSearchTotal(filteredChunks.length); + + if (filteredChunks.length === 0) { + message.info(t("document.chunk.search.noChunk")); + } + } catch (error) { + log.error("Failed to search chunks:", error); + message.error(t("document.chunk.error.searchFailed")); + resetChunkSearch(); + } finally { + setChunkSearchLoading(false); + } + }, [ + activeDocumentKey, + documents, + getDisplayName, + knowledgeBaseName, + message, + pagination.pageSize, + resetChunkSearch, + searchType, + searchValue, + t, + ]); + // Download chunk as txt file const handleDownloadChunk = (chunk: Chunk) => { try { @@ -173,19 +368,11 @@ const DocumentChunk: React.FC = ({ } }; - const getDisplayName = (name: string): string => { - const lastDotIndex = name.lastIndexOf("."); - if (lastDotIndex <= 0) { - return name; - } - return name.substring(0, lastDotIndex); - }; - const renderDocumentLabel = (doc: Document, chunkCount: number) => { const displayName = getDisplayName(doc.name || ""); return ( - +
{getFileIcon(doc.type)} @@ -197,27 +384,39 @@ const DocumentChunk: React.FC = ({ color="#1677ff" showZero count={chunkCount} - className="flex-shrink-0" + className="flex-shrink-0 chunk-count-badge" />
- + ); }; - const tabItems = documents.map((doc) => { + const tabItems = displayedDocuments.map((doc) => { const chunkCount = documentChunkCounts[doc.id] ?? doc.chunk_num ?? 0; const isActive = doc.id === activeDocumentKey; const docChunksData = isActive - ? { chunks, total, paginatedChunks: chunks } + ? isChunkSearchActive + ? { + chunks: chunkSearchResult ?? [], + total: chunkSearchTotal, + paginatedChunks: chunkSearchResult ?? [], + } + : { chunks, total, paginatedChunks: chunks } : { chunks: [], total: 0, paginatedChunks: [] }; + const showLoadingState = isActive + ? isChunkSearchActive + ? chunkSearchLoading && docChunksData.paginatedChunks.length === 0 + : loading && docChunksData.paginatedChunks.length === 0 + : false; + return { key: doc.id, label: renderDocumentLabel(doc, chunkCount), children: (
- {loading && docChunksData.paginatedChunks.length === 0 ? ( + {showLoadingState ? (
@@ -251,14 +450,73 @@ const DocumentChunk: React.FC = ({ })} + {chunk.score !== undefined && ( + + + + {formatScoreAsPercentage(chunk.score)} + + + )} +
+
+ {!isReadOnlyMode && ( + + +
-
} > @@ -275,7 +533,7 @@ const DocumentChunk: React.FC = ({ }; }); - if (loading && chunks.length === 0) { + if (!isChunkSearchActive && loading && chunks.length === 0) { return (
@@ -283,36 +541,118 @@ const DocumentChunk: React.FC = ({ ); } - const activeDocumentTotal = - documentChunkCounts[activeDocumentKey] ?? total ?? 0; - const shouldShowPagination = activeDocumentTotal > 0; + const activeDocumentTotal = isChunkSearchActive + ? chunkSearchTotal + : documentChunkCounts[activeDocumentKey] ?? total ?? 0; + const shouldShowPagination = !isChunkSearchActive && activeDocumentTotal > 0; return ( -
- - {shouldShowPagination && ( -
- - `${range[0]}-${range[1]} of ${pageTotal}` - } - /> + +
+ {/* Search and Add Button Bar */} +
+
+ setSearchValue(e.target.value)} + onPressEnter={() => { + void handleSearch(); + }} + style={{ width: 320 }} + addonBefore={ + - } suffix={
{searchValue && ( @@ -597,9 +715,7 @@ const DocumentChunk: React.FC = ({ void handleSearch(); }} size="small" - loading={ - searchType === "chunk" ? chunkSearchLoading : false - } + loading={chunkSearchLoading} />
} @@ -611,9 +727,7 @@ const DocumentChunk: React.FC = ({ @@ -652,6 +766,80 @@ const DocumentChunk: React.FC = ({
)}
+ { + void handleChunkSubmit(); + }} + okText={t("common.save")} + cancelText={t("common.cancel")} + confirmLoading={chunkSubmitting} + > +
+ + {t("document.chunk.form.documentName")} + + } + > +
+ {getDisplayName(activeDocument?.name || "")} +
+
+ + {t("document.chunk.form.content")} + + } + name="content" + > +