Yuxi 工具系统架构与开发指南:内置工具、知识库工具与 MCP 的三层装配与权限门控
2026/9/17 22:03:10 网站建设 项目流程

Yuxi 工具系统架构与开发指南:内置工具、知识库工具与 MCP 的三层装配与权限门控

【免费下载链接】Yuxi可私有部署的多租户知识智能体平台:统一 RAG、知识图谱、多智能体、MCP/Skills、沙盒与权限管理。Self-hosted knowledge agent platform for RAG, knowledge graphs and multi-agent workflows.项目地址: https://gitcode.com/GitHub_Trending/yu/Yuxi

Yuxi 是一个可私有部署的多租户知识智能体平台,其工具系统采用"内置工具、知识库工具、MCP 工具"三层结构,并在 Graph 构图时完成可执行工具注册、运行时再按用户权限与 Skill 激活状态决定模型可见性。本文基于 docs/agents/tools-system.md 展开,结合yuxi.agents.toolkits源码,完整讲解工具注册规范、内置能力清单、知识库工具机制、四步组装流程以及新增工具时的检查清单,帮助开发者在 Yuxi 中正确接入、扩展与审计工具。

三层工具体系的整体定位

Yuxi 的工具按来源分为三类,对应不同的注册方式与生命周期:

层级注册位置是否默认对模型可见说明
内置工具backend/package/yuxi/agents/toolkits/buildin由 Agent 配置的tools字段决定使用@tool装饰器注册,提供文件、OCR、交互、搜索等能力
知识库工具backend/package/yuxi/agents/toolkits/kbs否,需激活内置knowledge-baseSkill@tool(category="knowledge")注册,由 Skills middleware 统一开放
MCP 工具由 MCP 服务器提供,经 backend/package/yuxi/agents/mcp/service.py 发现由 Agent 配置的mcps字段与禁用列表决定连接与工具发现由 MCP 服务负责

整个生命周期遵循两个关键原则:

  1. Graph 创建时准备可执行工具:Agent 构图时把配置范围内、依赖满足的工具注册进 ToolNode,保证"可执行";
  2. 运行时决定模型可见性:模型每轮调用前,根据当前用户权限、Agent 配置和 Skill 激活状态,筛选"模型可见"的工具 schema。

注册一个内置工具

普通内置工具使用@tool装饰器注册,该装饰器定义在 registry.py,它基于langchain.tools.tool做了扩展,在创建 LangChain 工具对象的同时把附加元数据写入全局注册表:

from yuxi.agents.toolkits.registry import tool @tool(category="buildin", tags=["示例"], display_name="示例工具") def example_tool(text: str) -> str: """返回处理后的文本。""" return text

各参数的语义与底层影响:

  • category:用于前端分组,常见值是buildinknowledgedebug(从源码中的ToolExtraMetadata注释还可看到mysqlsubagents等取值)。服务层通过 service.py 的get_tool_instances_by_category按分类拉取工具实例;
  • tags:用于展示和筛选,例如内置工具的["搜索"]["文件", "交付物"]["交互"]
  • display_name:给用户看的名称,合并元数据时display_name的优先级高于tool.name(见 service.py);工具 ID(即函数名)才是给代码和模型协议使用的稳定名称;
  • iconconfig_guide:分别为前端图标与使用前配置提示,属可选元数据。

装饰器内部的注册动作(registry.py)包括:应用 LangChain 装饰器生成tool_obj、把ToolExtraMetadata写入_extra_registry、设置tool_obj.handle_tool_error = True并将实例追加进_all_tool_instances

注意:工具的注册不等于授权。装饰器只是让工具"存在",产生文件、网络或数据库副作用的工具必须在执行边界再次校验当前用户和目标资源。这一点在下面的执行处授权中会反复强调。

工具模块的导入与元数据暴露

装饰器只有在模块被导入时才会执行。toolkits包的 __init__.py 通过from . import buildin, debug触发各模块的@tool装饰器执行,自动完成注册。因此工具模块必须被toolkits包导入,装饰器才会执行注册

工具元数据通过服务层暴露给前端与组装流程:

  • get_tool_metadata(category):延迟加载全部工具元数据(含slugnamedescriptionargscategorytagsconfig_guide),供工具管理界面使用;
  • get_tool_instances_by_category(category):按分类返回真实工具实例,供 Graph 组装时注册进 ToolNode。

当前内置能力

常用内置工具实现在 buildin/tools.py 与 install_skill.py:

