agent功能开发增加MCP后端
This commit is contained in:
Binary file not shown.
177
mcp_server.py
177
mcp_server.py
@@ -6,9 +6,9 @@ MCP Server for Financial Data Search
|
|||||||
|
|
||||||
from fastapi import FastAPI, HTTPException, Request
|
from fastapi import FastAPI, HTTPException, Request
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse, StreamingResponse
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from typing import List, Dict, Any, Optional, Literal
|
from typing import List, Dict, Any, Optional, Literal, AsyncGenerator
|
||||||
from datetime import datetime, date
|
from datetime import datetime, date
|
||||||
import logging
|
import logging
|
||||||
import httpx
|
import httpx
|
||||||
@@ -16,6 +16,7 @@ from enum import Enum
|
|||||||
import mcp_database as db
|
import mcp_database as db
|
||||||
from openai import OpenAI
|
from openai import OpenAI
|
||||||
import json
|
import json
|
||||||
|
import asyncio
|
||||||
|
|
||||||
# 配置日志
|
# 配置日志
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
@@ -1327,7 +1328,7 @@ class MCPAgentIntegrated:
|
|||||||
tools: List[dict],
|
tools: List[dict],
|
||||||
tool_handlers: Dict[str, Any],
|
tool_handlers: Dict[str, Any],
|
||||||
) -> AgentResponse:
|
) -> AgentResponse:
|
||||||
"""主流程"""
|
"""主流程(非流式)"""
|
||||||
logger.info(f"[Agent] 处理查询: {user_query}")
|
logger.info(f"[Agent] 处理查询: {user_query}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -1368,6 +1369,131 @@ class MCPAgentIntegrated:
|
|||||||
message=f"处理失败: {str(e)}",
|
message=f"处理失败: {str(e)}",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def process_query_stream(
|
||||||
|
self,
|
||||||
|
user_query: str,
|
||||||
|
tools: List[dict],
|
||||||
|
tool_handlers: Dict[str, Any],
|
||||||
|
) -> AsyncGenerator[str, None]:
|
||||||
|
"""主流程(流式输出)- 逐步返回执行结果"""
|
||||||
|
logger.info(f"[Agent Stream] 处理查询: {user_query}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 发送开始事件
|
||||||
|
yield self._format_sse("status", {"stage": "start", "message": "开始处理查询"})
|
||||||
|
|
||||||
|
# 阶段1: Kimi 制定计划
|
||||||
|
yield self._format_sse("status", {"stage": "planning", "message": "正在制定执行计划..."})
|
||||||
|
|
||||||
|
plan = await self.create_plan(user_query, tools)
|
||||||
|
|
||||||
|
# 发送计划
|
||||||
|
yield self._format_sse("plan", {
|
||||||
|
"goal": plan.goal,
|
||||||
|
"reasoning": plan.reasoning,
|
||||||
|
"steps": [
|
||||||
|
{"tool": step.tool, "arguments": step.arguments, "reason": step.reason}
|
||||||
|
for step in plan.steps
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
# 阶段2: 执行工具(逐步返回)
|
||||||
|
yield self._format_sse("status", {"stage": "executing", "message": f"开始执行 {len(plan.steps)} 个步骤"})
|
||||||
|
|
||||||
|
step_results = []
|
||||||
|
collected_data = {}
|
||||||
|
|
||||||
|
for i, step in enumerate(plan.steps):
|
||||||
|
# 发送步骤开始事件
|
||||||
|
yield self._format_sse("step_start", {
|
||||||
|
"step_index": i,
|
||||||
|
"tool": step.tool,
|
||||||
|
"arguments": step.arguments,
|
||||||
|
"reason": step.reason,
|
||||||
|
})
|
||||||
|
|
||||||
|
start_time = datetime.now()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 替换占位符
|
||||||
|
arguments = step.arguments.copy()
|
||||||
|
if step.tool == "summarize_news":
|
||||||
|
if arguments.get("data") in ["前面的新闻数据", "前面收集的所有数据"]:
|
||||||
|
arguments["data"] = json.dumps(collected_data, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
# 执行工具
|
||||||
|
result = await self.execute_tool(step.tool, arguments, tool_handlers)
|
||||||
|
execution_time = (datetime.now() - start_time).total_seconds()
|
||||||
|
|
||||||
|
step_result = StepResult(
|
||||||
|
step_index=i,
|
||||||
|
tool=step.tool,
|
||||||
|
arguments=arguments,
|
||||||
|
status="success",
|
||||||
|
result=result,
|
||||||
|
execution_time=execution_time,
|
||||||
|
)
|
||||||
|
step_results.append(step_result)
|
||||||
|
collected_data[f"step_{i+1}_{step.tool}"] = result
|
||||||
|
|
||||||
|
# 发送步骤完成事件(包含结果)
|
||||||
|
yield self._format_sse("step_complete", {
|
||||||
|
"step_index": i,
|
||||||
|
"tool": step.tool,
|
||||||
|
"status": "success",
|
||||||
|
"result": result,
|
||||||
|
"execution_time": execution_time,
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
execution_time = (datetime.now() - start_time).total_seconds()
|
||||||
|
|
||||||
|
step_result = StepResult(
|
||||||
|
step_index=i,
|
||||||
|
tool=step.tool,
|
||||||
|
arguments=step.arguments,
|
||||||
|
status="failed",
|
||||||
|
error=str(e),
|
||||||
|
execution_time=execution_time,
|
||||||
|
)
|
||||||
|
step_results.append(step_result)
|
||||||
|
|
||||||
|
# 发送步骤失败事件
|
||||||
|
yield self._format_sse("step_complete", {
|
||||||
|
"step_index": i,
|
||||||
|
"tool": step.tool,
|
||||||
|
"status": "failed",
|
||||||
|
"error": str(e),
|
||||||
|
"execution_time": execution_time,
|
||||||
|
})
|
||||||
|
|
||||||
|
# 阶段3: Kimi 生成总结
|
||||||
|
yield self._format_sse("status", {"stage": "summarizing", "message": "正在生成最终总结..."})
|
||||||
|
|
||||||
|
final_summary = await self.generate_final_summary(user_query, plan, step_results)
|
||||||
|
|
||||||
|
# 发送最终总结
|
||||||
|
yield self._format_sse("summary", {
|
||||||
|
"content": final_summary,
|
||||||
|
"metadata": {
|
||||||
|
"total_steps": len(plan.steps),
|
||||||
|
"successful_steps": len([r for r in step_results if r.status == "success"]),
|
||||||
|
"failed_steps": len([r for r in step_results if r.status == "failed"]),
|
||||||
|
"total_execution_time": sum(r.execution_time for r in step_results),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
# 发送完成事件
|
||||||
|
yield self._format_sse("done", {"message": "处理完成"})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"[Agent Stream] 错误: {str(e)}", exc_info=True)
|
||||||
|
yield self._format_sse("error", {"message": f"处理失败: {str(e)}"})
|
||||||
|
|
||||||
|
def _format_sse(self, event: str, data: dict) -> str:
|
||||||
|
"""格式化 SSE 消息"""
|
||||||
|
return f"event: {event}\ndata: {json.dumps(data, ensure_ascii=False)}\n\n"
|
||||||
|
|
||||||
# 创建 Agent 实例(全局)
|
# 创建 Agent 实例(全局)
|
||||||
agent = MCPAgentIntegrated()
|
agent = MCPAgentIntegrated()
|
||||||
|
|
||||||
@@ -1406,7 +1532,7 @@ async def chat(request: ChatRequest):
|
|||||||
|
|
||||||
@app.post("/agent/chat", response_model=AgentResponse)
|
@app.post("/agent/chat", response_model=AgentResponse)
|
||||||
async def agent_chat(request: AgentChatRequest):
|
async def agent_chat(request: AgentChatRequest):
|
||||||
"""智能代理对话端点"""
|
"""智能代理对话端点(非流式)"""
|
||||||
logger.info(f"Agent chat: {request.message}")
|
logger.info(f"Agent chat: {request.message}")
|
||||||
|
|
||||||
# 获取工具列表
|
# 获取工具列表
|
||||||
@@ -1441,6 +1567,49 @@ async def agent_chat(request: AgentChatRequest):
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@app.post("/agent/chat/stream")
|
||||||
|
async def agent_chat_stream(request: AgentChatRequest):
|
||||||
|
"""智能代理对话端点(流式 SSE)"""
|
||||||
|
logger.info(f"Agent chat stream: {request.message}")
|
||||||
|
|
||||||
|
# 获取工具列表
|
||||||
|
tools = [tool.dict() for tool in TOOLS]
|
||||||
|
|
||||||
|
# 添加特殊工具:summarize_news
|
||||||
|
tools.append({
|
||||||
|
"name": "summarize_news",
|
||||||
|
"description": "使用 DeepMoney 模型总结新闻数据,提取关键信息",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "要总结的新闻数据(JSON格式)"
|
||||||
|
},
|
||||||
|
"focus": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "关注点,例如:'市场影响'、'投资机会'等"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["data"]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
# 返回流式响应
|
||||||
|
return StreamingResponse(
|
||||||
|
agent.process_query_stream(
|
||||||
|
user_query=request.message,
|
||||||
|
tools=tools,
|
||||||
|
tool_handlers=TOOL_HANDLERS,
|
||||||
|
),
|
||||||
|
media_type="text/event-stream",
|
||||||
|
headers={
|
||||||
|
"Cache-Control": "no-cache",
|
||||||
|
"Connection": "keep-alive",
|
||||||
|
"X-Accel-Buffering": "no", # 禁用 Nginx 缓冲
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
# ==================== 健康检查 ====================
|
# ==================== 健康检查 ====================
|
||||||
|
|
||||||
@app.get("/health")
|
@app.get("/health")
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ export const ChatInterfaceV2 = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 发送消息(Agent模式)
|
// 发送消息(Agent模式 - 流式)
|
||||||
const handleSendMessage = async () => {
|
const handleSendMessage = async () => {
|
||||||
if (!inputValue.trim() || isProcessing) return;
|
if (!inputValue.trim() || isProcessing) return;
|
||||||
|
|
||||||
@@ -106,10 +106,16 @@ export const ChatInterfaceV2 = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
addMessage(userMessage);
|
addMessage(userMessage);
|
||||||
|
const userInput = inputValue; // 保存输入值
|
||||||
setInputValue('');
|
setInputValue('');
|
||||||
setIsProcessing(true);
|
setIsProcessing(true);
|
||||||
setCurrentProgress(0);
|
setCurrentProgress(0);
|
||||||
|
|
||||||
|
// 用于存储步骤结果
|
||||||
|
let currentPlan = null;
|
||||||
|
let stepResults = [];
|
||||||
|
let executingMessageId = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. 显示思考状态
|
// 1. 显示思考状态
|
||||||
addMessage({
|
addMessage({
|
||||||
@@ -120,18 +126,40 @@ export const ChatInterfaceV2 = () => {
|
|||||||
|
|
||||||
setCurrentProgress(10);
|
setCurrentProgress(10);
|
||||||
|
|
||||||
// 调用 Agent API
|
// 使用 EventSource 接收流式数据
|
||||||
const response = await fetch(`${mcpService.baseURL.replace('/mcp', '')}/mcp/agent/chat`, {
|
const eventSource = new EventSource(
|
||||||
|
`${mcpService.baseURL.replace('/mcp', '')}/mcp/agent/chat/stream`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
message: userInput,
|
||||||
|
conversation_history: messages
|
||||||
|
.filter(m => m.type === MessageTypes.USER || m.type === MessageTypes.AGENT_RESPONSE)
|
||||||
|
.map(m => ({
|
||||||
|
isUser: m.type === MessageTypes.USER,
|
||||||
|
content: m.content,
|
||||||
|
})),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 由于 EventSource 不支持 POST,我们使用 fetch + ReadableStream
|
||||||
|
const response = await fetch(`${mcpService.baseURL.replace('/mcp', '')}/mcp/agent/chat/stream`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
message: inputValue,
|
message: userInput,
|
||||||
conversation_history: messages.filter(m => m.type === MessageTypes.USER || m.type === MessageTypes.AGENT_RESPONSE).map(m => ({
|
conversation_history: messages
|
||||||
isUser: m.type === MessageTypes.USER,
|
.filter(m => m.type === MessageTypes.USER || m.type === MessageTypes.AGENT_RESPONSE)
|
||||||
content: m.content,
|
.map(m => ({
|
||||||
})),
|
isUser: m.type === MessageTypes.USER,
|
||||||
|
content: m.content,
|
||||||
|
})),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -139,62 +167,139 @@ export const ChatInterfaceV2 = () => {
|
|||||||
throw new Error('Agent请求失败');
|
throw new Error('Agent请求失败');
|
||||||
}
|
}
|
||||||
|
|
||||||
const agentResponse = await response.json();
|
const reader = response.body.getReader();
|
||||||
logger.info('Agent response', agentResponse);
|
const decoder = new TextDecoder();
|
||||||
|
let buffer = '';
|
||||||
|
|
||||||
// 移除思考消息
|
// 读取流式数据
|
||||||
setMessages(prev => prev.filter(m => m.type !== MessageTypes.AGENT_THINKING));
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
if (done) break;
|
||||||
|
|
||||||
if (!agentResponse.success) {
|
buffer += decoder.decode(value, { stream: true });
|
||||||
throw new Error(agentResponse.message || '处理失败');
|
const lines = buffer.split('\n\n');
|
||||||
}
|
buffer = lines.pop(); // 保留不完整的行
|
||||||
|
|
||||||
setCurrentProgress(30);
|
for (const line of lines) {
|
||||||
|
if (!line.trim()) continue;
|
||||||
|
|
||||||
// 2. 显示执行计划
|
// 解析 SSE 消息
|
||||||
if (agentResponse.plan) {
|
const eventMatch = line.match(/^event: (.+)$/m);
|
||||||
addMessage({
|
const dataMatch = line.match(/^data: (.+)$/m);
|
||||||
type: MessageTypes.AGENT_PLAN,
|
|
||||||
content: '已制定执行计划',
|
|
||||||
plan: agentResponse.plan,
|
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setCurrentProgress(40);
|
if (!eventMatch || !dataMatch) continue;
|
||||||
|
|
||||||
// 3. 显示执行过程
|
const event = eventMatch[1];
|
||||||
if (agentResponse.step_results && agentResponse.step_results.length > 0) {
|
const data = JSON.parse(dataMatch[1]);
|
||||||
addMessage({
|
|
||||||
type: MessageTypes.AGENT_EXECUTING,
|
|
||||||
content: '正在执行步骤...',
|
|
||||||
plan: agentResponse.plan,
|
|
||||||
stepResults: agentResponse.step_results,
|
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// 模拟进度更新
|
logger.info(`SSE Event: ${event}`, data);
|
||||||
for (let i = 0; i < agentResponse.step_results.length; i++) {
|
|
||||||
setCurrentProgress(40 + (i + 1) / agentResponse.step_results.length * 50);
|
// 处理不同类型的事件
|
||||||
await new Promise(resolve => setTimeout(resolve, 100));
|
switch (event) {
|
||||||
|
case 'status':
|
||||||
|
if (data.stage === 'planning') {
|
||||||
|
// 移除思考消息,显示规划中
|
||||||
|
setMessages(prev => prev.filter(m => m.type !== MessageTypes.AGENT_THINKING));
|
||||||
|
addMessage({
|
||||||
|
type: MessageTypes.AGENT_THINKING,
|
||||||
|
content: data.message,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
setCurrentProgress(20);
|
||||||
|
} else if (data.stage === 'executing') {
|
||||||
|
setCurrentProgress(30);
|
||||||
|
} else if (data.stage === 'summarizing') {
|
||||||
|
setCurrentProgress(90);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'plan':
|
||||||
|
// 移除思考消息
|
||||||
|
setMessages(prev => prev.filter(m => m.type !== MessageTypes.AGENT_THINKING));
|
||||||
|
|
||||||
|
// 显示执行计划
|
||||||
|
currentPlan = data;
|
||||||
|
addMessage({
|
||||||
|
type: MessageTypes.AGENT_PLAN,
|
||||||
|
content: '已制定执行计划',
|
||||||
|
plan: data,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
setCurrentProgress(30);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'step_start':
|
||||||
|
// 如果还没有执行中消息,创建一个
|
||||||
|
if (!executingMessageId) {
|
||||||
|
const executingMsg = {
|
||||||
|
type: MessageTypes.AGENT_EXECUTING,
|
||||||
|
content: '正在执行步骤...',
|
||||||
|
plan: currentPlan,
|
||||||
|
stepResults: [],
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
addMessage(executingMsg);
|
||||||
|
executingMessageId = Date.now();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'step_complete':
|
||||||
|
// 添加步骤结果
|
||||||
|
stepResults.push({
|
||||||
|
step_index: data.step_index,
|
||||||
|
tool: data.tool,
|
||||||
|
status: data.status,
|
||||||
|
result: data.result,
|
||||||
|
error: data.error,
|
||||||
|
execution_time: data.execution_time,
|
||||||
|
arguments: data.arguments,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新执行中消息
|
||||||
|
setMessages(prev =>
|
||||||
|
prev.map(msg =>
|
||||||
|
msg.type === MessageTypes.AGENT_EXECUTING
|
||||||
|
? { ...msg, stepResults: [...stepResults] }
|
||||||
|
: msg
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 更新进度
|
||||||
|
if (currentPlan) {
|
||||||
|
const progress = 30 + ((data.step_index + 1) / currentPlan.steps.length) * 60;
|
||||||
|
setCurrentProgress(progress);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'summary':
|
||||||
|
// 移除执行中消息
|
||||||
|
setMessages(prev => prev.filter(m => m.type !== MessageTypes.AGENT_EXECUTING));
|
||||||
|
|
||||||
|
// 显示最终结果
|
||||||
|
addMessage({
|
||||||
|
type: MessageTypes.AGENT_RESPONSE,
|
||||||
|
content: data.content,
|
||||||
|
plan: currentPlan,
|
||||||
|
stepResults: stepResults,
|
||||||
|
metadata: data.metadata,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
setCurrentProgress(100);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'error':
|
||||||
|
throw new Error(data.message);
|
||||||
|
|
||||||
|
case 'done':
|
||||||
|
logger.info('Stream完成');
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
logger.warn('未知事件类型:', event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setCurrentProgress(100);
|
|
||||||
|
|
||||||
// 移除执行中消息
|
|
||||||
setMessages(prev => prev.filter(m => m.type !== MessageTypes.AGENT_EXECUTING));
|
|
||||||
|
|
||||||
// 4. 显示最终结果
|
|
||||||
addMessage({
|
|
||||||
type: MessageTypes.AGENT_RESPONSE,
|
|
||||||
content: agentResponse.message || agentResponse.final_summary,
|
|
||||||
plan: agentResponse.plan,
|
|
||||||
stepResults: agentResponse.step_results,
|
|
||||||
metadata: agentResponse.metadata,
|
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Agent chat error', error);
|
logger.error('Agent chat error', error);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user