ForcePilot/docs/latest/advanced/agents-config.md
肖泽涛 46b676db07 feat(skills): add skills management module
实现 Skills 管理功能,支持超级管理员导入、编辑、导出和删除技能包。

- 新增 Skill 数据模型(元数据存数据库,内容存文件系统)
- 新增 /api/system/skills/* 路由(权限分级:admin 可列表,superadmin 可管理)
- 新增 SelectedSkillsReadonlyBackend,运行时只读挂载到 /skills
- Agent 配置新增 skills 字段,运行时注入技能提示词
- 前端新增 Skills 管理页面(设置弹窗 + 独立路由)
- 新增测试覆盖:service/router/backend 单测

未实现功能:
 - 关联使用其他技能
 - 沙箱执行代码
 - 在技能中绑定工具
2026-02-21 02:48:20 +08:00

345 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 智能体
## 智能体开发
系统基于 [LangGraph](https://github.com/langchain-ai/langgraph) 并通过统一的 `AgentManager` 管理所有智能体。`src/agents/__init__.py` 会在启动时遍历 `src/agents` 目录,对每个包含 `__init__.py` 的子包执行自动发现:所有继承 `BaseAgent` 的类都会被注册并立即初始化,因此只要代码落位正确,就不需要再手动登记或修改管理器。
仓库预置了若干可直接运行的智能体:`chatbot` 聚焦对话与动态工具调度,`reporter` 演示报告类链路,`deep_agent` 提供深度分析能力。这些目录展示了上下文类、Graph 构造方式、子智能体引用以及中间件组合的范例,新增功能时可以直接复用。
### 智能体元数据配置
每个智能体可以通过在智能体目录下创建 `metadata.toml` 文件来配置元数据信息。这个文件使用 TOML 格式,包含以下字段:
- `name`: 智能体显示名称
- `description`: 智能体功能描述
- `examples`: 示例问题列表(数组格式)
例如,`src/agents/chatbot/metadata.toml`
<<< @/../src/agents/chatbot/metadata.toml
**注意**`metadata.toml` 文件是可选的如果没有提供系统将使用智能体类的基本属性
### 创建新的智能体
`src/agents` 下新建一个包保持与现有目录一致的结构放置 Graph 构造逻辑通常命名为 `graph.py`并在包内的 `__init__.py` 中暴露主类
智能体类必须继承 `src.agents.common.BaseAgent`同时实现异步的 `get_graph` 方法来返回编译后的 LangGraph 实例并配置好 `checkpointer`否则无法从历史对话中恢复
需要额外上下文字段时可继承 `BaseContext` 构建自己的配置表单再把类绑定到 `context_schema`平台会在 `saves/agents/<module>` 下生成默认配置
案例1 基于MySQL工具以及自定义 MCP Server 的数据库报表助手
<<< @/../src/agents/reporter/graph.py
### 工具系统
系统提供统一的工具获取函数 `get_tools_from_context(context)`自动从上下文配置中组装工具列表
```python
from src.agents.common.tools import get_tools_from_context
async def get_graph(self, **kwargs):
context = self.get_context()
tools = await get_tools_from_context(context)
# tools 已包含基础工具、知识库工具、MCP 工具
```
该函数会自动处理三类工具的组装
1. **基础工具**: `context.tools` 筛选的内置工具
2. **知识库工具**: 根据 `context.knowledges` 自动生成检索工具
3. **MCP 工具**: 根据 `context.mcps` 加载并过滤的 MCP 服务器工具
### BaseContext 配置字段
`BaseContext` 已内置以下常用配置字段所有智能体可直接复用
| 字段 | 类型 | 说明 |
|------|------|------|
| `model` | str | 使用的 LLM 模型 |
| `system_prompt` | str | 系统提示词 |
| `tools` | list[str] | 启用的内置工具列表 |
| `knowledges` | list[str] | 关联的知识库列表 |
| `mcps` | list[str] | 启用的 MCP 服务器名称 |
| `skills` | list[str] | 关联的 Skills运行时只读挂载到 `/skills` |
```python
from src.agents.common import BaseContext
@dataclass(kw_only=True)
class MyAgentContext(BaseContext):
# 继承所有 BaseContext 字段
# 可在此添加智能体特有的额外配置
custom_field: str = "默认值"
```
如需自定义工具选项 ReporterAgent MySQL 工具可覆盖 `tools` 字段的 `options` 元数据
```python
from src.agents.common import BaseContext, gen_tool_info
from src.agents.common.tools import get_buildin_tools
from src.agents.common.toolkits.mysql import get_mysql_tools
@dataclass(kw_only=True)
class ReporterContext(BaseContext):
tools: Annotated[list[dict], {"__template_metadata__": {"kind": "tools"}}] = field(
default_factory=lambda: [t.name for t in get_mysql_tools()],
metadata={
"name": "工具",
"options": lambda: gen_tool_info(get_buildin_tools() + get_mysql_tools()),
"description": "包含内置工具和 MySQL 工具包。",
},
)
def __post_init__(self):
self.mcps = ["mcp-server-chart"] # 默认启用图表 MCP
```
智能体实例的生命周期交给管理器处理会在自动发现时完成初始化并缓存单例以便快速响应请求在容器内热重载时只要保存文件即可触发重新导入需要强制刷新可调用 `agent_manager.get_agent(<id>, reload=True)`
更多动态工具选择与 MCP 注册的例子 `src/agents/chatbot/graph.py` 中的中间件组合
### Skills 只读挂载
`BaseContext.skills` 用于声明当前智能体可访问的技能目录slug 列表)。运行时会将这些目录只读挂载到 `/skills/<slug>/...`
1. 仅显示配置中选中的 skills未选中的 slug 在运行时不可见
2. `/skills` 仅支持读取能力`ls/read/glob/grep`写入和编辑会被拒绝
3. skills 元数据来自数据库索引内容目录来自共享存储 `/app/saves/skills`
### 拓展现有智能体
智能体保持为 LangGraph 的标准节点组合因此可以在原有 `graph.py` 中添加节点条件与消息转换器复用现成上下文时只需扩展当前 `context_schema` 的字段若功能差异较大可以创建新的上下文类并替换 `context_schema`
对工具模型或提示语的调整建议封装到中间件或独立函数里既方便多智能体共用又能保持 `BaseAgent` 的基础接口稳定变更提交后无需手动刷新注册表只要确保包结构未改变智能体会在热重载中自动更新
### 子智能体与中间件
子智能体集中放在 `src/agents/common/subagents` 目录典型例子是 `calc_agent`它通过 LangChain `create_agent` 构建计算器能力并以工具暴露给主图新增子智能体时沿用这一结构在目录内编写封装函数与 `@tool` 装饰器导出后即可被任意智能体调用
中间件位于 `src/agents/common/middlewares`包含上下文感知提示词模型选择动态工具加载以及附件注入等实现如果需要编写新的中间件请遵循 LangChain 官方文档中对 `AgentMiddleware`、`ModelRequest`、`ModelResponse` 等接口的定义完成后在该目录的 `__init__.py` 暴露入口主智能体即可在 `middleware` 列表中引用
#### RuntimeConfigMiddleware
`RuntimeConfigMiddleware`[runtime_config_middleware.py](https://github.com/xerrors/Yuxi-Know/blob/main/src/agents/common/middlewares/runtime_config_middleware.py)是系统默认的核心中间件之一负责在每次模型调用前自动注入运行时配置
1. **自动注入当前时间** system prompt 开头追加当前时间格式为 `当前时间YYYY-MM-DD HH:MM:SS`确保 LLM 能获取准确的时间上下文
2. **动态加载工具**根据 `context.tools`、`context.knowledges`、`context.mcps` 自动组装可用工具列表
3. **模型选择**根据 `context.model` 加载对应模型配置
如需自定义时间注入逻辑或禁用该行为可继承该中间件并覆盖 `awrap_model_call` 方法
#### 文件上传中间件
文件上传功能通过 `inject_attachment_context` 中间件实现位于 `src/agents/common/middlewares/attachment_middleware.py`)。该中间件基于 LangChain 1.0 `AgentMiddleware` 标准实现具有以下特点
1. **状态扩展**定义 `AttachmentState` 扩展 `AgentState`添加可选的 `attachments` 字段
2. **自动注入**在模型调用前 `request.state` 中读取附件并转换为 `SystemMessage`
3. **向后兼容**不使用文件上传的智能体不受影响
##### 为智能体启用文件上传
只需两步
**步骤 1声明能力**让前端显示上传按钮
```python
class MyAgent(BaseAgent):
capabilities = ["file_upload"]
```
**步骤 2添加中间件**让智能体能够处理附件内容
```python
from src.agents.common.middlewares import inject_attachment_context
async def get_graph(self):
graph = create_agent(
model=load_chat_model("..."),
tools=tools,
middleware=[
inject_attachment_context, # 添加附件中间件
context_aware_prompt, # 其他中间件...
# ...
],
checkpointer=await self._get_checkpointer(),
)
return graph
```
##### 工作流程
1. **前端上传**用户在聊天界面上传文档txtmddocxhtml
2. **API 解析**后端将文档转换为 Markdown 格式并存储到数据库超过 32k 会被截断
3. **自动加载**API 层在调用 agent 前从数据库加载附件数据
4. **中间件注入**`inject_attachment_context` 自动将附件内容注入为系统消息
5. **模型处理**LLM 接收到附件内容和用户问题进行综合回答
这种设计确保了附件功能的可选性和可扩展性任何智能体都可以通过添加中间件快速启用文件上传能力
## 内置工具与 MCP 集成
系统会根据配置自动组装工具集合涵盖知识图谱查询向量检索生成的动态工具MySQL 只读查询能力Tavily 搜索以及所有注册的 MCP 工具
MCP (Model Context Protocol) 服务的配置现已全面支持通过系统管理界面或 API 进行动态管理数据持久化存储在数据库中。`src/services/mcp_service.py` 仅作为核心逻辑层和默认配置的存放处不再建议直接修改代码来添加服务器
### MCP 服务器管理
系统提供了完善的 API (`/system/mcp-servers`) 和管理界面来执行 MCP 服务器的增删改查操作
#### 支持的传输协议
系统支持三种 MCP 传输协议
1. **SSE (Server-Sent Events)**: 标准的 HTTP SSE 连接
2. **Streamable HTTP**: 支持流式传输的 HTTP 连接远程
3. **Stdio**: 通过标准输入/输出运行本地进程支持 Python/Node.js
#### 配置示例
以下是通过管理界面添加 MCP 服务器时的常见配置参数示例对应 API 请求体
##### 1. 远程 HTTP/SSE 服务器
* **Server Name**: `sequentialthinking`
* **Transport**: `streamable_http` ( `sse`)
* **URL**: `https://remote.mcpservers.org/sequentialthinking/mcp`
**特点**
- 无需本地安装适合公开可用的 MCP 服务
- 启动速度快无需本地依赖
##### 2. 使用 npx 运行 Node.js 包
* **Server Name**: `mcp-server-chart`
* **Transport**: `stdio`
* **Command**: `npx`
* **Args**: `["-y", "@antv/mcp-server-chart"]`
**特点**
- 自动下载并运行 Node.js
- 适合 Node.js 生态的 MCP 服务
##### 3. 使用 uvx 运行 Python 包
* **Server Name**: `mysql-mcp-server`
* **Transport**: `stdio`
* **Command**: `uvx`
* **Args**: `["mysql_mcp_server"]`
* **Environment Variables**:
```json
{
"MYSQL_DATABASE": "your_database",
"MYSQL_HOST": "localhost",
"MYSQL_PASSWORD": "your_password",
"MYSQL_PORT": "3306",
"MYSQL_USER": "your_username"
}
```
**特点**
- 自动管理 Python 虚拟环境和依赖
- 适合 PyPI 上已发布的 MCP 服务
##### 4. 使用 uv 运行本地仓库
* **Server Name**: `arxiv-mcp-server`
* **Transport**: `stdio`
* **Command**: `uv`
* **Args**:
```json
[
"tool",
"run",
"arxiv-mcp-server",
"--storage-path", "src/agents/mcp_repos/arxiv-mcp-server"
]
```
**特点**
- 直接运行本地 git 仓库中的 MCP 服务
- 支持热重载适合开发调试
### 动态工具加载与管理
系统提供统一的 MCP 服务层 (`src/services/mcp_service.py`) 封装所有 MCP 相关操作
#### 1. 智能体获取工具
智能体开发时应使用 `get_enabled_mcp_tools()` 获取工具该函数会自动根据数据库中的配置过滤掉被禁用的工具
```python
from src.services.mcp_service import get_enabled_mcp_tools
# 获取指定服务器的工具(自动过滤掉在管理界面禁用的工具)
tools = await get_enabled_mcp_tools("sequentialthinking")
```
#### 2. 工具粒度控制
通过管理界面或 API (`PUT /system/mcp-servers/{name}/tools/{tool_name}/toggle`)管理员可以启用或禁用特定的 MCP 工具禁用后的工具不会出现在 `get_enabled_mcp_tools` 的返回列表中从而防止智能体调用不需要的能力
#### 3. 默认服务器配置
系统首次启动时会加载 `src/services/mcp_service.py` `_DEFAULT_MCP_SERVERS` 定义的默认服务器 `sequentialthinking` `mcp-server-chart`到数据库中后续的修改将以数据库为准
### MySQL 数据库
数据库报表助手SqlReporterAgent 可以通过配置下面环境变量 Agent 能够连接到 MySQL 数据库并通过执行 SQL 查询获取数据库中的数据
设置数据库连接时 `.env` 中提供以下字段
```env
MYSQL_HOST=192.168.1.100
MYSQL_USER=username
MYSQL_PASSWORD=your_secure_password
MYSQL_DATABASE=database_name
MYSQL_DATABASE_DESCRIPTION=业务主库(可选提示)
MYSQL_PORT=3306
MYSQL_CHARSET=utf8mb4
```
所有查询限定在只读范围SELECTSHOWDESCRIBEEXPLAIN请求会经过表名校验与超时控制默认限制 60 秒与 100 行输出并可通过配置调整上限连接信息会反馈给 LangGraph智能体可以自动陈述数据库用途并选择更准确的检索策略详见代码部分 `src/agents/common/toolkits/mysql/`
### 多模态图片支持
系统支持接收图片作为输入与文本结合形成多模态查询图片支持的核心特性如下
#### 1. 图片上传与处理
- 通过 `/chat/image/upload` 接口上传图片
- 自动处理图片格式转换和压缩
- 返回 base64 编码的图片数据
- 图片大小限制为 10MB
- 支持的图片格式JPEGPNGWebPGIFBMP
- 自动压缩超过 5MB 的图片
当发送包含图片的请求时消息格式为
```json
{
"query": "这张图片里有什么?",
"image_content": "<base64编码的图片数据>",
"config": {},
"meta": {}
}
```
智能体会自动识别多模态消息并将其传递给支持图片的模型如果模型不支持图片会自动忽略图片内容只处理文本部分系统会将图片转换为符合模型要求的格式通常是 base64 编码的 JPEG PNG确保与主流多模态模型兼容
目前仅支持上传单个图片图片以 base64 编码形式存储在数据库系统会自动处理图片的格式转换和压缩并生成缩略图以优化性能
### 图片上传响应格式
```json
{
"success": true,
"image_content": "<base64编码的原始图片数据>",
"thumbnail_content": "<base64编码的缩略图数据>",
"width": 1024,
"height": 768,
"format": "JPEG",
"mime_type": "image/jpeg"
}
```
系统会将图片信息与用户查询一同传递给支持多模态的模型并自动适配模型要求的格式