Skip to content

Commit da6b969

Browse files
committed
feat(paths): 引入虚拟路径常量,重构路径管理逻辑
1 parent 3ec8305 commit da6b969

File tree

8 files changed

+83
-133
lines changed

8 files changed

+83
-133
lines changed

backend/package/yuxi/agents/backends/sandbox/paths.py

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,19 @@
44
from pathlib import Path
55

66
from yuxi import config as conf
7+
from yuxi.utils.paths import (
8+
VIRTUAL_PATH_PREFIX,
9+
WORKSPACE_DIR_NAME,
10+
OUTPUTS_DIR_NAME,
11+
UPLOADS_DIR_NAME
12+
)
713

8-
DEFAULT_VIRTUAL_PATH_PREFIX = "/home/gem/user-data"
9-
VIRTUAL_PATH_PREFIX = DEFAULT_VIRTUAL_PATH_PREFIX
10-
_WORKSPACE_DIR_NAME = "workspace"
11-
_UPLOADS_DIR_NAME = "uploads"
12-
_OUTPUTS_DIR_NAME = "outputs"
1314

1415
_SAFE_THREAD_ID_RE = re.compile(r"^[A-Za-z0-9_-]+$")
1516

1617

1718
def get_virtual_path_prefix() -> str:
18-
configured = str(getattr(conf, "sandbox_virtual_path_prefix", "") or "").strip()
19-
if not configured:
20-
return DEFAULT_VIRTUAL_PATH_PREFIX
21-
return "/" + configured.strip("/")
19+
return "/" + VIRTUAL_PATH_PREFIX.strip("/")
2220

2321

2422
def _validate_thread_id(thread_id: str) -> str:
@@ -46,15 +44,15 @@ def sandbox_user_data_dir(thread_id: str) -> Path:
4644

4745
def sandbox_workspace_dir(thread_id: str) -> Path:
4846
_validate_thread_id(thread_id)
49-
return _global_user_data_dir() / _WORKSPACE_DIR_NAME
47+
return _global_user_data_dir() / WORKSPACE_DIR_NAME
5048

5149

5250
def sandbox_uploads_dir(thread_id: str) -> Path:
53-
return _thread_root_dir(thread_id) / _UPLOADS_DIR_NAME
51+
return _thread_root_dir(thread_id) / UPLOADS_DIR_NAME
5452

5553

5654
def sandbox_outputs_dir(thread_id: str) -> Path:
57-
return _thread_root_dir(thread_id) / _OUTPUTS_DIR_NAME
55+
return _thread_root_dir(thread_id) / OUTPUTS_DIR_NAME
5856

5957

