Skip to content

Answer 组件

概述

Answer组件是RAGFlow工作流的终端输出组件,负责接收上游组件生成的内容并将其作为最终答案返回给用户。它是工作流的最后一个环节,处理结果格式化、引用整理、以及与用户的交互反馈。

主要功能

  • 📤 工作流最终输出和结果展示
  • 📋 答案格式化和美化处理
  • 🔗 引用信息整理和展示
  • 💬 支持流式和批量输出模式
  • ⏸️ 支持多轮对话的暂停点

适用场景

  • 聊天机器人的最终回答展示
  • 知识问答系统的结果输出
  • 报告生成的最终呈现
  • 多轮对话的中间暂停点
  • 工作流执行结果的统一输出

参数配置

基础参数

参数名类型必填默认值说明
response_formatstring"text"响应格式类型
show_citationsbooleantrue是否显示引用信息
max_display_lengthnumber0最大显示长度(0为不限制)

格式化参数

参数名类型必填默认值说明
markdown_enabledbooleantrue启用Markdown渲染
auto_wrapbooleantrue自动换行处理
highlight_keywordsbooleanfalse关键词高亮显示

详细说明

response_format (响应格式)

  • 类型: 字符串枚举
  • 可选值: "text", "markdown", "html", "json"
  • 描述: 指定最终输出的格式类型
  • 默认值: "text"

show_citations (显示引用)

  • 类型: 布尔值
  • 描述: 是否在答案末尾显示引用来源信息
  • 默认值: true

max_display_length (最大显示长度)

  • 类型: 正整数
  • 描述: 限制显示内容的最大字符数,0表示不限制
  • 用途: 防止过长内容影响用户体验

输入输出

输入格式

Answer组件接受上游组件的任何内容:

json
{
  "content": "要展示的最终答案内容",
  "component_id": "upstream_component_id",
  "reference": [...],  // 可选的引用信息
  "metadata": {...}    // 可选的元数据
}

输出格式

json
{
  "content": "格式化后的最终答案",
  "component_id": "answer_component_id",
  "display_format": "text",
  "references_shown": true,
  "truncated": false,
  "user_interaction_required": true,
  "timestamp": "2024-01-12T10:30:00Z"
}

前端实现

节点组件

typescript
// web/src/pages/flow/canvas/node/answer-node.tsx
export function AnswerNode({ id, data, isConnectable, selected }: NodeProps) {
  return (
    <section className={`${styles.ragNode} ${selected ? styles.selectedNode : ''}`}>
      {/* 只有输入连接点,没有输出 */}
      <Handle
        type="target"
        position={Position.Left}
        isConnectable={isConnectable}
        className={styles.handle}
      />
      
      <NodeHeader id={id} name={data.name} label={data.label} />
      
      <div className={styles.nodeBody}>
        <div className={styles.nodeInfo}>
          <Icon component={CheckCircleOutlined} className={styles.nodeIcon} />
          <span className={styles.nodeDescription}>最终输出</span>
        </div>
        
        <div className={styles.configSummary}>
          <div className={styles.configItem}>
            <span className={styles.configLabel}>格式:</span>
            <span className={styles.configValue}>
              {data.form.response_format || 'text'}
            </span>
          </div>
        </div>
        
        <div className={styles.statusBadges}>
          {data.form.show_citations && (
            <span className={styles.statusBadge}>
              <Icon component={LinkOutlined} />
              引用
            </span>
          )}
          
          {data.form.markdown_enabled && (
            <span className={styles.statusBadge}>
              <Icon component={FormatPainterOutlined} />
              Markdown
            </span>
          )}
        </div>
      </div>
    </section>
  );
}

配置表单

typescript
// web/src/pages/flow/form/answer-form/index.tsx
const AnswerForm: React.FC<IOperatorForm> = ({ onValuesChange, form }) => {
  return (
    <Form form={form} layout="vertical" onValuesChange={onValuesChange}>
      <Form.Item
        name="response_format"
        label="响应格式"
        tooltip="选择最终输出的格式类型"
      >
        <Select placeholder="选择响应格式">
          <Option value="text">纯文本</Option>
          <Option value="markdown">Markdown</Option>
          <Option value="html">HTML</Option>
          <Option value="json">JSON</Option>
        </Select>
      </Form.Item>

      <Form.Item
        name="show_citations"
        label="显示引用信息"
        valuePropName="checked"
      >
        <Switch checkedChildren="显示" unCheckedChildren="隐藏" />
      </Form.Item>

      <Form.Item
        name="markdown_enabled"
        label="启用Markdown渲染"
        valuePropName="checked"
      >
        <Switch checkedChildren="启用" unCheckedChildren="禁用" />
      </Form.Item>

      <Form.Item
        name="max_display_length"
        label="最大显示长度"
        tooltip="0表示不限制长度"
      >
        <InputNumber
          min={0}
          max={10000}
          style={{ width: '100%' }}
          placeholder="0(不限制)"
        />
      </Form.Item>
    </Form>
  );
};

