Skip to content

Generate 组件

概述

Generate组件是RAGFlow的核心文本生成组件,负责调用大语言模型(LLM)生成回答。它支持多种LLM提供商、提示词模板、工具调用等高级功能,是RAG工作流中将检索到的知识转化为自然语言回答的关键组件。

主要功能

  • 🤖 多LLM提供商支持(OpenAI、Claude、本地模型等)
  • 📝 强大的提示词模板系统
  • 🔧 函数调用和工具集成
  • 📚 上下文感知的知识整合
  • 💬 流式和批量生成模式
  • 🎯 参数化控制(温度、最大长度等)

适用场景

  • 知识问答系统的回答生成
  • 聊天机器人的对话生成
  • 文档摘要和分析
  • 创意写作和内容生成
  • 代码生成和技术解答

参数配置

基础参数

参数名类型必填默认值说明
llm_idstring""LLM模型ID
promptstring""提示词模板
citebooleantrue是否包含引用
message_history_window_sizenumber12历史消息窗口大小

LLM参数

参数名类型必填默认值说明
temperaturenumber0.1生成温度(0-2)
top_pnumber0.3核心采样概率
max_tokensnumber2048最大生成长度
presence_penaltynumber0.4存在惩罚(-2到2)
frequency_penaltynumber0.7频率惩罚(-2到2)

高级参数

参数名类型必填默认值说明
system_rolestring""系统角色设定
toolsarray[]可用工具列表
tool_choicestring"auto"工具选择策略
response_formatstring"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_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: 优化建议:

  1. 改进提示词: 使用更清晰、具体的指令
  2. 调整温度: 降低temperature提高一致性
  3. 增加上下文: 提供更多相关信息
  4. 使用系统角色: 明确LLM的身份和专业领域

Q2: 生成速度慢如何优化?

A: 性能优化方案:

  • 减少max_tokens: 限制生成长度
  • 简化提示词: 减少不必要的指令
  • 使用更快的模型: 选择轻量级模型
  • 启用流式生成: 提供更好的用户体验

Q3: 如何控制生成内容的格式?

A: 格式控制方法:

  • 明确格式要求: 在提示词中指定输出格式
  • 使用结构化提示: 采用模板化的提示词结构
  • 设置response_format: 指定JSON等结构化格式
  • 后处理验证: 对生成结果进行格式检查

Q4: 工具调用不生效怎么办?

A: 工具调用问题排查:

  1. 确认模型支持工具调用功能
  2. 检查工具定义是否正确
  3. 验证tool_choice参数设置
  4. 查看LLM返回的错误信息

Q5: 如何处理敏感内容过滤?

A: 内容安全策略:

  • 设置内容过滤器: 在LLM调用前后添加检查
  • 使用安全提示词: 明确禁止生成有害内容
  • 配置模型安全级别: 调整模型的安全参数
  • 人工审核机制: 对敏感领域启用人工检查

相关组件

前置组件

后续组件

  • Answer: 输出最终结果
  • Relevant: 过滤生成内容
  • Email: 发送生成的内容

典型工作流

Retrieval → Generate → Answer
Begin → RewriteQuestion → Retrieval → Generate → Answer
Template → Generate → Relevant → Answer
Retrieval → Generate → Email

组件版本: v1.3.0
最后更新: 2025-01-12