chore: project initialization - Day 4 environment setup
This commit is contained in:
857
对话系统实现方案对比.md
Normal file
857
对话系统实现方案对比.md
Normal file
@@ -0,0 +1,857 @@
|
||||
# 对话系统实现方案详细对比
|
||||
|
||||
## 🎯 核心问题
|
||||
|
||||
**如果不使用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**
|
||||
|
||||
Reference in New Issue
Block a user