数据流
本文档描述 ChatAI Plugin 中消息的完整处理流程。
消息处理流程
用户消息
│
▼
ChatListener.js
│ 解析触发词
▼
chat.js
│ 1. 获取预设配置
│ 2. 构建上下文
│ 3. 创建 SkillsAgent
▼
SkillsAgent
│ getExecutableSkills()
▼
LLM Adapter (OpenAI/Claude/Gemini)
│ 调用 AI 模型
│ 模型返回 tool_calls
▼
SkillsAgent.execute()
│
▼
McpManager.callTool()
│ 路由到正确的工具源
▼
执行结果 → 返回给 AI → 生成回复 → 发送给用户详细流程
1. 消息接收
javascript
// ChatListener.js
async accept(e) {
// 检查是否应该触发
if (!this.shouldTrigger(e)) {
return false
}
// 解析消息内容
const content = this.parseMessage(e)
// 交给 chat.js 处理
return await handleChat(e, content)
}2. 触发判断
javascript
shouldTrigger(e) {
// @机器人
if (e.atBot) return true
// 前缀匹配
if (e.msg.startsWith(config.prefix)) return true
// 关键词匹配
if (this.matchKeywords(e.msg)) return true
// 随机触发
if (Math.random() * 100 < config.randomRate) return true
return false
}3. 上下文构建
javascript
// chat.js
async function handleChat(e, content) {
// 获取预设
const preset = await getPreset(e)
// 获取历史消息
const history = await getHistory(e.user_id, e.group_id)
// 构建消息列表
const messages = [
{ role: 'system', content: preset.systemPrompt },
...history,
{ role: 'user', content }
]
// 创建技能代理
const agent = await createSkillsAgent({ event: e, presetId: preset.id })
// 获取可用工具
const tools = agent.getToolDefinitions()
// 调用 AI
return await callAI(messages, tools, agent)
}4. AI 调用
javascript
async function callAI(messages, tools, agent) {
// 获取适配器
const adapter = getAdapter(config.channel)
// 发送请求
let response = await adapter.chat({
messages,
tools,
stream: true
})
// 处理工具调用
while (response.toolCalls?.length > 0) {
// 执行工具
const results = await executeToolCalls(response.toolCalls, agent)
// 将结果加入消息
messages.push({ role: 'assistant', tool_calls: response.toolCalls })
messages.push(...results.map(r => ({
role: 'tool',
tool_call_id: r.id,
content: JSON.stringify(r.result)
})))
// 继续对话
response = await adapter.chat({ messages, tools })
}
return response.content
}5. 工具执行
javascript
async function executeToolCalls(toolCalls, agent) {
const results = []
for (const call of toolCalls) {
try {
const result = await agent.execute(call.function.name, call.function.arguments)
results.push({ id: call.id, result })
} catch (error) {
results.push({ id: call.id, error: error.message })
}
}
return results
}6. 响应发送
javascript
// 格式化响应
const reply = formatReply(response)
// 发送消息
await e.reply(reply)
// 保存历史
await saveHistory(e.user_id, e.group_id, content, response)API 请求流程
客户端请求
│
▼
Express 中间件
│ 认证、限流
▼
路由处理器
│ /api/skills/execute
▼
SkillsAgent
│
▼
McpManager
│
▼
工具执行
│
▼
返回结果API 路由结构
/api
├── /auth # 认证
│ ├── /login
│ └── /verify
├── /config # 配置
│ ├── /get
│ └── /update
├── /skills # 技能(推荐)
│ ├── /status
│ ├── /tools
│ ├── /execute
│ └── /mcp/servers
├── /mcp # MCP(底层)
│ ├── /servers
│ ├── /resources
│ └── /prompts
└── /tools # 工具管理
├── /builtin
├── /custom
└── /logs流式响应
javascript
// 流式处理
async function streamChat(messages, tools, onChunk) {
const stream = await adapter.stream({ messages, tools })
let fullContent = ''
let toolCalls = []
for await (const chunk of stream) {
if (chunk.content) {
fullContent += chunk.content
onChunk({ type: 'content', data: chunk.content })
}
if (chunk.tool_calls) {
toolCalls = chunk.tool_calls
}
}
if (toolCalls.length > 0) {
onChunk({ type: 'tool_calls', data: toolCalls })
}
return { content: fullContent, toolCalls }
}错误处理
javascript
try {
const result = await handleChat(e, content)
await e.reply(result)
} catch (error) {
if (error instanceof RateLimitError) {
await e.reply('请求过于频繁,请稍后再试')
} else if (error instanceof AuthError) {
await e.reply('API 认证失败,请检查配置')
} else {
logger.error('Chat error:', error)
await e.reply('处理消息时出错,请稍后重试')
}
}