diff --git a/.gitignore b/.gitignore index 42b774e5d..2c212f5e2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ .idea -.gitignore /.env .vscode diff --git a/backend/agents/create_agent_info.py b/backend/agents/create_agent_info.py index 3cae4f1c0..ca801109f 100644 --- a/backend/agents/create_agent_info.py +++ b/backend/agents/create_agent_info.py @@ -9,6 +9,7 @@ from nexent.core.agents.agent_model import AgentRunInfo, ModelConfig, AgentConfig, ToolConfig from nexent.memory.memory_service import search_memory_in_levels +from services.file_management_service import get_llm_model from services.vectordatabase_service import ( ElasticSearchService, get_vector_db_core, @@ -17,13 +18,15 @@ from services.tenant_config_service import get_selected_knowledge_list from services.remote_mcp_service import get_remote_mcp_server_list from services.memory_config_service import build_memory_context +from services.image_service import get_vlm_model from database.agent_db import search_agent_info_by_agent_id, query_sub_agents_id_list from database.tool_db import search_tools_for_sub_agent from database.model_management_db import get_model_records, get_model_by_model_id +from database.client import minio_client from utils.model_name_utils import add_repo_to_name from utils.prompt_template_utils import get_agent_prompt_template 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 consts.const import LOCAL_MCP_SERVER, MODEL_CONFIG_MAPPING, LANGUAGE, DATA_PROCESS_SERVICE logger = logging.getLogger("create_agent_info") logger.setLevel(logging.DEBUG) @@ -236,6 +239,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 == "AnalyzeTextFileTool": + tool_config.metadata = { + "llm_model": get_llm_model(tenant_id=tenant_id), + "storage_client": minio_client, + "data_process_service_url": DATA_PROCESS_SERVICE + } + elif tool_config.class_name == "AnalyzeImageTool": + tool_config.metadata = { + "vlm_model": get_vlm_model(tenant_id=tenant_id), + "storage_client": minio_client, + } + tool_config_list.append(tool_config) return tool_config_list @@ -299,13 +314,12 @@ async def join_minio_file_description_to_query(minio_files, query): if minio_files and isinstance(minio_files, list): file_descriptions = [] for file in minio_files: - if isinstance(file, dict) and "description" in file and file["description"]: - file_descriptions.append(file["description"]) - + if isinstance(file, dict) and "url" in file and file["url"] and "name" in file and file["name"]: + file_descriptions.append(f"File name: {file['name']}, S3 URL: s3:/{file['url']}") if file_descriptions: - final_query = "User provided some reference files:\n" + final_query = "User uploaded files. The file information is as follows:\n" final_query += "\n".join(file_descriptions) + "\n\n" - final_query += f"User wants to answer questions based on the above information: {query}" + final_query += f"User wants to answer questions based on the information in the above files: {query}" return final_query diff --git a/backend/apps/agent_app.py b/backend/apps/agent_app.py index 3e63cb357..a9ce8f6c9 100644 --- a/backend/apps/agent_app.py +++ b/backend/apps/agent_app.py @@ -134,7 +134,11 @@ async def import_agent_api(request: AgentImportRequest, authorization: Optional[ import an agent """ try: - await import_agent_impl(request.agent_info, authorization) + await import_agent_impl( + request.agent_info, + authorization, + force_import=request.force_import + ) return {} except Exception as e: logger.error(f"Agent import error: {str(e)}") diff --git a/backend/apps/file_management_app.py b/backend/apps/file_management_app.py index 448b03a61..19e382ba1 100644 --- a/backend/apps/file_management_app.py +++ b/backend/apps/file_management_app.py @@ -1,16 +1,13 @@ import logging -import os from http import HTTPStatus from typing import List, Optional -from fastapi import APIRouter, Body, File, Form, Header, HTTPException, Path as PathParam, Query, Request, UploadFile +from fastapi import APIRouter, Body, File, Form, Header, HTTPException, Path as PathParam, Query, UploadFile from fastapi.responses import JSONResponse, RedirectResponse, StreamingResponse from consts.model import ProcessParams from services.file_management_service import upload_to_minio, upload_files_impl, \ - get_file_url_impl, get_file_stream_impl, delete_file_impl, list_files_impl, \ - preprocess_files_generator -from utils.auth_utils import get_current_user_info + get_file_url_impl, get_file_stream_impl, delete_file_impl, list_files_impl from utils.file_management_utils import trigger_data_process logger = logging.getLogger("file_management_app") @@ -271,61 +268,3 @@ async def get_storage_file_batch_urls( "failed_count": sum(1 for r in results if not r.get("success", False)), "results": results } - - -@file_management_runtime_router.post("/preprocess") -async def agent_preprocess_api( - request: Request, query: str = Form(...), - files: List[UploadFile] = File(...), - authorization: Optional[str] = Header(None) -): - """ - Preprocess uploaded files and return streaming response - """ - try: - # Pre-read and cache all file contents - user_id, tenant_id, language = get_current_user_info( - authorization, request) - file_cache = [] - for file in files: - try: - content = await file.read() - file_cache.append({ - "filename": file.filename or "", - "content": content, - "ext": os.path.splitext(file.filename or "")[1].lower() - }) - except Exception as e: - file_cache.append({ - "filename": file.filename or "", - "error": str(e) - }) - - # Generate unique task ID for this preprocess operation - import uuid - task_id = str(uuid.uuid4()) - conversation_id = request.query_params.get("conversation_id") - if conversation_id: - conversation_id = int(conversation_id) - else: - conversation_id = -1 # Default for cases without conversation_id - - # Call service layer to generate streaming response - return StreamingResponse( - preprocess_files_generator( - query=query, - file_cache=file_cache, - tenant_id=tenant_id, - language=language, - task_id=task_id, - conversation_id=conversation_id - ), - media_type="text/event-stream", - headers={ - "Cache-Control": "no-cache", - "Connection": "keep-alive" - } - ) - except Exception as e: - raise HTTPException( - status_code=500, detail=f"File preprocessing error: {str(e)}") diff --git a/backend/apps/vectordatabase_app.py b/backend/apps/vectordatabase_app.py index b2b410264..4eec301dd 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 ChunkCreateRequest, ChunkUpdateRequest, HybridSearchRequest, IndexingResponse from nexent.vector_database.base import VectorDatabaseCore from services.vectordatabase_service import ( ElasticSearchService, @@ -226,3 +226,125 @@ 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("/{index_name}/chunk") +def create_chunk( + index_name: str = Path(..., description="Name of the index"), + payload: ChunkCreateRequest = Body(..., description="Chunk data"), + vdb_core: VectorDatabaseCore = Depends(get_vector_db_core), + authorization: Optional[str] = Header(None), +): + """Create a manual chunk.""" + try: + user_id, _ = get_current_user_id(authorization) + result = ElasticSearchService.create_chunk( + index_name=index_name, + chunk_request=payload, + vdb_core=vdb_core, + user_id=user_id, + ) + return JSONResponse(status_code=HTTPStatus.OK, content=result) + except Exception as exc: + logger.error( + "Error creating chunk for index %s: %s", index_name, exc, exc_info=True + ) + raise HTTPException( + status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(exc) + ) + + +@router.put("/{index_name}/chunk/{chunk_id}") +def update_chunk( + index_name: str = Path(..., description="Name of the index"), + chunk_id: str = Path(..., description="Chunk identifier"), + payload: ChunkUpdateRequest = Body(..., + description="Chunk update payload"), + vdb_core: VectorDatabaseCore = Depends(get_vector_db_core), + authorization: Optional[str] = Header(None), +): + """Update an existing chunk.""" + try: + user_id, _ = get_current_user_id(authorization) + result = ElasticSearchService.update_chunk( + index_name=index_name, + chunk_id=chunk_id, + chunk_request=payload, + vdb_core=vdb_core, + user_id=user_id, + ) + 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( + "Error updating chunk %s for index %s: %s", + chunk_id, + index_name, + exc, + exc_info=True, + ) + raise HTTPException( + status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(exc) + ) + + +@router.delete("/{index_name}/chunk/{chunk_id}") +def delete_chunk( + index_name: str = Path(..., description="Name of the index"), + chunk_id: str = Path(..., description="Chunk identifier"), + vdb_core: VectorDatabaseCore = Depends(get_vector_db_core), + authorization: Optional[str] = Header(None), +): + """Delete a chunk.""" + try: + get_current_user_id(authorization) + result = ElasticSearchService.delete_chunk( + index_name=index_name, + chunk_id=chunk_id, + vdb_core=vdb_core, + ) + return JSONResponse(status_code=HTTPStatus.OK, content=result) + except ValueError as exc: + raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail=str(exc)) + except Exception as exc: + logger.error( + "Error deleting chunk %s for index %s: %s", + chunk_id, + index_name, + exc, + exc_info=True, + ) + raise HTTPException( + status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(exc) + ) + + +@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/const.py b/backend/consts/const.py index 53dac1068..754619d12 100644 --- a/backend/consts/const.py +++ b/backend/consts/const.py @@ -279,7 +279,7 @@ class VectorDatabaseType(str, Enum): os.getenv("LLM_SLOW_TOKEN_RATE_THRESHOLD", "10.0")) # tokens per second # APP Version -APP_VERSION = "v1.7.6" +APP_VERSION = "v1.7.7" DEFAULT_ZH_TITLE = "新对话" DEFAULT_EN_TITLE = "New Conversation" diff --git a/backend/consts/model.py b/backend/consts/model.py index 2d2208a91..bb0aefb7f 100644 --- a/backend/consts/model.py +++ b/backend/consts/model.py @@ -175,6 +175,43 @@ class IndexingResponse(BaseModel): total_submitted: int +class ChunkCreateRequest(BaseModel): + """Request payload for manual chunk creation.""" + + content: str = Field(..., min_length=1, description="Chunk content") + title: Optional[str] = Field(None, description="Optional chunk title") + filename: Optional[str] = Field(None, description="Associated file name") + path_or_url: Optional[str] = Field(None, description="Source path or URL") + chunk_id: Optional[str] = Field( + None, description="Explicit chunk identifier") + metadata: Dict[str, Any] = Field( + default_factory=dict, description="Additional chunk metadata") + + +class ChunkUpdateRequest(BaseModel): + """Request payload for chunk updates.""" + + content: Optional[str] = Field(None, description="Updated chunk content") + title: Optional[str] = Field(None, description="Updated chunk title") + filename: Optional[str] = Field(None, description="Updated file name") + path_or_url: Optional[str] = Field( + None, description="Updated source path or URL") + metadata: Dict[str, Any] = Field( + default_factory=dict, description="Additional metadata updates") + + +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" @@ -304,6 +341,7 @@ class ExportAndImportDataFormat(BaseModel): class AgentImportRequest(BaseModel): agent_info: ExportAndImportDataFormat + force_import: bool = False class ConvertStateRequest(BaseModel): diff --git a/backend/prompts/utils/prompt_generate.yaml b/backend/prompts/utils/prompt_generate.yaml index 9832ab671..c42a697ec 100644 --- a/backend/prompts/utils/prompt_generate.yaml +++ b/backend/prompts/utils/prompt_generate.yaml @@ -249,3 +249,49 @@ USER_PROMPT: |- {% else %} 你没有可用的助手 {% endif %} + + +AGENT_NAME_REGENERATE_SYSTEM_PROMPT: |- + ### 你是【Agent变量名调整专家】 + 你的工作是根据任务描述以及已有变量名,生成一个语义一致但不重复的 Python 变量名。 + + #### 约束 + 1. 变量名只能包含字母、数字和下划线,并且以字母或下划线开头,以“_assistant”结尾,长度不超过 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 41db11957..499d3c4ba 100644 --- a/backend/prompts/utils/prompt_generate_en.yaml +++ b/backend/prompts/utils/prompt_generate_en.yaml @@ -251,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, ending with "_assistant", 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, ending with "Assistant", 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 30bb3d453..aa537bb25 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 @@ -58,6 +59,8 @@ 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 +from utils.llm_utils import call_llm_for_system_prompt # Import monitoring utilities from utils.monitoring import monitoring_manager @@ -137,6 +140,278 @@ def _resolve_model_with_fallback( return 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: + if not value: + return False + 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(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_value: str, + *, + 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 +) -> str: + counter = 1 + while counter <= max_suffix_attempts: + 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("Failed to generate unique value after max attempts") + + +def _generate_unique_agent_name_with_suffix( + 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_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_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_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_value_with_llm( + *, + original_value: str, + existing_values: list[str], + task_description: str, + model_id: int, + tenant_id: str, + 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: + """ + Shared helper to regenerate agent-related values with an LLM. + """ + prompt_template = get_prompt_generate_prompt_template(language) + system_prompt = _render_prompt_template( + prompt_template.get(system_prompt_key, ""), + original_value=original_value + ) + user_prompt_template = prompt_template.get(user_prompt_key, "") + + 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 not system_prompt: + system_prompt = default_system_prompt + if not user_prompt: + user_prompt = default_user_prompt_builder(context) + + max_attempts = 5 + last_error: Exception | None = None + + for attempt in range(1, max_attempts + 1): + try: + 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 + ) + 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 value failed: {exc}" + ) + + logger.error( + "Failed to regenerate agent value with LLM after maximum retries", + exc_info=last_error + ) + return fallback_fn(original_value) + + +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"], + agents_cache: list[dict] | None = None, + exclude_agent_id: int | None = None +) -> 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 + ) + ) + + + +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, @@ -569,7 +844,7 @@ async def export_agent_by_agent_id(agent_id: int, tenant_id: str, user_id: str) # Check if any tool is KnowledgeBaseSearchTool and set its metadata to empty dict for tool in tool_list: - if tool.class_name == "KnowledgeBaseSearchTool": + if tool.class_name in ["KnowledgeBaseSearchTool", "AnalyzeTextFileTool", "AnalyzeImageTool"]: tool.metadata = {} # Get model_id and model display name from agent_info @@ -609,7 +884,11 @@ async def export_agent_by_agent_id(agent_id: int, tenant_id: str, user_id: str) return agent_info -async def import_agent_impl(agent_info: ExportAndImportDataFormat, authorization: str = Header(None)): +async def import_agent_impl( + agent_info: ExportAndImportDataFormat, + authorization: str = Header(None), + force_import: bool = False +): """ Import agent using DFS """ @@ -675,7 +954,9 @@ async def import_agent_impl(agent_info: ExportAndImportDataFormat, authorization import_agent_info=agent_info.agent_info[str( need_import_agent_id)], tenant_id=tenant_id, - user_id=user_id) + user_id=user_id, + skip_duplicate_regeneration=force_import + ) mapping_agent_id[need_import_agent_id] = new_agent_id agent_id_set.add(need_import_agent_id) @@ -690,7 +971,12 @@ async def import_agent_impl(agent_info: ExportAndImportDataFormat, authorization agent_stack.extend(managed_agents) -async def import_agent_by_agent_id(import_agent_info: ExportAndImportAgentInfo, tenant_id: str, user_id: str): +async def import_agent_by_agent_id( + import_agent_info: ExportAndImportAgentInfo, + tenant_id: str, + user_id: str, + skip_duplicate_regeneration: bool = False +): tool_list = [] # query all tools in the current tenant @@ -744,9 +1030,83 @@ async def import_agent_by_agent_id(import_agent_info: ExportAndImportAgentInfo, tenant_id=tenant_id ) + # Check for duplicate names and regenerate if needed (unless forced import) + 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")] + + if not skip_duplicate_regeneration: + # Check and regenerate name if duplicate + 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 + 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 + 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") + agent_name = _generate_unique_agent_name_with_suffix( + agent_name, + tenant_id=tenant_id, + agents_cache=all_agents + ) + else: + logger.warning("No model available for regeneration, using fallback") + agent_name = _generate_unique_agent_name_with_suffix( + agent_name, + tenant_id=tenant_id, + agents_cache=all_agents + ) + + # Check and regenerate display_name if duplicate + 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 + 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 + 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") + agent_display_name = _generate_unique_display_name_with_suffix( + agent_display_name, + tenant_id=tenant_id, + agents_cache=all_agents + ) + else: + logger.warning("No model available for regeneration, using fallback") + agent_display_name = _generate_unique_display_name_with_suffix( + agent_display_name, + tenant_id=tenant_id, + agents_cache=all_agents + ) + # 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/file_management_service.py b/backend/services/file_management_service.py index 1e1175228..1461c1b56 100644 --- a/backend/services/file_management_service.py +++ b/backend/services/file_management_service.py @@ -1,16 +1,13 @@ import asyncio -import json import logging import os from io import BytesIO from pathlib import Path -from typing import List, Optional, AsyncGenerator +from typing import List, Optional -import httpx from fastapi import UploadFile -from agents.preprocess_manager import preprocess_manager -from consts.const import UPLOAD_FOLDER, MAX_CONCURRENT_UPLOADS, DATA_PROCESS_SERVICE, LANGUAGE +from consts.const import UPLOAD_FOLDER, MAX_CONCURRENT_UPLOADS, MODEL_CONFIG_MAPPING from database.attachment_db import ( upload_fileobj, get_file_url, @@ -19,11 +16,13 @@ delete_file, list_files ) -from utils.attachment_utils import convert_image_to_text, convert_long_text_to_text from services.vectordatabase_service import ElasticSearchService, get_vector_db_core -from utils.prompt_template_utils import get_file_processing_messages_template +from utils.config_utils import tenant_config_manager, get_model_name_from_config from utils.file_management_utils import save_upload_file +from nexent import MessageObserver +from nexent.core.models import OpenAILongContextModel + # Create upload directory upload_dir = Path(UPLOAD_FOLDER) upload_dir.mkdir(exist_ok=True) @@ -184,224 +183,15 @@ async def list_files_impl(prefix: str, limit: Optional[int] = None): return files -def get_parsing_file_data(index: int, total_files: int, filename: str) -> dict: - """ - Get structured data for parsing file message - - Args: - index: Current file index (0-based) - total_files: Total number of files - filename: Name of the file being parsed - - Returns: - dict: Structured data with parameters for internationalization - """ - return { - "params": { - "index": index + 1, - "total": total_files, - "filename": filename - } - } - - -def get_truncation_data(filename: str, truncation_percentage: int) -> dict: - """ - Get structured data for truncation message - - Args: - filename: Name of the file being truncated - truncation_percentage: Percentage of content that was read - - Returns: - dict: Structured data with parameters for internationalization - """ - return { - "params": { - "filename": filename, - "percentage": truncation_percentage - } - } - - -async def preprocess_files_generator( - query: str, - file_cache: List[dict], - tenant_id: str, - language: str, - task_id: str, - conversation_id: int -) -> AsyncGenerator[str, None]: - """ - Generate streaming response for file preprocessing - - Args: - query: User query string - file_cache: List of cached file data - tenant_id: Tenant ID - language: Language preference - task_id: Unique task ID - conversation_id: Conversation ID - - Yields: - str: JSON formatted streaming messages - """ - file_descriptions = [] - total_files = len(file_cache) - - # Create and register the preprocess task - task = asyncio.current_task() - if task: - preprocess_manager.register_preprocess_task( - task_id, conversation_id, task) - - try: - for index, file_data in enumerate(file_cache): - if task and task.done(): - logger.info(f"Preprocess task {task_id} was cancelled") - break - - progress = int((index / total_files) * 100) - progress_message = json.dumps({ - "type": "progress", - "progress": progress, - "message_data": get_parsing_file_data(index, total_files, file_data['filename']) - }, ensure_ascii=False) - yield f"data: {progress_message}\n\n" - await asyncio.sleep(0.1) - - try: - # Check if file already has an error - if "error" in file_data: - raise Exception(file_data["error"]) - - if file_data["ext"] in ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']: - description = await process_image_file(query, file_data["filename"], file_data["content"], tenant_id, language) - truncation_percentage = None - else: - description, truncation_percentage = await process_text_file(query, file_data["filename"], file_data["content"], tenant_id, language) - file_descriptions.append(description) - - # Send processing result for each file - file_message_data = { - "type": "file_processed", - "filename": file_data["filename"], - "description": description - } - file_message = json.dumps( - file_message_data, ensure_ascii=False) - yield f"data: {file_message}\n\n" - await asyncio.sleep(0.1) - - # Send truncation notice immediately if file was truncated - if truncation_percentage is not None and int(truncation_percentage) < 100: - if int(truncation_percentage) == 0: - truncation_percentage = "< 1" - - truncation_message = json.dumps({ - "type": "truncation", - "message_data": get_truncation_data(file_data['filename'], truncation_percentage) - }, ensure_ascii=False) - yield f"data: {truncation_message}\n\n" - await asyncio.sleep(0.1) - except Exception as e: - error_description = f"Error parsing file {file_data['filename']}: {str(e)}" - logger.exception(error_description) - file_descriptions.append(error_description) - error_message = json.dumps({ - "type": "error", - "filename": file_data["filename"], - "message": error_description - }, ensure_ascii=False) - yield f"data: {error_message}\n\n" - await asyncio.sleep(0.1) - - # Send completion message - complete_message = json.dumps({ - "type": "complete", - "progress": 100, - "final_query": query - }, ensure_ascii=False) - yield f"data: {complete_message}\n\n" - finally: - preprocess_manager.unregister_preprocess_task(task_id) - - -async def process_image_file(query: str, filename: str, file_content: bytes, tenant_id: str, language: str = LANGUAGE["ZH"]) -> str: - """ - Process image file, convert to text using external API - """ - # Load messages based on language - messages = get_file_processing_messages_template(language) - - try: - image_stream = BytesIO(file_content) - text = convert_image_to_text(query, image_stream, tenant_id, language) - return messages["IMAGE_CONTENT_SUCCESS"].format(filename=filename, content=text) - except Exception as e: - return messages["IMAGE_CONTENT_ERROR"].format(filename=filename, error=str(e)) - - -async def process_text_file(query: str, filename: str, file_content: bytes, tenant_id: str, language: str = LANGUAGE["ZH"]) -> tuple[str, Optional[str]]: - """ - Process text file, convert to text using external API - """ - # Load messages based on language - messages = get_file_processing_messages_template(language) - - # file_content is byte data, need to send to API through file upload - data_process_service_url = DATA_PROCESS_SERVICE - api_url = f"{data_process_service_url}/tasks/process_text_file" - logger.info(f"Processing text file {filename} with API: {api_url}") - - try: - # Upload byte data as a file - files = { - 'file': (filename, file_content, 'application/octet-stream') - } - data = { - 'chunking_strategy': 'basic', - 'timeout': 60 - } - async with httpx.AsyncClient() as client: - response = await client.post(api_url, files=files, data=data, timeout=60) - - if response.status_code == 200: - result = response.json() - raw_text = result.get("text", "") - logger.info( - f"File processed successfully: {raw_text[:200]}...{raw_text[-200:]}..., length: {len(raw_text)}") - else: - error_detail = response.json().get('detail', 'unknown error') if response.headers.get( - 'content-type', '').startswith('application/json') else response.text - logger.error( - f"File processing failed (status code: {response.status_code}): {error_detail}") - raise Exception( - messages["FILE_PROCESSING_ERROR"].format(status_code=response.status_code, error_detail=error_detail)) - - except Exception as e: - return messages["FILE_CONTENT_ERROR"].format(filename=filename, error=str(e)), None - - try: - text, truncation_percentage = convert_long_text_to_text( - query, raw_text, tenant_id, language) - return messages["FILE_CONTENT_SUCCESS"].format(filename=filename, content=text), truncation_percentage - except Exception as e: - return messages["FILE_CONTENT_ERROR"].format(filename=filename, error=str(e)), None - - -def get_file_description(files: List[UploadFile]) -> str: - """ - Generate file description text - """ - if not files: - return "User provided some reference files:\nNo files provided" - - description = "User provided some reference files:\n" - for file in files: - ext = os.path.splitext(file.filename or "")[1].lower() - if ext in ['.jpg', '.jpeg', '.png', '.gif', '.bmp']: - description += f"- Image file {file.filename or ''}\n" - else: - description += f"- File {file.filename or ''}\n" - return description +def get_llm_model(tenant_id: str): + # Get the tenant config + main_model_config = tenant_config_manager.get_model_config( + key=MODEL_CONFIG_MAPPING["llm"], tenant_id=tenant_id) + long_text_to_text_model = OpenAILongContextModel( + observer=MessageObserver(), + model_id=get_model_name_from_config(main_model_config), + api_base=main_model_config.get("base_url"), + api_key=main_model_config.get("api_key"), + max_context_tokens=main_model_config.get("max_tokens") + ) + return long_text_to_text_model \ No newline at end of file diff --git a/backend/services/image_service.py b/backend/services/image_service.py index 939e87315..0e33a684f 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 consts.const import MODEL_CONFIG_MAPPING +from utils.config_utils import tenant_config_manager, get_model_name_from_config + +from nexent import MessageObserver +from nexent.core.models import OpenAIVLModel 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/prompt_service.py b/backend/services/prompt_service.py index 5b9d57e0e..bc277d6b9 100644 --- a/backend/services/prompt_service.py +++ b/backend/services/prompt_service.py @@ -5,99 +5,28 @@ from typing import Optional, List from jinja2 import StrictUndefined, Template -from smolagents import OpenAIServerModel -from consts.const import LANGUAGE, MESSAGE_ROLE, THINK_END_PATTERN, THINK_START_PATTERN +from consts.const import LANGUAGE from consts.model import AgentInfoRequest -from database.agent_db import update_agent, search_agent_info_by_agent_id -from database.model_management_db import get_model_by_model_id +from database.agent_db import update_agent, search_agent_info_by_agent_id, query_all_agent_info_by_tenant_id, \ + query_sub_agents_id_list from database.tool_db import query_tools_by_ids -from utils.config_utils import get_model_name_from_config +from services.agent_service import ( + get_enable_tool_id_by_agent_id, + _check_agent_name_duplicate, + _check_agent_display_name_duplicate, + _regenerate_agent_name_with_llm, + _regenerate_agent_display_name_with_llm, + _generate_unique_agent_name_with_suffix, + _generate_unique_display_name_with_suffix +) +from utils.llm_utils import call_llm_for_system_prompt from utils.prompt_template_utils import get_prompt_generate_prompt_template # Configure logging logger = logging.getLogger("prompt_service") -def _process_thinking_tokens(new_token: str, is_thinking: bool, token_join: list, callback=None) -> bool: - """ - Process tokens to filter out thinking content between and tags - - Args: - new_token: Current token from LLM stream - is_thinking: Current thinking state - token_join: List to accumulate non-thinking tokens - callback: Callback function for streaming output - - Returns: - bool: updated_is_thinking - """ - # Handle thinking mode - if is_thinking: - return THINK_END_PATTERN not in new_token - - # Handle start of thinking - if THINK_START_PATTERN in new_token: - return True - - # Normal token processing - token_join.append(new_token) - if callback: - callback("".join(token_join)) - - return False - - -def call_llm_for_system_prompt(model_id: int, user_prompt: str, system_prompt: str, callback=None, tenant_id: str = None) -> str: - """ - Call LLM to generate system prompt - - Args: - model_id: select model for generate prompt - user_prompt: description of the current task - system_prompt: system prompt for the LLM - callback: callback function - tenant_id: tenant id - - Returns: - str: Generated system prompt - """ - - llm_model_config = get_model_by_model_id(model_id=model_id, tenant_id=tenant_id) - - llm = OpenAIServerModel( - model_id=get_model_name_from_config( - llm_model_config) if llm_model_config else "", - api_base=llm_model_config.get("base_url", ""), - api_key=llm_model_config.get("api_key", ""), - temperature=0.3, - top_p=0.95 - ) - messages = [{"role": MESSAGE_ROLE["SYSTEM"], "content": system_prompt}, - {"role": MESSAGE_ROLE["USER"], "content": user_prompt}] - try: - completion_kwargs = llm._prepare_completion_kwargs( - messages=messages, - model=llm.model_id, - temperature=0.3, - top_p=0.95 - ) - current_request = llm.client.chat.completions.create( - stream=True, **completion_kwargs) - token_join = [] - is_thinking = False - for chunk in current_request: - new_token = chunk.choices[0].delta.content - if new_token is not None: - is_thinking = _process_thinking_tokens( - new_token, is_thinking, token_join, callback - ) - return "".join(token_join) - except Exception as e: - logger.error(f"Failed to generate prompt from LLM: {str(e)}") - raise e - - def gen_system_prompt_streamable(agent_id: int, model_id: int, task_description: str, user_id: str, tenant_id: str, language: str, tool_ids: Optional[List[int]] = None, sub_agent_ids: Optional[List[int]] = None): for system_prompt in generate_and_save_system_prompt_impl( agent_id=agent_id, @@ -129,8 +58,10 @@ def generate_and_save_system_prompt_impl(agent_id: int, tool_info_list = query_tools_by_ids(tool_ids) logger.debug(f"Using frontend-provided tool IDs: {tool_ids}") else: - tool_info_list = [] logger.debug("No tools selected (empty tool_ids list)") + # If no tool IDs provided, get enabled tools from database + tool_info_list = get_enabled_tool_description_for_generate_prompt( + tenant_id=tenant_id, agent_id=agent_id) # Handle sub-agent IDs if sub_agent_ids and len(sub_agent_ids) > 0: @@ -145,17 +76,121 @@ def generate_and_save_system_prompt_impl(agent_id: int, f"Failed to get sub-agent info for agent_id {sub_agent_id}: {str(e)}") logger.debug(f"Using frontend-provided sub-agent IDs: {sub_agent_ids}") else: - sub_agent_info_list = [] logger.debug("No sub-agents selected (empty sub_agent_ids list)") + # If no sub-agent IDs provided, get enabled sub-agents from database + sub_agent_info_list = get_enabled_sub_agent_description_for_generate_prompt( + tenant_id=tenant_id, agent_id=agent_id) # 1. Real-time streaming push final_results = {"duty": "", "constraint": "", "few_shots": "", "agent_var_name": "", "agent_display_name": "", "agent_description": ""} + + # Get all existing agent names and display names for duplicate checking (only if not in create mode) + 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 + ] + + # Collect results and yield non-name fields immediately, but hold name fields for duplicate checking for result_data in generate_system_prompt(sub_agent_info_list, task_description, tool_info_list, tenant_id, model_id, language): - # Update final results - final_results[result_data["type"]] = result_data["content"] - yield result_data + result_type = result_data["type"] + final_results[result_type] = result_data["content"] + + # Yield non-name fields immediately + if result_type not in ["agent_var_name", "agent_display_name"]: + yield result_data + else: + # If name field is complete, check for duplicates and regenerate if needed before yielding + if result_data.get("is_complete", False): + if result_type == "agent_var_name": + agent_name = final_results["agent_var_name"] + # Check and regenerate name if duplicate + if _check_agent_name_duplicate( + agent_name, + tenant_id=tenant_id, + exclude_agent_id=agent_id, + agents_cache=all_agents + ): + 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, + agents_cache=all_agents, + exclude_agent_id=agent_id + ) + logger.info(f"Regenerated agent name: '{agent_name}'") + final_results["agent_var_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, + tenant_id=tenant_id, + agents_cache=all_agents, + exclude_agent_id=agent_id + ) + final_results["agent_var_name"] = agent_name + + # Yield the (possibly regenerated) name + yield { + "type": "agent_var_name", + "content": final_results["agent_var_name"], + "is_complete": True + } + + elif result_type == "agent_display_name": + agent_display_name = final_results["agent_display_name"] + # Check and regenerate display_name if duplicate + if _check_agent_display_name_duplicate( + agent_display_name, + tenant_id=tenant_id, + exclude_agent_id=agent_id, + agents_cache=all_agents + ): + 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, + agents_cache=all_agents, + exclude_agent_id=agent_id + ) + logger.info(f"Regenerated agent display_name: '{agent_display_name}'") + final_results["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, + tenant_id=tenant_id, + agents_cache=all_agents, + exclude_agent_id=agent_id + ) + final_results["agent_display_name"] = agent_display_name + + # Yield the (possibly regenerated) display_name + yield { + "type": "agent_display_name", + "content": final_results["agent_display_name"], + "is_complete": True + } # 2. Update agent with the final result (skip in create mode) if agent_id == 0: @@ -163,6 +198,7 @@ def generate_and_save_system_prompt_impl(agent_id: int, else: logger.info( "Updating agent with business_description and prompt segments") + agent_info = AgentInfoRequest( agent_id=agent_id, business_description=task_description, @@ -312,3 +348,27 @@ def join_info_for_generate_system_prompt(prompt_for_generate, sub_agent_info_lis "assistant_description": assistant_description }) return content + + +def get_enabled_tool_description_for_generate_prompt(agent_id: int, tenant_id: str): + # Get tool information + logger.info("Fetching tool instances") + tool_id_list = get_enable_tool_id_by_agent_id( + agent_id=agent_id, tenant_id=tenant_id) + tool_info_list = query_tools_by_ids(tool_id_list) + return tool_info_list + + +def get_enabled_sub_agent_description_for_generate_prompt(agent_id: int, tenant_id: str): + logger.info("Fetching sub-agents information") + + sub_agent_id_list = query_sub_agents_id_list( + main_agent_id=agent_id, tenant_id=tenant_id) + + sub_agent_info_list = [] + for sub_agent_id in sub_agent_id_list: + sub_agent_info = search_agent_info_by_agent_id( + agent_id=sub_agent_id, tenant_id=tenant_id) + + sub_agent_info_list.append(sub_agent_info) + return sub_agent_info_list diff --git a/backend/services/tool_configuration_service.py b/backend/services/tool_configuration_service.py index 5aebaf614..ab971da1a 100644 --- a/backend/services/tool_configuration_service.py +++ b/backend/services/tool_configuration_service.py @@ -11,7 +11,7 @@ import jsonref from mcpadapt.smolagents_adapter import _sanitize_function_name -from consts.const import DEFAULT_USER_ID, LOCAL_MCP_SERVER +from consts.const import DEFAULT_USER_ID, LOCAL_MCP_SERVER, DATA_PROCESS_SERVICE from consts.exceptions import MCPConnectionError, ToolExecutionException, NotFoundException from consts.model import ToolInstanceInfoRequest, ToolInfo, ToolSourceEnum, ToolValidateRequest from database.remote_mcp_db import get_mcp_records_by_tenant, get_mcp_server_by_name_and_tenant @@ -23,8 +23,11 @@ search_last_tool_instance_by_tool_id, ) from database.user_tenant_db import get_all_tenant_ids +from services.file_management_service import get_llm_model from services.vectordatabase_service import get_embedding_model, get_vector_db_core from services.tenant_config_service import get_selected_knowledge_list +from database.client import minio_client +from services.image_service import get_vlm_model logger = logging.getLogger("tool_configuration_service") @@ -613,6 +616,27 @@ def _validate_local_tool( 'embedding_model': embedding_model, } tool_instance = tool_class(**params) + 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) + params = { + **instantiation_params, + 'vlm_model': image_to_text_model, + 'storage_client': minio_client + } + tool_instance = tool_class(**params) + elif tool_name == "analyze_text_file": + if not tenant_id or not user_id: + raise ToolExecutionException(f"Tenant ID and User ID are required for {tool_name} validation") + long_text_to_text_model = get_llm_model(tenant_id=tenant_id) + params = { + **instantiation_params, + 'llm_model': long_text_to_text_model, + 'storage_client': minio_client, + "data_process_service_url": DATA_PROCESS_SERVICE + } + tool_instance = tool_class(**params) else: tool_instance = tool_class(**instantiation_params) diff --git a/backend/services/vectordatabase_service.py b/backend/services/vectordatabase_service.py index bab6bd284..55d2a5e4a 100644 --- a/backend/services/vectordatabase_service.py +++ b/backend/services/vectordatabase_service.py @@ -13,6 +13,7 @@ import logging import os import time +import uuid from datetime import datetime, timezone from typing import Any, Dict, Generator, List, Optional @@ -24,6 +25,7 @@ from nexent.vector_database.elasticsearch_core import ElasticSearchCore from consts.const import ES_API_KEY, ES_HOST, LANGUAGE, VectorDatabaseType +from consts.model import ChunkCreateRequest, ChunkUpdateRequest from database.attachment_db import delete_file from database.knowledge_db import ( create_knowledge_record, @@ -35,8 +37,17 @@ from utils.config_utils import tenant_config_manager, get_model_name_from_config from utils.file_management_utils import get_all_files_status, get_file_size -ALLOWED_CHUNK_FIELDS = {"filename", - "path_or_url", "content", "create_time", "id"} +ALLOWED_CHUNK_FIELDS = { + "id", + "title", + "filename", + "path_or_url", + "content", + "create_time", + "language", + "author", + "date", +} # Configure logging logger = logging.getLogger("vectordatabase_service") @@ -996,3 +1007,193 @@ 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 create_chunk( + index_name: str, + chunk_request: ChunkCreateRequest, + vdb_core: VectorDatabaseCore = Depends(get_vector_db_core), + user_id: Optional[str] = None, + ): + """ + Create a manual chunk entry in the specified index. + """ + try: + chunk_payload = ElasticSearchService._build_chunk_payload( + base_fields={ + "id": chunk_request.chunk_id or ElasticSearchService._generate_chunk_id(), + "title": chunk_request.title, + "filename": chunk_request.filename, + "path_or_url": chunk_request.path_or_url, + "content": chunk_request.content, + "created_by": user_id, + }, + metadata=chunk_request.metadata, + ensure_create_time=True, + ) + result = vdb_core.create_chunk(index_name, chunk_payload) + return { + "status": "success", + "message": f"Chunk {result.get('id')} created successfully", + "chunk_id": result.get("id"), + } + except Exception as exc: + logger.error("Error creating chunk in index %s: %s", + index_name, exc, exc_info=True) + raise Exception(f"Error creating chunk: {exc}") + + @staticmethod + def update_chunk( + index_name: str, + chunk_id: str, + chunk_request: ChunkUpdateRequest, + vdb_core: VectorDatabaseCore = Depends(get_vector_db_core), + user_id: Optional[str] = None, + ): + """ + Update a chunk document. + """ + try: + update_fields = chunk_request.dict( + exclude_unset=True, exclude={"metadata"}) + metadata = chunk_request.metadata or {} + update_payload = ElasticSearchService._build_chunk_payload( + base_fields={ + **update_fields, + "updated_by": user_id, + "update_time": datetime.utcnow().strftime( + "%Y-%m-%dT%H:%M:%S"), + }, + metadata=metadata, + ensure_create_time=False, + ) + + if not update_payload: + raise ValueError("No update fields supplied.") + + result = vdb_core.update_chunk( + index_name, chunk_id, update_payload) + return { + "status": "success", + "message": f"Chunk {result.get('id')} updated successfully", + "chunk_id": result.get("id"), + } + except Exception as exc: + logger.error("Error updating chunk %s in index %s: %s", + chunk_id, index_name, exc, exc_info=True) + raise Exception(f"Error updating chunk: {exc}") + + @staticmethod + def delete_chunk( + index_name: str, + chunk_id: str, + vdb_core: VectorDatabaseCore = Depends(get_vector_db_core), + ): + """ + Delete a chunk document by id. + """ + try: + deleted = vdb_core.delete_chunk(index_name, chunk_id) + if not deleted: + raise ValueError( + f"Chunk {chunk_id} not found in index {index_name}") + return { + "status": "success", + "message": f"Chunk {chunk_id} deleted successfully", + "chunk_id": chunk_id, + } + except Exception as exc: + logger.error("Error deleting chunk %s in index %s: %s", + chunk_id, index_name, exc, exc_info=True) + raise Exception(f"Error deleting chunk: {exc}") + + @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)}") + + @staticmethod + def _generate_chunk_id() -> str: + """Generate a deterministic chunk id.""" + return f"chunk_{uuid.uuid4().hex}" + + @staticmethod + def _build_chunk_payload( + base_fields: Dict[str, Any], + metadata: Optional[Dict[str, Any]], + ensure_create_time: bool = True, + ) -> Dict[str, Any]: + """ + Merge and sanitize chunk payload fields. + """ + payload = { + key: value for key, value in (base_fields or {}).items() if value is not None + } + if metadata: + for key, value in metadata.items(): + if value is not None: + payload[key] = value + + if ensure_create_time and "create_time" not in payload: + payload["create_time"] = datetime.utcnow().strftime( + "%Y-%m-%dT%H:%M:%S") + + return payload diff --git a/backend/utils/llm_utils.py b/backend/utils/llm_utils.py new file mode 100644 index 000000000..7335cfe2b --- /dev/null +++ b/backend/utils/llm_utils.py @@ -0,0 +1,84 @@ +import logging +from typing import Callable, List, Optional + +from smolagents import OpenAIServerModel + +from consts.const import MESSAGE_ROLE, THINK_END_PATTERN, THINK_START_PATTERN +from database.model_management_db import get_model_by_model_id +from utils.config_utils import get_model_name_from_config + +logger = logging.getLogger("llm_utils") + + +def _process_thinking_tokens( + new_token: str, + is_thinking: bool, + token_join: List[str], + callback: Optional[Callable[[str], None]] = None, +) -> bool: + """ + Process tokens to filter out thinking content between and tags. + """ + if is_thinking: + return THINK_END_PATTERN not in new_token + + if THINK_START_PATTERN in new_token: + return True + + token_join.append(new_token) + if callback: + callback("".join(token_join)) + + return False + + +def call_llm_for_system_prompt( + model_id: int, + user_prompt: str, + system_prompt: str, + callback: Optional[Callable[[str], None]] = None, + tenant_id: Optional[str] = None, +) -> str: + """ + Call the LLM to generate a system prompt with optional streaming callbacks. + """ + llm_model_config = get_model_by_model_id(model_id=model_id, tenant_id=tenant_id) + + llm = OpenAIServerModel( + model_id=get_model_name_from_config(llm_model_config) if llm_model_config else "", + api_base=llm_model_config.get("base_url", ""), + api_key=llm_model_config.get("api_key", ""), + temperature=0.3, + top_p=0.95, + ) + messages = [ + {"role": MESSAGE_ROLE["SYSTEM"], "content": system_prompt}, + {"role": MESSAGE_ROLE["USER"], "content": user_prompt}, + ] + try: + completion_kwargs = llm._prepare_completion_kwargs( + messages=messages, + model=llm.model_id, + temperature=0.3, + top_p=0.95, + ) + current_request = llm.client.chat.completions.create(stream=True, **completion_kwargs) + token_join: List[str] = [] + is_thinking = False + for chunk in current_request: + new_token = chunk.choices[0].delta.content + if new_token is not None: + is_thinking = _process_thinking_tokens( + new_token, + is_thinking, + token_join, + callback, + ) + return "".join(token_join) + except Exception as exc: + logger.error("Failed to generate prompt from LLM: %s", str(exc)) + raise + + +__all__ = ["call_llm_for_system_prompt", "_process_thinking_tokens"] + diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 000000000..3c7a5668b --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +pnpm-lock.yaml +package-lock.json +docs/.vitepress/cache +docs/.vitepress/dist diff --git a/doc/docs/.vitepress/config.mts b/doc/docs/.vitepress/config.mts index ddf8d1dba..014eced15 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: [ @@ -157,6 +163,10 @@ export default defineConfig({ text: "MCP Ecosystem", items: [ { text: "Overview", link: "/en/mcp-ecosystem/overview" }, + { + text: "MCP Server Development", + link: "/en/mcp-ecosystem/mcp-server-development", + }, { text: "Use Cases", link: "/en/mcp-ecosystem/use-cases" }, ], }, @@ -231,19 +241,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: [ @@ -317,6 +330,10 @@ export default defineConfig({ text: "MCP 生态系统", items: [ { text: "概览", link: "/zh/mcp-ecosystem/overview" }, + { + text: "MCP 服务开发", + link: "/zh/mcp-ecosystem/mcp-server-development", + }, { text: "用例场景", link: "/zh/mcp-ecosystem/use-cases" }, ], }, 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/docs-development.md b/doc/docs/en/docs-development.md index 9d40e3a0f..4e29f294b 100644 --- a/doc/docs/en/docs-development.md +++ b/doc/docs/en/docs-development.md @@ -33,7 +33,7 @@ pnpm vitepress dev docs After successfully start, visit: -- `http://localhost:5173/doc/` +- `http://localhost:5173/` ## ✍️ Add or Edit Docs - Put Chinese docs under `doc/docs/zh` and English docs under `doc/docs/en`. 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/mcp-ecosystem/mcp-server-development.md b/doc/docs/en/mcp-ecosystem/mcp-server-development.md new file mode 100644 index 000000000..06602b978 --- /dev/null +++ b/doc/docs/en/mcp-ecosystem/mcp-server-development.md @@ -0,0 +1,200 @@ +# MCP Server Development Guide + +This guide walks you through building your own MCP server with Python and the FastMCP framework, then connecting it to the Nexent platform. + +## 🌐 Language Support + +The MCP protocol provides SDKs for multiple programming languages: + +- **Python** ⭐ (recommended) +- **TypeScript** +- **Java** +- **Go** +- **Rust** +- Any other language that implements the MCP protocol + +### Why Do We Recommend Python? + +We use **Python** for the examples in this guide because it offers: + +- ✅ **Beginner-friendly syntax**: concise code that is easy to read +- ✅ **Rich ecosystem**: frameworks like FastMCP remove most boilerplate +- ✅ **Rapid prototyping**: you can stand up a working server in minutes +- ✅ **Mature libraries**: thousands of third-party packages are available + +If you are already comfortable in another language, feel free to use the corresponding MCP SDK. For a first MCP server, however, Python gives you the smoothest experience. + +## 📋 Prerequisites + +Install FastMCP before you start coding: + +```bash +pip install fastmcp +``` + +## 🚀 Quick Start + +### Minimal Example + +The snippet below creates a simple string utility server with FastMCP: + +```python +from fastmcp import FastMCP + +# Create an MCP server instance +mcp = FastMCP(name="String MCP Server") + +@mcp.tool( + name="calculate_string_length", + description="Calculate the length of a string" +) +def calculate_string_length(text: str) -> int: + return len(text) + +@mcp.tool( + name="to_uppercase", + description="Convert text to uppercase" +) +def to_uppercase(text: str) -> str: + return text.upper() + +@mcp.tool( + name="to_lowercase", + description="Convert text to lowercase" +) +def to_lowercase(text: str) -> str: + return text.lower() + +if __name__ == "__main__": + # Start with SSE transport + mcp.run(transport="sse", port=8000) +``` + +### Run the Server + +Save the code as `mcp_server.py` and execute: + +```bash +python mcp_server.py +``` + +You should see the server start successfully with the endpoint `http://127.0.0.1:8000/sse`. + +## 🔌 Integrate MCP Services with Nexent + +Once your MCP server is up, connect it to Nexent: + +### Step 1: Start the MCP Server + +Keep the server process running and note the public endpoint (for example `http://127.0.0.1:8000/sse`). + +### Step 2: Register the MCP Service in Nexent + +1. Open the **[Agent Development](../user-guide/agent-development.md)** page. +2. On the “Select Agent Tools” tab, click **MCP Configuration** on the right. +3. Enter the server name and server URL. + - ⚠️ **Important**: + 1. The server name must contain only letters and digits—no spaces or other symbols. + 2. When Nexent runs inside Docker and the MCP server runs on the host, replace `127.0.0.1` with `host.docker.internal`, for example `http://host.docker.internal:8000`. +4. Click **Add** to finish the registration. + +### Step 3: Use the MCP Tool + +During agent creation or editing, the newly registered MCP tool appears in the tool list and can be attached to any agent. + +## 🔧 Wrap Existing Workloads + +To expose existing business logic as MCP tools, call your internal APIs or libraries inside the tool functions. + +### Example: Wrap a REST API + +```python +from fastmcp import FastMCP +import requests + +# Create an MCP server instance +mcp = FastMCP("Course Statistics Server") + +@mcp.tool( + name="get_course_statistics", + description="Get course statistics such as average, max, min, and total students" +) +def get_course_statistics(course_id: str) -> str: + api_url = "https://your-school-api.com/api/courses/statistics" + response = requests.get(api_url, params={"course_id": course_id}) + + if response.status_code == 200: + data = response.json() + stats = data.get("statistics", {}) + return ( + f"Course {course_id} statistics:\n" + f"Average: {stats.get('average', 'N/A')}\n" + f"Max: {stats.get('max', 'N/A')}\n" + f"Min: {stats.get('min', 'N/A')}\n" + f"Total Students: {stats.get('total_students', 'N/A')}" + ) + return f"API request failed: {response.status_code}" + +if __name__ == "__main__": + # Start with SSE transport + mcp.run(transport="sse", port=8000) +``` + +### Example: Wrap an Internal Module + +```python +from fastmcp import FastMCP +from your_school_module import query_course_statistics + +# Create an MCP server instance +mcp = FastMCP("Course Statistics Server") + +@mcp.tool( + name="get_course_statistics", + description="Get course statistics such as average, max, min, and total students" +) +def get_course_statistics(course_id: str) -> str: + try: + stats = query_course_statistics(course_id) + return ( + f"Course {course_id} statistics:\n" + f"Average: {stats.get('average', 'N/A')}\n" + f"Max: {stats.get('max', 'N/A')}\n" + f"Min: {stats.get('min', 'N/A')}\n" + f"Total Students: {stats.get('total_students', 'N/A')}" + ) + except Exception as exc: + return f"Failed to query statistics: {exc}" + +if __name__ == "__main__": + # Start with SSE transport + mcp.run(transport="sse", port=8000) +``` + +## 📚 Additional Resources + +### Python + +- [FastMCP Documentation](https://github.com/modelcontextprotocol/python-sdk) (used throughout this guide) + +### Other Languages + +- [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) +- [MCP Java SDK](https://github.com/modelcontextprotocol/java-sdk) +- [MCP Go SDK](https://github.com/modelcontextprotocol/go-sdk) +- [MCP Rust SDK](https://github.com/modelcontextprotocol/rust-sdk) + +### General References + +- [MCP Protocol Specification](https://modelcontextprotocol.io/) +- [Nexent Agent Development Guide](../user-guide/agent-development.md) +- [MCP Tool Ecosystem Overview](./overview.md) + +## 🆘 Need Help? + +If you run into issues while developing MCP servers: + +1. Check the **[FAQ](../getting-started/faq.md)** +2. Ask questions in [GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions) +3. Review sample servers on the [ModelScope MCP Marketplace](https://www.modelscope.cn/mcp) + diff --git a/doc/docs/en/opensource-memorial-wall.md b/doc/docs/en/opensource-memorial-wall.md index 299a26c71..bdc7c9c22 100644 --- a/doc/docs/en/opensource-memorial-wall.md +++ b/doc/docs/en/opensource-memorial-wall.md @@ -16,12 +16,12 @@ Each message should include your name/handle and date. Keep messages respectful and in line with our Code of Conduct. --> -::: info techbro_kevin - 2025-06-15 -first time doing open source, nexent is pretty cool! natural language to create agents, way easier than i thought +::: tip product_guy - 2025-02-07 +no more PRDs!! just describe in plain english and devs get it instantly ::: -::: warning Dr. Chen - AI Algorithm Engineer - 2025-06-25 -Moving from LangChain to the MCP ecosystem was definitely the right choice. Nexent's MCP tool integration makes our team's workflow much more standardized, especially when handling enterprise applications. The "USB-C of AI" analogy is so apt! Now our agents can seamlessly connect to various external services - no more headaches over different API formats. +::: info techbro_kevin - 2025-06-15 +first time doing open source, nexent is pretty cool! natural language to create agents, way easier than i thought ::: ::: tip startup_dev - 2025-06-18 @@ -36,6 +36,10 @@ working on multi-agent stuff, the knowledge tracing feature caught my eye. built first contribution was fixing a typo lmao... but community is super friendly! now using nexent for personal knowledge management, feels like having a second brain ::: +::: warning Dr. Chen - AI Algorithm Engineer - 2025-06-25 +Moving from LangChain to the MCP ecosystem was definitely the right choice. Nexent's MCP tool integration makes our team's workflow much more standardized, especially when handling enterprise applications. The "USB-C of AI" analogy is so apt! Now our agents can seamlessly connect to various external services - no more headaches over different API formats. +::: + ::: warning mldev2025 - 2025-06-25 moved from langchain to MCP, good choice. the "USB-C of AI" analogy is spot on ::: @@ -44,10 +48,6 @@ moved from langchain to MCP, good choice. the "USB-C of AI" analogy is spot on zero-code actually works! just described what i wanted and boom, got an agent. handles pdfs and everything ::: -::: tip product_guy - 2025-02-07 -no more PRDs!! just describe in plain english and devs get it instantly -::: - ::: info Tom Park - University of Toronto CS Student - 2025-08-03 International student here! Started contributing to Nexent as part of my open source class assignment, but ended up loving the project. The documentation is so well-written that even non-native English speakers like me can easily understand and contribute. I helped translate some docs and built a study group coordination agent for our international student community. The multimodal support works great for students who prefer different communication styles! ::: @@ -64,6 +64,10 @@ just dropping by to say nice work 👍 starred the repo Really impressed by Nexent — smooth interface and powerful agent framework. Great work! ::: -::: info uu - 2024-01-15 -华为ICT智能体,感谢nexent平台支持! +::: info gsong - 2025-11-26 +Nexent represents the future of intelligent agent creation — simple, elegant, and incredibly powerful. As a true zero-code platform, it eliminates the need for orchestration or complex drag-and-drop workflows, allowing anyone to build agents using pure natural language. Built on the robust MCP ecosystem with rich tool integrations, Nexent goes far beyond basic automation by offering a suite of built-in agents tailored for real-world scenarios — from work and travel to everyday life. Its capabilities for agent execution control, multi-agent collaboration, data processing and knowledge tracing, multimodal dialogue, and batch scaling set a new benchmark in intelligent service platforms. Nexent doesn’t just accelerate development — it redefines what’s possible. +::: + +::: info uu - 2025-11-27 +Huawei ICT Agent, thanks to the nexent platform for its support! ::: diff --git a/doc/docs/en/sdk/core/tools.md b/doc/docs/en/sdk/core/tools.md index cd2d588ee..24396ef2d 100644 --- a/doc/docs/en/sdk/core/tools.md +++ b/doc/docs/en/sdk/core/tools.md @@ -28,6 +28,10 @@ The current SDK includes the following tool types: - **GetEmailTool**: Email retrieval tool via IMAP - **SendEmailTool**: Email sending tool via SMTP +### Multimodal Tools +- **AnalyzeTextFileTool**: A document question-answering tool based on data processing and large language models +- **AnalyzeImageTool**: An image question-answering tool based on visual language models + ## 🔧 Common Characteristics ### 1. Basic Architecture diff --git a/doc/docs/en/user-guide/agent-configuration.md b/doc/docs/en/user-guide/agent-configuration.md deleted file mode 100644 index 348cc60d7..000000000 --- a/doc/docs/en/user-guide/agent-configuration.md +++ /dev/null @@ -1,107 +0,0 @@ -# Agent Configuration - -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. - -## 🛠️ Agent Management - -### Create an Agent - -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 - -
- -
- -### Agent List - -On the Agent Management tab, you can view all created agents. Click an agent to select it for detailed configuration; click again to deselect. - -## 👥 Configure Agent Capabilities - -You can configure other collaborative agents for your created agent, as well as assign available tools to empower the agent to complete complex tasks. - -### Collaborative Agents - -1. Click the plus sign under the "Collaborative Agent" tab to open the selectable agent list -2. Select the agents you want to add from the dropdown -3. Multiple collaborative agents can be selected -4. Click × to remove an agent from the selection - -
- -
- -### Select Tools - -Agents can use various tools to complete tasks, such as knowledge base search, email sending, file management, and more. They can also integrate third-party MCP tools or custom tools. - -1. On the "Select Tools" tab, click "Refresh Tools" to update the available tool list -2. Select the group containing the tool you want to add -3. View all available tools under the group; click the gear icon to view tool details and configure parameters -4. Click the tool name to select/deselect it - -
- -
- -### Add MCP Tools - -Nexent allows you to quickly use third-party MCP tools to enrich agent capabilities. - -1. On the "Select Tools" tab, click "MCP Config" to configure MCP servers in the popup and view configured servers -2. Enter the server name and URL (currently only SSE protocol is supported) -3. Click "Add" to complete the addition - -
- -
- -Many third-party services such as ModelScope provide MCP services, which you can quickly integrate and use. - -### Custom Tools - -You can refer to the following guides to develop your own tools and integrate them into Nexent to enrich agent capabilities: -- [LangChain Tools Guide](../backend/tools/langchain) -- [MCP Tool Development](../backend/tools/mcp) -- [SDK Tool Documentation](../sdk/core/tools) - -## 📝 Describe Business Logic - -### Describe How should this Agent 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. - -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 - -
- -
- -### Debug Agent - -After completing the initial agent configuration, you can debug the agent and optimize its configuration based on the results to continuously improve agent performance. - -1. In the lower right corner of the page, click the "Debug" button to open the agent debug page -2. Test conversations with the agent and observe its responses and behavior -3. Review conversation performance and error messages, and optimize the agent configuration based on the results - -### 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) - -## 🚀 Next Steps - -After completing agent configuration, you can click "Complete Setup" to proceed to: - -1. **[Chat Interface](./chat-interface)** – Interact with your agent - -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 diff --git a/doc/docs/en/user-guide/agent-development.md b/doc/docs/en/user-guide/agent-development.md new file mode 100644 index 000000000..54970e5c2 --- /dev/null +++ b/doc/docs/en/user-guide/agent-development.md @@ -0,0 +1,151 @@ +# Agent Development + +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. + +## 🔧 Create an Agent + +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 file selection dialog, 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 + +
+ +
+ +## 👥 Configure Collaborative Agents/Tools + +You can configure other collaborative agents for your created agent, as well as assign available tools to empower the agent to complete complex tasks. + +### 🤝 Collaborative Agents + +1. Click the plus sign under the "Collaborative Agent" tab to open the selectable agent list +2. Select the agents you want to add from the dropdown list +3. Multiple collaborative agents can be selected +4. Click × to remove an agent from the selection + +
+ +
+ +### 🛠️ Select Agent Tools + +Agents can use various tools to complete tasks, such as knowledge base search, email sending, file management, and other local tools. They can also integrate third-party MCP tools or custom tools. + +1. On the "Select Tools" tab, click "Refresh Tools" to update the available tool list +2. Select the group containing the tool you want to add +3. View all available tools under the group; click ⚙️ to view tool details and configure parameters +4. Click the tool name to select/deselect it + - If the tool has required parameters that are not configured, a popup will appear to guide you through parameter configuration + - If all required parameters are already configured, the tool will be selected directly + +
+ +
+ +> 💡 **Tips**: +> 1. Please select the `knowledge_base_search` tool to enable the knowledge base search function. +> 2. Please select the `analyze_text_file` tool to enable the parsing function for document and text files. +> 3. Please select the `analyze_image` tool to enable the parsing function for image files. +> +> 📚 Want to learn about all the built-in local tools available in the system? Please refer to [Local Tools Overview](./local-tools/index.md). + +### 🔌 Add MCP Tools + +Nexent allows you to quickly and easily use third-party MCP tools to enrich agent capabilities. + +1. On the "Select Agent Tools" tab, click "MCP Config" to configure MCP servers in the popup and view configured servers +2. Enter the server name and URL (currently only SSE protocol is supported) + - ⚠️ **Note:** The server name must contain only English letters or digits; spaces, underscores, and other characters are not allowed. +3. Click "Add" to complete the addition + +
+ +
+ +Many third-party services such as [ModelScope](https://www.modelscope.cn/mcp) provide MCP services, which you can quickly integrate and use. +You can also develop your own MCP services and connect them to Nexent; see [MCP Server Development](../mcp-ecosystem/mcp-server-development.md). + +### ⚙️ Custom Tools + +You can refer to the following guides to develop your own tools and integrate them into Nexent to enrich agent capabilities: + +- [LangChain Tools Guide](../backend/tools/langchain) +- [MCP Tool Development](../backend/tools/mcp) +- [SDK Tool Documentation](../sdk/core/tools) + +### 🧪 Tool Testing + +Nexent provides a "Tool Testing" capability for all types of tools—whether they are built-in tools, externally integrated MCP tools, or custom-developed tools. If you are unsure about a tool's effectiveness when creating an agent, you can use the testing feature to verify that the tool works as expected. + +1. Click the gear icon ⚙️ next to the tool to open the tool's detailed configuration popup +2. First, ensure that all required parameters (marked with red asterisks) are configured +3. Click the "Test Tool" button in the lower left corner of the popup +4. A new test panel will appear on the right side +5. Enter the tool's input parameters in the test panel. For example: + - When testing the local knowledge base search tool `knowledge_base_search`, you need to enter: + - The test `query`, such as "benefits of vitamin C" + - The search `search_mode` (default is `hybrid`) + - The target index list `index_names`, such as `["Medical", "Vitamin Encyclopedia"]` + - If `index_names` is not entered, it will default to searching all knowledge bases selected on the knowledge base page +6. After entering the parameters, click "Execute Test" to start the test and view the test results below + +
+ +
+ +## 📝 Describe Business Logic + +### ✍️ Describe How the Agent Should Work + +Based on the selected collaborative agents and tools, you can now describe in simple language how you expect this agent to work. Nexent will automatically generate the agent name, description, and prompts based on your configuration and description. + +1. In the editor under "Describe how should this agent work", enter a brief description, such as "You are a professional knowledge Q&A assistant with local knowledge search and online search capabilities, synthesizing information to answer user questions" +2. Click the "Generate" button, and Nexent will generate detailed agent content for you, including basic information and prompts (role, usage requirements, few shots) +3. You can edit and fine-tune the auto-generated content (especially the prompts) in the Agent Detail Content below + +
+ +
+ +### 🐛 Debug and Save + +After completing the initial agent configuration, you can debug the agent and fine-tune the prompts based on the debugging results to continuously improve agent performance. + +1. Click the "Debug" button in the lower right corner of the page to open the agent debug page +2. Test conversations with the agent and observe its responses and behavior +3. Review conversation performance and error messages, and optimize the agent prompts based on the test results + +After successful debugging, click the "Save" button in the lower right corner, and the agent will be saved and appear in the agent list. + +## 📋 Manage Agents + +In the agent list on the left, you can perform the following operations on existing agents: + +### 🔗 View Call Relationships + +View the collaborative agents/tools used by the agent, displayed in a tree diagram to clearly see the agent call relationships. + +
+ +
+ +### 📤 Export + +Export successfully debugged agents as JSON configuration files. You can use this JSON file to create a copy by importing it when creating an agent. + +### 🗑️ Delete + +Delete an agent (this cannot be undone, please proceed with caution). + +## 🚀 Next Steps + +After completing agent development, you can: + +1. View and manage all agents in **[Agent Space](./agent-space)** +2. Interact with agents in **[Start Chat](./start-chat)** +3. Configure **[Memory Management](./memory-management)** to enhance the agent's personalization capabilities + +If you encounter any issues during agent development, please refer to our **[FAQ](../getting-started/faq)** or ask for support in [GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions). 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..b4929d454 --- /dev/null +++ b/doc/docs/en/user-guide/agent-space.md @@ -0,0 +1,65 @@ +# 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 Space](./assets/agent-space/agent-space.png) + +## 📦 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. + +![Agent Details](./assets/agent-space/agent-details.png) + +### 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/assets/agent-development/agent-relationship.png b/doc/docs/en/user-guide/assets/agent-development/agent-relationship.png new file mode 100644 index 000000000..bc6979894 Binary files /dev/null and b/doc/docs/en/user-guide/assets/agent-development/agent-relationship.png differ diff --git a/doc/docs/en/user-guide/assets/agent/generate-agent.png b/doc/docs/en/user-guide/assets/agent-development/generate-agent.png similarity index 100% rename from doc/docs/en/user-guide/assets/agent/generate-agent.png rename to doc/docs/en/user-guide/assets/agent-development/generate-agent.png diff --git a/doc/docs/en/user-guide/assets/agent/import.png b/doc/docs/en/user-guide/assets/agent-development/import.png similarity index 100% rename from doc/docs/en/user-guide/assets/agent/import.png rename to doc/docs/en/user-guide/assets/agent-development/import.png diff --git a/doc/docs/en/user-guide/assets/agent/mcp.png b/doc/docs/en/user-guide/assets/agent-development/mcp.png similarity index 100% rename from doc/docs/en/user-guide/assets/agent/mcp.png rename to doc/docs/en/user-guide/assets/agent-development/mcp.png diff --git a/doc/docs/en/user-guide/assets/agent/set-collaboration.png b/doc/docs/en/user-guide/assets/agent-development/set-collaboration.png similarity index 100% rename from doc/docs/en/user-guide/assets/agent/set-collaboration.png rename to doc/docs/en/user-guide/assets/agent-development/set-collaboration.png diff --git a/doc/docs/en/user-guide/assets/agent/set-tool.png b/doc/docs/en/user-guide/assets/agent-development/set-tool.png similarity index 100% rename from doc/docs/en/user-guide/assets/agent/set-tool.png rename to doc/docs/en/user-guide/assets/agent-development/set-tool.png diff --git a/doc/docs/en/user-guide/assets/agent-development/tool-test-run.png b/doc/docs/en/user-guide/assets/agent-development/tool-test-run.png new file mode 100644 index 000000000..d06503ae0 Binary files /dev/null and b/doc/docs/en/user-guide/assets/agent-development/tool-test-run.png differ diff --git a/doc/docs/en/user-guide/assets/agent-space/agent-details.png b/doc/docs/en/user-guide/assets/agent-space/agent-details.png new file mode 100644 index 000000000..6980e93d9 Binary files /dev/null and b/doc/docs/en/user-guide/assets/agent-space/agent-details.png differ diff --git a/doc/docs/en/user-guide/assets/agent-space/agent-space.png b/doc/docs/en/user-guide/assets/agent-space/agent-space.png new file mode 100644 index 000000000..952596236 Binary files /dev/null and b/doc/docs/en/user-guide/assets/agent-space/agent-space.png differ diff --git a/doc/docs/en/user-guide/assets/home-page/homepage.png b/doc/docs/en/user-guide/assets/home-page/homepage.png new file mode 100644 index 000000000..c6bc61a82 Binary files /dev/null and b/doc/docs/en/user-guide/assets/home-page/homepage.png differ diff --git a/doc/docs/en/user-guide/assets/knowledge/create-knowledge-base.png b/doc/docs/en/user-guide/assets/knowledge-base/create-knowledge-base.png similarity index 100% rename from doc/docs/en/user-guide/assets/knowledge/create-knowledge-base.png rename to doc/docs/en/user-guide/assets/knowledge-base/create-knowledge-base.png diff --git a/doc/docs/en/user-guide/assets/knowledge/delete-knowledge-base.png b/doc/docs/en/user-guide/assets/knowledge-base/delete-knowledge-base.png similarity index 100% rename from doc/docs/en/user-guide/assets/knowledge/delete-knowledge-base.png rename to doc/docs/en/user-guide/assets/knowledge-base/delete-knowledge-base.png diff --git a/doc/docs/en/user-guide/assets/knowledge/knowledge-base-file-list.png b/doc/docs/en/user-guide/assets/knowledge-base/knowledge-base-file-list.png similarity index 100% rename from doc/docs/en/user-guide/assets/knowledge/knowledge-base-file-list.png rename to doc/docs/en/user-guide/assets/knowledge-base/knowledge-base-file-list.png diff --git a/doc/docs/en/user-guide/assets/knowledge/knowledge-base-summary.png b/doc/docs/en/user-guide/assets/knowledge-base/knowledge-base-summary.png similarity index 100% rename from doc/docs/en/user-guide/assets/knowledge/knowledge-base-summary.png rename to doc/docs/en/user-guide/assets/knowledge-base/knowledge-base-summary.png diff --git a/doc/docs/en/user-guide/assets/knowledge/summary-knowledge-base.png b/doc/docs/en/user-guide/assets/knowledge-base/summary-knowledge-base.png similarity index 100% rename from doc/docs/en/user-guide/assets/knowledge/summary-knowledge-base.png rename to doc/docs/en/user-guide/assets/knowledge-base/summary-knowledge-base.png diff --git a/doc/docs/en/user-guide/assets/memory/add-mem.png b/doc/docs/en/user-guide/assets/memory-management/add-mem.png similarity index 100% rename from doc/docs/en/user-guide/assets/memory/add-mem.png rename to doc/docs/en/user-guide/assets/memory-management/add-mem.png diff --git a/doc/docs/en/user-guide/assets/memory/delete-mem.png b/doc/docs/en/user-guide/assets/memory-management/delete-mem.png similarity index 100% rename from doc/docs/en/user-guide/assets/memory/delete-mem.png rename to doc/docs/en/user-guide/assets/memory-management/delete-mem.png diff --git a/doc/docs/en/user-guide/assets/memory/mem-config.png b/doc/docs/en/user-guide/assets/memory-management/mem-config.png similarity index 100% rename from doc/docs/en/user-guide/assets/memory/mem-config.png rename to doc/docs/en/user-guide/assets/memory-management/mem-config.png diff --git a/doc/docs/en/user-guide/assets/model/add-model-batch.png b/doc/docs/en/user-guide/assets/model-management/add-model-batch.png similarity index 100% rename from doc/docs/en/user-guide/assets/model/add-model-batch.png rename to doc/docs/en/user-guide/assets/model-management/add-model-batch.png diff --git a/doc/docs/en/user-guide/assets/model/add-model.png b/doc/docs/en/user-guide/assets/model-management/add-model.png similarity index 100% rename from doc/docs/en/user-guide/assets/model/add-model.png rename to doc/docs/en/user-guide/assets/model-management/add-model.png diff --git a/doc/docs/en/user-guide/assets/app/app-name-description-setting.png b/doc/docs/en/user-guide/assets/model-management/app-name-description-setting.png similarity index 100% rename from doc/docs/en/user-guide/assets/app/app-name-description-setting.png rename to doc/docs/en/user-guide/assets/model-management/app-name-description-setting.png diff --git a/doc/docs/en/user-guide/assets/app/customized-app-icon-setting.png b/doc/docs/en/user-guide/assets/model-management/customized-app-icon-setting.png similarity index 100% rename from doc/docs/en/user-guide/assets/app/customized-app-icon-setting.png rename to doc/docs/en/user-guide/assets/model-management/customized-app-icon-setting.png diff --git a/doc/docs/en/user-guide/assets/model/delete-model-1.png b/doc/docs/en/user-guide/assets/model-management/delete-model-1.png similarity index 100% rename from doc/docs/en/user-guide/assets/model/delete-model-1.png rename to doc/docs/en/user-guide/assets/model-management/delete-model-1.png diff --git a/doc/docs/en/user-guide/assets/model/delete-model-2.png b/doc/docs/en/user-guide/assets/model-management/delete-model-2.png similarity index 100% rename from doc/docs/en/user-guide/assets/model/delete-model-2.png rename to doc/docs/en/user-guide/assets/model-management/delete-model-2.png diff --git a/doc/docs/en/user-guide/assets/model/edit-model-1.png b/doc/docs/en/user-guide/assets/model-management/edit-model-1.png similarity index 100% rename from doc/docs/en/user-guide/assets/model/edit-model-1.png rename to doc/docs/en/user-guide/assets/model-management/edit-model-1.png diff --git a/doc/docs/en/user-guide/assets/model/edit-model-2.png b/doc/docs/en/user-guide/assets/model-management/edit-model-2.png similarity index 100% rename from doc/docs/en/user-guide/assets/model/edit-model-2.png rename to doc/docs/en/user-guide/assets/model-management/edit-model-2.png diff --git a/doc/docs/en/user-guide/assets/model/edit-model-3.png b/doc/docs/en/user-guide/assets/model-management/edit-model-3.png similarity index 100% rename from doc/docs/en/user-guide/assets/model/edit-model-3.png rename to doc/docs/en/user-guide/assets/model-management/edit-model-3.png diff --git a/doc/docs/en/user-guide/assets/model/edit-model-4.png b/doc/docs/en/user-guide/assets/model-management/edit-model-4.png similarity index 100% rename from doc/docs/en/user-guide/assets/model/edit-model-4.png rename to doc/docs/en/user-guide/assets/model-management/edit-model-4.png diff --git a/doc/docs/en/user-guide/assets/model/edit-model-5.png b/doc/docs/en/user-guide/assets/model-management/edit-model-5.png similarity index 100% rename from doc/docs/en/user-guide/assets/model/edit-model-5.png rename to doc/docs/en/user-guide/assets/model-management/edit-model-5.png diff --git a/doc/docs/en/user-guide/assets/model/edit-model-6.png b/doc/docs/en/user-guide/assets/model-management/edit-model-6.png similarity index 100% rename from doc/docs/en/user-guide/assets/model/edit-model-6.png rename to doc/docs/en/user-guide/assets/model-management/edit-model-6.png diff --git a/doc/docs/en/user-guide/assets/app/homepage.png b/doc/docs/en/user-guide/assets/model-management/homepage.png similarity index 100% rename from doc/docs/en/user-guide/assets/app/homepage.png rename to doc/docs/en/user-guide/assets/model-management/homepage.png diff --git a/doc/docs/en/user-guide/assets/app/predefined-app-icon-setting.png b/doc/docs/en/user-guide/assets/model-management/predefined-app-icon-setting.png similarity index 100% rename from doc/docs/en/user-guide/assets/app/predefined-app-icon-setting.png rename to doc/docs/en/user-guide/assets/model-management/predefined-app-icon-setting.png diff --git a/doc/docs/en/user-guide/assets/model/select-model-1.png b/doc/docs/en/user-guide/assets/model-management/select-model-1.png similarity index 100% rename from doc/docs/en/user-guide/assets/model/select-model-1.png rename to doc/docs/en/user-guide/assets/model-management/select-model-1.png diff --git a/doc/docs/en/user-guide/assets/model/select-model-2.png b/doc/docs/en/user-guide/assets/model-management/select-model-2.png similarity index 100% rename from doc/docs/en/user-guide/assets/model/select-model-2.png rename to doc/docs/en/user-guide/assets/model-management/select-model-2.png diff --git a/doc/docs/en/user-guide/assets/model/select-model-3.png b/doc/docs/en/user-guide/assets/model-management/select-model-3.png similarity index 100% rename from doc/docs/en/user-guide/assets/model/select-model-3.png rename to doc/docs/en/user-guide/assets/model-management/select-model-3.png diff --git a/doc/docs/en/user-guide/assets/chat/agent-selection.png b/doc/docs/en/user-guide/assets/start-chat/agent-selection.png similarity index 100% rename from doc/docs/en/user-guide/assets/chat/agent-selection.png rename to doc/docs/en/user-guide/assets/start-chat/agent-selection.png diff --git a/doc/docs/en/user-guide/assets/chat/chat-management-1.png b/doc/docs/en/user-guide/assets/start-chat/chat-management-1.png similarity index 100% rename from doc/docs/en/user-guide/assets/chat/chat-management-1.png rename to doc/docs/en/user-guide/assets/start-chat/chat-management-1.png diff --git a/doc/docs/en/user-guide/assets/chat/chat-management-2.png b/doc/docs/en/user-guide/assets/start-chat/chat-management-2.png similarity index 100% rename from doc/docs/en/user-guide/assets/chat/chat-management-2.png rename to doc/docs/en/user-guide/assets/start-chat/chat-management-2.png diff --git a/doc/docs/en/user-guide/assets/chat/dialog-box.png b/doc/docs/en/user-guide/assets/start-chat/dialog-box.png similarity index 100% rename from doc/docs/en/user-guide/assets/chat/dialog-box.png rename to doc/docs/en/user-guide/assets/start-chat/dialog-box.png diff --git a/doc/docs/en/user-guide/assets/chat/reference-image.png b/doc/docs/en/user-guide/assets/start-chat/reference-image.png similarity index 100% rename from doc/docs/en/user-guide/assets/chat/reference-image.png rename to doc/docs/en/user-guide/assets/start-chat/reference-image.png diff --git a/doc/docs/en/user-guide/assets/chat/reference-source.png b/doc/docs/en/user-guide/assets/start-chat/reference-source.png similarity index 100% rename from doc/docs/en/user-guide/assets/chat/reference-source.png rename to doc/docs/en/user-guide/assets/start-chat/reference-source.png 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..9ee4cd1a3 --- /dev/null +++ b/doc/docs/en/user-guide/home-page.md @@ -0,0 +1,51 @@ +# 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: + +![Homepage Overview](./assets/home-page/homepage.png) + +### 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 Page** – 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. Conduct **[Agent Development](./agent-development)** on top of the models and knowledge base. +4. When everything is ready, chat with your agents via **[Start Chat](./start-chat)**. + +Alternatively, you can click the "Quick Setup" button on the homepage or in the navigation bar and follow the guided flow to complete the setup. + +## 💡 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..7a8c39332 --- /dev/null +++ b/doc/docs/en/user-guide/knowledge-base.md @@ -0,0 +1,78 @@ +# 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 + > **Note:** Knowledge base names must be unique and can only contain Chinese characters or lowercase letters. Spaces, slashes, and other special characters are not allowed. + +## 📁 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-base/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-base/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-base/delete-knowledge-base.png) + +2. **Delete or 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..5946ca133 100644 --- a/doc/docs/en/user-guide/local-tools/index.md +++ b/doc/docs/en/user-guide/local-tools/index.md @@ -1,53 +1,63 @@ # 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: +### 🖼️ Multimodal Tools -- **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 +- **analyze_text_file**: Based on user queries and the S3 URL, HTTP URL, and HTTPS URL of a text file, parse the file and use a large language model to understand it, answering user questions. An available large language model needs to be configured on the model management page. +- **analyze_image**: Based on user queries and the S3 URL, HTTP URL, and HTTPS URL of an image, use a visual language model to analyze and understand the image, answering user questions. An available visual language model needs to be configured on the model management page. -## 🚀 Next Steps +### 🖥️ Terminal Tool + +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 + +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..033b67fd9 --- /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..22e0e0f22 --- /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). 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 68% rename from doc/docs/en/user-guide/chat-interface.md rename to doc/docs/en/user-guide/start-chat.md index 2db2d39d6..d37b09e7b 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 @@ -19,7 +19,7 @@ Before starting a chat, you need to select an agent. - You can start a new chat after switching
- Select Agent + Select Agent
### Send Text Messages @@ -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** @@ -39,12 +40,12 @@ After selecting an agent, you can send text messages in the following ways: - The reasoning process will be shown as cards for easy distinction
- Select Agent + Select Agent
### 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,13 +62,19 @@ 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 -You can upload files during a chat, allowing agents to assist you based on file content: +You can upload files during a chat so the agent can reason over their content: -1. **Choose File Upload Method** +> ⚠️ **Important:** +> 1. Multimodal file conversations require the agent to have the corresponding parsing tools enabled during agent development. +> 2. For document or text files select the `analyze_text_file` tool. +> 3. For image files select the `analyze_image` tool. +> 2. Each uploaded file should ideally be under 10 MB. Split large documents into multiple uploads. + +1. **Choose a File Upload Method** - Click the file upload button in the lower right corner of the input box - Or drag files directly into the chat area @@ -77,17 +84,15 @@ You can upload files during a chat, allowing agents to assist you based on file - **Images:** JPG, PNG, GIF, and other common formats 3. **File Processing Flow** - - The system will automatically process your uploaded files - - Extract file content and add it to the current chat context - - The agent will answer your questions based on the file content + - The platform stores the uploaded file in MinIO and returns an S3 URL + - It builds structured file metadata and injects it into the active conversation + - The agent then answers your questions based on both the prompt and file metadata 4. **File-based Chat** - - After uploading a file, you can ask questions about its content - - The agent can analyze, summarize, or process information from the file + - After uploading a file, ask questions about its contents at any time + - The agent can call the relevant multimodal tools to analyze, summarize, or process the data - Multiple files can be uploaded and processed simultaneously -> ⚠️ **Note:** There is a file size limit for uploads. It is recommended that a single file not exceed 10MB. For large documents, upload in batches. - ## 📚 Manage Your Chat History The left sidebar provides complete chat history management: @@ -116,14 +121,19 @@ The left sidebar provides complete chat history management: > 💡 **Tip:** Regularly cleaning up unnecessary chat records keeps the interface tidy and improves search efficiency.
- Chat Edit - Chat Edit + Chat Edit + 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 @@ -148,15 +158,15 @@ The right sidebar provides two tabs: "Source" and "Images" to help you understan - Helps you visually understand relevant information
- Reference Source - Reference Image + Reference Source + Reference Image
## 🎭 Multimodal Interaction Experience ### Image Processing -Nexent supports image input and processing (requires configuration of a vision model): +Nexent supports image input and processing (make sure a vision model **and** the `analyze_image` tool are configured): 1. **Upload Images** - Drag image files directly into the chat area @@ -198,8 +208,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/docs-development.md b/doc/docs/zh/docs-development.md index 8f99611a6..74371cba0 100644 --- a/doc/docs/zh/docs-development.md +++ b/doc/docs/zh/docs-development.md @@ -33,7 +33,7 @@ pnpm vitepress dev docs 启动成功后,请通过以下地址访问: -- `http://localhost:5173/doc/` +- `http://localhost:5173/` ## ✍️ 新增与编辑文档 - 中文文档放在 `doc/docs/zh`,英文文档放在 `doc/docs/en`。 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/mcp-ecosystem/mcp-server-development.md b/doc/docs/zh/mcp-ecosystem/mcp-server-development.md new file mode 100644 index 000000000..11a8b790e --- /dev/null +++ b/doc/docs/zh/mcp-ecosystem/mcp-server-development.md @@ -0,0 +1,195 @@ +# MCP 服务器开发指南 + +本指南将帮助您使用 Python 和 FastMCP 框架开发自己的 MCP 服务器,并将其集成到 Nexent 平台中。 + +## 🌐 语言支持 + +MCP 协议支持多种编程语言,包括: + +- **Python** ⭐(推荐) +- **TypeScript** +- **Java** +- **Go** +- **Rust** +- 以及其他支持 MCP 协议的语言 + +### 为什么推荐 Python? + +本指南使用 **Python** 作为示例语言,原因如下: + +- ✅ **简单易学**:语法简洁,上手快速 +- ✅ **丰富的框架**:FastMCP 等框架让开发变得非常简单 +- ✅ **快速开发**:几行代码即可创建一个可用的 MCP 服务器 +- ✅ **生态完善**:丰富的第三方库支持 + +如果您熟悉其他语言,也可以使用相应的 MCP SDK 进行开发。但如果您是第一次开发 MCP 服务器,我们强烈推荐从 Python 开始。 + +## 📋 前置要求 + +在开始之前,请确保您已安装以下依赖: + +```bash +pip install fastmcp +``` + +## 🚀 快速开始 + +### 基础示例 + +以下是一个简单的 MCP 服务器示例,展示了如何使用 FastMCP 创建一个提供字符串处理功能的服务器: + +```python +from fastmcp import FastMCP + +# 创建MCP服务器实例 +mcp = FastMCP(name="String MCP Server") + +@mcp.tool( + name="calculate_string_length", + description="计算输入字符串的长度" +) +def calculate_string_length(text: str) -> int: + return len(text) + +@mcp.tool( + name="to_uppercase", + description="将字符串转换为大写" +) +def to_uppercase(text: str) -> str: + return text.upper() + +@mcp.tool( + name="to_lowercase", + description="将字符串转换为小写" +) +def to_lowercase(text: str) -> str: + return text.lower() + +if __name__ == "__main__": + # 使用SSE协议启动服务 + mcp.run(transport="sse", port=8000) +``` + +### 运行服务器 + +保存上述代码为 `mcp_server.py`,然后运行: + +```bash +python mcp_server.py +``` + +您将看到 MCP server 成功启动,且 Server URL 为`http://127.0.0.1:8000/sse`。 + +## 🔌 在 Nexent 中集成 MCP 服务 + +开发并启动 MCP 服务后,您需要将其添加到 Nexent 平台中进行使用: + +### 步骤 1:启动 MCP 服务器 + +确保您的 MCP 服务器正在运行,并记录其访问地址(例如:`http://127.0.0.1:8000/sse`)。 + +### 步骤 2:在 Nexent 中添加 MCP 服务 + +1. 进入 **[智能体开发](../user-guide/agent-development.md)** 页面 +2. 在"选择Agent的工具"页签右侧,点击"**MCP配置**" +3. 在弹出的配置窗口中,输入服务器名称和服务器URL + - ⚠️ **注意**: + 1. 服务器名称只能包含英文字母和数字,不能包含空格、下划线等其他字符; + 2. 如果您使用 Docker 容器部署 Nexent,并且 MCP 服务器运行在宿主机上,需要将 `127.0.0.1` 替换为 `host.docker.internal`,即`http://host.docker.internal:8000`才可成功访问宿主机上运行的 MCP 服务器。 +4. 点击"**添加**"按钮完成配置 + +### 步骤 3:使用 MCP 工具 + +配置完成后,在创建或编辑智能体时,您可以在工具列表中找到并选择您添加的 MCP 工具。 + +## 🔧 包装现有业务 + +如果您已有现成的业务代码,想要将其包装成 MCP 服务,只需要在工具函数中进行调用即可。这种方式可以快速将现有服务集成到 MCP 生态系统中。 + +### 示例:包装 REST API + +如果您的业务逻辑已有现成Restful API: + +```python +from fastmcp import FastMCP +import requests + +# 创建MCP服务器实例 +mcp = FastMCP("Course Statistics Server") + +@mcp.tool( + name="get_course_statistics", + description="根据课程号获取某门课程的成绩统计信息(包含平均分、最高分、最低分等)" +) +def get_course_statistics(course_id: str) -> str: + # 调用现有的业务API + api_url = "https://your-school-api.com/api/courses/statistics" + response = requests.get(api_url, params={"course_id": course_id}) + + # 处理响应并返回结果 + if response.status_code == 200: + data = response.json() + stats = data.get("statistics", {}) + return f"课程 {course_id} 成绩统计:\n平均分: {stats.get('average', 'N/A')}\n最高分: {stats.get('max', 'N/A')}\n最低分: {stats.get('min', 'N/A')}\n总人数: {stats.get('total_students', 'N/A')}" + else: + return f"API调用失败: {response.status_code}" + +if __name__ == "__main__": + # 使用SSE协议启动服务 + mcp.run(transport="sse", port=8000) +``` + +### 示例:包装内部服务 + +如果您的业务逻辑在本地服务中: + +```python +from fastmcp import FastMCP +from your_school_module import query_course_statistics + +# 创建MCP服务器实例 +mcp = FastMCP("Course Statistics Server") + +@mcp.tool( + name="get_course_statistics", + description="根据课程号获取某门课程的成绩统计信息(包含平均分、最高分、最低分等)" +) +def get_course_statistics(course_id: str) -> str: + # 直接调用内部业务函数 + try: + stats = query_course_statistics(course_id) + return f"课程 {course_id} 成绩统计:\n平均分: {stats.get('average', 'N/A')}\n最高分: {stats.get('max', 'N/A')}\n最低分: {stats.get('min', 'N/A')}\n总人数: {stats.get('total_students', 'N/A')}" + except Exception as e: + return f"查询成绩统计时出错: {str(e)}" + +if __name__ == "__main__": + # 使用SSE协议启动服务 + mcp.run(transport="sse", port=8000) +``` + +## 📚 更多资源 + +### Python + +- [FastMCP 文档](https://github.com/modelcontextprotocol/python-sdk)(本指南使用的框架) + +### 其他语言 + +- [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) +- [MCP Java SDK](https://github.com/modelcontextprotocol/java-sdk) +- [MCP Go SDK](https://github.com/modelcontextprotocol/go-sdk) +- [MCP Rust SDK](https://github.com/modelcontextprotocol/rust-sdk) + +### 通用资源 + +- [MCP 协议规范](https://modelcontextprotocol.io/) +- [Nexent 智能体开发指南](../user-guide/agent-development.md) +- [MCP 工具生态系统概览](./overview.md) + +## 🆘 获取帮助 + +如果您在开发 MCP 服务器时遇到问题,可以: + +1. 查看我们的 **[常见问题](../getting-started/faq.md)** +2. 在 [GitHub Discussions](https://github.com/ModelEngine-Group/nexent/discussions) 中提问 +3. 参考 [ModelScope MCP Marketplace](https://www.modelscope.cn/mcp) 中的示例服务器 diff --git a/doc/docs/zh/opensource-memorial-wall.md b/doc/docs/zh/opensource-memorial-wall.md index 6592039ff..d1c9a3ccc 100644 --- a/doc/docs/zh/opensource-memorial-wall.md +++ b/doc/docs/zh/opensource-memorial-wall.md @@ -15,29 +15,6 @@ 每条消息应包含您的姓名/昵称和日期。 请保持消息的礼貌和尊重,符合我们的行为准则。 --> -::: china-king-hs - 2025-11-20 -希望能正常使用nexent -::: - -::: info happyzhang - 2025-11-13 -也许我们正见证着未来的“后起之秀”😀 -::: - -::: info KevinLeeNJ - 2025-11-13 -来参加华为ICT大赛的,nexent很不错,希望后续能有更多功能! -::: - -::: info lzysleep - 2025-11-7 -非常不错的项目,很适合快速上手搭建自己的Agent,赞赞赞! -::: - -::: info fishcat - 2025-10-31 -很好的项目,希望蒸蒸日上 -::: - -::: tip xiaomi250 - 2025-10-18 -打算冲一波 ICT 大赛!正好借着这个机会多捣鼓捣鼓,把我的技术再升个级,想想还挺有意思的~ -::: ::: tip aibito - 某创业公司后端开发 - 2025-05-18 我们是一家只有 15 人的小公司,之前一直想做智能客服但技术门槛太高。发现 Nexent 后如获至宝!20+ 文件格式支持让我们轻松处理用户上传的各种文档,多模态对话功能完美解决了语音客服需求。最重要的是,我们的产品经理现在也能直接用自然语言调整智能体逻辑,开发效率提升了好几倍! @@ -47,20 +24,12 @@ 第一次玩开源项目,nexent真的挺好用的!用自然语言就能搞智能体,比我想象的简单多了 ::: -::: tip xingkongF001 - 2025-10-30 -来参加华为ICT的,希望一切顺利!!! -::: - ::: tip bytedancer2023 - 2025-05-18 我们小公司想做客服机器人,之前技术门槛太高了。nexent的多文件格式支持真的帮了大忙,产品经理现在也能自己调智能体了哈哈 ::: -::: Ottwo - 2025-10-27 -nexent真是中国学生的Agent启蒙老师!在校大学生充分学习了很多,感谢有这么好的项目! -::: - -::: info 小刘 - 前端转全栈的独立开发者 - 2025-06-22 -说来惭愧,做了 3 年前端,第一次参与开源项目竟然是通过给文档修 typo 开始的😅。但 Nexent 的社区真的很友好,群里耐心回答我的每个问题。现在我不仅学会了 FastAPI 后端开发,还用 Nexent 做了个人知识管理系统。实时文件导入和自动摘要功能简直是我的第二大脑!感谢开源让我成长这么多。 +::: info saladjay - 清华大学计算机系 - 2025-06-15 +第一次接触开源项目就是 Nexent!作为 AI 专业的研究生,看到"零代码生成智能体"的概念就被吸引了。用自然语言就能创建智能体,这对我做自定义的学术研究挺方便的。现在我用 Nexent 搭建了一个论文总结助手,MCP 工具生态系统让集成各种学术数据库变得超级简单。 ::: ::: warning researcher_anon - 2025-06-20 @@ -71,12 +40,8 @@ nexent真是中国学生的Agent启蒙老师!在校大学生充分学习了很 第一次贡献开源是改了个typo...社区很友好,现在用nexent做知识管理,感觉像有了第二个大脑! ::: -::: info saladjay - 清华大学计算机系 - 2025-06-15 -第一次接触开源项目就是 Nexent!作为 AI 专业的研究生,看到"零代码生成智能体"的概念就被吸引了。用自然语言就能创建智能体,这对我做自定义的学术研究挺方便的。现在我用 Nexent 搭建了一个论文总结助手,MCP 工具生态系统让集成各种学术数据库变得超级简单。 -::: - -::: info ai_lover - 2025-07-28 -零代码真的做到了!我就说了句话就做出来智能体,pdf word都能处理,太强了 +::: info 小刘 - 前端转全栈的独立开发者 - 2025-06-22 +说来惭愧,做了 3 年前端,第一次参与开源项目竟然是通过给文档修 typo 开始的😅。但 Nexent 的社区真的很友好,群里耐心回答我的每个问题。现在我不仅学会了 FastAPI 后端开发,还用 Nexent 做了个人知识管理系统。实时文件导入和自动摘要功能简直是我的第二大脑!感谢开源让我成长这么多。 ::: ::: tip 产品汪 - 2025-07-01 @@ -91,6 +56,10 @@ nexent真是中国学生的Agent启蒙老师!在校大学生充分学习了很 就是来留个脚印 👍 项目不错,给个star~ ::: +::: info ai_lover - 2025-07-28 +零代码真的做到了!我就说了句话就做出来智能体,pdf word都能处理,太强了 +::: + ::: info cokefish - 2025-08-05 Nexent的自然语言生成Agent以及多智能体协同是我一直在研究的方向,AI的护城河,个人一直认为是提示词和上下文,AI如同有霸王之力的幼儿,提示词教会他如何使用力量,而上下文让他能记住,Agent是包裹三者的容器,Nexent则赋予Agent更多的可能性 ::: @@ -115,7 +84,7 @@ Nexent的自然语言生成Agent以及多智能体协同是我一直在研究的 在网站上搜AI Agent看到了开源项目,帮了很大忙,是智能体学习路上的一次有意义的实践! ::: -::: info EXUAN0312 - 2024-01-15 +::: info EXUAN0312 - 2024-09-12 我要参加华为ICT大赛了!!! ::: @@ -135,14 +104,14 @@ Nexent的自然语言生成Agent以及多智能体协同是我一直在研究的 遇到一个搞不懂的问题, Phinéase 帮忙连线解决问题, 非常棒的体验. ::: -::: info blxh - 2025-09-25 -参加华为ICT大赛来的!希望有个美好的体验! -::: - ::: tip fy-create - 2025-09-22 很有意思的项目~ ::: +::: info blxh - 2025-09-25 +参加华为ICT大赛来的!希望有个美好的体验! +::: + ::: info fc6657 - 2025-09-27 希望借助参加ict大赛的机会,提高自己的技术 ::: @@ -183,68 +152,39 @@ Nexent的自然语言生成Agent以及多智能体协同是我一直在研究的 第一次接触智能体编排,是为了参加华为ICT大赛而了解 Nexent 的。 没想到入门比想象中容易,文档也写得很清晰。 ::: -::: tip YuXiaoLoong - 2025-10-27 -Nexent是一个十分便利的开发平台,文档清晰,工具齐全,有幸能用上这么好用的平台,希望能在这个平台上学到更多技术和思想。 -::: - -::: tip hud0567 - 2025-10-17 -第一次接触这个平台 入门超级艰难 很智能化 +::: info nobody - 2025-10-15 +参加ICT大赛来了解 Nexent,这真的是个很好的平台,未来一起前行吧 +参加ICT大赛来了解 Nexent,好难弄啊啊啊,电脑硬盘容量不够了,然而硬盘价格还没下跌,该死的贩子 +通过ict大赛了解到了Nexent平台,很惊讶居然还有这么方便的平台,希望以后可以一起努力 +参加华为ICT,学习Nexent,AI改变你和我,赋能未来! +就是来留个脚印 👍 项目不错,给个star~ +感叹科技的跃迁,大大降低我学习的难度,是我进步飞快 ::: ::: info jjcc6 - 2025-10-15 希望能参加ict大赛长长见识,提高水平~ ::: -::: info niceman - 2025-10-27  -希望能参加ict大赛可以学习到更多知识,感谢 Nexent 让我踏上了开源之旅~ - -::: info violet-dq - 2025-10-28  -想要自己尝试搭建智能体,感叹Nexent的功能如此强大! +::: info jjcc6 - 2025-10-15 +希望能参加ict大赛长长见识,提高水平~ ::: -::: info 龙城三少 - 2025-10-29 -中华有为 +::: tip shiou - 2025-10-17 +感谢nexent让我了解了开源项目,帮助我快速上手 ::: -::: info zhouyin2516 - 2025-10-24 -希望能借助 Nexent 开发一个智能问答助手! +::: tip hud0567 - 2025-10-17 +第一次接触这个平台 入门超级艰难 很智能化 ::: -::: info tanzitong - 2025-10-24 -通过ict大赛接触到的这个平台,之前用过dify,coze,n8n等智能体平台,对比之下Nexent显得更加简洁和高效 +::: tip xiaomi250 - 2025-10-18 +打算冲一波 ICT 大赛!正好借着这个机会多捣鼓捣鼓,把我的技术再升个级,想想还挺有意思的~ ::: ::: info Nebula-11 - 2025-10-22 第一次用Mexent参加华为ict大赛,希望发展越来越好。 ::: -::: info feixin - 2025-10-24 -希望能一直好好做下去,有机会的话,我也会试着提交pr,加油!! -::: - -::: info 916443155@qq.com - 2025-10-25 -希望能参加ict大赛长长见识,提高水平~ -::: - -::: info nobody - 2025-10-15 -参加ICT大赛来了解 Nexent,这真的是个很好的平台,未来一起前行吧 -参加ICT大赛来了解 Nexent,好难弄啊啊啊,电脑硬盘容量不够了,然而硬盘价格还没下跌,该死的贩子 -通过ict大赛了解到了Nexent平台,很惊讶居然还有这么方便的平台,希望以后可以一起努力 -参加华为ICT,学习Nexent,AI改变你和我,赋能未来! -就是来留个脚印 👍 项目不错,给个star~ -感叹科技的跃迁,大大降低我学习的难度,是我进步飞快 -::: - -::: info yang 2025-11-02 -Nexent功能如此之强大,给我很多帮助,感谢开发者!厉害 -::: - -::: info y-dq - 2025-10-28  -想要自己尝试搭建智能体,感叹Nexent的功能如此强大! -::: tip cai7777 - 2025-10 23 -参加ICT大赛来了解 Nexent -::: - ::: tip wulong - 2025-10-22 出发华为!感谢 Nexent 一起赋能! ::: @@ -269,40 +209,52 @@ Nexent功能如此之强大,给我很多帮助,感谢开发者!厉害 小白勇闯华为ICT大赛,感谢nexent平台支持 ::: -::: info jjcc6 - 2025-10-15 -希望能参加ict大赛长长见识,提高水平~ +::: tip cai7777 - 2025-10-23 +参加ICT大赛来了解 Nexent ::: -::: tip shiou - 2025-10-17 -感谢nexent让我了解了开源项目,帮助我快速上手 +::: tip iocion - 2025-10-24 +第一次在了解到nexent平台,看到现在ai发展对智能化的影响巨大,以往对于ai入门就是复杂的算法逻辑,现在可以更快的入门,文档写的也很清晰,希望model engine社区可以不断完善和壮大。 +::: + +::: info zhouyin2516 - 2025-10-24 +希望能借助 Nexent 开发一个智能问答助手! +::: + +::: info tanzitong - 2025-10-24 +通过ict大赛接触到的这个平台,之前用过dify,coze,n8n等智能体平台,对比之下Nexent显得更加简洁和高效 +::: + +::: info feixin - 2025-10-24 +希望能一直好好做下去,有机会的话,我也会试着提交pr,加油!! ::: ::: tip shev - 2025-10-25 感谢nexent我开始来了解开源项目,第一次参加华为ict大赛 ::: -::: tip iocion - 2025-10-24 -第一次在了解到nexent平台,看到现在ai发展对智能化的影响巨大,以往对于ai入门就是复杂的算法逻辑,现在可以更快的入门,文档写的也很清晰,希望model engine社区可以不断完善和壮大。 +::: info 916443155@qq.com - 2025-10-25 +希望能参加ict大赛长长见识,提高水平~ ::: -::: tip iocion - 2025-11-05 -本地快速搭建,离不开大家开源的贡献,希望model engine社区可以不断完善和壮大。 +::: tip YuXiaoLoong - 2025-10-27 +Nexent是一个十分便利的开发平台,文档清晰,工具齐全,有幸能用上这么好用的平台,希望能在这个平台上学到更多技术和思想。 ::: -::: info zhangwt0601 - 2025-11-1 -希望借助开源大赛以及Nexent平台提高自己的技术 +::: info niceman - 2025-10-27  +希望能参加ict大赛可以学习到更多知识,感谢 Nexent 让我踏上了开源之旅~ ::: -::: info - 2025-11-01 -来参加ict大赛培训大会,努力学习:) +::: info Ottwo - 2025-10-27 +nexent真是中国学生的Agent启蒙老师!在校大学生充分学习了很多,感谢有这么好的项目! ::: -::: info kisskisszhou - 2025-10-31 -感谢 Nexent 让我踏上了开源之旅!希望参加ict大赛来提高自己的能力 +::: info violet-dq - 2025-10-28  +想要自己尝试搭建智能体,感叹Nexent的功能如此强大! ::: -::: Pharaoh-C - 2025-11-2 -研究多智能体协作方向,Nexent 的多智能体协同功能让我眼前一亮,这在学术研究中太重要了。我用 Nexent 搭建了一个AI赛博医生,能够自动索引中西医文献、给一些症状做出解答。开源的力量真的很强大,希望更多研究者能加入进来! +::: info y-dq - 2025-10-28  +想要自己尝试搭建智能体,感叹Nexent的功能如此强大! ::: ::: info xUxIAOrUI -2025-10-28 @@ -317,7 +269,43 @@ Nexent功能如此之强大,给我很多帮助,感谢开发者!厉害 感谢 Nexent 让我踏上了开源之旅!希望能参加ict大赛长长见识。项目不错,给个star~ ::: -:::info XxHosxX - 2025-11-5 +::: info 龙城三少 - 2025-10-29 +中华有为 +::: + +::: tip xingkongF001 - 2025-10-30 +来参加华为ICT的,希望一切顺利!!! +::: + +::: info fishcat - 2025-10-31 +很好的项目,希望蒸蒸日上 +::: + +::: info kisskisszhou - 2025-10-31 +感谢 Nexent 让我踏上了开源之旅!希望参加ict大赛来提高自己的能力 +::: + +::: info zhangwt0601 - 2025-11-01 +希望借助开源大赛以及Nexent平台提高自己的技术 +::: + +::: info user - 2025-11-01 +来参加ict大赛培训大会,努力学习:) +::: + +::: info yang 2025-11-02 +Nexent功能如此之强大,给我很多帮助,感谢开发者!厉害 +::: + +::: info Pharaoh-C - 2025-11-2 +研究多智能体协作方向,Nexent 的多智能体协同功能让我眼前一亮,这在学术研究中太重要了。我用 Nexent 搭建了一个AI赛博医生,能够自动索引中西医文献、给一些症状做出解答。开源的力量真的很强大,希望更多研究者能加入进来! +::: + +::: tip iocion - 2025-11-05 +本地快速搭建,离不开大家开源的贡献,希望model engine社区可以不断完善和壮大。 +::: + +:::info XxHosxX - 2025-11-05 希望参与ICT大赛以及Nexent平台提升自己的能力:) ::: @@ -333,6 +321,10 @@ Nexent功能如此之强大,给我很多帮助,感谢开发者!厉害 期待能使用Nexent成为智能体开发大佬 ::: +::: info lzysleep - 2025-11-7 +非常不错的项目,很适合快速上手搭建自己的Agent,赞赞赞! +::: + ::: info xiaochenIpter - 2025-11-08 希望能参加ict大赛可以学习到更多知识,感谢 Nexent 让我踏上了开源之旅!平台开发智能体的能力十分强大,希望能够学习到更多东西! ::: @@ -341,11 +333,11 @@ Nexent功能如此之强大,给我很多帮助,感谢开发者!厉害 "🎉 很高兴加入Nexent社区!作为新贡献者,我期待在代码优化、文档完善或测试反馈中尽一份力。#NexentCommunity 让我们一起构建更棒的项目!🚀" ::: -::: pxqn-xing - 2025-11-08 +::: info pxqn-xing - 2025-11-08 感谢Nexent让我参与ICT大赛以提升自己的能力 ::: -::: QIYAN - 2025-11-09 +::: info QIYAN - 2025-11-09 体验华为AI技术的一天 ::: @@ -401,6 +393,14 @@ Nexent功能如此之强大,给我很多帮助,感谢开发者!厉害 我又来了,通过华为ICT了解到nexent,正在学习中... ::: +::: info happyzhang - 2025-11-13 +也许我们正见证着未来的“后起之秀”😀 +::: + +::: info KevinLeeNJ - 2025-11-13 +来参加华为ICT大赛的,nexent很不错,希望后续能有更多功能! +::: + ::: info user - 2025-11-14 我要参加华为ICT ::: @@ -425,7 +425,7 @@ Nexent功能如此之强大,给我很多帮助,感谢开发者!厉害 感谢 Nexent 让我踏上了开源之旅!这个项目的文档真的很棒,帮助我快速上手。 ::: -::: tip user - 2025-11-19 +::: tip user - 2025-11-19 感谢 Nexent 让我第一次感受到智能体 希望参加ICT比赛过程中可以学到更多知识 能够对该领域有更多的了解和认识! ::: @@ -437,6 +437,10 @@ Nexent功能如此之强大,给我很多帮助,感谢开发者!厉害 感谢 Nexent 让我踏上了开源之旅!给我一个机会制作智能体 ::: +::: info 开源小白 - 2025-11-19 +感谢 Nexent 让我踏上了开源之旅!这个项目的文档真的很棒,帮助我快速上手。 +::: + ::: info chengyudan - 2025-10-20 感谢 Nexent 让我踏上了开源之旅! ::: @@ -444,3 +448,107 @@ Nexent功能如此之强大,给我很多帮助,感谢开发者!厉害 ::: info user - 2025-11-20 学习ai - agent非常好的项目,后面会持续输出贡献! ::: + +::: china-king-hs - 2025-11-20 +希望能正常使用nexent +::: + +::: info user - 2025-11-22 +感谢nexent这个开源项目 +::: + +::: tip xiaofu-2025-11-23 +xiaofu到此一游,感谢 Nexent 让我踏上了开源之旅! +::: + +::: info DUTBenjamin - 2025-11-23 +来参加华为ICT大赛的,正好借着这个机会多捣鼓捣鼓,学到更多东西,加油! +::: + +::: info dean-stock - 2025-11-23 +感谢nexent让我第一次接触到了智能体,让我从使用到创作智能体的转变。 +::: + +::: info user - 2025-11-23 +学习到ai了,很好用 +::: + +::: info chao - 2025-11-23 +使用 Nexent 开发了项目,MCP 工具集成特别强大,节省了大量开发时间! +::: + +::: info adasibi - 2025-11-23 +学习ai很好用,感谢 Nexent 让我踏上了开源之旅! +::: + +::: info user - 2025-11-23 +Nexent越来越好! +::: + +::: info DUTBenjamin - 2025-11-23 +来参加华为ICT大赛的,正好借着这个机会学到更多东西,加油! +::: + +::: info aurorahashcat - 2025-11-23 +nexent看起来超棒的自动化智能体构建平台,祝越来越好😀 +::: + +::: williamllk from SJTU - 2025-11-23 +感谢 Nexent 让我第一次制作智能体,尝试将AI4Science的理念付诸实践 +::: + +::: tip lostlight530 - 2025-11-24 +通过 Nexent 实现了 Router-Worker 架构的完美落地。无论是构建高情商的拟人化伴侣,还是处理严苛的结构化数据约束,这套框架都游刃有余。多智能体编排体验极佳! +::: + +::: info jackliu631 - 2025-11-25 +通过 Nexent 实现了AI产品的完美落地。智能体的编排使用体验非常好 +::: + +::: info June - 2025-11-26 +nexent智能体帮助我学到更多的东西,赞! +::: + +::: info sharkkk - 2025-11-26 +越来越好! +::: + +::: info SkyWalker - 2025-11-26 +第一次使用nexent,想借此更快入手ai应用开发呀! +::: + +:::info user - 2025-11-26 +Nexent开发者加油 +::: + +:::info NOSN - 2025-11-27 +Nexent越做越强大! +::: + +:::info Chenpi-Sakura - 2025-11-27 +开源共创未来! +::: + +:::info yy-cy - 2025-11-27 +Nexent加油 +::: + +:::info AstreoX - 2025-11-27 +感谢Nexent为智能体开发提出了更多可能! +::: + +:::info user - 2025-11-26 +祝nexent平台越做越胡奥 +::: + +::: info Phoebe246824 - 2025-11-27 +感谢 Nexent, 可以让我快速上手构建智能体,祝越来越好! +::: + +::: info user - 2025-11-27 +祝Nexent平台越做越好 +::: + +::: info kj - 2025-11-27 +祝越来越好 +::: diff --git a/doc/docs/zh/sdk/core/tools.md b/doc/docs/zh/sdk/core/tools.md index 9c4d3efe3..501ca0c24 100644 --- a/doc/docs/zh/sdk/core/tools.md +++ b/doc/docs/zh/sdk/core/tools.md @@ -28,6 +28,10 @@ - **GetEmailTool**: 通过 IMAP 的邮件获取工具 - **SendEmailTool**: 通过 SMTP 的邮件发送工具 +### 多模态工具 +- **AnalyzeTextFileTool**: 基于数据处理和大语言模型的文档问答工具 +- **AnalyzeImageTool**: 基于视觉语言模型的图片问答工具 + ## 🔧 工具共性特征 ### 1. 基础架构 diff --git a/doc/docs/zh/user-guide/agent-configuration.md b/doc/docs/zh/user-guide/agent-configuration.md deleted file mode 100644 index e6fabebde..000000000 --- a/doc/docs/zh/user-guide/agent-configuration.md +++ /dev/null @@ -1,103 +0,0 @@ -# 智能体配置 - -在智能体配置模块中,您可以创建、配置和管理智能体。智能体是Nexent的核心功能,它们能够理解您的需求并执行相应的任务。 - -## 🔧 Agent管理 - -### 创建智能体 - -在Agent管理页签下,点击“创建Agent”即可创建一个空白智能体,点击“退出创建”即可退出创建模式。 -如果您有现成的智能体配置,也可以导入使用: -1. 点击“导入Agent” -2. 在弹出的文件中选择智能体配置文件(JSON 格式) -3. 点击“打开”按钮,系统会验证配置文件的格式和内容,并显示导入的智能体信息 - -
- -
- -### 智能体列表 -在Agent管理页签下,您可以看到已创建的所有智能体列表,点击智能体会选中它以进行详细配置,再次点击则会取消选中。 - -## 👥 配置Agent能力 - -您可以为创建的智能体配置其他协作智能体,也可以为它配置可使用的工具,以赋予智能体能力完成复杂任务。 - -### 协作Agent - -1. 点击“协作Agent页签”下的加号,弹出可选择的智能体列表 -2. 在下拉列表中选择要添加的智能体 -3. 允许选择多个协作智能体 -4. 可点击×取消选择此智能体 - -
- -
- - -### 选择Agent的工具 -智能体可以使用各种工具来完成任务,如知识库检索、收发邮件、文件管理等本地工具,也可接入第三方MCP工具,或自定义工具。 - -1. 在“选择Agent的工具”页签右侧,点击“刷新工具”来刷新可用工具列表 -2. 选择想要添加工具所在的分组 -3. 查看分组下可选用的所有工具,可点击⚙查看工具描述,进行工具参数配置 -4. 点击工具名即可选中改工具,再次点击可取消选择 - -
- -
- -### 添加MCP工具 -Nexent支持您快速便捷地使用第三方MCP工具,丰富Agent能力。 - -1. 在“选择Agent的工具”页签右侧,点击“MCP配置”,可在弹窗中进行MCP服务器的配置,查看已配置的MCP服务器 -2. 输入服务器名称和服务器URL(目前仅支持SSE协议) -3. 点击“添加”按钮,即可完成添加 - -
- -
- -有许多三方服务如modelscope提供了MCP服务,您可以快速接入使用。 - -### 自定义工具 -您可参考以下指导文档,开发自己的工具,并接入Nexent使用,丰富Agent能力。 -- [LangChain 工具指南](../backend/tools/langchain) -- [MCP 工具开发](../backend/tools/mcp) -- [SDK 工具文档](../sdk/core/tools) - - -## 📝 描述业务逻辑 - -### 描述Agent应该如何工作 - -根据选择的协作Agent和工具,您现在可以用简洁的语言来描述,您希望这个Agent应该如何工作。Nexent会根据您的配置和描述,自动为您生成Agent名称、描述以及提示词等信息。 - -1. 在“描述Agent应该如何工作”下的编辑框中,输入简洁描述 -2. 点击“生成智能体”按钮,Nexent会为您生成Agent详细内容 -3. 您可在下方Agent详细内容中,针对自动生成的内容进行编辑微调 - -
- -
- -### 调试Agent -在完成初步Agent配置后,您可以对Agent进行调试,根据调试结果微调配置,持续提升Agent表现。 - -1. 在页面右下角点击"调试"按钮,弹出智能体调试页面 -2. 与智能体进行测试对话,观察智能体的响应和行为 -3. 查看对话表现和错误信息,根据测试结果优化智能体配置 - -### 管理Agent - -- **保存:** 在调试成功后,可点击右下角“保存”按钮,此智能体将会被保存,在后续对话页面中可以选用此智能体来进行对话。 -- **导出:** 可将调试成功的Agent配置到处为JSON配置文件,在创建Agent时可以使用此JSON文件以导入的方式创建副本 -- **删除:** 删除智能体(谨慎操作) - -## 🚀 下一步 - -完成智能体配置后,您可以点击“完成配置”按钮,进入: - -1. **[对话页面](./chat-interface)** - 与智能体进行交互 - -如果您在智能体配置过程中遇到任何问题,请参考我们的 **[常见问题](../getting-started/faq)** 或加入我们的 [Discord 社区](https://discord.gg/tb5H3S3wyv) 获取支持。 \ No newline at end of file diff --git a/doc/docs/zh/user-guide/agent-development.md b/doc/docs/zh/user-guide/agent-development.md new file mode 100644 index 000000000..5f9eb0646 --- /dev/null +++ b/doc/docs/zh/user-guide/agent-development.md @@ -0,0 +1,151 @@ +# 智能体开发 + +在智能体开发页面中,您可以创建、配置和管理智能体。智能体是 Nexent 的核心功能,它们能够理解您的需求并执行相应的任务。 + +## 🔧 创建智能体 + +在 Agent 管理页签下,点击"创建 Agent"即可创建一个空白智能体,点击"退出创建"即可退出创建模式。 +如果您有现成的智能体配置,也可以导入使用: + +1. 点击"导入 Agent" +2. 在弹出的文件选择对话框中选择智能体配置文件(JSON 格式) +3. 点击"打开"按钮,系统会验证配置文件的格式和内容,并显示导入的智能体信息 + +
+ +
+ +## 👥 配置协作智能体/工具 + +您可以为创建的智能体配置其他协作智能体,也可以为它配置可使用的工具,以赋予智能体能力完成复杂任务。 + +### 🤝 协作 Agent + +1. 点击"协作 Agent"页签下的加号,弹出可选择的智能体列表 +2. 在下拉列表中选择要添加的智能体 +3. 允许选择多个协作智能体 +4. 可点击 × 取消选择此智能体 + +
+ +
+ +### 🛠️ 选择 Agent 的工具 + +智能体可以使用各种工具来完成任务,如知识库检索、文件解析、图片解析、收发邮件、文件管理等本地工具,也可接入第三方 MCP 工具,或自定义工具。 + +1. 在"选择 Agent 的工具"页签右侧,点击"刷新工具"来刷新可用工具列表 +2. 选择想要添加工具所在的分组 +3. 查看分组下可选用的所有工具,可点击 ⚙️ 查看工具描述,进行工具参数配置 +4. 点击工具名即可选中该工具,再次点击可取消选择 + - 如果工具有必备参数没有配置,选择时会弹出弹窗引导进行参数配置 + - 如果所有必备参数已配置完成,选择则会直接选中 + +
+ +
+ +> 💡 **小贴士**: +> 1. 请选择 `knowledge_base_search` 工具,启用知识库的检索功能。 +> 2. 请选择 `analyze_text_file` 工具,启用文档类、文本类文件的解析功能。 +> 3. 请选择 `analyze_image` 工具,启用图片类文件的解析功能。 +> +> 📚 想了解系统已经内置的所有本地工具能力?请参阅 [本地工具概览](./local-tools/index.md)。 + +### 🔌 添加 MCP 工具 + +Nexent 支持您快速便捷地使用第三方 MCP 工具,丰富 Agent 能力。 + +1. 在"选择 Agent 的工具"页签右侧,点击"MCP 配置",可在弹窗中进行 MCP 服务器的配置,查看已配置的 MCP 服务器 +2. 输入服务器名称和服务器 URL(目前仅支持 SSE 协议) + - ⚠️ **注意**:服务器名称只能包含英文字母和数字,不能包含空格、下划线等其他字符 +3. 点击"添加"按钮,即可完成添加 + +
+ +
+ +有许多第三方服务如 [ModelScope](https://www.modelscope.cn/mcp) 提供了 MCP 服务,您可以快速接入使用。 +您也可以自行开发 MCP 服务并接入 Nexent 使用,参考文档 [MCP 服务开发](../mcp-ecosystem/mcp-server-development.md)。 + +### ⚙️ 自定义工具 + +您可参考以下指导文档,开发自己的工具,并接入 Nexent 使用,丰富 Agent 能力。 + +- [LangChain 工具指南](../backend/tools/langchain) +- [MCP 工具开发](../backend/tools/mcp) +- [SDK 工具文档](../sdk/core/tools) + +### 🧪 工具测试 + +无论是什么类型的工具(内置工具、外部接入的 MCP 工具,还是自定义开发工具),Nexent 都提供了"工具测试"能力。如果您在创建 Agent 时不确定某个工具的效果,可以使用测试功能来验证工具是否按预期工作。 + +1. 点击工具的小齿轮按钮 ⚙️,进入工具的详细配置弹窗 +2. 首先确保已经配置了工具的必备参数(带红色星号的参数) +3. 在弹窗的左下角点击"工具测试"按钮 +4. 右侧会新弹出一个测试框 +5. 在测试框中输入测试工具的入参,例如: + - 测试本地知识库检索工具 `knowledge_base_search` 时,需要输入: + - 测试的 `query`,例如"维生素C的功效" + - 检索的模式 `search_mode`(默认为 `hybrid`) + - 目标检索的知识库列表 `index_names`,如 `["医疗", "维生素知识大全"]` + - 若不输入 `index_names`,则默认检索知识库页面所选中的全部知识库 +6. 输入完成后点击"执行测试"开始测试,并在下方查看测试结果 + +
+ +
+ +## 📝 描述业务逻辑 + +### ✍️ 描述 Agent 应该如何工作 + +根据选择的协作 Agent 和工具,您现在可以用简洁的语言来描述,您希望这个 Agent 应该如何工作。Nexent 会根据您的配置和描述,自动为您生成 Agent 名称、描述以及提示词等信息。 + +1. 在"描述 Agent 应该如何工作"下的编辑框中,输入简洁描述,如"你是一个专业的知识问答小助手,具备本地知识检索和联网检索能力,综合信息以回答用户问题" +2. 点击"生成智能体"按钮,Nexent 会为您生成 Agent 详细内容,包括基础信息以及提示词(角色、使用要求、示例) +3. 您可在下方 Agent 详细内容中,针对自动生成的内容(特别是提示词)进行编辑微调 + +
+ +
+ +### 🐛 调试与保存 + +在完成初步 Agent 配置后,您可以对 Agent 进行调试,根据调试结果微调提示词,持续提升 Agent 表现。 + +1. 在页面右下角点击"调试"按钮,弹出智能体调试页面 +2. 与智能体进行测试对话,观察智能体的响应和行为 +3. 查看对话表现和错误信息,根据测试结果优化智能体提示词 + +调试成功后,可点击右下角"保存"按钮,此智能体将会被保存并出现在智能体列表中。 + +## 📋 管理智能体 + +在左侧智能体列表中,您可对已有的智能体进行以下操作: + +### 🔗 查看调用关系 + +查看智能体所使用的协作智能体/工具,以树状图形式明晰查看智能体调用关系。 + +
+ +
+ +### 📤 导出 + +可将调试成功的智能体导出为 JSON 配置文件,在创建 Agent 时可以使用此 JSON 文件以导入的方式创建副本。 + +### 🗑️ 删除 + +删除智能体(不可撤销,请谨慎操作)。 + +## 🚀 下一步 + +完成智能体开发后,您可以: + +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/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..288d83768 --- /dev/null +++ b/doc/docs/zh/user-guide/agent-space.md @@ -0,0 +1,69 @@ +# 智能体空间 + +智能体空间是您管理所有已开发智能体的中心。在这里,您可以卡片形式查看所有智能体及智能体详细配置,进行智能体删除、导出等管理操作。 +![智能体空间](./assets/agent-space/agent-space.png) + +## 📦 智能体卡片展示 + +智能体空间以卡片形式展示所有已开发好的智能体,每个卡片包含: + +- **智能体图标**:智能体的标识图标 +- **智能体名称**:智能体的显示名称 +- **智能体描述**:智能体的功能描述 +- **智能体状态**:智能体是否可用的状态 +- **操作按钮**:快速操作入口 + +## 🔧 管理智能体 + +在智能体空间中,您可以对每个智能体进行以下操作: + +### 查看智能体详细信息 + +点击智能体卡片,即可查看智能体详细信息: + +- **基础信息**:智能体ID、名称、描述、状态等 +- **模型配置**:模型名称、最大部署、业务逻辑模型名称等 +- **提示词**:包含角色提示词、约束提示词、示例提示词、以及原始业务描述 +- **工具**:配置的工具 +- **子智能体**:配置的子智能体 + +![智能体详细信息](./assets/agent-space/agent-details.png) + +### 编辑智能体 + +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/assets/agent-development/agent-relationship.png b/doc/docs/zh/user-guide/assets/agent-development/agent-relationship.png new file mode 100644 index 000000000..6e3f4cf70 Binary files /dev/null and b/doc/docs/zh/user-guide/assets/agent-development/agent-relationship.png differ diff --git a/doc/docs/zh/user-guide/assets/agent/generate-agent.png b/doc/docs/zh/user-guide/assets/agent-development/generate-agent.png similarity index 100% rename from doc/docs/zh/user-guide/assets/agent/generate-agent.png rename to doc/docs/zh/user-guide/assets/agent-development/generate-agent.png diff --git a/doc/docs/zh/user-guide/assets/agent/import.png b/doc/docs/zh/user-guide/assets/agent-development/import.png similarity index 100% rename from doc/docs/zh/user-guide/assets/agent/import.png rename to doc/docs/zh/user-guide/assets/agent-development/import.png diff --git a/doc/docs/zh/user-guide/assets/agent/mcp.png b/doc/docs/zh/user-guide/assets/agent-development/mcp.png similarity index 100% rename from doc/docs/zh/user-guide/assets/agent/mcp.png rename to doc/docs/zh/user-guide/assets/agent-development/mcp.png diff --git a/doc/docs/zh/user-guide/assets/agent/set-collaboration.png b/doc/docs/zh/user-guide/assets/agent-development/set-collaboration.png similarity index 100% rename from doc/docs/zh/user-guide/assets/agent/set-collaboration.png rename to doc/docs/zh/user-guide/assets/agent-development/set-collaboration.png diff --git a/doc/docs/zh/user-guide/assets/agent/set-tool.png b/doc/docs/zh/user-guide/assets/agent-development/set-tool.png similarity index 100% rename from doc/docs/zh/user-guide/assets/agent/set-tool.png rename to doc/docs/zh/user-guide/assets/agent-development/set-tool.png diff --git a/doc/docs/zh/user-guide/assets/agent-development/tool-test-run.png b/doc/docs/zh/user-guide/assets/agent-development/tool-test-run.png new file mode 100644 index 000000000..d06503ae0 Binary files /dev/null and b/doc/docs/zh/user-guide/assets/agent-development/tool-test-run.png differ diff --git a/doc/docs/zh/user-guide/assets/agent-space/agent-details.png b/doc/docs/zh/user-guide/assets/agent-space/agent-details.png new file mode 100644 index 000000000..bc6ed1f49 Binary files /dev/null and b/doc/docs/zh/user-guide/assets/agent-space/agent-details.png differ diff --git a/doc/docs/zh/user-guide/assets/agent-space/agent-space.png b/doc/docs/zh/user-guide/assets/agent-space/agent-space.png new file mode 100644 index 000000000..1937b26e4 Binary files /dev/null and b/doc/docs/zh/user-guide/assets/agent-space/agent-space.png differ diff --git a/doc/docs/zh/user-guide/assets/home-page/homepage.png b/doc/docs/zh/user-guide/assets/home-page/homepage.png new file mode 100644 index 000000000..7de1ffc07 Binary files /dev/null and b/doc/docs/zh/user-guide/assets/home-page/homepage.png differ diff --git a/doc/docs/zh/user-guide/assets/knowledge/create-knowledge-base.png b/doc/docs/zh/user-guide/assets/knowledge-base/create-knowledge-base.png similarity index 100% rename from doc/docs/zh/user-guide/assets/knowledge/create-knowledge-base.png rename to doc/docs/zh/user-guide/assets/knowledge-base/create-knowledge-base.png diff --git a/doc/docs/zh/user-guide/assets/knowledge/delete-knowledge-base.png b/doc/docs/zh/user-guide/assets/knowledge-base/delete-knowledge-base.png similarity index 100% rename from doc/docs/zh/user-guide/assets/knowledge/delete-knowledge-base.png rename to doc/docs/zh/user-guide/assets/knowledge-base/delete-knowledge-base.png diff --git a/doc/docs/zh/user-guide/assets/knowledge/knowledge-base-file-list.png b/doc/docs/zh/user-guide/assets/knowledge-base/knowledge-base-file-list.png similarity index 100% rename from doc/docs/zh/user-guide/assets/knowledge/knowledge-base-file-list.png rename to doc/docs/zh/user-guide/assets/knowledge-base/knowledge-base-file-list.png diff --git a/doc/docs/zh/user-guide/assets/knowledge/knowledge-base-summary.png b/doc/docs/zh/user-guide/assets/knowledge-base/knowledge-base-summary.png similarity index 100% rename from doc/docs/zh/user-guide/assets/knowledge/knowledge-base-summary.png rename to doc/docs/zh/user-guide/assets/knowledge-base/knowledge-base-summary.png diff --git a/doc/docs/zh/user-guide/assets/knowledge/summary-knowledge-base.png b/doc/docs/zh/user-guide/assets/knowledge-base/summary-knowledge-base.png similarity index 100% rename from doc/docs/zh/user-guide/assets/knowledge/summary-knowledge-base.png rename to doc/docs/zh/user-guide/assets/knowledge-base/summary-knowledge-base.png diff --git a/doc/docs/zh/user-guide/assets/memory/add-mem.png b/doc/docs/zh/user-guide/assets/memory-management/add-mem.png similarity index 100% rename from doc/docs/zh/user-guide/assets/memory/add-mem.png rename to doc/docs/zh/user-guide/assets/memory-management/add-mem.png diff --git a/doc/docs/zh/user-guide/assets/memory/delete-mem.png b/doc/docs/zh/user-guide/assets/memory-management/delete-mem.png similarity index 100% rename from doc/docs/zh/user-guide/assets/memory/delete-mem.png rename to doc/docs/zh/user-guide/assets/memory-management/delete-mem.png diff --git a/doc/docs/zh/user-guide/assets/memory/mem-config.png b/doc/docs/zh/user-guide/assets/memory-management/mem-config.png similarity index 100% rename from doc/docs/zh/user-guide/assets/memory/mem-config.png rename to doc/docs/zh/user-guide/assets/memory-management/mem-config.png diff --git a/doc/docs/zh/user-guide/assets/model/add-model-batch.png b/doc/docs/zh/user-guide/assets/model-management/add-model-batch.png similarity index 100% rename from doc/docs/zh/user-guide/assets/model/add-model-batch.png rename to doc/docs/zh/user-guide/assets/model-management/add-model-batch.png diff --git a/doc/docs/zh/user-guide/assets/model/add-model.png b/doc/docs/zh/user-guide/assets/model-management/add-model.png similarity index 100% rename from doc/docs/zh/user-guide/assets/model/add-model.png rename to doc/docs/zh/user-guide/assets/model-management/add-model.png diff --git a/doc/docs/zh/user-guide/assets/app/app-name-description-setting.png b/doc/docs/zh/user-guide/assets/model-management/app-name-description-setting.png similarity index 100% rename from doc/docs/zh/user-guide/assets/app/app-name-description-setting.png rename to doc/docs/zh/user-guide/assets/model-management/app-name-description-setting.png diff --git a/doc/docs/zh/user-guide/assets/app/customized-app-icon-setting.png b/doc/docs/zh/user-guide/assets/model-management/customized-app-icon-setting.png similarity index 100% rename from doc/docs/zh/user-guide/assets/app/customized-app-icon-setting.png rename to doc/docs/zh/user-guide/assets/model-management/customized-app-icon-setting.png diff --git a/doc/docs/zh/user-guide/assets/model/delete-model-1.png b/doc/docs/zh/user-guide/assets/model-management/delete-model-1.png similarity index 100% rename from doc/docs/zh/user-guide/assets/model/delete-model-1.png rename to doc/docs/zh/user-guide/assets/model-management/delete-model-1.png diff --git a/doc/docs/zh/user-guide/assets/model/delete-model-2.png b/doc/docs/zh/user-guide/assets/model-management/delete-model-2.png similarity index 100% rename from doc/docs/zh/user-guide/assets/model/delete-model-2.png rename to doc/docs/zh/user-guide/assets/model-management/delete-model-2.png diff --git a/doc/docs/zh/user-guide/assets/model/edit-model-1.png b/doc/docs/zh/user-guide/assets/model-management/edit-model-1.png similarity index 100% rename from doc/docs/zh/user-guide/assets/model/edit-model-1.png rename to doc/docs/zh/user-guide/assets/model-management/edit-model-1.png diff --git a/doc/docs/zh/user-guide/assets/model/edit-model-2.png b/doc/docs/zh/user-guide/assets/model-management/edit-model-2.png similarity index 100% rename from doc/docs/zh/user-guide/assets/model/edit-model-2.png rename to doc/docs/zh/user-guide/assets/model-management/edit-model-2.png diff --git a/doc/docs/zh/user-guide/assets/model/edit-model-3.png b/doc/docs/zh/user-guide/assets/model-management/edit-model-3.png similarity index 100% rename from doc/docs/zh/user-guide/assets/model/edit-model-3.png rename to doc/docs/zh/user-guide/assets/model-management/edit-model-3.png diff --git a/doc/docs/zh/user-guide/assets/model/edit-model-4.png b/doc/docs/zh/user-guide/assets/model-management/edit-model-4.png similarity index 100% rename from doc/docs/zh/user-guide/assets/model/edit-model-4.png rename to doc/docs/zh/user-guide/assets/model-management/edit-model-4.png diff --git a/doc/docs/zh/user-guide/assets/model/edit-model-5.png b/doc/docs/zh/user-guide/assets/model-management/edit-model-5.png similarity index 100% rename from doc/docs/zh/user-guide/assets/model/edit-model-5.png rename to doc/docs/zh/user-guide/assets/model-management/edit-model-5.png diff --git a/doc/docs/zh/user-guide/assets/model/edit-model-6.png b/doc/docs/zh/user-guide/assets/model-management/edit-model-6.png similarity index 100% rename from doc/docs/zh/user-guide/assets/model/edit-model-6.png rename to doc/docs/zh/user-guide/assets/model-management/edit-model-6.png diff --git a/doc/docs/zh/user-guide/assets/app/homepage.png b/doc/docs/zh/user-guide/assets/model-management/homepage.png similarity index 100% rename from doc/docs/zh/user-guide/assets/app/homepage.png rename to doc/docs/zh/user-guide/assets/model-management/homepage.png diff --git a/doc/docs/zh/user-guide/assets/app/predefined-app-icon-setting.png b/doc/docs/zh/user-guide/assets/model-management/predefined-app-icon-setting.png similarity index 100% rename from doc/docs/zh/user-guide/assets/app/predefined-app-icon-setting.png rename to doc/docs/zh/user-guide/assets/model-management/predefined-app-icon-setting.png diff --git a/doc/docs/zh/user-guide/assets/model/select-model-1.png b/doc/docs/zh/user-guide/assets/model-management/select-model-1.png similarity index 100% rename from doc/docs/zh/user-guide/assets/model/select-model-1.png rename to doc/docs/zh/user-guide/assets/model-management/select-model-1.png diff --git a/doc/docs/zh/user-guide/assets/model/select-model-2.png b/doc/docs/zh/user-guide/assets/model-management/select-model-2.png similarity index 100% rename from doc/docs/zh/user-guide/assets/model/select-model-2.png rename to doc/docs/zh/user-guide/assets/model-management/select-model-2.png diff --git a/doc/docs/zh/user-guide/assets/model/select-model-3.png b/doc/docs/zh/user-guide/assets/model-management/select-model-3.png similarity index 100% rename from doc/docs/zh/user-guide/assets/model/select-model-3.png rename to doc/docs/zh/user-guide/assets/model-management/select-model-3.png diff --git a/doc/docs/zh/user-guide/assets/chat/agent-selection.png b/doc/docs/zh/user-guide/assets/start-chat/agent-selection.png similarity index 100% rename from doc/docs/zh/user-guide/assets/chat/agent-selection.png rename to doc/docs/zh/user-guide/assets/start-chat/agent-selection.png diff --git a/doc/docs/zh/user-guide/assets/chat/chat-management-1.png b/doc/docs/zh/user-guide/assets/start-chat/chat-management-1.png similarity index 100% rename from doc/docs/zh/user-guide/assets/chat/chat-management-1.png rename to doc/docs/zh/user-guide/assets/start-chat/chat-management-1.png diff --git a/doc/docs/zh/user-guide/assets/chat/chat-management-2.png b/doc/docs/zh/user-guide/assets/start-chat/chat-management-2.png similarity index 100% rename from doc/docs/zh/user-guide/assets/chat/chat-management-2.png rename to doc/docs/zh/user-guide/assets/start-chat/chat-management-2.png diff --git a/doc/docs/zh/user-guide/assets/chat/dialog-box.png b/doc/docs/zh/user-guide/assets/start-chat/dialog-box.png similarity index 100% rename from doc/docs/zh/user-guide/assets/chat/dialog-box.png rename to doc/docs/zh/user-guide/assets/start-chat/dialog-box.png diff --git a/doc/docs/zh/user-guide/assets/chat/reference-image.png b/doc/docs/zh/user-guide/assets/start-chat/reference-image.png similarity index 100% rename from doc/docs/zh/user-guide/assets/chat/reference-image.png rename to doc/docs/zh/user-guide/assets/start-chat/reference-image.png diff --git a/doc/docs/zh/user-guide/assets/chat/reference-source.png b/doc/docs/zh/user-guide/assets/start-chat/reference-source.png similarity index 100% rename from doc/docs/zh/user-guide/assets/chat/reference-source.png rename to doc/docs/zh/user-guide/assets/start-chat/reference-source.png 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..9bd7a129c --- /dev/null +++ b/doc/docs/zh/user-guide/home-page.md @@ -0,0 +1,51 @@ +# 用户指南 + +Nexent是一款面向未来的零代码智能体开发平台,致力于让每个人都能轻松构建和部署专属的AI智能体,无需编程基础,也无需繁琐操作! + +本用户指南将带您全面了解Nexent的强大功能和使用方法,助您快速上手各类操作~ + +通过学习本指南,您将能够高效利用Nexent,把创意变为现实,让智能体为您的工作和生活带来真正的价值与惊喜! + +## 🏠 首页概览 + +Nexent首页展示了平台的核心功能,为您提供快速入口: + +![首页概览](./assets/home-page/homepage.png) + +### 主要功能按钮 + +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 60% rename from doc/docs/zh/user-guide/knowledge-base-configuration.md rename to doc/docs/zh/user-guide/knowledge-base.md index 7ae6f1af5..3a099ee53 100644 --- a/doc/docs/zh/user-guide/knowledge-base-configuration.md +++ b/doc/docs/zh/user-guide/knowledge-base.md @@ -1,11 +1,12 @@ -# 知识库配置 +# 知识库 -在知识库配置模块中,您可以创建和管理知识库,上传各种格式的文件,并生成内容总结。知识库是智能体的重要信息来源,让智能体能够访问您的私有数据和文档。 +在知识库模块,您可以创建和管理知识库,上传各种格式的文件,并生成内容总结。知识库是智能体的重要信息来源,让智能体能够访问您的私有数据和文档。 ## 🔧 创建知识库 -1. 点击“创建知识库”按钮 -2. 为知识库设置一个易于识别的名称,注意知识库名称不能重复 +1. 点击"创建知识库"按钮 +2. 为知识库设置一个易于识别的名称 + > **注意**:知识库名称不能重复,只能使用中文或者小写字母,且不能包含空格、斜线等特殊字符 ## 📁 上传文件 @@ -16,12 +17,12 @@ 3. 系统会自动处理上传的文件,提取文本内容并进行向量化 4. 可在列表中查看文件的处理状态(解析中/入库中/已就绪) -![文件上传](./assets/knowledge/create-knowledge-base.png) - +![文件上传](./assets/knowledge-base/create-knowledge-base.png) ### 支持的文件格式 Nexent支持多种文件格式,包括: + - **文本**: .txt, .md文件 - **PDF**: .pdf文件 - **Word**: .docx文件 @@ -29,18 +30,16 @@ Nexent支持多种文件格式,包括: - **Excel**: .xlsx文件 - **数据文件**: .csv文件 - ## 📊 知识库总结 -建议您为每个知识库配置准确且完整的总结描述,这有助于后续智能体在进行知识库检索时,准确选择合适的知识库进行检索。 +建议您为每个知识库配置准确且完整的总结描述,这有助于后续智能体在进行检索时,准确选择合适的知识库。 1. 点击“详细内容”按钮进入知识库详细内容查看界面 2. 选择合适的模型,点击“自动总结”按钮为知识库自动生成内容总结 3. 您可对生成的内容总结进行编辑修改,使其更准确 4. 最后记得点击“保存”将您的修改保存 - -![内容总结](./assets/knowledge/summary-knowledge-base.png) +![内容总结](./assets/knowledge-base/summary-knowledge-base.png) ## 🔍 知识库管理 @@ -55,8 +54,8 @@ Nexent支持多种文件格式,包括: - 点击“详细内容”,可查看知识库的内容总结
- - + +
### 编辑知识库 @@ -64,10 +63,9 @@ Nexent支持多种文件格式,包括: 1. **删除知识库** - 点击知识库名称右侧“删除”按钮 - 确认删除操作(此操作不可恢复) -![删除知识库](./assets/knowledge/delete-knowledge-base.png) - +![删除知识库](./assets/knowledge-base/delete-knowledge-base.png) -2. **删除/新增文件** +2. **删除或新增文件** - 点击知识库名称,在文件列表中点击“删除”按钮,可从知识库中删除文件 - 点击知识库名称,在文件列表下方文件上传区域,可新增文件到知识库中 @@ -75,7 +73,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..d4eb6d8da 100644 --- a/doc/docs/zh/user-guide/local-tools/index.md +++ b/doc/docs/zh/user-guide/local-tools/index.md @@ -4,29 +4,47 @@ 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。 + + +### 🖼️ 多模态工具(Multimodal) + +- **analyze_text_file**:基于用户提问和文本文件的s3 url、http url、https url,解析文件并使用大语言模型理解文件,回答用户问题。需要在模型管理页面配置可用的大语言模型。 +- **analyze_image**:基于用户提问和图片的s3 url、http url、https url,使用视觉语言模型分析理解图像,回答用户问题。需要在模型管理页面配置可用的视觉语言模型。 + +### 🖥️ Terminal工具 + +**Terminal工具** 是 Nexent 平台的核心本地工具之一,提供持久化 SSH 会话能力,可在 Agent 中执行远程命令、进行系统巡检、读取日志或部署服务。详细的部署、参数和安全指引请查看专门的 [Terminal 使用手册](./terminal-tool.md)。 ## 🔧 工具配置 -所有本地工具都需要在智能体配置中进行设置: +所有本地工具都需要在智能体开发中进行设置: -1. 进入 **[智能体配置](../agent-configuration)** 页面 +1. 进入 **[智能体开发](../agent-development)** 页面 2. 选择要配置的智能体 3. 在"选择Agent的工具"页签中找到相应的本地工具 4. 点击配置按钮,填写必要的连接参数 @@ -43,11 +61,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..55df5de08 --- /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 61% rename from doc/docs/zh/user-guide/model-configuration.md rename to doc/docs/zh/user-guide/model-management.md index 6725695a8..15493cee1 100644 --- a/doc/docs/zh/user-guide/model-configuration.md +++ b/doc/docs/zh/user-guide/model-management.md @@ -1,15 +1,52 @@ -# 模型配置 +# 模型管理 -在模型配置模块中,您可以接入各类AI模型,包括大语言模型、向量化模型和视觉语言模型。Nexent支持多种模型提供商,帮助您根据实际需求灵活选择最适合的模型。 +在模型管理模块中,您可以配置应用的基本信息,并接入各类AI模型,包括大语言模型、向量化模型和视觉语言模型。Nexent支持多种模型提供商,帮助您根据实际需求灵活选择最适合的模型。 -## 🔄 同步ModelEngine模型 +## 🖼️ 应用配置 + +应用配置是模型管理的第一步,您可以配置应用的基本信息,包括应用图标、名称和描述。合理配置有助于提升应用的辨识度和用户体验。 + +- 应用图标和名称会展示在对话页面的左上角,帮助用户快速识别当前应用。 +- 应用的描述在生成智能体时会作为背景信息提供给模型,提升模型对应用场景的理解。 + +### 应用图标配置 + +点击应用图标可进行图标的配置。Nexent 提供了两种图标配置方式: + +- **使用预设图标**:从预设的图标库中选择,可选择图像及背景颜色,适合快速配置。 +- **上传自定义图片**:支持PNG、JPG图片格式,文件大小不超过2MB。 + +
+ + +
+ +### 应用名称及描述配置 + +#### 应用名称 + +- 应用名称会展示在对话页面的左上角,帮助用户快速识别当前应用。 +- 建议使用简洁明了、能体现应用功能的名称,避免使用特殊字符。 + +#### 应用描述 + +- 应用描述会作为背景信息提供给模型,帮助理解应用场景。 +- 建议突出应用核心功能,完整流畅且简洁明了。 + +
+ +
+ +## 🤖 模型配置 + +### 🔄 同步ModelEngine模型 Nexent即将支持与ModelEngine平台的无缝对接,届时可自动同步并使用您在ModelEngine上部署的所有模型,敬请期待! -## 🛠️ 添加自定义模型 +### 🛠️ 添加自定义模型 + +#### 添加单个模型 -### 添加单个模型 -#### 添加单个模型步骤 1. **添加自定义模型** - 点击"添加自定义模型"按钮,进入添加模型弹窗。 2. **选择模型类型** @@ -31,10 +68,10 @@ Nexent即将支持与ModelEngine平台的无缝对接,届时可自动同步并 - 配置完成后,点击"确定"按钮,模型将被添加到可用模型列表中。
- +
-### 批量添加模型 +#### 批量添加模型 为了提升模型导入效率,Nexent提供了批量模型导入功能。 @@ -54,84 +91,96 @@ Nexent即将支持与ModelEngine平台的无缝对接,届时可自动同步并 - 配置完成后,点击"确定"按钮,所有选中的模型将被添加到可用模型列表中。
- +
-## 🔧 修改自定义模型 +### 🔧 修改自定义模型 当您需要修改模型配置或删除不再使用的模型时,可以通过以下步骤进行操作: 1. 点击"修改自定义模型"按钮。 2. 选择要修改或删除的模型类型(大语言模型/向量化模型/视觉语言模型)。 -3. 选择是批量修改模型,还是修改单例自定义模型。 -4. 如果批量修改模型,可以通过启动或关闭模型开关,添加或删除模型。您可以通过点击右上角的"修改配置"按钮,对选中的模型进行批量的配置修改。 -5. 如果是修改单例自定义模型,点击删除按钮 🗑️ ,即可删除目标模型;想要修改相关配置,点击模型名称,即可弹出修改弹窗进行修改。 +3. 选择是批量修改模型,还是修改单个自定义模型。 +4. 如果批量修改模型,可以通过启动或关闭模型开关来添加或删除模型。您也可以通过点击右上角的"修改配置"按钮,对选中的模型进行批量配置修改。 +5. 如果是修改单个自定义模型,点击删除按钮 🗑️ 即可删除目标模型;想要修改相关配置,点击模型名称即可弹出修改弹窗进行修改。
- - + +

- - + +

- - + +
-## ⚙️ 配置系统模型 +### ⚙️ 配置系统模型 添加模型后,您需要配置系统基础模型,该模型将用于标题生成、实时文件读取等基础功能。在智能体运行时,您可以为每个智能体指定特定的运行模型。 -### 基础模型配置 +#### 基础模型配置 + 系统基础模型用于处理平台的核心功能,包括: + - 标题生成 - 实时文件读取 - 基础文本处理 **配置步骤**: + - 点击基础模型下拉框,从已添加的大语言模型中选择一个作为系统基础模型。 -### 向量化模型 +#### 向量化模型 + 向量化模型主要用于知识库的文本、图片等数据的向量化处理,是实现高效检索和语义理解的基础。配置合适的向量化模型,可以显著提升知识库的搜索准确率和多模态数据的处理能力。 + - 点击向量模型下拉框,从已添加的向量化模型中选择一个。 -### 多模态模型 +#### 多模态模型 + 多模态模型结合了视觉和语言能力,能够处理包含文本、图片等多种信息的复杂场景。例如,在对话页面上传图片文件时,系统会自动调用多模态模型进行内容解析和智能对话。 + - 点击视觉语言模型下拉框,从已添加的视觉语言模型中选择一个。
- - - + + +
-## ✅ 检查模型连通性 +### ✅ 检查模型连通性 定期检查模型连通性是确保系统稳定运行的重要环节。通过连通性检查功能,您可以及时发现和解决模型连接问题,保证服务的连续性和可靠性。 **检查流程**: + - 点击"检查模型连通性"按钮 - 系统将自动测试所有已配置的系统模型的连接状态 **状态指示**: + - 🔵 **蓝色圆点**:表示正在检测中,请耐心等待 - 🔴 **红色圆点**:表示连接失败,需要检查配置或网络状态 - 🟢 **绿色圆点**:表示连接正常,模型可以正常使用 **故障排查建议**: + - 检查网络连接是否稳定 - 验证API密钥是否有效且未过期 - 确认模型服务商的服务状态 - 检查防火墙和安全策略设置 -## 🤖 支持的模型提供商 +### 🤖 支持的模型提供商 + +#### 🤖 大语言模型LLM -### 🤖 大语言模型LLM Nexent 支持任何 **遵循OpenAI API规范** 的大语言模型供应商,包括: + - [硅基流动](https://siliconflow.cn/) - [阿里云百炼](https://bailian.console.aliyun.com/) - [小马算力](https://www.tokenpony.cn/) @@ -141,27 +190,30 @@ Nexent 支持任何 **遵循OpenAI API规范** 的大语言模型供应商,包 - [月之暗面](https://platform.moonshot.cn/) 可参考以下步骤进行模型接入: + 1. 访问模型供应商官网,注册账户; 2. 创建并复制API Key; 3. 在文档中查看API端点(即模型URL,一般以`/v1`为结尾); 4. 在Nexent模型配置页面点击添加自定义模型,填入必备信息,即可接入。 -### 🎭 多模态视觉模型 +#### 🎭 多模态视觉模型 使用与大语言模型相同的API Key和模型URL,但指定多模态模型名称,如硅基流动提供的**Qwen/Qwen2.5-VL-32B-Instruct**。 -### 🔤 向量模型 +#### 🔤 向量模型 使用与大语言模型相同的API Key,但模型URL一般会有所差异,一般以`/v1/embeddings`为结尾,同时指定向量模型名称,如硅基流动提供的**BAAI/bge-m3**。 -### 🎤 语音模型 +#### 🎤 语音模型 目前仅支持火山引擎语音,且需要在`.env`中进行配置 + - **网站**: [volcengine.com/product/voice-tech](https://www.volcengine.com/product/voice-tech) - **免费额度**: 个人使用可用 - **特色**: 高质量中英文语音合成 **开始使用**: + 1. 注册火山引擎账户 2. 访问语音技术服务 3. 创建应用并获取 API Key @@ -170,6 +222,7 @@ Nexent 支持任何 **遵循OpenAI API规范** 的大语言模型供应商,包 ## 💡 需要帮助 如果您在模型提供商方面遇到问题: + 1. 查看提供商特定文档 2. 验证 API 密钥权限和配额 3. 使用提供商官方示例进行测试 @@ -177,8 +230,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)中进行提问获取支持。 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 70% rename from doc/docs/zh/user-guide/chat-interface.md rename to doc/docs/zh/user-guide/start-chat.md index b93d6581b..1afa80448 100644 --- a/doc/docs/zh/user-guide/chat-interface.md +++ b/doc/docs/zh/user-guide/start-chat.md @@ -1,8 +1,8 @@ -# 对话页面 +# 开始问答 -对话页面是您与智能体进行交互的核心界面。在这里,您可以与不同的智能体进行对话,上传文件,使用语音输入,并管理您的对话历史。 +开始问答页面是您与智能体进行交互的核心界面。在这里,您可以与不同的智能体进行对话,上传文件,使用语音输入,并管理您的对话历史。 -## 🤖 开始对话 +## 🤖 开始问答 ### 选择智能体 @@ -19,7 +19,7 @@ - 切换后即可开始新的对话
- 选择智能体 + 选择智能体
### 发送文本消息 @@ -28,6 +28,7 @@ 1. **输入您的问题** - 在对话框底部的输入框中输入您的问题或指令 + - Shift+Enter键可换行 2. **发送消息** - 点击输入框右侧的发送按钮 @@ -39,12 +40,12 @@ - 思考过程会以卡片形式展示,便于区分
- 选择智能体 + 选择智能体
### 使用语音输入 -Nexent支持语音输入功能,让您可以通过说话与智能体交互: +Nexent支持语音输入功能,让您可以通过语音与智能体交互。前提是您已经配置了语音模型,配置教程可参考[模型管理](./model-management)。 1. **启用语音输入** - 在输入框右下角找到麦克风图标 @@ -63,10 +64,16 @@ Nexent支持语音输入功能,让您可以通过说话与智能体交互: > 💡 **小贴士**:为了获得更好的语音识别效果,请确保在安静的环境中使用,并清晰地发音。 -### 上传文件进行对话 +### 上传多模态文件进行对话 您可以在对话中上传文件,让智能体基于文件内容为您提供帮助: +> ⚠️ **注意事项**: +> 1. 多模态文件对话功能,需在智能体开发时,选择对应的多模态解析工具 +> 1. 文档类、文本类文件需选择 `analyze_text_file` 工具 +> 2. 工具、图片类文件需选择 `analyze_image` 工具 +> 2. 上传的文件大小有限制,建议单个文件不超过10MB。对于大型文档,建议分批上传 + 1. **选择文件上传方式** - 点击输入框右下角的文件上传按钮 - 或直接将文件拖拽到对话区域 @@ -77,18 +84,15 @@ Nexent支持语音输入功能,让您可以通过说话与智能体交互: - **图片类**:JPG、PNG、GIF 等常见图片格式 3. **文件处理流程** - - 系统会自动处理您上传的文件 - - 提取文件内容并添加到当前对话的上下文中 - - 智能体会基于文件内容回答您的问题 + - 系统会将您上传的文件存储至MinIO中,并返回S3 URL + - 构建文件元信息并添加到当前对话的上下文中 + - 智能体会基于文件元信息回答您的问题 4. **基于文件的对话** - 上传文件后,您可以询问关于文件内容的问题 - - 智能体可以分析、总结或处理文件中的信息 + - 智能体可以调用对应的多模态工具,分析、总结或处理文件中的信息 - 支持多文件同时上传和处理 - -> ⚠️ **注意事项**:上传的文件大小有限制,建议单个文件不超过10MB。对于大型文档,建议分批上传。 - ## 📚 管理您的对话历史 左侧边栏提供了完整的对话历史管理功能: @@ -121,14 +125,18 @@ Nexent支持语音输入功能,让您可以通过说话与智能体交互: > 💡 **小贴士**:定期清理不需要的对话记录可以保持界面整洁,提高查找效率。
- 对话编辑 - 对话编辑 + 对话编辑 + 对话编辑
-### 访问配置页面 +### 访问其他功能 -- 在左侧边栏左下角找到⚙️设置图标 -- 点击图标进入配置页面,可以修改智能体配置和系统设置 +你可通过左侧导航栏可以快速访问其他功能模块 +- **智能体空间**:管理所有已开发的智能体 +- **智能体开发**:创建和配置新的智能体 +- **模型管理**:配置AI模型和应用信息 +- **知识库**:管理知识库和文档 +- **记忆管理**:配置和管理智能记忆系统 ## 🔍 查看知识引用来源 @@ -148,6 +156,10 @@ Nexent支持语音输入功能,让您可以通过说话与智能体交互: - 点击“展开”可查看引用的详细内容 - 点击网页标题可直接跳转到原始网页 +💡 **小贴士**: +1. 智能体开发时,请选择 `knowledge_base_search` 工具,启用本地知识库检索功能。 +2. 智能体开发时,请选择 `exa_search`、 `tavily_search`、 `linkup_search` 工具,启用网络检索功能。 + ### 图片标签页 - 展示从网络检索中获取的相关图片 @@ -155,15 +167,15 @@ Nexent支持语音输入功能,让您可以通过说话与智能体交互: - 帮助您更直观地了解相关信息
- 知识引用来源 - 知识引用图片 + 知识引用来源 + 知识引用图片
## 🎭 多模态交互体验 ### 图像处理功能 -Nexent支持图像输入和处理(需要配置视觉模型): +Nexent支持图像输入和处理(需要配置视觉模型、图片解析工具`analyze_image`): 1. **上传图像** - 直接将图像文件拖拽到对话区域 @@ -171,7 +183,7 @@ Nexent支持图像输入和处理(需要配置视觉模型): - 支持常见的图片格式(JPG、PNG、GIF等) 2. **图像分析能力** - - 智能体会自动分析图像内容 + - 智能体会调用图片解析工具,自动分析图像内容 - 可以识别图像中的物体、文字、场景等元素 - 基于图像内容回答您的问题 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) 与我们分享! diff --git a/doc/package-lock.json b/doc/package-lock.json deleted file mode 100644 index 0e5f229df..000000000 --- a/doc/package-lock.json +++ /dev/null @@ -1,2435 +0,0 @@ -{ - "name": "doc", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "devDependencies": { - "vitepress": "^1.6.3" - } - }, - "node_modules/@algolia/abtesting": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.1.0.tgz", - "integrity": "sha512-sEyWjw28a/9iluA37KLGu8vjxEIlb60uxznfTUmXImy7H5NvbpSO6yYgmgH5KiD7j+zTUUihiST0jEP12IoXow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.35.0", - "@algolia/requester-browser-xhr": "5.35.0", - "@algolia/requester-fetch": "5.35.0", - "@algolia/requester-node-http": "5.35.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/autocomplete-core": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz", - "integrity": "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", - "@algolia/autocomplete-shared": "1.17.7" - } - }, - "node_modules/@algolia/autocomplete-plugin-algolia-insights": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz", - "integrity": "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/autocomplete-shared": "1.17.7" - }, - "peerDependencies": { - "search-insights": ">= 1 < 3" - } - }, - "node_modules/@algolia/autocomplete-preset-algolia": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz", - "integrity": "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/autocomplete-shared": "1.17.7" - }, - "peerDependencies": { - "@algolia/client-search": ">= 4.9.1 < 6", - "algoliasearch": ">= 4.9.1 < 6" - } - }, - "node_modules/@algolia/autocomplete-shared": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz", - "integrity": "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@algolia/client-search": ">= 4.9.1 < 6", - "algoliasearch": ">= 4.9.1 < 6" - } - }, - "node_modules/@algolia/client-abtesting": { - "version": "5.35.0", - "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.35.0.tgz", - "integrity": "sha512-uUdHxbfHdoppDVflCHMxRlj49/IllPwwQ2cQ8DLC4LXr3kY96AHBpW0dMyi6ygkn2MtFCc6BxXCzr668ZRhLBQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.35.0", - "@algolia/requester-browser-xhr": "5.35.0", - "@algolia/requester-fetch": "5.35.0", - "@algolia/requester-node-http": "5.35.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-analytics": { - "version": "5.35.0", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.35.0.tgz", - "integrity": "sha512-SunAgwa9CamLcRCPnPHx1V2uxdQwJGqb1crYrRWktWUdld0+B2KyakNEeVn5lln4VyeNtW17Ia7V7qBWyM/Skw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.35.0", - "@algolia/requester-browser-xhr": "5.35.0", - "@algolia/requester-fetch": "5.35.0", - "@algolia/requester-node-http": "5.35.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-common": { - "version": "5.35.0", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.35.0.tgz", - "integrity": "sha512-ipE0IuvHu/bg7TjT2s+187kz/E3h5ssfTtjpg1LbWMgxlgiaZIgTTbyynM7NfpSJSKsgQvCQxWjGUO51WSCu7w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-insights": { - "version": "5.35.0", - "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.35.0.tgz", - "integrity": "sha512-UNbCXcBpqtzUucxExwTSfAe8gknAJ485NfPN6o1ziHm6nnxx97piIbcBQ3edw823Tej2Wxu1C0xBY06KgeZ7gA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.35.0", - "@algolia/requester-browser-xhr": "5.35.0", - "@algolia/requester-fetch": "5.35.0", - "@algolia/requester-node-http": "5.35.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-personalization": { - "version": "5.35.0", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.35.0.tgz", - "integrity": "sha512-/KWjttZ6UCStt4QnWoDAJ12cKlQ+fkpMtyPmBgSS2WThJQdSV/4UWcqCUqGH7YLbwlj3JjNirCu3Y7uRTClxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.35.0", - "@algolia/requester-browser-xhr": "5.35.0", - "@algolia/requester-fetch": "5.35.0", - "@algolia/requester-node-http": "5.35.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-query-suggestions": { - "version": "5.35.0", - "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.35.0.tgz", - "integrity": "sha512-8oCuJCFf/71IYyvQQC+iu4kgViTODbXDk3m7yMctEncRSRV+u2RtDVlpGGfPlJQOrAY7OONwJlSHkmbbm2Kp/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.35.0", - "@algolia/requester-browser-xhr": "5.35.0", - "@algolia/requester-fetch": "5.35.0", - "@algolia/requester-node-http": "5.35.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/client-search": { - "version": "5.35.0", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.35.0.tgz", - "integrity": "sha512-FfmdHTrXhIduWyyuko1YTcGLuicVbhUyRjO3HbXE4aP655yKZgdTIfMhZ/V5VY9bHuxv/fGEh3Od1Lvv2ODNTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.35.0", - "@algolia/requester-browser-xhr": "5.35.0", - "@algolia/requester-fetch": "5.35.0", - "@algolia/requester-node-http": "5.35.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/ingestion": { - "version": "1.35.0", - "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.35.0.tgz", - "integrity": "sha512-gPzACem9IL1Co8mM1LKMhzn1aSJmp+Vp434An4C0OBY4uEJRcqsLN3uLBlY+bYvFg8C8ImwM9YRiKczJXRk0XA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.35.0", - "@algolia/requester-browser-xhr": "5.35.0", - "@algolia/requester-fetch": "5.35.0", - "@algolia/requester-node-http": "5.35.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/monitoring": { - "version": "1.35.0", - "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.35.0.tgz", - "integrity": "sha512-w9MGFLB6ashI8BGcQoVt7iLgDIJNCn4OIu0Q0giE3M2ItNrssvb8C0xuwJQyTy1OFZnemG0EB1OvXhIHOvQwWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.35.0", - "@algolia/requester-browser-xhr": "5.35.0", - "@algolia/requester-fetch": "5.35.0", - "@algolia/requester-node-http": "5.35.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/recommend": { - "version": "5.35.0", - "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.35.0.tgz", - "integrity": "sha512-AhrVgaaXAb8Ue0u2nuRWwugt0dL5UmRgS9LXe0Hhz493a8KFeZVUE56RGIV3hAa6tHzmAV7eIoqcWTQvxzlJeQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.35.0", - "@algolia/requester-browser-xhr": "5.35.0", - "@algolia/requester-fetch": "5.35.0", - "@algolia/requester-node-http": "5.35.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/requester-browser-xhr": { - "version": "5.35.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.35.0.tgz", - "integrity": "sha512-diY415KLJZ6x1Kbwl9u96Jsz0OstE3asjXtJ9pmk1d+5gPuQ5jQyEsgC+WmEXzlec3iuVszm8AzNYYaqw6B+Zw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.35.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/requester-fetch": { - "version": "5.35.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.35.0.tgz", - "integrity": "sha512-uydqnSmpAjrgo8bqhE9N1wgcB98psTRRQXcjc4izwMB7yRl9C8uuAQ/5YqRj04U0mMQ+fdu2fcNF6m9+Z1BzDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.35.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@algolia/requester-node-http": { - "version": "5.35.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.35.0.tgz", - "integrity": "sha512-RgLX78ojYOrThJHrIiPzT4HW3yfQa0D7K+MQ81rhxqaNyNBu4F1r+72LNHYH/Z+y9I1Mrjrd/c/Ue5zfDgAEjQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/client-common": "5.35.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/types": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", - "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@docsearch/css": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.8.2.tgz", - "integrity": "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@docsearch/js": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.8.2.tgz", - "integrity": "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@docsearch/react": "3.8.2", - "preact": "^10.0.0" - } - }, - "node_modules/@docsearch/react": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.8.2.tgz", - "integrity": "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/autocomplete-core": "1.17.7", - "@algolia/autocomplete-preset-algolia": "1.17.7", - "@docsearch/css": "3.8.2", - "algoliasearch": "^5.14.2" - }, - "peerDependencies": { - "@types/react": ">= 16.8.0 < 19.0.0", - "react": ">= 16.8.0 < 19.0.0", - "react-dom": ">= 16.8.0 < 19.0.0", - "search-insights": ">= 1 < 3" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "search-insights": { - "optional": true - } - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@iconify-json/simple-icons": { - "version": "1.2.45", - "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.45.tgz", - "integrity": "sha512-POOz+NjYQDy2fy1u+sIZi05N6r6oSooIGBaBcZLh7w8QOmLgJAZ6mBt+7Messp7ku9ucRua61if33BPoOZCwRQ==", - "dev": true, - "license": "CC0-1.0", - "dependencies": { - "@iconify/types": "*" - } - }, - "node_modules/@iconify/types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", - "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz", - "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz", - "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz", - "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz", - "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz", - "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz", - "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz", - "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz", - "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz", - "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz", - "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz", - "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz", - "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz", - "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz", - "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz", - "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz", - "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz", - "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz", - "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz", - "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz", - "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@shikijs/core": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-2.5.0.tgz", - "integrity": "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/engine-javascript": "2.5.0", - "@shikijs/engine-oniguruma": "2.5.0", - "@shikijs/types": "2.5.0", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4", - "hast-util-to-html": "^9.0.4" - } - }, - "node_modules/@shikijs/engine-javascript": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz", - "integrity": "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "2.5.0", - "@shikijs/vscode-textmate": "^10.0.2", - "oniguruma-to-es": "^3.1.0" - } - }, - "node_modules/@shikijs/engine-oniguruma": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz", - "integrity": "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "2.5.0", - "@shikijs/vscode-textmate": "^10.0.2" - } - }, - "node_modules/@shikijs/langs": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-2.5.0.tgz", - "integrity": "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "2.5.0" - } - }, - "node_modules/@shikijs/themes": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-2.5.0.tgz", - "integrity": "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/types": "2.5.0" - } - }, - "node_modules/@shikijs/transformers": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-2.5.0.tgz", - "integrity": "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/core": "2.5.0", - "@shikijs/types": "2.5.0" - } - }, - "node_modules/@shikijs/types": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.5.0.tgz", - "integrity": "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/@shikijs/vscode-textmate": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", - "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/markdown-it": { - "version": "14.1.2", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", - "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/linkify-it": "^5", - "@types/mdurl": "^2" - } - }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/web-bluetooth": { - "version": "0.0.21", - "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", - "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true, - "license": "ISC" - }, - "node_modules/@vitejs/plugin-vue": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", - "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "vite": "^5.0.0 || ^6.0.0", - "vue": "^3.2.25" - } - }, - "node_modules/@vue/compiler-core": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.18.tgz", - "integrity": "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.28.0", - "@vue/shared": "3.5.18", - "entities": "^4.5.0", - "estree-walker": "^2.0.2", - "source-map-js": "^1.2.1" - } - }, - "node_modules/@vue/compiler-dom": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.18.tgz", - "integrity": "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/compiler-core": "3.5.18", - "@vue/shared": "3.5.18" - } - }, - "node_modules/@vue/compiler-sfc": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.18.tgz", - "integrity": "sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.28.0", - "@vue/compiler-core": "3.5.18", - "@vue/compiler-dom": "3.5.18", - "@vue/compiler-ssr": "3.5.18", - "@vue/shared": "3.5.18", - "estree-walker": "^2.0.2", - "magic-string": "^0.30.17", - "postcss": "^8.5.6", - "source-map-js": "^1.2.1" - } - }, - "node_modules/@vue/compiler-ssr": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.18.tgz", - "integrity": "sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/compiler-dom": "3.5.18", - "@vue/shared": "3.5.18" - } - }, - "node_modules/@vue/devtools-api": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.7.tgz", - "integrity": "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/devtools-kit": "^7.7.7" - } - }, - "node_modules/@vue/devtools-kit": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.7.tgz", - "integrity": "sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/devtools-shared": "^7.7.7", - "birpc": "^2.3.0", - "hookable": "^5.5.3", - "mitt": "^3.0.1", - "perfect-debounce": "^1.0.0", - "speakingurl": "^14.0.1", - "superjson": "^2.2.2" - } - }, - "node_modules/@vue/devtools-shared": { - "version": "7.7.7", - "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.7.tgz", - "integrity": "sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "rfdc": "^1.4.1" - } - }, - "node_modules/@vue/reactivity": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.18.tgz", - "integrity": "sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/shared": "3.5.18" - } - }, - "node_modules/@vue/runtime-core": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.18.tgz", - "integrity": "sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/reactivity": "3.5.18", - "@vue/shared": "3.5.18" - } - }, - "node_modules/@vue/runtime-dom": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.18.tgz", - "integrity": "sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/reactivity": "3.5.18", - "@vue/runtime-core": "3.5.18", - "@vue/shared": "3.5.18", - "csstype": "^3.1.3" - } - }, - "node_modules/@vue/server-renderer": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.18.tgz", - "integrity": "sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/compiler-ssr": "3.5.18", - "@vue/shared": "3.5.18" - }, - "peerDependencies": { - "vue": "3.5.18" - } - }, - "node_modules/@vue/shared": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.18.tgz", - "integrity": "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@vueuse/core": { - "version": "12.8.2", - "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz", - "integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/web-bluetooth": "^0.0.21", - "@vueuse/metadata": "12.8.2", - "@vueuse/shared": "12.8.2", - "vue": "^3.5.13" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@vueuse/integrations": { - "version": "12.8.2", - "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-12.8.2.tgz", - "integrity": "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vueuse/core": "12.8.2", - "@vueuse/shared": "12.8.2", - "vue": "^3.5.13" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "async-validator": "^4", - "axios": "^1", - "change-case": "^5", - "drauu": "^0.4", - "focus-trap": "^7", - "fuse.js": "^7", - "idb-keyval": "^6", - "jwt-decode": "^4", - "nprogress": "^0.2", - "qrcode": "^1.5", - "sortablejs": "^1", - "universal-cookie": "^7" - }, - "peerDependenciesMeta": { - "async-validator": { - "optional": true - }, - "axios": { - "optional": true - }, - "change-case": { - "optional": true - }, - "drauu": { - "optional": true - }, - "focus-trap": { - "optional": true - }, - "fuse.js": { - "optional": true - }, - "idb-keyval": { - "optional": true - }, - "jwt-decode": { - "optional": true - }, - "nprogress": { - "optional": true - }, - "qrcode": { - "optional": true - }, - "sortablejs": { - "optional": true - }, - "universal-cookie": { - "optional": true - } - } - }, - "node_modules/@vueuse/metadata": { - "version": "12.8.2", - "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz", - "integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/@vueuse/shared": { - "version": "12.8.2", - "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz", - "integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "vue": "^3.5.13" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/algoliasearch": { - "version": "5.35.0", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.35.0.tgz", - "integrity": "sha512-Y+moNhsqgLmvJdgTsO4GZNgsaDWv8AOGAaPeIeHKlDn/XunoAqYbA+XNpBd1dW8GOXAUDyxC9Rxc7AV4kpFcIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@algolia/abtesting": "1.1.0", - "@algolia/client-abtesting": "5.35.0", - "@algolia/client-analytics": "5.35.0", - "@algolia/client-common": "5.35.0", - "@algolia/client-insights": "5.35.0", - "@algolia/client-personalization": "5.35.0", - "@algolia/client-query-suggestions": "5.35.0", - "@algolia/client-search": "5.35.0", - "@algolia/ingestion": "1.35.0", - "@algolia/monitoring": "1.35.0", - "@algolia/recommend": "5.35.0", - "@algolia/requester-browser-xhr": "5.35.0", - "@algolia/requester-fetch": "5.35.0", - "@algolia/requester-node-http": "5.35.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/birpc": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.5.0.tgz", - "integrity": "sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/copy-anything": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", - "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-what": "^4.1.8" - }, - "engines": { - "node": ">=12.13" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" - } - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true, - "license": "MIT" - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/emoji-regex-xs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", - "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==", - "dev": true, - "license": "MIT" - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/focus-trap": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.6.5.tgz", - "integrity": "sha512-7Ke1jyybbbPZyZXFxEftUtxFGLMpE2n6A+z//m4CRDlj0hW+o3iYSmh8nFlYMurOiJVDmJRilUQtJr08KfIxlg==", - "dev": true, - "license": "MIT", - "dependencies": { - "tabbable": "^6.2.0" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/hast-util-to-html": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", - "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-whitespace": "^3.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "stringify-entities": "^4.0.0", - "zwitch": "^2.0.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hookable": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", - "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/html-void-elements": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-what": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", - "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.13" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" - } - }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/mark.js": { - "version": "8.11.1", - "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", - "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", - "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", - "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/minisearch": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.1.2.tgz", - "integrity": "sha512-R1Pd9eF+MD5JYDDSPAp/q1ougKglm14uEkPMvQ/05RGmx6G9wvmLTrTI/Q5iPNJLYqNdsDQ7qTGIcNWR+FrHmA==", - "dev": true, - "license": "MIT" - }, - "node_modules/mitt": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/oniguruma-to-es": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz", - "integrity": "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex-xs": "^1.0.0", - "regex": "^6.0.1", - "regex-recursion": "^6.0.2" - } - }, - "node_modules/perfect-debounce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", - "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/preact": { - "version": "10.27.0", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.27.0.tgz", - "integrity": "sha512-/DTYoB6mwwgPytiqQTh/7SFRL98ZdiD8Sk8zIUVOxtwq4oWcwrcd1uno9fE/zZmUaUrFNYzbH14CPebOz9tZQw==", - "dev": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/preact" - } - }, - "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/regex/-/regex-6.0.1.tgz", - "integrity": "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==", - "dev": true, - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-recursion": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", - "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", - "dev": true, - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-utilities": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", - "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", - "dev": true, - "license": "MIT" - }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "dev": true, - "license": "MIT" - }, - "node_modules/rollup": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz", - "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.46.2", - "@rollup/rollup-android-arm64": "4.46.2", - "@rollup/rollup-darwin-arm64": "4.46.2", - "@rollup/rollup-darwin-x64": "4.46.2", - "@rollup/rollup-freebsd-arm64": "4.46.2", - "@rollup/rollup-freebsd-x64": "4.46.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", - "@rollup/rollup-linux-arm-musleabihf": "4.46.2", - "@rollup/rollup-linux-arm64-gnu": "4.46.2", - "@rollup/rollup-linux-arm64-musl": "4.46.2", - "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", - "@rollup/rollup-linux-ppc64-gnu": "4.46.2", - "@rollup/rollup-linux-riscv64-gnu": "4.46.2", - "@rollup/rollup-linux-riscv64-musl": "4.46.2", - "@rollup/rollup-linux-s390x-gnu": "4.46.2", - "@rollup/rollup-linux-x64-gnu": "4.46.2", - "@rollup/rollup-linux-x64-musl": "4.46.2", - "@rollup/rollup-win32-arm64-msvc": "4.46.2", - "@rollup/rollup-win32-ia32-msvc": "4.46.2", - "@rollup/rollup-win32-x64-msvc": "4.46.2", - "fsevents": "~2.3.2" - } - }, - "node_modules/search-insights": { - "version": "2.17.3", - "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", - "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/shiki": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-2.5.0.tgz", - "integrity": "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@shikijs/core": "2.5.0", - "@shikijs/engine-javascript": "2.5.0", - "@shikijs/engine-oniguruma": "2.5.0", - "@shikijs/langs": "2.5.0", - "@shikijs/themes": "2.5.0", - "@shikijs/types": "2.5.0", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/speakingurl": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", - "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "dev": true, - "license": "MIT", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/superjson": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz", - "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "copy-anything": "^3.0.2" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/tabbable": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", - "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", - "dev": true, - "license": "MIT" - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", - "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vite": { - "version": "5.4.19", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", - "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vitepress": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.6.3.tgz", - "integrity": "sha512-fCkfdOk8yRZT8GD9BFqusW3+GggWYZ/rYncOfmgcDtP3ualNHCAg+Robxp2/6xfH1WwPHtGpPwv7mbA3qomtBw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@docsearch/css": "3.8.2", - "@docsearch/js": "3.8.2", - "@iconify-json/simple-icons": "^1.2.21", - "@shikijs/core": "^2.1.0", - "@shikijs/transformers": "^2.1.0", - "@shikijs/types": "^2.1.0", - "@types/markdown-it": "^14.1.2", - "@vitejs/plugin-vue": "^5.2.1", - "@vue/devtools-api": "^7.7.0", - "@vue/shared": "^3.5.13", - "@vueuse/core": "^12.4.0", - "@vueuse/integrations": "^12.4.0", - "focus-trap": "^7.6.4", - "mark.js": "8.11.1", - "minisearch": "^7.1.1", - "shiki": "^2.1.0", - "vite": "^5.4.14", - "vue": "^3.5.13" - }, - "bin": { - "vitepress": "bin/vitepress.js" - }, - "peerDependencies": { - "markdown-it-mathjax3": "^4", - "postcss": "^8" - }, - "peerDependenciesMeta": { - "markdown-it-mathjax3": { - "optional": true - }, - "postcss": { - "optional": true - } - } - }, - "node_modules/vue": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.18.tgz", - "integrity": "sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vue/compiler-dom": "3.5.18", - "@vue/compiler-sfc": "3.5.18", - "@vue/runtime-dom": "3.5.18", - "@vue/server-renderer": "3.5.18", - "@vue/shared": "3.5.18" - }, - "peerDependencies": { - "typescript": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - } -} diff --git a/docker/.env.example b/docker/.env.example index 2e18a6068..e770040e7 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -151,3 +151,6 @@ PROMETHEUS_PORT=8000 TELEMETRY_SAMPLE_RATE=1.0 LLM_SLOW_REQUEST_THRESHOLD_SECONDS=5.0 LLM_SLOW_TOKEN_RATE_THRESHOLD=10.0 + +# Market Backend Address +MARKET_BACKEND=http://localhost:8010 \ No newline at end of file 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 diff --git a/frontend/app/[locale]/agents/components/AgentSetupOrchestrator.tsx b/frontend/app/[locale]/agents/components/AgentSetupOrchestrator.tsx index dcb08d29a..1b1484cc5 100644 --- a/frontend/app/[locale]/agents/components/AgentSetupOrchestrator.tsx +++ b/frontend/app/[locale]/agents/components/AgentSetupOrchestrator.tsx @@ -4,20 +4,20 @@ import { useState, useEffect, useCallback, useRef, useMemo } from "react"; import { useTranslation } from "react-i18next"; import { TFunction } from "i18next"; -import { App, Modal, Button } from "antd"; +import { App, Modal, Button, Tooltip } from "antd"; import { WarningFilled } from "@ant-design/icons"; import { TooltipProvider } from "@/components/ui/tooltip"; import { fetchAgentList, updateAgent, - importAgent, deleteAgent, exportAgent, searchAgentInfo, searchToolConfig, updateToolConfig, } from "@/services/agentConfigService"; +import { useAgentImport } from "@/hooks/useAgentImport"; import { Agent, AgentSetupOrchestratorProps, @@ -98,6 +98,12 @@ export default function AgentSetupOrchestrator({ const [toolConfigDrafts, setToolConfigDrafts] = useState< Record >({}); + const [pendingImportData, setPendingImportData] = useState<{ + agentInfo: any; + } | null>(null); + const [importingAction, setImportingAction] = useState< + "force" | "regenerate" | null + >(null); // Use generation state passed from parent component, not local state // Delete confirmation popup status @@ -494,7 +500,9 @@ export default function AgentSetupOrchestrator({ const confirmOrRun = useCallback( (action: PendingAction) => { - if (hasUnsavedChanges) { + // In creation mode, always show save confirmation dialog when clicking debug + // Also show when there are unsaved changes + if ((isCreatingNewAgent && !isEditingAgent) || hasUnsavedChanges) { setPendingAction(() => action); setConfirmContext("switch"); setIsSaveConfirmOpen(true); @@ -502,7 +510,7 @@ export default function AgentSetupOrchestrator({ void Promise.resolve(action()); } }, - [hasUnsavedChanges] + [hasUnsavedChanges, isCreatingNewAgent, isEditingAgent] ); const handleToolConfigDraftSave = useCallback( @@ -899,6 +907,8 @@ export default function AgentSetupOrchestrator({ setSelectedTools([]); setEnabledToolIds([]); setEnabledAgentIds([]); + setToolConfigDrafts({}); + setMainAgentId?.(null); // Clear business logic model to allow default from global settings // The useEffect in PromptManager will set it to the default from localStorage @@ -910,6 +920,12 @@ export default function AgentSetupOrchestrator({ setMainAgentModel(null); setMainAgentModelId(null); + try { + await onToolsRefresh?.(false); + } catch (error) { + log.error("Failed to refresh tools in creation mode:", error); + } + onEditingStateChange?.(false, null); }; @@ -1525,6 +1541,54 @@ export default function AgentSetupOrchestrator({ } }; + // Use unified import hooks - one for normal import, one for force import + const { importFromData: runNormalImport } = useAgentImport({ + onSuccess: () => { + message.success(t("businessLogic.config.error.agentImportSuccess")); + refreshAgentList(t, false); + }, + onError: (error) => { + log.error(t("agentConfig.agents.importFailed"), error); + message.error(t("businessLogic.config.error.agentImportFailed")); + }, + forceImport: false, + }); + + const { importFromData: runForceImport } = useAgentImport({ + onSuccess: () => { + message.success(t("businessLogic.config.error.agentImportSuccess")); + refreshAgentList(t, false); + }, + onError: (error) => { + log.error(t("agentConfig.agents.importFailed"), error); + message.error(t("businessLogic.config.error.agentImportFailed")); + }, + forceImport: true, + }); + + const runAgentImport = useCallback( + async ( + agentPayload: any, + translationFn: TFunction, + options?: { forceImport?: boolean } + ) => { + setIsImporting(true); + try { + if (options?.forceImport) { + await runForceImport(agentPayload); + } else { + await runNormalImport(agentPayload); + } + return true; + } catch (error) { + return false; + } finally { + setIsImporting(false); + } + }, + [runNormalImport, runForceImport] + ); + // Handle importing agent const handleImportAgent = (t: TFunction) => { // Create a hidden file input element @@ -1532,6 +1596,7 @@ export default function AgentSetupOrchestrator({ fileInput.type = "file"; fileInput.accept = ".json"; fileInput.onchange = async (event) => { + setPendingImportData(null); const file = (event.target as HTMLInputElement).files?.[0]; if (!file) return; @@ -1541,7 +1606,6 @@ export default function AgentSetupOrchestrator({ return; } - setIsImporting(true); try { // Read file content const fileContent = await file.text(); @@ -1551,33 +1615,117 @@ export default function AgentSetupOrchestrator({ agentInfo = JSON.parse(fileContent); } catch (parseError) { message.error(t("businessLogic.config.error.invalidFileType")); - setIsImporting(false); return; } - // Call import API - const result = await importAgent(agentInfo); + const normalizeValue = (value?: string | null) => + typeof value === "string" ? value.trim() : ""; - if (result.success) { - message.success(t("businessLogic.config.error.agentImportSuccess")); - // Refresh agent list - refreshAgentList(t); + const extractImportedAgents = (data: any): any[] => { + if (!data) { + return []; + } + + if (Array.isArray(data)) { + return data; + } + + if (data.agent_info && typeof data.agent_info === "object") { + return Object.values(data.agent_info).filter( + (item) => item && typeof item === "object" + ); + } + + if (data.agentInfo && typeof data.agentInfo === "object") { + return Object.values(data.agentInfo).filter( + (item) => item && typeof item === "object" + ); + } + + return [data]; + }; + + const importedAgents = extractImportedAgents(agentInfo); + const agentList = Array.isArray(subAgentList) ? subAgentList : []; + + const existingNames = new Set( + agentList + .map((agent) => normalizeValue(agent?.name)) + .filter((name) => !!name) + ); + const existingDisplayNames = new Set( + agentList + .map((agent) => normalizeValue(agent?.display_name)) + .filter((name) => !!name) + ); + + const duplicateNames = Array.from( + new Set( + importedAgents + .map((agent) => normalizeValue(agent?.name)) + .filter( + (name) => name && existingNames.has(name) + ) as string[] + ) + ); + const duplicateDisplayNames = Array.from( + new Set( + importedAgents + .map((agent) => + normalizeValue(agent?.display_name ?? agent?.displayName) + ) + .filter( + (displayName) => + displayName && existingDisplayNames.has(displayName) + ) as string[] + ) + ); + + const hasNameConflict = duplicateNames.length > 0; + const hasDisplayNameConflict = duplicateDisplayNames.length > 0; + + if (hasNameConflict || hasDisplayNameConflict) { + setPendingImportData({ + agentInfo, + }); } else { - message.error( - result.message || t("businessLogic.config.error.agentImportFailed") - ); + await runAgentImport(agentInfo, t); } } catch (error) { log.error(t("agentConfig.agents.importFailed"), error); message.error(t("businessLogic.config.error.agentImportFailed")); - } finally { - setIsImporting(false); } }; fileInput.click(); }; + const handleConfirmedDuplicateImport = useCallback(async () => { + if (!pendingImportData) { + return; + } + setImportingAction("regenerate"); + const success = await runAgentImport(pendingImportData.agentInfo, t); + if (success) { + setPendingImportData(null); + } + setImportingAction(null); + }, [pendingImportData, runAgentImport, t]); + + const handleForceDuplicateImport = useCallback(async () => { + if (!pendingImportData) { + return; + } + setImportingAction("force"); + const success = await runAgentImport(pendingImportData.agentInfo, t, { + forceImport: true, + }); + if (success) { + setPendingImportData(null); + } + setImportingAction(null); + }, [pendingImportData, runAgentImport, t]); + // Handle confirmed deletion const handleConfirmDelete = async (t: TFunction) => { if (!agentToDelete) return; @@ -1621,9 +1769,26 @@ export default function AgentSetupOrchestrator({ skipUnsavedCheckRef.current = false; }, 0); onEditingStateChange?.(false, null); + } else { + // If deleting another agent that is in enabledAgentIds, remove it and update baseline + // to avoid triggering false unsaved changes indicator + const deletedId = Number(agentToDelete.id); + if (enabledAgentIds.includes(deletedId)) { + const updatedEnabledAgentIds = enabledAgentIds.filter( + (id) => id !== deletedId + ); + setEnabledAgentIds(updatedEnabledAgentIds); + // Update baseline to reflect this change so it doesn't trigger unsaved changes + if (baselineRef.current) { + baselineRef.current = { + ...baselineRef.current, + enabledAgentIds: updatedEnabledAgentIds.sort((a, b) => a - b), + }; + } + } } - // Refresh agent list - refreshAgentList(t); + // Refresh agent list without clearing tools to avoid triggering false unsaved changes indicator + refreshAgentList(t, false); } else { message.error( result.message || t("businessLogic.config.error.agentDeleteFailed") @@ -1762,6 +1927,12 @@ export default function AgentSetupOrchestrator({ fewShotsContent?.trim()) ); + const isForceDuplicateDisabled = + isImporting && importingAction === "regenerate"; + const isRegenerateDuplicateDisabled = + isImporting && importingAction === "force"; + const isForceDuplicateLoading = isImporting && importingAction === "force"; + return (
@@ -2014,7 +2185,77 @@ export default function AgentSetupOrchestrator({ // Only close modal, don't execute discard logic setIsSaveConfirmOpen(false); }} + canSave={localCanSaveAgent} + invalidReason={ + localCanSaveAgent ? undefined : getLocalButtonTitle() || undefined + } /> + {/* Duplicate import confirmation */} + + + {t("businessLogic.config.import.duplicateTitle")} +
+ } + onCancel={() => { + if (isImporting) { + return; + } + setPendingImportData(null); + }} + maskClosable={!isImporting} + closable={!isImporting} + centered + footer={ +
+ + + + + + + +
+ } + > +

+ {t("businessLogic.config.import.duplicateDescription")} +

+ {/* Auto unselect knowledge_base_search notice when embedding not configured */} diff --git a/frontend/app/[locale]/agents/components/SaveConfirmModal.tsx b/frontend/app/[locale]/agents/components/SaveConfirmModal.tsx index cb523d54f..02dd7e90d 100644 --- a/frontend/app/[locale]/agents/components/SaveConfirmModal.tsx +++ b/frontend/app/[locale]/agents/components/SaveConfirmModal.tsx @@ -12,6 +12,8 @@ interface SaveConfirmModalProps { onSave: AsyncOrSyncHandler; onClose?: AsyncOrSyncHandler; width?: number; + canSave?: boolean; + invalidReason?: string; } export default function SaveConfirmModal({ @@ -20,6 +22,8 @@ export default function SaveConfirmModal({ onSave, onClose, width = 520, + canSave = true, + invalidReason, }: SaveConfirmModalProps) { const { t } = useTranslation("common"); @@ -46,10 +50,14 @@ export default function SaveConfirmModal({ centered footer={
- - + {canSave ? ( + + ) : null}
} width={width} @@ -61,9 +69,19 @@ export default function SaveConfirmModal({ style={{ fontSize: "48px" }} />
-
- {t("agentConfig.modals.saveConfirm.content")} -
+ {canSave ? ( +
+ {t("agentConfig.modals.saveConfirm.content")} +
+ ) : ( +
+ + {t("agentConfig.modals.saveConfirm.invalidContent", { + invalidReason, + })} + +
+ )}
diff --git a/frontend/app/[locale]/agents/components/agent/AgentConfigModal.tsx b/frontend/app/[locale]/agents/components/agent/AgentConfigModal.tsx index 098cd1b93..7e465d1d1 100644 --- a/frontend/app/[locale]/agents/components/agent/AgentConfigModal.tsx +++ b/frontend/app/[locale]/agents/components/agent/AgentConfigModal.tsx @@ -1071,13 +1071,13 @@ export default function AgentConfigModal({ const tooltipText = getButtonTitle(); return ( tooltipText || - t("businessLogic.config.button.saveToAgentPool") + t("common.save") ); } - return t("businessLogic.config.button.saveToAgentPool"); + return t("common.save"); })()} > - {t("businessLogic.config.button.saveToAgentPool")} + {t("common.save")} ) : ( + + + {t("document.chunk.tooltip.create")} + + + )} + + + + {shouldShowPagination && ( +
+ + t("document.chunk.pagination.range", { + defaultValue: "{{start}}-{{end}} of {{total}}", + start: range[0], + end: range[1], + total: pageTotal, + }) + } + /> +
+ )} + + { + 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" + > +