Tổng quan plugin EzyAI
Back to ezyaiEzyAI là bộ plugin cho EzyPlatform, cung cấp một lớp trung gian thống nhất
để gọi tới các nhà cung cấp AI (ChatGPT, Claude, DeepSeek, Gemini, Grok,
Qwen). Ngoài việc gọi chat, EzyAI còn bổ sung:
- Profile: mỗi dịch vụ có nhiều profile, mỗi profile có api key,endpoint, model, giới hạn token, timeout riêng.
- Skill: đoạn system prompt kèm danh sách tool được phép dùng, gắn vàoprofile.
- Tool (MCP): AI có thể gọi tool do bất kỳ plugin nào cung cấp, chỉcần đăng ký event handler theo quy ước tên.
- Knowledge (RAG): tool tìm kiếm dữ liệu tri thức (bài viết, sản phẩm,dữ liệu tuỳ biến) để AI trả lời dựa trên nội dung của hệ thống.
- Topic guard: giới hạn chủ đề AI được phép trả lời.
- Skill Library: thư viện skill được quản trị ở admin và công khai ratrang web.
Kiến trúc module
flowchart TB
subgraph EzyPlatform
subgraph SDK["ezyai-sdk"]
CS["AbstractAIChatService<br/>ChatGpt / Claude / DeepSeek /<br/>Gemini / Grok / Qwen"]
PS["EzyAIChatProfileService"]
SS["AISkillService"]
ST["EzyAISettingService"]
TP["McpAIChatToolProvider"]
KN["KnowledgeDataSourceManager<br/>DefaultKnowledgeDataSource"]
end
ADMIN["ezyai-admin-plugin<br/>Trang quản trị + Admin API"]
WEB["ezyai-web-plugin<br/>Trang Skill Library + Web API"]
SOCKET["ezyai-socket-plugin<br/>Đăng ký bot cho ezychat,<br/>gom MCP tool toàn zone"]
EZYCHAT["ezychat<br/>ChatBotServiceManager"]
OTHER["Các plugin khác<br/>(*_mcp_tool_event)"]
end
AI[("AI providers<br/>OpenAI, Anthropic, ...")]
DB[("MySQL<br/>chat_profile, settings,<br/>data_meta, post, ...")]
ADMIN --> SDK
WEB --> SDK
SOCKET --> SDK
SOCKET -- "addChatBotServices" --> EZYCHAT
TP -. "gọi tool" .-> OTHER
CS -- "HTTP / SSE" --> AI
SDK --> DB
| Module | Vai trò |
|---|---|
ezyai-sdk | Toàn bộ logic dùng chung: các chat service theo từng nhà cung cấp, quản lý profile, skill, setting, tool provider, knowledge. Không chứa bean, mỗi plugin tự khai báo lớp con (Admin*, Web*, Socket*) để đưa vào bean context của mình. |
ezyai-admin-plugin | Giao diện quản trị: dịch vụ AI, profile, skill, Skill Library, cài đặt; chat thử qua SSE; đăng ký các post type riêng của EzyAI. |
ezyai-web-plugin | Trang công khai /ezyai/skill-library và API đọc Skill Library; có sẵn chat service, tool, knowledge để các plugin web khác dùng. |
ezyai-socket-plugin | Khi server sẵn sàng, đăng ký các chat service của EzyAI vào ChatBotServiceManager của app ezychat (để bot trong ezychat dùng được AI), và gom các MCP tool của mọi plugin/app trong zone; cung cấp command mcpToolList cho admin. |
Các chat service của EzyAI cài đặt interface
ChatBotService củaezychat-sdk, vì vậy cách gọi AI ở mọi nơi đều đi quaChatBotServiceManager.chatStreamAsync(...).Các nhà cung cấp AI
classDiagram
class ChatBotService {
<<interface ezychat>>
createInputStream(request)
createOutputStream(handler)
getServiceName()
}
class AIChatBotService {
<<interface>>
getModelNames()
}
class AbstractAIChatService
class ClaudeChatService
class ChatGptChatService
ChatBotService <|.. AbstractAIChatService
AIChatBotService <|.. AbstractAIChatService
AbstractAIChatService <|-- ClaudeChatService
AbstractAIChatService <|-- ChatGptChatService
ChatGptChatService <|-- DeepSeekChatService
ChatGptChatService <|-- GeminiChatService
ChatGptChatService <|-- GrokChatService
ChatGptChatService <|-- QwenChatService
| Dịch vụ | Endpoint mặc định | Model mặc định | API |
|---|---|---|---|
CHATGPT | https://api.openai.com | gpt-4o | /v1/chat/completions |
CLAUDE | https://api.anthropic.com | claude-opus-5 | /v1/messages |
DEEPSEEK | https://api.deepseek.com | deepseek-chat | tương thích OpenAI |
GEMINI | https://generativelanguage.googleapis.com/v1beta/openai | gemini-2.5-flash | tương thích OpenAI |
GROK | https://api.x.ai | grok-4.6 | tương thích OpenAI |
QWEN | https://dashscope-intl.aliyuncs.com/compatible-mode | qwen-plus | tương thích OpenAI |
AbstractAIChatService lo phần chung (skill, topic guard, vòng lặp tool,retry, stream); lớp con chỉ cần mô tả khác biệt của nhà cung cấp: URL,
header, cách dựng message/request body, cách đọc tool call, cách đọc từng
event stream. Thêm nhà cung cấp tương thích OpenAI thường chỉ cần kế thừa
ChatGptChatService và đổi tên dịch vụ, endpoint mặc định.Lỗi mạng tạm thời (
UnknownHostException, ConnectException,NoRouteToHostException, SocketTimeoutException) được thử lại tối đa 3lần, cách nhau 500ms. Các lỗi khác (sai key, request sai, lỗi nhà cung
cấp) được ném ra ngay.
Luồng một cuộc chat
sequenceDiagram
autonumber
participant C as Caller<br/>(admin API / ezychat bot)
participant M as ChatBotServiceManager
participant S as AbstractAIChatService
participant DB as Profile / Skill / Setting
participant T as McpAIChatToolProvider
participant AI as AI provider
C->>M: chatStreamAsync(serviceName, request, handler)
M->>S: createInputStream(request)
S->>DB: api key, skill ids, skills, allowed tools
S->>S: applySkills + applyTopicGuard (system message)
alt Có tool được phép
loop tối đa max_allowed_tool_interactions vòng
S->>AI: request (không stream) + tools
AI-->>S: answer / tool calls
alt Không có tool call
S-->>M: answer đã sẵn sàng (không cần stream)
else Có tool call
S->>T: callTool(context, toolCall)
T-->>S: tool result (+ metadata)
S->>S: thêm assistant message + tool result
end
end
end
S->>AI: request stream=true (tool_choice = none nếu có tools)
AI-->>S: SSE events
M->>S: createOutputStream(handler)
loop mỗi event
S-->>C: onDelta(deltaContent)
end
S-->>C: onCompleted(answer: nội dung, token, metadata)
Một số điểm cần lưu ý:
- System message được ghép theo thứ tự: system prompt của các skill →system message của request → chỉ dẫn topic guard.
- Vòng tool chạy ở chế độ không stream. Nếu AI trả lời bằng văn bảnngay ở một vòng, câu trả lời được dùng luôn, không mở stream nữa. Nếuhết số vòng cho phép, vòng cuối được gửi kèm
tool_choice = noneđểbuộc AI trả lời bằng văn bản. - Token của các vòng tool được cộng dồn vào câu trả lời cuối.
- Metadata mà tool trả về (ví dụ
knowledgeList: danh sách nguồn trithức đã dùng) được gộp lại và trả kèm câu trả lời, phía giao diện có thểdùng để hiển thị nguồn tham khảo. - Context của tool: mỗi lần gọi tool, tham số do AI sinh ra được trộnthêm context của request và metadata của profile (ví dụ
profileWebUrl,rag_collection_name, các meta key bổ sung). - Lời gọi chạy trên thread pool riêng của
ChatBotServiceManager, kết quảtrả về quaChatBotStreamHandler(onDelta,onCompleted,onError).
Ví dụ gọi từ code
chatBotServiceManager.chatStreamAsync(
"CLAUDE",
ChatBotRequestModel.builder()
.profile(profile)
.systemMessage("You are a helpful assistant.")
.messages(
Collections.singletonList(
ChatBotMessageModel.ofUser("Xin chào")
)
)
.build(),
new ChatBotStreamHandler() {
@Override
public void onDelta(String deltaContent) {}
@Override
public void onCompleted(ChatBotAnswerModel answer) {}
@Override
public void onError(Exception exception) {}
}
);
Chat thử ở trang quản trị
POST /api/v1/ai-chat-services/{serviceName}/chat/stream trả vềtext/event-stream với 3 loại event:| Event | Dữ liệu |
|---|---|
delta | Một phần câu trả lời |
done | Toàn bộ câu trả lời kèm số token, metadata |
error | Thông tin lỗi của nhà cung cấp |
Request được giữ mở bằng
AsyncContext vì câu trả lời được ghi từ threadkhác sau khi method controller đã return.
Profile và cấu hình
erDiagram
CHAT_PROFILE ||--o| SETTINGS : "api key (PASSWORD)"
CHAT_PROFILE ||--o{ DATA_META : "metadata, skill_id"
DATA_META }o--|| POST_AI_SKILL : "skill_id"
POST_AI_SKILL ||--o{ POST_META : "ai_skill_allowed_tool"
CHAT_PROFILE {
long id
string chatBotServiceName
string name
string endpoint
string model
string status
}
SETTINGS {
string name "ezyai_[service]_profile_[id]_api_key"
string value "đã mã hoá"
}
DATA_META {
string dataType "chat_profile"
long dataId "profile id"
string metaKey
string metaValue
}
POST_AI_SKILL {
long id
string type "AI_SKILL"
string content "system prompt"
}
POST_META {
long postId
string metaKey
string metaValue "tên tool"
}
- Profile dùng entity
ChatProfilecủa ezychat. Profile mặc định của mỗidịch vụ lưu ở setting của ezychat; nếu chưa đặt, hệ thống lấy profileđangACTIVATEDđầu tiên của dịch vụ đó. - Api key luôn lưu ở setting kiểu
PASSWORDđể được platform mã hoá, tênsetting làezyai_[service]_profile_[profileId]_api_key([service]viết thường). Khi sửa profile mà api key là chuỗi đã che (****), key cũđược giữ nguyên. - Metadata của profile lưu ở
data_meta. Mặc định córag_collection_name,có thể khai báo thêm meta key qua settingezyai_additional_ai_profile_meta_keys. Các meta này được đưa vào contextkhi gọi tool. - Cấu hình không được cache, thay đổi ở trang quản trị có hiệu lực ngay ởlần chat tiếp theo.
Các setting chung
| Setting | Mặc định | Ý nghĩa |
|---|---|---|
ezyai_max_allowed_tool_interactions | 2 | Số vòng gọi tool tối đa trong một lượt chat |
ezyai_topic_guard_allowed_topics | trống | Các chủ đề được phép; trống là tắt topic guard |
ezyai_topic_guard_refusal_message | câu từ chối mặc định | Câu AI dùng khi từ chối |
ezyai_knowledge_data_types | post, product | Loại dữ liệu được tìm trong data index |
ezyai_knowledge_post_types | WIKI, LIBRARY, FAQ, POST, BLOG, NEWS | Loại bài viết được coi là tri thức |
ezyai_knowledge_limit_data_search | 10 | Số kết quả mặc định của tool tìm kiếm |
ezyai_knowledge_limit_indexed_data_search | 15 | Số kết quả tối thiểu lấy từ data index |
ezyai_knowledge_chunk_max_length | 600 | Độ dài tối đa một chunk nội dung |
ezyai_knowledge_chunk_excerpt_length | 160 | Độ dài đoạn trích |
ezyai_knowledge_chunk_paragraph_separator | nn | Dấu tách đoạn |
ezyai_knowledge_chunk_sentence_boundary_pattern | (?<=[.!?])s+ | Regex tách câu |
ezyai_additional_ai_profile_meta_keys | trống | Meta key bổ sung cho profile |
Skill
Skill là một bài viết có
type = AI_SKILL:- Nội dung là system prompt, được ghép vào đầu system message.
- Allowed tools lưu ở
post_metavới keyai_skill_allowed_tool, mỗidòng một tên tool.
Một profile gắn nhiều skill (lưu ở
data_meta, key skill_id). Khi chat,
AI chỉ thấy các tool nằm trong hợp các allowed tools của những skill gắn
với profile. Profile không có skill nào có tool thì cuộc chat bỏ qua vòng
tool và stream thẳng.
flowchart LR
P["Profile"] -->|skill_id| S1["Skill A<br/>prompt + tools"]
P -->|skill_id| S2["Skill B<br/>prompt + tools"]
S1 --> SP["System message"]
S2 --> SP
S1 --> AT["Allowed tools"]
S2 --> AT
ALL["Mọi MCP tool<br/>trong hệ thống"] --> F{"lọc"}
AT --> F
F --> TOOLS["Tool gửi cho AI"]
Tool theo chuẩn MCP
McpAIChatToolProvider không có danh sách tool cố định. Một tool là mộtcặp event handler đặt tên theo quy ước:
| Event | Vai trò |
|---|---|
[tên tool]_mcp_tool_schema_event | Trả về name, description, inputSchema (JSON Schema) |
[tên tool]_mcp_tool_event | Nhận tham số, trả về kết quả dạng MCP |
Kết quả của tool có dạng:
{
"content": [{ "type": "text", "text": "..." }],
"structuredContent": { },
"metadata": { },
"isError": false
}
Phần
text và structuredContent được ghép lại thành nội dung trả cho AI;metadata không gửi cho AI mà được gộp vào câu trả lời cuối.Vì vậy, plugin nào muốn cho AI dùng thêm tool chỉ cần khai báo hai event
handler trên, EzyAI không cần biết trước. Danh sách tool được đọc lại ở mỗi
lượt chat nên tool mới có hiệu lực ngay. Ở socket plugin,
SocketEzyAIMcpEventHandlerProvider gom các handler *_mcp_tool_* của mọiplugin và app trong zone.
Tool có sẵn của EzyAI:
| Tool | Chức năng |
|---|---|
search_knowledge_data_list | Tìm dữ liệu tri thức theo từ khoá, trả về danh sách đoạn trích kèm link nguồn |
search_knowledge_data_by_type_and_id | Lấy chi tiết một mục tri thức theo type và id/code |
Knowledge (RAG)
flowchart TB
Q["search_knowledge_data_list<br/>(query, limit, dataSource)"] --> MGR["KnowledgeDataSourceManager"]
MGR -->|theo tên| DS["DefaultKnowledgeDataSource"]
MGR -.->|nguồn khác| PRV["KnowledgeDataSourceProvider"]
DS --> STR["KnowledgeSearchStrategy<br/>(sắp theo priority)"]
STR --> IDX["KnowledgeIndexedDataSearchStrategy<br/>DataIndexService<br/>Reciprocal Rank Fusion"]
IDX -->|"(dataType, dataId)"| FET["EzyAIKnowledgeDataFetcher"]
FET --> F1["Post fetcher"]
FET --> F2["Product fetcher"]
FET --> F3["Event fetcher<br/>ezyai_knowledge_data_list_fetcher"]
F1 & F2 & F3 --> CH["KnowledgeContentChunker"]
CH --> RANK["Ưu tiên chunk chứa từ khoá<br/>cắt theo limit"]
RANK --> R["Kết quả cho AI<br/>+ metadata knowledgeList"]
- Data source: mặc định là
DEFAULT. Có thể thêm nguồn khác bằng cáchkhai báo beanKnowledgeDataSourcehoặcKnowledgeDataSourceProvider. - Search strategy: chiến lược mặc định tìm trong data index của platform(kết hợp nhiều kết quả bằng Reciprocal Rank Fusion) theo các loại dữ liệutrong
ezyai_knowledge_data_types. Có thể thêm chiến lược khác bằng beanKnowledgeSearchStrategy. - Fetcher: đổi
(dataType, dataId)thànhKnowledgeData. Có sẵnfetcher cho bài viết và sản phẩm. Loại dữ liệu khác được hỗ trợ quainternal eventezyai_knowledge_data_list_fetcher/ezyai_knowledge_data_fetcher, nên plugin khác có thể cung cấp dữ liệutri thức mà không phụ thuộc vào EzyAI. - Chunker: bỏ thẻ HTML, gọn khoảng trắng rồi chia nội dung dài thành cácchunk (ưu tiên theo đoạn, sau đó theo câu, cuối cùng cắt cứng) để chỉ gửiphần liên quan cho AI, giảm token.
- Kết quả gửi cho AI gồm id, loại, tiêu đề có link nguồn, mã sản phẩm(nếu có) và đoạn trích. Link được dựng từ
profileWebUrlcủa profile, AIđược nhắc giữ nguyên link trong câu trả lời.
Topic guard
Khi
ezyai_topic_guard_allowed_topics có giá trị, một chỉ dẫn được thêm vàocuối system message: AI chỉ được hỗ trợ các chủ đề đã khai báo, nếu câu hỏi
(xét cùng ngữ cảnh hội thoại) nằm ngoài chủ đề hoặc không phù hợp thì từ
chối bằng câu tương tự
ezyai_topic_guard_refusal_message.Skill Library
Skill Library là thư viện skill để chia sẻ, khác với
AI_SKILL dùng choprofile:
- Mỗi skill là bài viết
type = SKILL_LIBRARY, thuộc một danh mục (termSKILL_CATEGORY, tự tạo khi chưa có). - Admin có thể publish, publish nội bộ, gỡ publish.
- Web plugin cung cấp trang
/ezyai/skill-libraryvà API/api/v1/skill-library/categories,/api/v1/skill-library/skillsđể xemcác skill đã publish.
Các post type EzyAI đăng ký
| Post type | Mục đích |
|---|---|
AI_SKILL | Skill gắn vào profile |
KNOWLEDGE_DATA | Dữ liệu tri thức nhập tay |
KNOWLEDGE_FETCHER_SCRIPT | Script lấy dữ liệu tri thức |
SKILL_LIBRARY | Mục trong Skill Library |
Các loại này không cần short content;
AI_SKILL, KNOWLEDGE_DATA,KNOWLEDGE_FETCHER_SCRIPT không tạo link công khai.Danh sách API quản trị
Tất cả nằm dưới tiền tố
/api/v1 của admin và yêu cầu đăng nhập.| Method | Đường dẫn | Mô tả |
|---|---|---|
PUT | /settings | Lưu cài đặt EzyAI |
PUT | /ai-chat-services/{serviceName}/set-as-default | Đặt dịch vụ mặc định |
POST | /ai-chat-services/{serviceName}/chat/stream | Chat thử (SSE) |
GET | /ai-chat-services/{serviceName}/models | Lấy danh sách model từ nhà cung cấp |
GET | /ai-chat-services/{serviceName}/profiles | Danh sách profile |
POST | /ai-chat-services/{serviceName}/profiles/add | Thêm profile |
PUT | /ai-chat-services/{serviceName}/profiles/{profileId} | Sửa profile |
DELETE | /ai-chat-services/{serviceName}/profiles/{profileId} | Xoá profile (kèm api key, metadata) |
PUT | /ai-chat-services/{serviceName}/profiles/{profileId}/set-as-default | Đặt profile mặc định |
GET | /ai-chat-services/{serviceName}/profiles/{profileId}/skills | Skill của profile |
PUT | /ai-chat-services/{serviceName}/profiles/{profileId}/skill-post-ids | Gắn skill cho profile |
GET | /ai-skills | Danh sách skill |
POST | /ai-skills/add | Thêm skill |
PUT | /ai-skills/{skillId} | Sửa skill |
DELETE | /ai-skills/{skillId} | Xoá skill |
GET / PUT | /ai-skills/{skillId}/allowed-tool-names | Xem / lưu tool được phép |
GET | /skill-library, /skill-library/categories, /skill-library/{skillId} | Xem Skill Library |
POST | /skill-library/add | Thêm skill vào thư viện |
PUT | /skill-library/{skillId} | Sửa |
PUT | /skill-library/{skillId}/publish, /internal-publish, /unpublish | Đổi trạng thái publish |
DELETE | /skill-library/{skillId} | Xoá |
Điểm mở rộng
| Muốn làm | Cách làm |
|---|---|
| Thêm nhà cung cấp AI | Kế thừa AbstractAIChatService (hoặc ChatGptChatService nếu tương thích OpenAI), khai báo lớp con là singleton trong plugin |
| Thêm tool cho AI | Khai báo cặp event handler [tool]_mcp_tool_schema_event và [tool]_mcp_tool_event, rồi thêm tên tool vào allowed tools của skill |
| Thêm loại dữ liệu tri thức | Xử lý internal event ezyai_knowledge_data_list_fetcher / ezyai_knowledge_data_fetcher, hoặc khai báo bean EzyAIKnowledgeDataFetcher |
| Thêm cách tìm kiếm tri thức | Khai báo bean KnowledgeSearchStrategy với priority phù hợp |
| Thêm nguồn tri thức riêng | Khai báo bean KnowledgeDataSource hoặc KnowledgeDataSourceProvider |
| Đưa thêm thông tin profile vào tool | Thêm meta key vào setting ezyai_additional_ai_profile_meta_keys |