Appearance
Generate 组件
概述
Generate组件是RAGFlow的核心文本生成组件,负责调用大语言模型(LLM)生成回答。它支持多种LLM提供商、提示词模板、工具调用等高级功能,是RAG工作流中将检索到的知识转化为自然语言回答的关键组件。
主要功能
- 🤖 多LLM提供商支持(OpenAI、Claude、本地模型等)
- 📝 强大的提示词模板系统
- 🔧 函数调用和工具集成
- 📚 上下文感知的知识整合
- 💬 流式和批量生成模式
- 🎯 参数化控制(温度、最大长度等)
适用场景
- 知识问答系统的回答生成
- 聊天机器人的对话生成
- 文档摘要和分析
- 创意写作和内容生成
- 代码生成和技术解答
参数配置
基础参数
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
llm_id | string | 是 | "" | LLM模型ID |
prompt | string | 是 | "" | 提示词模板 |
cite | boolean | 否 | true | 是否包含引用 |
message_history_window_size | number | 否 | 12 | 历史消息窗口大小 |
LLM参数
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
temperature | number | 否 | 0.1 | 生成温度(0-2) |
top_p | number | 否 | 0.3 | 核心采样概率 |
max_tokens | number | 否 | 2048 | 最大生成长度 |
presence_penalty | number | 否 | 0.4 | 存在惩罚(-2到2) |
frequency_penalty | number | 否 | 0.7 | 频率惩罚(-2到2) |
高级参数
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
system_role | string | 否 | "" | 系统角色设定 |
tools | array | 否 | [] | 可用工具列表 |
tool_choice | string | 否 | "auto" | 工具选择策略 |
response_format | string | 否 | "text" | 响应格式 |
详细说明
llm_id (LLM模型ID)
- 类型: 字符串
- 描述: 指定要使用的大语言模型
- 获取方式: 从模型配置页面获取已配置的模型ID
- 支持模型:
- OpenAI:
gpt-4,gpt-3.5-turbo - Anthropic:
claude-3-opus,claude-3-sonnet - 本地模型:
llama-7b,chatglm-6b
- OpenAI:
- 示例:
"openai_gpt4","claude_sonnet"
prompt (提示词模板)
- 类型: 多行字符串
- 描述: 引导LLM生成回答的提示词模板
- 支持功能:
- 变量插值:
{变量名},{组件ID:index} - 条件逻辑:
{% if condition %}...{% endif %} - 循环结构:
{% for item in items %}...{% endfor %} - 内置函数:
now(),random()
- 变量插值:
- 最佳实践:
你是一个专业的AI助手。请基于以下知识内容回答用户问题: 知识内容: {retrieval:0} 用户问题: {begin@question} 要求: 1. 回答要准确、详细 2. 如果知识内容中没有相关信息,请诚实说明 3. 请在回答末尾标注引用来源
cite (引用标注)
- 类型: 布尔值
- 描述: 是否在生成的回答中包含引用信息
- 启用效果: 自动在回答中添加文档来源标注
- 格式示例:
[1] 来源:技术文档.pdf,第5页
temperature (生成温度)
- 类型: 浮点数 (0-2)
- 描述: 控制回答的创造性和随机性
- 建议值:
- 0.0-0.3: 事实性回答,一致性高
- 0.4-0.7: 平衡创造性和准确性
- 0.8-1.2: 创意生成,多样性高
- 1.3-2.0: 高度创造性,可能不连贯
max_tokens (最大生成长度)
- 类型: 正整数
- 描述: 限制生成回答的最大token数量
- 注意事项:
- 包含输入和输出的总token数
- 不同模型的token计算方式可能不同
- 设置过小可能导致回答被截断
输入输出
输入格式
Generate组件接受上游组件的内容,通常是检索结果:
json
{
"content": "检索到的知识内容或上下文信息",
"component_id": "upstream_component_id",
"chunks": [...], // 可选的文档片段
"reference": [...] // 可选的引用信息
}输出格式
json
{
"content": "LLM生成的回答内容",
"component_id": "generate_component_id",
"llm_response": {
"model": "gpt-4",
"usage": {
"prompt_tokens": 256,
"completion_tokens": 128,
"total_tokens": 384
},
"finish_reason": "stop",
"response_time": 2.34
},
"tools_called": [
{
"tool_name": "search_web",
"parameters": {"query": "具体查询"},
"result": "工具执行结果"
}
],
"reference": [
{
"document_id": "doc_123",
"document_name": "技术文档.pdf",
"chunk_id": "chunk_456",
"page_number": 5,
"citation_text": "[1]"
}
]
}前端配置界面
配置表单
typescript
interface GenerateFormData {
llm_id: string; // 模型ID
prompt: string; // 提示词模板
cite: boolean; // 启用引用
message_history_window_size: number; // 历史窗口大小
temperature: number; // 生成温度
top_p: number; // 核心采样
max_tokens: number; // 最大长度
presence_penalty: number; // 存在惩罚
frequency_penalty: number; // 频率惩罚
system_role: string; // 系统角色
tools: string[]; // 工具列表
response_format: string; // 响应格式
}界面元素
- 模型选择器: 下拉选择,显示可用模型和配置状态
- 提示词编辑器: 支持语法高亮和变量提示的代码编辑器
- 参数滑块: 直观的温度、top_p等参数调节
- 引用开关: 简单的开关组件
- 高级选项折叠面板: 工具配置、系统角色等
使用示例
示例1: 基础知识问答
json
{
"component_name": "Generate",
"params": {
"llm_id": "openai_gpt4",
"prompt": "你是一个专业的知识助手。请基于以下内容回答用户问题:\n\n{retrieval:0}\n\n请提供准确、详细的回答。",
"cite": true,
"temperature": 0.1,
"max_tokens": 1024
}
}示例2: 创意文案生成
json
{
"component_name": "Generate",
"params": {
"llm_id": "claude_sonnet",
"prompt": "你是一个资深的内容创作专家。请基于以下素材创作一篇吸引人的文案:\n\n素材内容:{retrieval:0}\n\n要求:\n1. 风格轻松活泼\n2. 突出产品优势\n3. 包含行动呼吁\n4. 字数控制在200字以内",
"cite": false,
"temperature": 0.8,
"max_tokens": 512,
"system_role": "创意文案专家"
}
}示例3: 技术问题解答(带工具调用)
json
{
"component_name": "Generate",
"params": {
"llm_id": "gpt4_with_tools",
"prompt": "你是一个技术专家。请分析用户的技术问题,必要时使用搜索工具获取最新信息:\n\n用户问题:{begin@question}\n背景资料:{retrieval:0}\n\n请提供详细的技术解答。",
"cite": true,
"temperature": 0.2,
"max_tokens": 2048,
"tools": ["web_search", "code_search"],
"tool_choice": "auto"
}
}前端实现
节点组件
typescript
// web/src/pages/flow/canvas/node/generate-node.tsx
export function GenerateNode({ id, data, isConnectable, selected }: NodeProps) {
const { llmList } = useLLMModels();
const selectedModel = useMemo(() => {
return llmList.find(model => model.id === data.form.llm_id);
}, [data.form.llm_id, llmList]);
return (
<section className={`${styles.ragNode} ${selected ? styles.selectedNode : ''}`}>
<Handle type="target" position={Position.Left} isConnectable={isConnectable} />
<Handle type="source" position={Position.Right} isConnectable={isConnectable} />
<NodeHeader id={id} name={data.name} label={data.label} />
<div className={styles.nodeBody}>
<div className={styles.nodeInfo}>
<Icon component={RobotOutlined} className={styles.nodeIcon} />
<span className={styles.nodeDescription}>LLM文本生成</span>
</div>
<div className={styles.modelInfo}>
<div className={styles.configItem}>
<span className={styles.configLabel}>模型:</span>
<span className={styles.configValue} title={selectedModel?.name}>
{selectedModel?.name?.substring(0, 15) || '未选择'}
{selectedModel?.name?.length > 15 && '...'}
</span>
</div>
<div className={styles.configItem}>
<span className={styles.configLabel}>温度:</span>
<span className={styles.configValue}>
{data.form.temperature || 0.1}
</span>
</div>
</div>
<div className={styles.statusBadges}>
{data.form.cite && (
<span className={styles.statusBadge}>
<Icon component={LinkOutlined} />
引用
</span>
)}
{data.form.tools?.length > 0 && (
<span className={styles.statusBadge}>
<Icon component={ToolOutlined} />
{data.form.tools.length} 工具
</span>
)}
{data.form.system_role && (
<span className={styles.statusBadge}>
<Icon component={UserOutlined} />
角色
</span>
)}
</div>
{data.form.prompt && (
<div className={styles.promptPreview}>
<Icon component={EditOutlined} className={styles.promptIcon} />
<span className={styles.promptText} title={data.form.prompt}>
{data.form.prompt.length > 30
? `${data.form.prompt.substring(0, 30)}...`
: data.form.prompt
}
</span>
</div>
)}
</div>
</section>
);
}配置表单
typescript
// web/src/pages/flow/form/generate-form/index.tsx
const GenerateForm: React.FC<IOperatorForm> = ({ onValuesChange, form }) => {
const { llmList, loading } = useLLMModels();
const [promptVariables, setPromptVariables] = useState<string[]>([]);
const [showAdvanced, setShowAdvanced] = useState(false);
// 提示词编辑器配置
const editorOptions = {
language: 'markdown',
theme: 'vs-dark',
minimap: { enabled: false },
wordWrap: 'on',
fontSize: 14,
lineNumbers: 'on',
folding: true
};
return (
<Form form={form} layout="vertical" onValuesChange={onValuesChange}>
{/* LLM模型选择 */}
<Form.Item
name="llm_id"
label="选择LLM模型"
rules={[{ required: true, message: '请选择LLM模型' }]}
>
<LLMSelect
loading={loading}
showStatus={true}
placeholder="请选择要使用的LLM模型"
/>
</Form.Item>
{/* 提示词模板编辑器 */}
<Form.Item
name="prompt"
label={
<Space>
<span>提示词模板</span>
<Tooltip title="支持变量插值和Jinja2语法">
<InfoCircleOutlined />
</Tooltip>
</Space>
}
rules={[{ required: true, message: '请输入提示词模板' }]}
>
<div className={styles.promptEditor}>
<PromptEditor
value={form.getFieldValue('prompt')}
onChange={(value) => {
form.setFieldsValue({ prompt: value });
onValuesChange({ prompt: value }, form.getFieldsValue());
}}
options={editorOptions}
variables={promptVariables}
/>
<div className={styles.promptTools}>
<Space>
<Button size="small" onClick={() => insertVariable('{retrieval:0}')}>
插入检索结果
</Button>
<Button size="small" onClick={() => insertVariable('{begin@question}')}>
插入用户问题
</Button>
<Button size="small" onClick={() => insertVariable('{now()}')}>
插入当前时间
</Button>
</Space>
</div>
</div>
</Form.Item>
{/* 基础配置 */}
<Row gutter={16}>
<Col span={12}>
<Form.Item
name="temperature"
label="生成温度"
tooltip="控制回答的创造性,0为最保守,2为最创新"
>
<Slider
min={0}
max={2}
step={0.1}
marks={{
0: '保守',
0.5: '平衡',
1: '创新',
2: '极创新'
}}
tipFormatter={(value) => `${value} (${getTemperatureDescription(value)})`}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="max_tokens"
label="最大长度"
rules={[
{ required: true, message: '请设置最大长度' },
{ type: 'number', min: 1, max: 8192, message: '长度应在1-8192之间' }
]}
>
<InputNumber
min={1}
max={8192}
style={{ width: '100%' }}
placeholder="推荐2048"
/>
</Form.Item>
</Col>
</Row>
{/* 引用设置 */}
<Form.Item
name="cite"
label="自动引用"
tooltip="在回答中自动包含文档来源引用"
valuePropName="checked"
>
<Switch checkedChildren="开启" unCheckedChildren="关闭" />
</Form.Item>
{/* 高级选项 */}
<Collapse ghost>
<Panel header="高级选项" key="advanced">
<Form.Item
name="system_role"
label="系统角色"
tooltip="为LLM设定特定的角色身份"
>
<Input.TextArea
rows={3}
placeholder="例如:你是一个专业的法律顾问..."
/>
</Form.Item>
<Form.Item
name="tools"
label="可用工具"
tooltip="LLM可以调用的外部工具"
>
<Select
mode="multiple"
placeholder="选择可用工具"
options={[
{ label: '网络搜索', value: 'web_search' },
{ label: '代码搜索', value: 'code_search' },
{ label: '计算器', value: 'calculator' },
{ label: '邮件发送', value: 'send_email' }
]}
/>
</Form.Item>
<Row gutter={16}>
<Col span={12}>
<Form.Item
name="top_p"
label="核心采样(Top-P)"
tooltip="控制词汇选择的多样性"
>
<InputNumber
min={0}
max={1}
step={0.1}
style={{ width: '100%' }}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="presence_penalty"
label="存在惩罚"
tooltip="减少重复内容的程度"
>
<InputNumber
min={-2}
max={2}
step={0.1}
style={{ width: '100%' }}
/>
</Form.Item>
</Col>
</Row>
</Panel>
</Collapse>
</Form>
);
};
function getTemperatureDescription(value: number): string {
if (value <= 0.2) return "事实性强";
if (value <= 0.5) return "平衡模式";
if (value <= 1.0) return "有创意";
return "高度创新";
}后端实现
参数类
python
# agent/component/generate.py
class GenerateParam(ComponentParamBase):
"""Generate组件参数"""
def __init__(self):
super().__init__()
self.llm_id = "" # LLM模型ID
self.prompt = "" # 提示词模板
self.cite = True # 启用引用
self.message_history_window_size = 12 # 历史窗口大小
self.temperature = 0.1 # 生成温度
self.top_p = 0.3 # 核心采样
self.max_tokens = 2048 # 最大长度
self.presence_penalty = 0.4 # 存在惩罚
self.frequency_penalty = 0.7 # 频率惩罚
self.system_role = "" # 系统角色
self.tools = [] # 工具列表
self.tool_choice = "auto" # 工具选择
self.response_format = "text" # 响应格式
def check(self):
"""参数验证"""
self.check_empty(["llm_id", "prompt"], "LLM模型ID和提示词不能为空")
if not 0 <= self.temperature <= 2:
raise ValueError("温度参数必须在0-2之间")
if not 0 <= self.top_p <= 1:
raise ValueError("top_p参数必须在0-1之间")
if not 1 <= self.max_tokens <= 8192:
raise ValueError("最大token数必须在1-8192之间")
if not -2 <= self.presence_penalty <= 2:
raise ValueError("存在惩罚必须在-2到2之间")
if not -2 <= self.frequency_penalty <= 2:
raise ValueError("频率惩罚必须在-2到2之间")
def get_openai_message(self):
"""生成OpenAI消息格式"""
messages = []
if self.system_role:
messages.append({
"role": "system",
"content": self.system_role
})
messages.append({
"role": "user",
"content": self.prompt
})
return messages组件类
python
class Generate(ComponentBase):
"""Generate组件实现"""
component_name = "Generate"
def _run(self, history, **kwargs):
"""
执行文本生成
"""
import time
start_time = time.time()
# 获取输入内容
input_df = self.get_input()
# 处理提示词模板
processed_prompt = self._process_prompt_template(input_df, **kwargs)
# 构建消息历史
messages = self._build_message_history(processed_prompt, history)
# 调用LLM生成
response = self._call_llm(messages, **kwargs)
# 处理引用(如果启用)
if self._param.cite:
response = self._add_citations(response, input_df)
# 记录性能指标
response_time = time.time() - start_time
return pd.DataFrame([{
"content": response["content"],
"component_id": self._id,
"llm_response": {
"model": self._param.llm_id,
"usage": response.get("usage", {}),
"finish_reason": response.get("finish_reason", "stop"),
"response_time": response_time
},
"tools_called": response.get("tools_called", []),
"reference": self._extract_references(input_df)
}])
def _process_prompt_template(self, input_df, **kwargs):
"""
处理提示词模板中的变量替换
"""
from jinja2 import Template, Environment
import re
prompt = self._param.prompt
# 准备模板变量
template_vars = {
'now': 'datetime.now().isoformat()',
'random': 'random.random()',
**kwargs
}
# 处理组件引用 {component_id:index}
if not input_df.empty:
# 检索结果引用
template_vars['retrieval'] = input_df.iloc[0]["content"]
# 支持索引访问
for i, row in input_df.iterrows():
template_vars[f'retrieval_{i}'] = row["content"]
# 使用Jinja2处理模板
try:
env = Environment()
template = env.from_string(prompt)
processed_prompt = template.render(**template_vars)
except Exception as e:
# 模板处理失败,使用简单字符串替换
processed_prompt = self._simple_variable_replacement(prompt, template_vars)
return processed_prompt
def _build_message_history(self, prompt, history):
"""
构建完整的消息历史
"""
messages = []
# 添加系统角色(如果有)
if self._param.system_role:
messages.append({
"role": "system",
"content": self._param.system_role
})
# 添加历史对话(受窗口大小限制)
window_size = self._param.message_history_window_size
if history and window_size > 0:
recent_history = history[-window_size:]
for msg in recent_history:
messages.append({
"role": msg.get("role", "user"),
"content": msg.get("content", "")
})
# 添加当前提示词
messages.append({
"role": "user",
"content": prompt
})
return messages
def _call_llm(self, messages, **kwargs):
"""
调用LLM生成回答
"""
from rag.llm.chat_model import ChatModel
# 获取LLM实例
llm = ChatModel.get_model(self._param.llm_id)
# 准备调用参数
llm_params = {
"messages": messages,
"temperature": self._param.temperature,
"top_p": self._param.top_p,
"max_tokens": self._param.max_tokens,
"presence_penalty": self._param.presence_penalty,
"frequency_penalty": self._param.frequency_penalty
}
# 添加工具支持(如果配置了工具)
if self._param.tools:
llm_params["tools"] = self._get_tool_definitions()
llm_params["tool_choice"] = self._param.tool_choice
# 执行LLM调用
try:
if kwargs.get("stream", False):
# 流式生成
return self._stream_llm_call(llm, llm_params)
else:
# 批量生成
response = llm.chat(**llm_params)
return self._process_llm_response(response)
except Exception as e:
raise RuntimeError(f"LLM调用失败: {str(e)}")
def _add_citations(self, response, input_df):
"""
添加引用信息到回答中
"""
if input_df.empty or "reference" not in input_df.columns:
return response
references = input_df.iloc[0].get("reference", [])
if not references:
return response
# 在回答末尾添加引用
citation_text = "\n\n参考来源:\n"
for i, ref in enumerate(references, 1):
citation_text += f"[{i}] {ref.get('document_name', '未知文档')}"
if ref.get('page_number'):
citation_text += f",第{ref['page_number']}页"
citation_text += "\n"
response["content"] += citation_text
return response
def _stream_llm_call(self, llm, params):
"""
处理流式LLM调用
"""
full_response = ""
tools_called = []
for chunk in llm.stream_chat(**params):
if chunk.get("content"):
full_response += chunk["content"]
yield {
"content": chunk["content"],
"streaming": True,
"finish_reason": chunk.get("finish_reason")
}
if chunk.get("tool_calls"):
tools_called.extend(chunk["tool_calls"])
return {
"content": full_response,
"tools_called": tools_called,
"streaming": False,
"finish_reason": "stop"
}最佳实践
1. 提示词工程
python
# 高质量提示词模板结构
PROMPT_TEMPLATE = """
# 角色定义
你是一个专业的{domain}专家,具有丰富的经验和深厚的专业知识。
# 任务描述
请基于以下提供的知识内容,回答用户的问题。
# 知识内容
{retrieval:0}
# 用户问题
{begin@question}
# 回答要求
1. 回答必须基于提供的知识内容
2. 如果知识内容不足,请明确说明
3. 保持回答的准确性和专业性
4. 使用清晰的结构组织回答
5. 必要时提供具体的建议或解决方案
# 回答格式
请按以下格式组织回答:
## 核心回答
[主要回答内容]
## 详细说明
[补充信息和详细解释]
## 建议
[实用建议或下一步行动]
"""2. 参数优化策略
python
# 不同场景的参数推荐
SCENARIO_CONFIGS = {
"factual_qa": {
"temperature": 0.1,
"top_p": 0.3,
"presence_penalty": 0.0,
"frequency_penalty": 0.0
},
"creative_writing": {
"temperature": 0.8,
"top_p": 0.9,
"presence_penalty": 0.6,
"frequency_penalty": 0.8
},
"technical_support": {
"temperature": 0.2,
"top_p": 0.5,
"presence_penalty": 0.3,
"frequency_penalty": 0.4
},
"casual_chat": {
"temperature": 0.7,
"top_p": 0.8,
"presence_penalty": 0.5,
"frequency_penalty": 0.5
}
}3. 错误处理和重试
python
def robust_llm_call(self, messages, max_retries=3):
"""
带重试机制的LLM调用
"""
for attempt in range(max_retries):
try:
response = self._call_llm(messages)
return response
except Exception as e:
if attempt == max_retries - 1:
raise e
# 指数退避重试
time.sleep(2 ** attempt)
# 降级策略:降低参数要求
if attempt > 0:
self._param.max_tokens = min(self._param.max_tokens, 1024)
self._param.temperature = min(self._param.temperature, 0.5)常见问题
Q1: LLM回答质量不高怎么办?
A: 优化建议:
- 改进提示词: 使用更清晰、具体的指令
- 调整温度: 降低temperature提高一致性
- 增加上下文: 提供更多相关信息
- 使用系统角色: 明确LLM的身份和专业领域
Q2: 生成速度慢如何优化?
A: 性能优化方案:
- 减少max_tokens: 限制生成长度
- 简化提示词: 减少不必要的指令
- 使用更快的模型: 选择轻量级模型
- 启用流式生成: 提供更好的用户体验
Q3: 如何控制生成内容的格式?
A: 格式控制方法:
- 明确格式要求: 在提示词中指定输出格式
- 使用结构化提示: 采用模板化的提示词结构
- 设置response_format: 指定JSON等结构化格式
- 后处理验证: 对生成结果进行格式检查
Q4: 工具调用不生效怎么办?
A: 工具调用问题排查:
- 确认模型支持工具调用功能
- 检查工具定义是否正确
- 验证tool_choice参数设置
- 查看LLM返回的错误信息
Q5: 如何处理敏感内容过滤?
A: 内容安全策略:
- 设置内容过滤器: 在LLM调用前后添加检查
- 使用安全提示词: 明确禁止生成有害内容
- 配置模型安全级别: 调整模型的安全参数
- 人工审核机制: 对敏感领域启用人工检查
相关组件
前置组件
- Retrieval: 提供知识上下文
- RewriteQuestion: 优化输入问题
- Template: 预处理提示词模板
后续组件
典型工作流
Retrieval → Generate → Answer
Begin → RewriteQuestion → Retrieval → Generate → Answer
Template → Generate → Relevant → Answer
Retrieval → Generate → Email组件版本: v1.3.0
最后更新: 2025-01-12