工具作用源码要点
ask_user_question等待用户回答交互式问题通过interrupt()暂停执行,使用normalize_questions规范化问题结构,支持单选/多选/Other 文本
ocr_parse_file把工作区中受支持的 PDF、Office 或图片转换为 Markdown结果写入当前 Project Workdir 的outputs/ocr/下,返回结果路径与短预览(1200 字符)而非全文
present_artifacts展示当前用户可见的文件产物通过Command(update={"artifacts": ...})登记交付物,前端在对话结束后渲染结果文件卡片
install_skill从允许的沙盒路径或 Git 来源安装个人 Skill;子智能体不可用校验 slug 合法性、沙盒路径范围,Git 安装时必须传skill_names
web_search使用已配置的豆包或 Tavily 搜索网页WEB_SEARCH_PROVIDER或环境变量自动探测选择供应商

几个值得注意的实现细节:

web_search 的供应商解析(buildin/tools.py):_WEB_SEARCH_PROVIDERS映射了doubaoDOUBAO_SEARCH_API_KEY)与tavilyTAVILY_API_KEY)。若设置了WEB_SEARCH_PROVIDER环境变量则按其选择;否则按环境中存在的 API Key 自动探测。两个供应商产出的工具名统一为web_search。豆包搜索参数包括query(1-100 字符)、count(1-50,默认 10)、time_rangeOneDay/OneWeek/OneMonth/OneYearYYYY-MM-DD..YYYY-MM-DD)、sites(最多 20 个域名)、block_hosts(最多 5 个)、content_formattext/markdown)。

文件读写和命令执行由 Agent 的 Sandbox backend 提供,不属于上述内置工具表。present_artifacts推荐展示当前 Project 的outputs/文件,且其_normalize_presented_artifact_path会强制校验路径位于当前 Workdir、虚拟用户数据路径或 Skills 路径范围内,越界立即抛错。large_tool_results和会话摘要等内部文件不会作为交付物展示。

图片生成能力由内置image-genSkill 提供,不再作为独立的 Python 工具注册,具体依赖和文件位置以该 Skill 的说明为准。

知识库工具

知识库工具以@tool(category="knowledge")注册在 kbs/tools.py,但不默认出现在模型工具列表。Agent 激活内置knowledge-baseSkill 后,Skills middleware 才会向模型开放这组工具:

工具作用
list_kbs列出当前运行可见的知识库(含kb_id、名称与描述)
query_kbkb_id检索片段,返回kb_idfile_id和内容
find_kb_document在指定文件中按关键词或正则定位内容(patterns支持列表,use_regexcase_sensitive可配)
open_kb_documentfile_id分段读取解析后的文档(window_size默认 1800 字符窗口)
get_mindmap读取知识导图,递归把 JSON 导图转为层级文本
search_file按文件名搜索可见知识库中的文件(offset默认 0,limit默认 300、上限 5000)
download_kb_file把有权限的原始文件下载到当前 Project 的outputs/,支持save_as重命名

权限在执行处重新校验是这组工具的安全核心:每个工具都先调用_resolve_visible_knowledge_bases_for_query(runtime)取得当前会话可见知识库列表(来自context._visible_knowledge_bases,该字段由prepare_agent_runtime_context在构图前按用户角色与部门过滤后写入),再经_find_query_target校验传入的kb_id必须存在于可见列表中,否则直接返回"不存在或当前会话未启用"。因此,工具参数中的kb_idfile_id和文件名都会在工具执行处重新检查,不能用模型提示词或 Agent 配置绕过知识库权限download_kb_file在落盘时还会剥离save_as中的目录成分、重名时追加_1/_2后缀,防止路径穿越。

知识库不会挂载为沙盒目录,读取方式见知识库机制详解。

需要在 Python 中直接取得知识库工具时:

from yuxi.agents.toolkits.kbs import get_common_kb_tools kb_tools = get_common_kb_tools()

get_common_kb_tools()返回 7 个通用工具(list_kbsget_mindmapquery_kbfind_kb_documentopen_kb_documentsearch_filedownload_kb_file)。返回的具体顺序由函数实现维护,不要把顺序当作协议——依赖下标访问不可靠,应按工具名引用。

工具组装流程

内置 Agent 创建 Graph 时,执行以下四步(实现在 graph.py 与相关服务中):

  1. prepare_agent_runtime_context按当前用户权限过滤工具、知识库、MCP、Skills 和子智能体。其实现见 context.py:读取用户并做资源归一化(normalize_agent_context_config)、调用resolve_visible_knowledge_bases_for_context填充可见知识库、调用resolve_runtime_skills_for_context生成_skill_runtime_snapshot,并将结果缓存到context._runtime_prepared以避免重复构图时重复解析。
  2. resolve_configured_runtime_tools(context)注册 Agent 配置和可见 Skill 依赖的可执行本地工具,并加载配置的 MCP 工具。实现见 service.py:按context.tools从 buildin 分类中取工具;用asyncio.gather并发加载各 MCP 服务器的工具(避免串行累积 1-2 秒延迟);再通过resolve_skill_gated_tools把 Skill 依赖的本地工具一并注册进 ToolNode。注意工具名冲突会直接抛错:MCP 工具与本地工具同名、Skill 本地工具与 MCP 工具同名都会触发RuntimeError
  3. SkillsMiddleware根据当前已预加载或已激活的 Skill,向模型请求开放相应工具 schema。其门控逻辑见 middlewares/skills.py:gated_tool_names(可见 Skill 依赖、不属于基础工具集的工具)减去当前已激活 Skill 的工具集合后,从request.tools中剔除;再追加已激活或预加载 Skill 的依赖工具与 MCP 工具。动态激活由"读取SKILL.md"触发:当模型调用read_file读取某个可见 Skill 的SKILL.md时,_process_tool_call_result会把该 slug 合并进activated_skills状态(middlewares/skills.py)。
  4. 工具执行器再次检查具体文件、知识库、MCP 和用户身份。如前所述,present_artifactsocr_parse_filedownload_kb_file等都在函数体内基于ToolRuntime重新解析thread_iduidworkdir_relative_path并校验路径范围。