6058
def ensure_thread_dirs(thread_id: str) -> None:
@@ -72,16 +70,16 @@ def _resolve_user_data_base_dir(thread_id: str, relative_path: str) -> tuple[Pat
7270
return base_dir.resolve(), base_dir.resolve()
7371

7472
namespace = parts[0]
75-
if namespace == _WORKSPACE_DIR_NAME:
73+
if namespace == WORKSPACE_DIR_NAME:
7674
# Workspace is shared across threads, so it lives outside the per-thread root.
7775
base_dir = sandbox_workspace_dir(thread_id)
7876
target_path = base_dir.joinpath(*parts[1:]) if len(parts) > 1 else base_dir
7977
return base_dir.resolve(), target_path.resolve()
80-
if namespace == _UPLOADS_DIR_NAME:
78+
if namespace == UPLOADS_DIR_NAME:
8179
base_dir = sandbox_uploads_dir(thread_id)
8280
target_path = base_dir.joinpath(*parts[1:]) if len(parts) > 1 else base_dir
8381
return base_dir.resolve(), target_path.resolve()
84-
if namespace == _OUTPUTS_DIR_NAME:
82+
if namespace == OUTPUTS_DIR_NAME:
8583
base_dir = sandbox_outputs_dir(thread_id)
8684
target_path = base_dir.joinpath(*parts[1:]) if len(parts) > 1 else base_dir
8785
return base_dir.resolve(), target_path.resolve()
@@ -124,7 +122,7 @@ def virtual_path_for_thread_file(thread_id: str, path: str | Path) -> str:
124122
else:
125123
workspace_relative = relative_path.as_posix()
126124
relative_path_str = (
127-
_WORKSPACE_DIR_NAME if workspace_relative in {"", "."} else f"{_WORKSPACE_DIR_NAME}/{workspace_relative}"
125+
WORKSPACE_DIR_NAME if workspace_relative in {"", "."} else f"{WORKSPACE_DIR_NAME}/{workspace_relative}"
128126
)
129127

130128
prefix = get_virtual_path_prefix().rstrip("/")
Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,28 @@
1-
PROMPT = """
1+
from yuxi.utils.paths import (
2+
VIRTUAL_PATH_PREFIX,
3+
VIRTUAL_PATH_WORKSPACE,
4+
VIRTUAL_PATH_OUTPUTS,
5+
VIRTUAL_PATH_UPLOADS,
6+
VIRTUAL_KBS_PATH
7+
)
8+
9+
10+
PROMPT = f"""
211
你是一个人工智能助手 “语析”,专门用来回答用户的问题。请根据用户提供的信息,尽可能详细地回答问题。
312
如果你不确定答案,可以说你不知道,但请尽量提供相关的信息或建议。请保持礼貌和专业。
413
5-
系统主要工作路径为 /home/gem/user-data/,但必须遵守规范:
6-
- /home/gem/user-data/workspace/:用于存放工作文件(用户目录,不要轻易写入)
7-
- /home/gem/user-data/outputs/:用于写入的文件夹
8-
- /home/gem/user-data/outputs/tmp/:用于存放中间结果或备份内容
9-
- /home/gem/user-data/uploads/:用于存放用户上传的文件
14+
系统主要工作路径为 {VIRTUAL_PATH_PREFIX},但必须遵守规范:
15+
- {VIRTUAL_PATH_WORKSPACE}:用于存放工作文件(用户目录,不要轻易写入)
16+
- {VIRTUAL_PATH_OUTPUTS}:用于写入的文件夹
17+
- {VIRTUAL_PATH_OUTPUTS}/tmp/:用于存放中间结果或备份内容
18+
- {VIRTUAL_PATH_UPLOADS}:用于存放用户上传的文件
1019
1120
非必要不写入其他路径
1221
1322
如果启用了知识库,除了使用知识库工具之外,
1423
当需要精准获取信息的时候,或者 query_kb 中没有找到相关的内容,还可以直接访问知识库文件系统
15-
(路径为 /home/gem/kbs/)来获取信息。
16-
源文件可能无法解析,可以在 /home/gem/kbs/<db_name>/parsed/ 中找到解析后的 markdown 文件。
24+
(路径为 {VIRTUAL_KBS_PATH})来获取信息。
25+
源文件可能无法解析,可以在 {VIRTUAL_KBS_PATH}/<db_name>/parsed/ 中找到解析后的 markdown 文件。
1726
1827
你需要根据任务的复杂程度来使用 write_todos 来记录规划和待办事项,确保任务的每个步骤都被记录和跟踪。
1928
"""

backend/package/yuxi/agents/buildin/deep_agent/prompt.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
DEEP_PROMPT = """你是一位专家级研究员。你的工作是进行彻底的研究,然后撰写一份精美的报告。
1+
from yuxi.utils.paths import (
2+
VIRTUAL_PATH_PREFIX,
3+
VIRTUAL_PATH_WORKSPACE,
4+
VIRTUAL_PATH_OUTPUTS,
5+
VIRTUAL_PATH_UPLOADS,
6+
)
7+
8+
9+
DEEP_PROMPT = f"""你是一位专家级研究员。你的工作是进行彻底的研究,然后撰写一份精美的报告。
210
311
你应该做的第一件事是把原始的用户问题写入 `question.txt`,以便你有一个记录。
412
@@ -78,9 +86,9 @@
7886
7987
你可以使用一些工具。
8088
81-
允许的写入路径为 /home/gem/user-data/,请将需要保存的文件写入该目录下。
82-
推荐使用的路径:
83-
- /home/gem/user-data/workspace/:用于存放工作文件和中间结果
84-
- /home/gem/user-data/outputs/:用于存放最终输出结果
85-
- /home/gem/user-data/uploads/:用于存放用户上传的文件
89+
系统主要工作路径为 {VIRTUAL_PATH_PREFIX},但必须遵守规范:
90+
- {VIRTUAL_PATH_WORKSPACE}:用于存放工作文件(用户目录,不要轻易写入)
91+
- {VIRTUAL_PATH_OUTPUTS}:用于写入的文件夹
92+
- {VIRTUAL_PATH_OUTPUTS}/tmp/:用于存放中间结果或备份内容
93+
- {VIRTUAL_PATH_UPLOADS}:用于存放用户上传的文件
8694
"""

backend/package/yuxi/agents/toolkits/buildin/tools.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from yuxi.storage.minio import aupload_file_to_minio
1717
from yuxi.utils import logger
1818
from yuxi.utils.question_utils import normalize_questions
19+
from yuxi.utils.paths import VIRTUAL_PATH_OUTPUTS
1920

2021
# Lazy initialization for TavilySearch (only when API key is available)
2122
_tavily_search_instance = None
@@ -67,7 +68,7 @@ class PresentArtifactsInput(BaseModel):
6768
"""Expose artifact files to the frontend after the agent finishes."""
6869

6970
filepaths: list[str] = Field(
70-
description="需要展示给用户的文件绝对路径列表,只允许位于 /home/gem/user-data/outputs/ 下"
71+
description=f"需要展示给用户的文件绝对路径列表,只允许位于 {VIRTUAL_PATH_OUTPUTS} 下"
7172
)
7273

7374

@@ -130,16 +131,16 @@ def calculator(a: float, b: float, operation: str) -> float:
130131
raise
131132

132133

133-
PRESENT_ARTIFACTS_DESCRIPTION = """
134+
PRESENT_ARTIFACTS_DESCRIPTION = f"""
134135
将已经生成好的结果文件展示给用户。
135136
136137
使用场景:
137-
1. 你已经在 `/home/gem/user-data/outputs/` 下写好了最终结果文件
138+
1. 你已经在 `{VIRTUAL_PATH_OUTPUTS}` 下写好了最终结果文件
138139
2. 你希望前端在对话结束后显示这些结果文件卡片
139140
3. 这些文件需要支持下载或预览
140141
141142
注意事项:
142-
1. 只能传入 `/home/gem/user-data/outputs/` 下的文件
143+
1. 只能传入 `{VIRTUAL_PATH_OUTPUTS}` 下的文件
143144
2. 不要传入中间过程文件,只有真正需要给用户看的结果文件才调用
144145
3. 可以一次传多个文件
145146
"""

backend/package/yuxi/services/conversation_service.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@
1313
from yuxi.config import config as app_config
1414
from yuxi.plugins.parser import Parser
1515
from yuxi.repositories.conversation_repository import ConversationRepository
16-
from yuxi.services.doc_converter import ATTACHMENT_ALLOWED_EXTENSIONS, MAX_ATTACHMENT_SIZE_BYTES
1716
from yuxi.utils.datetime_utils import utc_isoformat
1817
from yuxi.utils.logging_config import logger
18+
from yuxi.utils.paths import VIRTUAL_PATH_UPLOADS
1919

20-
UPLOADS_VIRTUAL_PREFIX = "/home/gem/user-data/uploads"
20+
ATTACHMENT_ALLOWED_EXTENSIONS: tuple[str, ...] = ()
21+
MAX_ATTACHMENT_SIZE_BYTES = 5 * 1024 * 1024 # 5 MB
2122
MAX_ATTACHMENT_MARKDOWN_CHARS = 32_000
23+
MAX_ATTACHMENT_MARKDOWN_CHARS = 32_000 # TODO: 转 MARKDOWN的时候,不应该裁剪
2224

2325

2426
@dataclass(slots=True)
@@ -107,7 +109,7 @@ async def require_user_conversation(conv_repo: ConversationRepository, thread_id
107109

108110
def _make_upload_virtual_path(file_name: str) -> str:
109111
safe_name = file_name.replace("/", "_").replace("\\", "_").strip(" .")
110-
return f"{UPLOADS_VIRTUAL_PREFIX}/{safe_name or 'attachment.bin'}"
112+
return f"{VIRTUAL_PATH_UPLOADS}/{safe_name or 'attachment.bin'}"
111113

112114

113115
def _make_attachment_path(file_name: str) -> str:
@@ -127,7 +129,7 @@ def _make_attachment_path(file_name: str) -> str:
127129
def _build_attachment_storage_path(*, user_id: str, thread_id: str, file_name: str) -> tuple[str, Path]:
128130
"""返回附件虚拟路径和宿主机落盘路径。"""
129131
relative_name = _make_attachment_path(file_name)
130-
virtual_path = f"/home/gem/user-data/uploads/attachments/{relative_name}"
132+
virtual_path = f"{VIRTUAL_PATH_UPLOADS}/attachments/{relative_name}"
131133

132134
host_dir = Path(app_config.save_dir) / "threads" / thread_id / "user-data" / "uploads" / "attachments"
133135
host_dir.mkdir(parents=True, exist_ok=True)

backend/package/yuxi/services/doc_converter.py

Lines changed: 0 additions & 94 deletions
This file was deleted.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from pathlib import Path
2+
from yuxi import config
3+
4+
VIRTUAL_PATH_PREFIX = config.sandbox_virtual_path_prefix
5+
WORKSPACE_DIR_NAME = "workspace"
6+
UPLOADS_DIR_NAME = "uploads"
7+
OUTPUTS_DIR_NAME = "outputs"
8+
VIRTUAL_SKILLS_PATH = "/home/gem/skills"
9+
VIRTUAL_KBS_PATH = "/home/gem/kbs"
10+
11+
VIRTUAL_PATH_WORKSPACE = (Path(VIRTUAL_PATH_PREFIX) / WORKSPACE_DIR_NAME).as_posix()
12+
VIRTUAL_PATH_UPLOADS = (Path(VIRTUAL_PATH_PREFIX) / UPLOADS_DIR_NAME).as_posix()
13+
VIRTUAL_PATH_OUTPUTS = (Path(VIRTUAL_PATH_PREFIX) / OUTPUTS_DIR_NAME).as_posix()
14+
15+
__all__ = [
16+
"VIRTUAL_PATH_PREFIX",
17+
"WORKSPACE_DIR_NAME",
18+
"UPLOADS_DIR_NAME",
19+
"OUTPUTS_DIR_NAME",
20+
"VIRTUAL_PATH_WORKSPACE",
21+
"VIRTUAL_PATH_UPLOADS",
22+
"VIRTUAL_PATH_OUTPUTS",
23+
"VIRTUAL_SKILLS_PATH",
24+
"VIRTUAL_KBS_PATH",
25+
]

backend/server/routers/chat_router.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
from yuxi.repositories.agent_config_repository import AgentConfigRepository
4444
from yuxi.utils.logging_config import logger
4545
from yuxi.utils.image_processor import process_uploaded_image
46+
from yuxi.utils.paths import VIRTUAL_PATH_PREFIX
4647

4748

4849
# TODO:当前文件的功能过于庞杂,路由标签混乱
@@ -817,7 +818,7 @@ async def delete_thread_attachment(
817818
@chat.get("/thread/{thread_id}/files", response_model=ThreadFileListResponse)
818819
async def list_thread_files(
819820
thread_id: str,
820-
path: str = Query("/home/gem/user-data"),
821+
path: str = Query(f"{VIRTUAL_PATH_PREFIX}"),
821822
recursive: bool = Query(False),
822823
db: AsyncSession = Depends(get_db),
823824
current_user: User = Depends(get_required_user),

0 commit comments

Comments
 (0)