- Complete knowledge base list and detail pages - Complete document upload component - Fix CORS config (add PUT/DELETE method support) - Fix file upload issues (disabled state and beforeUpload return value) - Add detailed debug logs (cleaned up) - Create Day 21-22 completion summary document
861 lines
24 KiB
Markdown
861 lines
24 KiB
Markdown
# 对话系统实现方案详细对比
|
||
|
||
## 🎯 核心问题
|
||
|
||
**如果不使用LobeChat整体框架,基于大模型的对话聊天该怎么实现?**
|
||
|
||
让我为您详细对比3种方案:
|
||
|
||
---
|
||
|
||
## 方案A:使用Dify的对话功能
|
||
|
||
### Dify的对话能力
|
||
|
||
Dify确实提供了完整的对话功能,包括:
|
||
- ✅ LLM对话管理
|
||
- ✅ 流式输出
|
||
- ✅ 对话历史管理
|
||
- ✅ RAG知识库集成
|
||
- ✅ 提供WebApp界面
|
||
|
||
### 架构图
|
||
|
||
```
|
||
用户浏览器
|
||
↓
|
||
Dify自带的WebApp界面
|
||
↓
|
||
Dify后端(对话+RAG)
|
||
↓
|
||
LLM API (DeepSeek/Qwen)
|
||
```
|
||
|
||
### 实现方式
|
||
|
||
**方式1:直接使用Dify的WebApp**
|
||
```bash
|
||
# 部署Dify后,直接访问其Web界面
|
||
docker-compose up -d
|
||
# 访问 http://localhost:3000
|
||
# 创建12个应用(对应12个智能体)
|
||
```
|
||
|
||
**方式2:通过Dify API集成**
|
||
```javascript
|
||
// 调用Dify的对话API
|
||
const response = await fetch('https://api.dify.ai/v1/chat-messages', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Authorization': `Bearer ${DIFY_API_KEY}`,
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
inputs: {},
|
||
query: "请帮我构建PICOS",
|
||
response_mode: "streaming", // 流式输出
|
||
conversation_id: "conv-xxx", // 对话ID
|
||
user: "user-123"
|
||
})
|
||
});
|
||
|
||
// 接收流式响应
|
||
const reader = response.body.getReader();
|
||
while (true) {
|
||
const { done, value } = await reader.read();
|
||
if (done) break;
|
||
// 处理流式数据
|
||
}
|
||
```
|
||
|
||
### 优点 ✅
|
||
|
||
1. **开发成本极低**
|
||
- 不需要自己实现对话逻辑
|
||
- 不需要自己实现流式输出
|
||
- 不需要自己管理对话历史
|
||
- RAG功能开箱即用
|
||
|
||
2. **功能完整**
|
||
- 对话管理
|
||
- Prompt管理
|
||
- 流式输出
|
||
- 知识库集成
|
||
- 文件上传
|
||
|
||
3. **快速上线**
|
||
- 1-2周即可搭建MVP
|
||
- 无需深入理解LLM API细节
|
||
|
||
### 缺点 ❌
|
||
|
||
1. **12个智能体管理复杂**
|
||
```
|
||
问题:需要在Dify中创建12个独立的应用(App)
|
||
|
||
Dify界面:
|
||
├── 应用1: 选题评价
|
||
├── 应用2: PICOS构建
|
||
├── 应用3: 论文润色
|
||
├── ... (共12个)
|
||
└── 应用12: 期刊稿约评审
|
||
|
||
影响:
|
||
- 用户需要在12个应用间切换(体验差)
|
||
- 无法统一管理对话历史
|
||
- 无法共享项目上下文
|
||
```
|
||
|
||
2. **项目管理功能缺失**
|
||
```
|
||
我们的需求:
|
||
- 用户创建"项目/课题"
|
||
- 在项目内使用多个智能体
|
||
- 项目背景信息自动注入
|
||
- 对话可"固定"到项目背景
|
||
|
||
Dify的能力:
|
||
- ❌ 没有"项目"概念
|
||
- ❌ 无法跨应用共享上下文
|
||
- ❌ 每个对话是独立的
|
||
```
|
||
|
||
3. **前端定制困难**
|
||
```
|
||
我们的原型图:
|
||
- 左侧:项目列表 + 智能体列表
|
||
- 中间:智能体卡片选择
|
||
- 右侧:对话界面
|
||
|
||
Dify的界面:
|
||
- 固定的单应用界面
|
||
- 定制需要修改Dify源码(复杂)
|
||
```
|
||
|
||
4. **业务逻辑受限**
|
||
```
|
||
我们的特殊需求:
|
||
- 全局快速问答 vs 项目内深度研究
|
||
- 动态背景信息管理(固定功能)
|
||
- 对话溯源(引用知识库)
|
||
- 多模型即时切换
|
||
|
||
Dify的支持:
|
||
- 🟡 部分支持,但需要复杂配置
|
||
- 🟡 或需要修改源码
|
||
```
|
||
|
||
### 适用场景 ✅
|
||
|
||
**适合:** 如果您的需求是:
|
||
- 简单的知识库问答
|
||
- 单一的对话场景
|
||
- 快速验证想法(MVP)
|
||
|
||
**不适合:** 我们的项目
|
||
- ❌ 12个智能体需要统一管理
|
||
- ❌ 项目管理是核心功能
|
||
- ❌ 需要高度定制的UI
|
||
|
||
---
|
||
|
||
## 方案B:参考LobeChat + 自研后端(推荐 ⭐)
|
||
|
||
### 核心思路
|
||
|
||
```
|
||
前端:参考LobeChat的UI组件(不用整体框架)
|
||
后端:自己实现对话逻辑(调用LLM API)
|
||
RAG:使用Dify(专注知识库检索)
|
||
```
|
||
|
||
### 架构图
|
||
|
||
```
|
||
┌─────────────────────────────────────────┐
|
||
│ 前端(React + Vite) │
|
||
│ ┌──────────────────────────────────┐ │
|
||
│ │ 聊天UI(参考LobeChat实现) │ │
|
||
│ │ - ChatMessage 组件 │ │
|
||
│ │ - ChatInput 组件 │ │
|
||
│ │ - StreamRenderer 组件 │ │
|
||
│ │ - MarkdownContent 组件 │ │
|
||
│ └──────────────────────────────────┘ │
|
||
│ ┌──────────────────────────────────┐ │
|
||
│ │ 业务界面(自己实现) │ │
|
||
│ │ - 项目管理 │ │
|
||
│ │ - 智能体选择 │ │
|
||
│ │ - 历史记录 │ │
|
||
│ └──────────────────────────────────┘ │
|
||
└─────────────────────────────────────────┘
|
||
↓ REST API
|
||
┌─────────────────────────────────────────┐
|
||
│ 后端(Node.js + TypeScript) │
|
||
│ ┌──────────────────────────────────┐ │
|
||
│ │ 对话服务(自己实现) │ │
|
||
│ │ - 接收用户消息 │ │
|
||
│ │ - 组装上下文(项目背景+历史) │ │
|
||
│ │ - 调用LLM API │ │
|
||
│ │ - 流式返回结果 │ │
|
||
│ └──────────────────────────────────┘ │
|
||
│ ┌──────────────────────────────────┐ │
|
||
│ │ 业务服务(自己实现) │ │
|
||
│ │ - 项目管理 │ │
|
||
│ │ - 智能体路由 │ │
|
||
│ │ - 权限控制 │ │
|
||
│ └──────────────────────────────────┘ │
|
||
└─────────────────────────────────────────┘
|
||
↓ ↓
|
||
┌──────────────────┐ ┌─────────────────┐
|
||
│ Dify (RAG) │ │ LLM API │
|
||
│ - 知识库检索 │ │ - DeepSeek-V3 │
|
||
│ (仅RAG) │ │ - Qwen3 │
|
||
└──────────────────┘ └─────────────────┘
|
||
```
|
||
|
||
### 详细实现
|
||
|
||
#### 1. 前端聊天UI(参考LobeChat)
|
||
|
||
**提取LobeChat的核心组件:**
|
||
|
||
```tsx
|
||
// src/components/chat/ChatMessage.tsx
|
||
// 从LobeChat移植,做少量修改
|
||
|
||
import React from 'react';
|
||
import ReactMarkdown from 'react-markdown';
|
||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||
|
||
interface Message {
|
||
id: string;
|
||
role: 'user' | 'assistant';
|
||
content: string;
|
||
timestamp: number;
|
||
}
|
||
|
||
export function ChatMessage({ message, onCopy, onPin }: Props) {
|
||
const isUser = message.role === 'user';
|
||
|
||
return (
|
||
<div className={`flex gap-3 ${isUser ? 'flex-row-reverse' : ''}`}>
|
||
{/* 头像 */}
|
||
<Avatar type={isUser ? 'user' : 'ai'} />
|
||
|
||
{/* 消息内容 */}
|
||
<div className={`flex-1 ${isUser ? 'items-end' : 'items-start'}`}>
|
||
<div className={`
|
||
max-w-3xl p-4 rounded-lg
|
||
${isUser ? 'bg-blue-600 text-white' : 'bg-white border'}
|
||
`}>
|
||
{isUser ? (
|
||
<p className="whitespace-pre-wrap">{message.content}</p>
|
||
) : (
|
||
<ReactMarkdown
|
||
components={{
|
||
code({ node, inline, className, children, ...props }) {
|
||
const match = /language-(\w+)/.exec(className || '');
|
||
return !inline && match ? (
|
||
<SyntaxHighlighter
|
||
language={match[1]}
|
||
PreTag="div"
|
||
{...props}
|
||
>
|
||
{String(children).replace(/\n$/, '')}
|
||
</SyntaxHighlighter>
|
||
) : (
|
||
<code className={className} {...props}>
|
||
{children}
|
||
</code>
|
||
);
|
||
}
|
||
}}
|
||
>
|
||
{message.content}
|
||
</ReactMarkdown>
|
||
)}
|
||
</div>
|
||
|
||
{/* 操作按钮 */}
|
||
{!isUser && (
|
||
<div className="flex gap-2 mt-2">
|
||
<button onClick={onCopy}>复制</button>
|
||
<button onClick={onPin}>固定到项目</button>
|
||
<button>重新生成</button>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
```
|
||
|
||
```tsx
|
||
// src/components/chat/ChatInput.tsx
|
||
// 从LobeChat移植
|
||
|
||
export function ChatInput({ onSend, onUpload, onKbSelect }: Props) {
|
||
const [message, setMessage] = useState('');
|
||
|
||
const handleSend = () => {
|
||
if (!message.trim()) return;
|
||
onSend(message);
|
||
setMessage('');
|
||
};
|
||
|
||
return (
|
||
<div className="border rounded-lg p-2 bg-white">
|
||
<textarea
|
||
value={message}
|
||
onChange={(e) => setMessage(e.target.value)}
|
||
onKeyDown={(e) => {
|
||
if (e.key === 'Enter' && !e.shiftKey) {
|
||
e.preventDefault();
|
||
handleSend();
|
||
}
|
||
}}
|
||
placeholder="输入您的问题..."
|
||
className="w-full resize-none"
|
||
rows={3}
|
||
/>
|
||
|
||
<div className="flex justify-between items-center mt-2">
|
||
<div className="flex gap-2">
|
||
<button onClick={onUpload}>📎 上传文件</button>
|
||
<button onClick={onKbSelect}>@ 引用知识库</button>
|
||
</div>
|
||
|
||
<button
|
||
onClick={handleSend}
|
||
className="px-4 py-2 bg-blue-600 text-white rounded-lg"
|
||
>
|
||
发送
|
||
</button>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
```
|
||
|
||
#### 2. 后端对话逻辑(自己实现)
|
||
|
||
**核心代码:**
|
||
|
||
```typescript
|
||
// backend/src/services/chat.service.ts
|
||
|
||
import { agentConfig } from '../config/agent-loader';
|
||
import { LLMFactory } from '../adapters/llm-factory';
|
||
import { difyClient } from '../clients/dify';
|
||
|
||
export class ChatService {
|
||
|
||
/**
|
||
* 处理对话请求(流式输出)
|
||
*/
|
||
async handleChatStream(req: Request, res: Response) {
|
||
const {
|
||
message,
|
||
projectId,
|
||
agentId,
|
||
conversationId,
|
||
modelName = 'deepseek-v3'
|
||
} = req.body;
|
||
|
||
// 1. 获取智能体配置
|
||
const agent = agentConfig.getAgent(agentId);
|
||
if (!agent) {
|
||
return res.status(404).json({ error: 'Agent not found' });
|
||
}
|
||
|
||
// 2. 组装上下文
|
||
const context = await this.buildContext({
|
||
projectId,
|
||
agentId,
|
||
conversationId,
|
||
message,
|
||
agent
|
||
});
|
||
|
||
// 3. 设置流式响应头
|
||
res.setHeader('Content-Type', 'text/event-stream');
|
||
res.setHeader('Cache-Control', 'no-cache');
|
||
res.setHeader('Connection', 'keep-alive');
|
||
|
||
try {
|
||
// 4. 调用LLM(流式)
|
||
const llm = LLMFactory.create(modelName);
|
||
const stream = await llm.chatStream({
|
||
messages: context,
|
||
temperature: agent.models[modelName]?.temperature || 0.7,
|
||
max_tokens: agent.models[modelName]?.max_tokens || 2000
|
||
});
|
||
|
||
// 5. 转发流式数据
|
||
let fullResponse = '';
|
||
for await (const chunk of stream) {
|
||
fullResponse += chunk;
|
||
res.write(`data: ${JSON.stringify({
|
||
type: 'token',
|
||
content: chunk
|
||
})}\n\n`);
|
||
}
|
||
|
||
// 6. 保存对话记录
|
||
await this.saveMessage({
|
||
conversationId,
|
||
projectId,
|
||
agentId,
|
||
userMessage: message,
|
||
aiMessage: fullResponse
|
||
});
|
||
|
||
// 7. 结束流
|
||
res.write(`data: ${JSON.stringify({ type: 'done' })}\n\n`);
|
||
res.end();
|
||
|
||
} catch (error) {
|
||
res.write(`data: ${JSON.stringify({
|
||
type: 'error',
|
||
message: error.message
|
||
})}\n\n`);
|
||
res.end();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 组装对话上下文
|
||
*/
|
||
private async buildContext(params: BuildContextParams) {
|
||
const { projectId, agentId, conversationId, message, agent } = params;
|
||
|
||
const messages = [];
|
||
|
||
// 1. 项目背景(如果有)
|
||
if (projectId) {
|
||
const project = await db.projects.findOne({ id: projectId });
|
||
messages.push({
|
||
role: 'system',
|
||
content: `# 项目背景\n${project.description}`
|
||
});
|
||
}
|
||
|
||
// 2. 智能体系统提示词
|
||
const systemPrompt = agentConfig.getPrompt(agent.system_prompt_file);
|
||
messages.push({
|
||
role: 'system',
|
||
content: systemPrompt
|
||
});
|
||
|
||
// 3. 历史对话(最近10轮)
|
||
const history = await db.messages.find({
|
||
conversation_id: conversationId
|
||
}).limit(20).sort({ created_at: -1 });
|
||
|
||
for (const msg of history.reverse()) {
|
||
messages.push(
|
||
{ role: 'user', content: msg.user_message },
|
||
{ role: 'assistant', content: msg.ai_message }
|
||
);
|
||
}
|
||
|
||
// 4. 知识库检索(如果需要)
|
||
if (agent.rag_enabled && message.includes('@')) {
|
||
const kbName = this.extractKbReference(message);
|
||
if (kbName) {
|
||
const ragResults = await difyClient.queryKnowledgeBase({
|
||
datasetId: this.getKbId(kbName),
|
||
query: message,
|
||
topK: 5
|
||
});
|
||
|
||
messages.push({
|
||
role: 'system',
|
||
content: `# 相关知识库内容\n${
|
||
ragResults.map(r => `[${r.metadata.filename}]\n${r.content}`).join('\n\n')
|
||
}`
|
||
});
|
||
}
|
||
}
|
||
|
||
// 5. 当前用户问题
|
||
messages.push({
|
||
role: 'user',
|
||
content: message
|
||
});
|
||
|
||
return messages;
|
||
}
|
||
|
||
/**
|
||
* 保存对话记录
|
||
*/
|
||
private async saveMessage(params: SaveMessageParams) {
|
||
await db.messages.create({
|
||
conversation_id: params.conversationId,
|
||
project_id: params.projectId,
|
||
agent_id: params.agentId,
|
||
user_message: params.userMessage,
|
||
ai_message: params.aiMessage,
|
||
created_at: new Date()
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 3. LLM Adapter(统一接口)
|
||
|
||
```typescript
|
||
// backend/src/adapters/llm-factory.ts
|
||
|
||
// DeepSeek Adapter
|
||
export class DeepSeekAdapter {
|
||
constructor(private apiKey: string) {}
|
||
|
||
async chatStream(params: ChatParams): AsyncGenerator<string> {
|
||
const response = await fetch('https://api.deepseek.com/v1/chat/completions', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Authorization': `Bearer ${this.apiKey}`,
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
model: 'deepseek-chat',
|
||
messages: params.messages,
|
||
temperature: params.temperature,
|
||
max_tokens: params.max_tokens,
|
||
stream: true
|
||
})
|
||
});
|
||
|
||
const reader = response.body.getReader();
|
||
const decoder = new TextDecoder();
|
||
|
||
while (true) {
|
||
const { done, value } = await reader.read();
|
||
if (done) break;
|
||
|
||
const chunk = decoder.decode(value);
|
||
const lines = chunk.split('\n').filter(line => line.trim());
|
||
|
||
for (const line of lines) {
|
||
if (line.startsWith('data: ')) {
|
||
const data = line.slice(6);
|
||
if (data === '[DONE]') continue;
|
||
|
||
try {
|
||
const json = JSON.parse(data);
|
||
const content = json.choices[0]?.delta?.content;
|
||
if (content) {
|
||
yield content;
|
||
}
|
||
} catch (e) {
|
||
// 忽略解析错误
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Qwen Adapter
|
||
export class QwenAdapter {
|
||
constructor(private apiKey: string) {}
|
||
|
||
async chatStream(params: ChatParams): AsyncGenerator<string> {
|
||
// 类似实现,调用阿里云DashScope API
|
||
// ...
|
||
}
|
||
}
|
||
|
||
// 工厂
|
||
export class LLMFactory {
|
||
static create(modelName: string) {
|
||
switch (modelName) {
|
||
case 'deepseek-v3':
|
||
return new DeepSeekAdapter(process.env.DEEPSEEK_API_KEY);
|
||
case 'qwen3-72b':
|
||
return new QwenAdapter(process.env.DASHSCOPE_API_KEY);
|
||
default:
|
||
throw new Error(`Unsupported model: ${modelName}`);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 4. 前端调用(流式接收)
|
||
|
||
```typescript
|
||
// frontend/src/services/chat.ts
|
||
|
||
export async function sendMessage(params: SendMessageParams) {
|
||
const { message, projectId, agentId, conversationId, onToken, onDone } = params;
|
||
|
||
const response = await fetch('/api/chat/stream', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
message,
|
||
project_id: projectId,
|
||
agent_id: agentId,
|
||
conversation_id: conversationId,
|
||
model_name: 'deepseek-v3'
|
||
})
|
||
});
|
||
|
||
const reader = response.body.getReader();
|
||
const decoder = new TextDecoder();
|
||
|
||
while (true) {
|
||
const { done, value } = await reader.read();
|
||
if (done) break;
|
||
|
||
const chunk = decoder.decode(value);
|
||
const lines = chunk.split('\n');
|
||
|
||
for (const line of lines) {
|
||
if (line.startsWith('data: ')) {
|
||
const data = JSON.parse(line.slice(6));
|
||
|
||
if (data.type === 'token') {
|
||
onToken(data.content); // 实时显示
|
||
} else if (data.type === 'done') {
|
||
onDone();
|
||
} else if (data.type === 'error') {
|
||
throw new Error(data.message);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// React组件中使用
|
||
function ChatView() {
|
||
const [messages, setMessages] = useState([]);
|
||
const [currentResponse, setCurrentResponse] = useState('');
|
||
|
||
const handleSend = async (message: string) => {
|
||
// 添加用户消息
|
||
setMessages(prev => [...prev, { role: 'user', content: message }]);
|
||
|
||
// 添加空的AI消息(准备接收流式内容)
|
||
const aiMessageId = Date.now();
|
||
setMessages(prev => [...prev, {
|
||
id: aiMessageId,
|
||
role: 'assistant',
|
||
content: ''
|
||
}]);
|
||
|
||
// 发送请求
|
||
await sendMessage({
|
||
message,
|
||
projectId: currentProject.id,
|
||
agentId: currentAgent.id,
|
||
conversationId: currentConversation.id,
|
||
|
||
// 接收每个token
|
||
onToken: (token) => {
|
||
setMessages(prev => prev.map(msg =>
|
||
msg.id === aiMessageId
|
||
? { ...msg, content: msg.content + token }
|
||
: msg
|
||
));
|
||
},
|
||
|
||
// 完成
|
||
onDone: () => {
|
||
console.log('Stream completed');
|
||
}
|
||
});
|
||
};
|
||
|
||
return (
|
||
<div className="chat-container">
|
||
<div className="messages">
|
||
{messages.map(msg => (
|
||
<ChatMessage key={msg.id} message={msg} />
|
||
))}
|
||
</div>
|
||
<ChatInput onSend={handleSend} />
|
||
</div>
|
||
);
|
||
}
|
||
```
|
||
|
||
### 优点 ✅
|
||
|
||
1. **完全可控**
|
||
- ✅ 业务逻辑完全自主
|
||
- ✅ UI 100%匹配原型图
|
||
- ✅ 可以实现任何定制需求
|
||
|
||
2. **功能完整**
|
||
- ✅ 项目管理(核心功能)
|
||
- ✅ 12个智能体统一管理
|
||
- ✅ 上下文动态管理
|
||
- ✅ 对话固定到项目背景
|
||
|
||
3. **聊天体验好**
|
||
- ✅ 参考LobeChat的成熟实现
|
||
- ✅ 流式输出流畅
|
||
- ✅ Markdown渲染完美
|
||
|
||
4. **成本可控**
|
||
- ✅ 只需实现业务逻辑
|
||
- ✅ 聊天UI复用LobeChat(节省9.5天)
|
||
- ✅ RAG交给Dify(节省40天)
|
||
|
||
### 缺点 ❌
|
||
|
||
1. **需要自己实现对话逻辑**
|
||
- 但其实不复杂(上面的代码已展示)
|
||
- 核心就是:接收消息 → 组装上下文 → 调用LLM → 返回流式结果
|
||
|
||
2. **需要处理流式输出**
|
||
- 但有成熟的实现可参考(LobeChat、OpenAI SDK)
|
||
- Server-Sent Events (SSE) 是标准协议
|
||
|
||
### 开发工作量
|
||
|
||
| 模块 | 工作量 | 说明 |
|
||
|------|-------|------|
|
||
| 前端聊天UI | 1天 | 复用LobeChat组件 |
|
||
| 流式输出前端 | 0.5天 | 参考LobeChat实现 |
|
||
| Markdown渲染 | 0.5天 | 使用react-markdown |
|
||
| 后端对话服务 | 2天 | 上下文组装 + LLM调用 |
|
||
| 流式输出后端 | 1天 | SSE实现 |
|
||
| LLM Adapter | 1天 | DeepSeek + Qwen接入 |
|
||
| **总计** | **6天** | vs 纯手写13天 |
|
||
|
||
---
|
||
|
||
## 方案C:完全自研(不推荐)
|
||
|
||
### 架构
|
||
|
||
```
|
||
前端:完全自己实现聊天UI
|
||
后端:完全自己实现对话逻辑
|
||
RAG:完全自己实现向量检索
|
||
```
|
||
|
||
### 缺点 ❌
|
||
|
||
- 开发周期长(4-6个月)
|
||
- 技术难度高
|
||
- RAG系统复杂度被严重低估
|
||
- 不推荐!
|
||
|
||
---
|
||
|
||
## 📊 三种方案对比总结
|
||
|
||
| 维度 | 方案A: 用Dify对话 | 方案B: 自研对话+Dify RAG ⭐ | 方案C: 完全自研 |
|
||
|------|------------------|---------------------------|----------------|
|
||
| **开发周期** | 1-2周 | 2.5个月 | 4-6个月 |
|
||
| **开发成本** | ¥10k-20k | **¥49k** ⭐ | ¥80k-180k |
|
||
| **12个智能体管理** | 🔴 复杂 | **🟢 简单** ⭐ | 🟢 简单 |
|
||
| **项目管理** | 🔴 不支持 | **🟢 完全支持** ⭐ | 🟢 完全支持 |
|
||
| **UI定制** | 🔴 困难 | **🟢 完全可控** ⭐ | 🟢 完全可控 |
|
||
| **业务逻辑灵活性** | 🟡 受限 | **🟢 完全灵活** ⭐ | 🟢 完全灵活 |
|
||
| **聊天体验** | 🟢 好 | **🟢 好** ⭐ | 🟡 需大量调试 |
|
||
| **RAG能力** | 🟢 强 | **🟢 强** ⭐ | 🟡 需自己实现 |
|
||
| **技术难度** | ⭐ | **⭐⭐⭐** | ⭐⭐⭐⭐⭐ |
|
||
| **维护成本** | 🟢 低 | **🟡 中** ⭐ | 🔴 高 |
|
||
| **功能匹配度** | 🔴 40% | **🟢 95%** ⭐ | 🟢 100% |
|
||
|
||
---
|
||
|
||
## 🎯 最终推荐:方案B
|
||
|
||
### 核心理由
|
||
|
||
**✅ Dify能帮我们什么?**
|
||
- ✅ RAG知识库功能(文档解析、向量检索、答案溯源)
|
||
- ❌ 对话功能不适合我们(12个智能体管理困难)
|
||
|
||
**✅ 我们自己做什么?**
|
||
- ✅ 对话逻辑(接收消息 → 组装上下文 → 调用LLM)
|
||
- ✅ 项目管理(核心功能)
|
||
- ✅ 智能体编排(12个智能体统一管理)
|
||
- ✅ 前端UI(100%匹配原型图)
|
||
|
||
**✅ LobeChat帮我们什么?**
|
||
- ✅ 聊天UI组件(开源可复用)
|
||
- ✅ 流式输出参考实现
|
||
- ✅ Markdown渲染方案
|
||
|
||
### 技术可行性
|
||
|
||
**对话功能其实不复杂!** 核心只有4步:
|
||
|
||
```typescript
|
||
// 1. 接收消息
|
||
const { message, projectId, agentId } = req.body;
|
||
|
||
// 2. 组装上下文
|
||
const context = [
|
||
{ role: 'system', content: '项目背景...' },
|
||
{ role: 'system', content: '智能体提示词...' },
|
||
{ role: 'user', content: '历史对话...' },
|
||
{ role: 'assistant', content: '历史回复...' },
|
||
{ role: 'user', content: message }
|
||
];
|
||
|
||
// 3. 调用LLM
|
||
const stream = await deepseek.chatStream({ messages: context });
|
||
|
||
// 4. 返回流式结果
|
||
for await (const chunk of stream) {
|
||
res.write(`data: ${JSON.stringify({ content: chunk })}\n\n`);
|
||
}
|
||
```
|
||
|
||
**就这么简单!** 不需要Dify的对话功能。
|
||
|
||
### 最佳实践
|
||
|
||
```
|
||
Dify擅长的:RAG(知识库检索)
|
||
→ 让Dify做它擅长的
|
||
|
||
我们需要的:复杂业务逻辑(项目管理、智能体编排)
|
||
→ 自己实现,完全可控
|
||
|
||
聊天UI:成熟方案(LobeChat)
|
||
→ 参考实现,节省时间
|
||
```
|
||
|
||
---
|
||
|
||
## 📝 总结
|
||
|
||
**问题:如果不使用LobeChat,对话聊天该怎么实现?**
|
||
|
||
**答案:**
|
||
1. **前端聊天UI**:参考LobeChat开源组件(复用,不是不用)
|
||
2. **后端对话逻辑**:自己实现(调用DeepSeek/Qwen API)
|
||
3. **RAG知识库**:使用Dify(专注检索)
|
||
|
||
**Dify的角色:**
|
||
- ✅ 用于RAG知识库功能
|
||
- ❌ 不用其对话功能(不适合我们的复杂业务)
|
||
|
||
**这个方案的优势:**
|
||
- ✅ 开发周期:2.5个月(可接受)
|
||
- ✅ 开发成本:¥49,300(可控)
|
||
- ✅ 功能匹配度:95%(满足需求)
|
||
- ✅ 聊天体验:优秀(参考LobeChat)
|
||
- ✅ 灵活性:高(业务逻辑完全自主)
|
||
|
||
**关键点:对话功能不复杂,核心是业务逻辑!**
|
||
|
||
---
|
||
|
||
**文档版本:v1.0**
|
||
**更新时间:2025-10-10**
|
||
|
||
|
||
|
||
|