整条链路的图示:

Agent 配置 + 用户权限 ↓ 运行时资源快照 ↓ 可执行工具注册 ──→ Skill 激活门控 ──→ 模型可见工具 ↓ 执行处的目标授权

"可执行"与"模型可见"是两件事:工具可以先注册到 ToolNode,等 Skill 激活后才对模型开放(这正是resolve_configured_runtime_tools注册全部 Skill 依赖工具、而SkillsMiddleware只放行已激活依赖的原因);反之,模型看见某个工具也不代表它可以访问任意资源——真正的资源授权发生在工具执行处。

MCP 和 Skills

MCP 工具由已启用的 MCP 服务器提供,服务器配置和工具禁用列表由 MCP 管理链路读取。Skills 可以声明本地工具、MCP 和其他 Skill 依赖:

  • 预加载 Skill从首轮就开放其依赖工具(SkillsMiddleware会把预加载 Skill 的完整说明注入系统提示段,并直接把其依赖工具加入模型可见列表);
  • 普通 Skill在模型通过read_file读取其SKILL.md后激活,激活后其本地工具与声明的 MCP 依赖才开放(deps_bundlebuild_dependency_bundle构建,包含toolsmcps两部分)。

职责边界划分:

  • 工具实现放在toolkits(内置与知识库工具)或由 MCP 服务器提供;
  • Skill 的使用说明和依赖放在 Skill 目录(SKILL.md);
  • MCP 的连接和工具发现由 MCP 服务负责;
  • Agent 配置只选择资源范围(toolsmcpsskills等字段),不直接复制工具实现。

详细规则见 Skills 管理 和 MCP 集成。

新增工具时检查

在 Yuxi 中新增一个工具前,建议逐条核对以下清单(对应 tools-system.md 的"新增工具时检查"):

  1. 确定归属层级:工具属于内置工具、知识库工具、MCP 还是 Skill?这决定了注册位置、category 取值与元数据来源。
  2. 确定可见性策略:是否需要在ToolNode注册(保证可执行)?是否需要 Skill 激活后才让模型看见(如知识库工具)?如果走 Skill 门控,要确认resolve_skill_gated_tools能解析出该工具,并且不在基础工具集(context.tools)中,否则不会进入门控集合。
  3. 执行处权限校验:产生副作用(文件、网络、数据库)时,执行处是否有用户、路径和资源权限校验?参考present_artifacts的路径白名单、download_kb_file的可见知识库校验与路径穿越防护。
  4. 结构化错误返回:错误是否以结构化结果返回,并保留可排查信息(如ocr_parse_file返回{"error": ...}install_skillToolMessage返回失败明细)?避免裸抛异常导致整个 Agent 运行中断。
  5. 稳定展示元数据:前端展示名称是否来自稳定元数据?通过display_name(而非函数名)提供用户可读名称,通过tags支持筛选,避免前端硬编码。
  6. 补齐测试:是否补充纯逻辑和真实 HTTP/文件链路的相应测试?仓库中 test_web_search_provider.py 覆盖 web_search 供应商解析,test_agent_stream_close.py 等测试覆盖工具调用与流式场景,可作为参考模板。

总结

Yuxi 的工具系统把"注册"、"可执行"、"模型可见"、"目标授权"四个环节彻底分离:@tool装饰器与toolkits包负责注册与元数据;resolve_configured_runtime_tools负责把配置范围内工具注册进 ToolNode;SkillsMiddleware负责按 Skill 激活状态门控模型可见性;每个工具的执行体则承担最终的资源授权。理解这四层,就能在新增工具、接入 MCP 或编写 Skill 时,既保证功能可用,又不破坏多租户权限边界。

【免费下载链接】Yuxi可私有部署的多租户知识智能体平台:统一 RAG、知识图谱、多智能体、MCP/Skills、沙盒与权限管理。Self-hosted knowledge agent platform for RAG, knowledge graphs and multi-agent workflows.项目地址: https://gitcode.com/GitHub_Trending/yu/Yuxi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询