后端实现

参数类

python
# agent/component/answer.py
class AnswerParam(ComponentParamBase):
    """Answer组件参数"""
    
    def __init__(self):
        super().__init__()
        self.response_format = "text"      # 响应格式
        self.show_citations = True         # 显示引用
        self.max_display_length = 0       # 最大显示长度
        self.markdown_enabled = True       # Markdown渲染
        
    def check(self):
        """参数验证"""
        valid_formats = ["text", "markdown", "html", "json"]
        if self.response_format not in valid_formats:
            raise ValueError(f"响应格式必须是: {valid_formats}")
            
        if self.max_display_length < 0:
            raise ValueError("最大显示长度不能为负数")

组件类

python
class Answer(ComponentBase):
    """Answer组件实现"""
    
    component_name = "Answer"
    
    def _run(self, history, **kwargs):
        """
        处理最终答案输出
        """
        # 获取上游输入
        input_df = self.get_input()
        
        if input_df.empty:
            content = "没有可显示的内容。"
            references = []
        else:
            content = input_df.iloc[0]["content"]
            references = input_df.iloc[0].get("reference", [])
        
        # 格式化内容
        formatted_content = self._format_content(content)
        
        # 添加引用信息
        if self._param.show_citations and references:
            formatted_content = self._add_citations(formatted_content, references)
        
        # 长度截断
        if self._param.max_display_length > 0:
            formatted_content = self._truncate_content(formatted_content)
        
        return pd.DataFrame([{
            "content": formatted_content,
            "component_id": self._id,
            "display_format": self._param.response_format,
            "references_shown": self._param.show_citations and bool(references),
            "truncated": len(content) > self._param.max_display_length > 0,
            "user_interaction_required": True,
            "timestamp": datetime.now().isoformat()
        }])
    
    def _format_content(self, content):
        """格式化内容"""
        if self._param.response_format == "markdown" and self._param.markdown_enabled:
            return self._enhance_markdown(content)
        elif self._param.response_format == "html":
            return self._convert_to_html(content)
        elif self._param.response_format == "json":
            return self._structure_as_json(content)
        else:
            return content
    
    def _add_citations(self, content, references):
        """添加引用信息"""
        if not references:
            return content
            
        citation_text = "\n\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"
        
        return content + citation_text

使用示例

示例1: 基础文本输出

json
{
  "component_name": "Answer",
  "params": {
    "response_format": "text",
    "show_citations": true,
    "max_display_length": 0
  }
}

示例2: Markdown格式输出

json
{
  "component_name": "Answer", 
  "params": {
    "response_format": "markdown",
    "show_citations": true,
    "markdown_enabled": true,
    "max_display_length": 2000
  }
}

示例3: 结构化JSON输出

json
{
  "component_name": "Answer",
  "params": {
    "response_format": "json",
    "show_citations": false,
    "max_display_length": 0
  }
}

最佳实践

1. 格式选择建议

  • text: 简单问答场景
  • markdown: 需要富文本展示
  • html: Web页面嵌入
  • json: API接口返回

2. 引用展示优化

python
def format_citations(references):
    """优化引用格式"""
    if not references:
        return ""
    
    grouped_refs = {}
    for ref in references:
        doc_name = ref.get('document_name', '未知文档')
        if doc_name not in grouped_refs:
            grouped_refs[doc_name] = []
        grouped_refs[doc_name].append(ref.get('page_number'))
    
    citation_parts = []
    for doc_name, pages in grouped_refs.items():
        if pages and all(p for p in pages):
            page_list = sorted(set(p for p in pages if p))
            citation_parts.append(f"{doc_name} (第{','.join(map(str, page_list))}页)")
        else:
            citation_parts.append(doc_name)
    
    return "参考来源:" + ";".join(citation_parts)

3. 长度控制策略

  • 保留完整句子
  • 智能截断位置
  • 添加截断提示

常见问题

Q1: Answer组件是否必需?

A: 是的,每个工作流必须包含至少一个Answer组件作为输出端点。

Q2: 可以有多个Answer组件吗?

A: 可以,多个Answer组件用于不同的输出场景或条件分支。

Q3: 如何处理空输入?

A: Answer组件会显示默认提示信息,不会报错。

Q4: 引用格式可以自定义吗?

A: 可以通过修改组件参数和后端逻辑来自定义引用格式。

相关组件

前置组件

  • Generate: 提供生成的回答内容
  • Template: 提供格式化的内容
  • Concentrator: 提供聚合的结果

典型工作流模式

Generate → Answer
Retrieval → Generate → Answer
Begin → Categorize → [处理路径] → Answer
Template → Answer

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