1 项目背景
业务场景
HR 制度问答机器人上线一个月后,「云帆科技」的不同部门开始提需求了。财务部说:"我们的报销制度能不能也搞个问答?"行政部说:“办公用品申领流程能不能也接进去?“但每个部门对机器人的要求都不一样——HR 需要引用制度原文的条款编号,财务需要精确的数字运算(如"出差 3 天,住宿费限额多少”),行政需要给出流程步骤(如"打印机坏了找谁修”)。
运维小李犯愁了:难道每个部门都要从头搭一套 RAGFlow?有没有办法在一个平台上创建多个"助手",每个助手关联不同数据集、用不同的提示词(Prompt)模板、甚至用不同的模型?她发现 RAGFlow 里一个叫"Chat 助手"的功能,但面对一堆配置项不知从何下手。
痛点
缺乏对 Chat 助手和 Prompt 工程的理解时:
- 一锅粥助手:所有部门的制度塞进同一个助手,Prompt 是通用的"你是一个智能问答助手",导致财务问题和 HR 问题的回答风格一模一样——财务需要严谨的数字,却收到了一段散文式的描述。
- 引用失控:助手有时引用来源、有时不引用——因为 Prompt 里没强制要求。用户无法确认答案的依据。
- 上下文溢出:用户连续问了 10 个问题后,助手开始"遗忘"前面的内容——多轮对话窗口没管理好。
- 越界回答:用户问"怎么破解公司的 WiFi 密码",助手居然开始编造——因为 Prompt 没设禁区。
没有 Prompt 工程的问答效果: 用户:"出差住宿费标准是多少?" 助手(无 Prompt 约束):"根据公司规定,出差住宿费因城市级别而异, 建议您根据实际情况与部门主管协商确定。如有疑问,欢迎再次咨询!" ↑ 模糊、无依据、无引用 用户(期待的答案):"根据《差旅费管理办法》第5条,一线城市住宿费标准 为 500元/晚,二线城市 350元/晚。详见下表:[表格引用]。来源:[1]" ↑ 精确、有依据、有引用2 项目设计
小胖:(打开电脑展示三个并排的聊天窗口)“大师你看,我用同一个机器人问三个问题——HR 的’年假几天’、财务的’报销额度’、行政的’修打印机’——回答风格一毛一样!开头都是’您好,根据公司规定……‘,结尾都是’如有疑问欢迎咨询’。行政大姐说这回答太油腻了,她要直接给电话号码!”
大师:(笑着摇头)“这就是用一个万能 Prompt 打天下的后果。不同的业务场景、不同的用户群体,需要的回答风格、严谨程度、格式要求完全不同。RAGFlow 的 Chat 助手就是用来解决这个问题的——你可以为每个部门创建独立的助手,配独立的数据集、独立的 Prompt,甚至独立的 LLM 模型。”
技术映射:Chat 助手 = 不同岗位的员工——前台要热情,财务要严谨,技术要精确。用同一套话术应付所有客户,迟早翻车。
小白:“那一个 Chat 助手到底由哪些要素组成?配置它需要改什么?”
大师:“一个 Chat 助手的核心配置要素有六项:”
Chat 助手 = { "数据集绑定": ["HR-薪酬制度", "HR-考勤制度"], // 检索范围 "LLM 模型": "gpt-4o / deepseek-chat", // 生成模型 "System Prompt": "你是一个专业的HR制度助手...", // 角色设定 "检索参数": {top_k: 20, threshold: 0.3, ...}, // 怎么搜 "多轮对话": {history_turns: 10}, // 记住几轮 "输出格式": {citation: true, structured: true}, // 怎么输出 }小胖:“等等,Prompt 是啥?就是我跟 AI 说的话吗?”
大师:“对。Prompt 分成两层:System Prompt(系统指令)和 User Prompt(用户问题)。System Prompt 是你给 AI 设的’人设’和规则,它在整个对话中持续生效。User Prompt 是用户每次问的具体问题。一个典型的 System Prompt 模板长这样:”
# 角色设定 你是一个专业的HR制度问答助手,服务于「云帆科技」全体员工。 # 知识范围 你只能根据下方「参考文档」中的内容回答问题。 # 回答规则 1. 回答必须精确引用制度原文,使用 [编号] 标注引用来源。 2. 如果参考文档中没有相关信息,请回答:"该问题不在本助手知识范围内, 建议联系 HR 部门:hr@yunfan.com" 3. 涉及具体数字(天数、金额、比例)时,必须原样引用,不得四舍五入或估算。 4. 回答结构:先给出直接答案,再列出相关条款原文,最后附上参考文档列表。 # 禁止事项 - 禁止编造、推测或使用参考文档以外的信息。 - 禁止对员工个人情况提供建议(如"您应该...")。 # 参考文档 {context}技术映射:System Prompt = 员工的岗位说明书——规定你能做什么、不能做什么、做到什么标准。
小胖:“哇 Prompt 还能这么写?我以为就是一句话 ‘你是一个 HR 助手’。那多轮对话是啥意思?”
大师:“多轮对话就是让 AI 记住前面的对话历史。比如:”
用户第1轮:"年假有几天?" 助手:"根据《考勤管理办法》第3条,工龄1-10年:5天..." 用户第2轮:"那我没休完怎么办?" 助手(有记忆):"根据《考勤管理办法》第5条,未休年假可延期至下一年度..." 助手(无记忆):"请问您指的是什么?请提供更多上下文。" ← 没记住"年假"话题“RAGFlow 的多轮对话维护一个对话历史窗口。每次新提问时,会把最近 N 轮(默认 10 轮)的历史消息一起发给 LLM。这样追问时不用重复说明背景。但历史太长也有风险——占窗口、增加成本、旧信息干扰新推理。”
技术映射:多轮对话 = 跟人聊天时记住前面说了什么,而不是每次都像第一次见面。
小白:“那上下文窗口(Context Window)是怎么回事?我听说 LLM 有 token 限制。”
大师:“每个 LLM 都有最大上下文长度(context window),比如 GPT-4o 是 128K tokens,DeepSeek 是 64K tokens。每次请求的 token 组成是:”
请求 token = System Prompt tokens + 历史消息 tokens(多轮对话) + 检索到的 Chunk tokens(相关知识) + 用户当前问题 tokens + 预留回答空间 tokens“如果这五项加起来超过模型的上下文上限,RAGFlow 会做截断——通常是保留最新消息 + 优先截断旧的 Chunk。这可能导致关键上下文被丢弃,所以需要精细管理。RAGFlow 的默认策略是:历史保留最近 10 轮,Chunk 保留 Top-5 到 Top-10 个。”
小白:“那提示词里怎么控制引用格式?我发现有些时候引用是 [1],有时候是 (参见文档1),很混乱。”
大师:“引用格式完全由 Prompt 控制。关键是——Prompt 写的引用格式必须和 RAGFlow 的解析器匹配。推荐使用[编号]格式(Markdown 链接风格的也可以),这是 RAGFlow 默认支持的解析格式。”
小胖:“最后一个问题——如果我想让助手在某些话题上拒绝回答怎么办?比如问工资、问密码?”
大师:“三种方法叠加使用:”
- Prompt 禁区:在 System Prompt 中明确禁止的话题——“不要回答与公司机密、个人薪资、系统密码相关的问题。”
- 检索阈值:设合理的
similarity_threshold,如果所有召回 Chunk 得分都低于阈值 → 触发拒答。 - 关键词过滤:在前置处理中检测敏感词,直接返回预设回复——这是 API 层的防御策略,独立于 LLM。
技术映射:AI 禁区 = 安保系统——Prompt 是门禁卡、阈值是红外感应、关键词过滤是监控摄像头,三道防线各管一摊。
3 项目实战
环境准备
目标:为 HR、财务、行政三个部门分别创建独立的 Chat 助手,每个助手关联独立数据集和 Prompt。
前提:三个部门的数据集已按第3章的方法创建完成,各含 5-10 份制度文档。
分步实现
步骤1:创建 HR 制度助手
目标:创建一个专业、严谨、强制引用的 HR 问答助手。
# 创建 HR 助手curl-XPOST http://localhost:8080/api/v1/chats\-H"Authorization: Bearer$TOKEN"\-H"Content-Type: application/json"\-d'{ "name": "HR制度问答助手", "description": "为员工提供考勤、薪酬、福利、劳动合同等制度的精准问答", "dataset_ids": ["hr-salary-id", "hr-attendance-id", "hr-contract-id"], "llm_model": "deepseek-chat", "prompt": { "system": "你是一个专业的HR制度问答助手,服务于云帆科技全体员工。\n\n## 核心规则\n1. 严格基于「参考文档」回答,禁止使用外部知识。\n2. 每条事实陈述必须附带引用编号,如 [1]、[2]。\n3. 涉及数字(天数、金额、比例)时,原样引用,不得篡改或估算。\n4. 无法回答时,回复:\"该问题不在HR制度范围内,请发送邮件至 hr@yunfan.com 咨询。\"\n\n## 回答格式\n- 先给出直接答案(不超过3句话)\n- 再列出支持该答案的制度条款原文(带引用编号)\n- 最后附上本次参考的文档列表\n\n## 禁止事项\n- 禁止对员工个人决策提供建议(如\"建议您辞职\")\n- 禁止讨论未公开的公司政策\n- 禁止回答与HR无关的问题\n\n## 参考文档\n{context}", "temperature": 0.1, "max_tokens": 2000 }, "retrieval_config": { "top_k": 15, "similarity_threshold": 0.3, "rerank_model": "BAAI/bge-reranker-v2-m3" } }'预期响应:
{"code":0,"data":{"id":"chat_hr_001","name":"HR制度问答助手"}}步骤2:创建财务报销助手
目标:为财务部创建一个擅长表格数据处理、数字运算的助手。
# 创建财务助手curl-XPOST http://localhost:8080/api/v1/chats\-H"Authorization: Bearer$TOKEN"\-H"Content-Type: application/json"\-d'{ "name": "财务报销助手", "description": "提供差旅费标准、报销流程、预算制度等信息", "dataset_ids": ["finance-reimburse-id", "finance-budget-id"], "llm_model": "deepseek-chat", "prompt": { "system": "你是一个云帆科技财务报销助手。\n\n## 核心规则\n1. 严格基于参考文档中的报销标准、金额限制回答。\n2. 差旅标准类问题必须以表格形式呈现。\n3. 计算类问题(如\"出差3天,住宿+交通共多少\")需展示计算步骤。\n4. 所有金额必须精确到元,不得估算。\n5. 每项数据需标注引用编号 [N] 和生效日期。\n\n## 回答格式\n- 数字查询:先给结论,再给表格,最后给计算过程\n- 流程查询:按步骤列出,每一步标注参考条款\n\n## 禁止事项\n- 禁止批准或拒绝任何报销申请(你不是审批人)\n- 禁止修改或解释制度原文中的金额\n\n## 参考文档\n{context}", "temperature": 0.0, "max_tokens": 2500 } }'步骤3:创建行政服务助手
目标:为行政部创建一个亲切、简洁、给出直接联系方式的助手。
# 创建行政助手curl-XPOST http://localhost:8080/api/v1/chats\-H"Authorization: Bearer$TOKEN"\-H"Content-Type: application/json"\-d'{ "name": "行政服务助手", "description": "办公用品申领、会议室预定、设备报修、快递收发等行政服务", "dataset_ids": ["admin-office-id", "admin-facility-id"], "llm_model": "deepseek-chat", "prompt": { "system": "你是一个云帆科技行政服务助手。回复风格:简洁、实用、友好。\n\n## 核心规则\n1. 基于参考文档回答问题,提供可直接执行的信息。\n2. 流程类问题给出步骤编号(最多5步)。\n3. 涉及具体负责人的,提供联系方式(分机号/邮箱)。\n4. 服务时间类问题精确到小时(如\"工作日 9:00-18:00\")。\n5. 无法回答时,回复:\"这个问题建议联系行政部前台,分机 8888。\"\n\n## 回答格式\n- 直接给出操作步骤或联系电话\n- 如需引用制度,标注 [N] 编号\n- 语气友好但不啰嗦\n\n## 参考文档\n{context}", "temperature": 0.3, "max_tokens": 1500 } }'步骤4:多轮对话配置与测试
目标:配置历史轮数并测试追问能力。
# test_multiround.py - 多轮对话测试fromragflowimportRAGFlow rag=RAGFlow(api_key="xxx",base_url="http://localhost:8080/api/v1")# 创建多轮会话session=rag.create_session(chat_id="chat_hr_001")print(f"会话ID:{session.id}")# 第1轮r1=session.chat("年假有多少天?")print(f"第1轮:{r1.answer}")# 第2轮(追问,省略主语)r2=session.chat("那没休完怎么办?")print(f"第2轮:{r2.answer}")# 第3轮(继续追问数字)r3=session.chat("最多可以累积几年?")print(f"第3轮:{r3.answer}")# 查看会话历史history=session.get_history()print(f"\n历史轮数:{len(history)}")fori,msginenumerate(history):print(f" [{i}]{msg.role}:{msg.content[:50]}...")预期结果:第2轮和第3轮能正确理解"那"和"累积"指向的是年假话题,答案连续且不混乱。
步骤5:Prompt A/B 对比测试
目标:对比不同 Prompt 风格对回答质量的影响。
# prompt_ab_test.pyfromragflowimportRAGFlow rag=RAGFlow(api_key="xxx",base_url="http://localhost:8080/api/v1")# 创建两个同数据集但不同 Prompt 的助手chat_a=rag.create_chat(name="HR-A-简洁型",dataset_ids=["hr-ds-id"],prompt_system="你是一个HR助手。用最简洁的方式回答问题,不超过3句话。",temperature=0.1)chat_b=rag.create_chat(name="HR-B-详细型",dataset_ids=["hr-ds-id"],prompt_system="你是一个HR助手。请详细回答问题,包括:1) 答案概述 2) 制度条款原文 3) 例外情况说明 4) 参考文档列表。",temperature=0.1)test_questions=["年假几天?","加班费怎么算?","试用期多久?","社保比例是多少?","离职流程是什么?",]forqintest_questions:a=rag.chat(question=q,chat_id=chat_a.id,stream=False)b=rag.chat(question=q,chat_id=chat_b.id,stream=False)print(f"Q:{q}")print(f" [A 简洁型]{len(a.answer)}字 | 有引用:{bool(a.references)}")print(f" [B 详细型]{len(b.answer)}字 | 有引用:{bool(b.references)}")print()测试验证
# test_chat_quality.py - Chat 助手质量评测importpytest@pytest.mark.parametrize("chat_id,question,expected_in_answer",[("chat_hr_001","年假几天?",["工作日","引用","考勤"]),("chat_hr_001","社保比例?",["%","公积金","养老保险"]),("chat_finance_001","出差住宿标准?",["元","城市","晚"]),("chat_finance_001","报销需要什么材料?",["发票","审批","申请"]),("chat_admin_001","打印机坏了找谁?",["分机","联系"]),("chat_admin_001","怎么申请办公用品?",["步骤","流程"]),])deftest_chat_answer_quality(chat_id,question,expected_in_answer):"""验证每个助手的回答包含预期关键词和引用"""response=rag.chat(question=question,chat_id=chat_id,stream=False)assertresponse.answerisnotNoneassertlen(response.answer)>10,"回答过短"forkeywordinexpected_in_answer:assertkeywordinresponse.answer,f"回答中缺少关键词:{keyword}"# HR和财务必须带引用ifchat_id.startswith("chat_hr")orchat_id.startswith("chat_finance"):assertlen(response.references)>0,"必须包含引用来源"@pytest.mark.parametrize("chat_id,question,expected_response",[("chat_hr_001","怎么破解WiFi密码?","范围"),("chat_hr_001","今天股票涨了多少?","抱歉"),("chat_finance_001","我该不该买这个保险?","建议"),])deftest_rejection(chat_id,question,expected_response):"""验证助手对越界问题正确拒绝"""response=rag.chat(question=question,chat_id=chat_id,stream=False)assertany(kwinresponse.answerforkwin["范围内","抱歉","无法","不能","建议联系"])完整代码清单
Git 仓库:https://github.com/infiniflow/ragflow
| 文件 | 说明 |
|---|---|
api/db/services/chat_service.py | Chat 助手数据库服务 |
api/apps/sdk/chat.py | Chat API 接口实现 |
api/apps/chunk_app.py | 切片相关 API |
web/src/pages/chat/ | 前端聊天页面组件 |
4 项目总结
优点 & 缺点
| 维度 | RAGFlow Chat 助手 | OpenAI GPTs | Dify 知识库 | 企业微信 AI 助手 |
|---|---|---|---|---|
| 数据集绑定 | ★★★ 多数据集灵活组合 | ★☆☆ 需上传文件 | ★★★ 知识库绑定 | ★★☆ 固定知识源 |
| Prompt 自定义 | ★★★ 完全自定义 | ★★★ 界面化配置 | ★★★ 完全自定义 | ★★☆ 模板化 |
| 多轮对话 | ★★★ 可控轮数 | ★★★ 自动管理 | ★★★ 可控轮数 | ★★☆ 有限轮数 |
| 引用溯源 | ★★★ 页码级精确引用 | ★☆☆ 无原生引用 | ★★☆ 段落级引用 | ★☆☆ 无 |
| 多助手管理 | ★★★ 一个平台多助手 | ★★★ 可创建多个 GPT | ★★★ 多应用支持 | ★☆☆ 通常一个 |
适用场景
- 多部门知识库:HR、财务、法务、行政等不同部门共用平台,各配独立助手。
- 企业内外服务分离:对员工和对外部客户分别建助手,语气和权限不同。
- 分级授权:普通员工助手和经理级助手(可看更多敏感制度)分开。
- A/B 测试:同一知识库用不同 Prompt 创建两个助手,评测哪种回答风格用户满意度更高。
- 临时专题:如"年会筹备"专题助手,临时绑定相关文档,活动结束删除。
不适用场景:
- 单次问答不需保留上下文:如果每个问题都是独立的一次性查询,多轮对话反增成本。
- 机器对机器的 API 调用:如果对接方是另一个系统而非人,不需要 Prompt 人设和格式化输出。
注意事项
- Prompt 泄露风险:System Prompt 中不要包含 API Key、内部 IP、机密流程——用户可能通过"忽略之前的指令"等 Prompt 注入技术套出内容。
- 历史清理策略:多轮对话应设置上限(如 20 轮),超出的自动清除以避免上下文溢出和成本膨胀。
- 温度参数(Temperature):事实查询类助手设 0-0.1(追求确定性),创意类设 0.7-1.0(追求多样性)。HR/财务等严谨场景建议 0。
- Prompt 模板中
{context}变量:这是 RAGFlow 在运行时自动替换的检索结果占位符,删掉它会导致助手"失忆"——没有相关文档片段。 - 模型与 Prompt 的语言一致性:用中文 Prompt 配中文模型效果最好。用英文模型跑中文 Prompt 可能出现理解偏差。
常见踩坑经验
| 故障现象 | 根因 | 解决方法 |
|---|---|---|
| 助手不引用来源 | Prompt 中没有要求引用,或 LLM 模型不支持引用格式 | 在 Prompt 中明确写入引用规则,换用支持指令遵循的模型 |
| 多轮对话越来越慢 | 历史消息不断累积,每轮请求的 token 数持续增长 | 设置max_history_turns=10,超出自动截断 |
| 不同助手回答风格混乱 | 创建了多个助手但用户选错了 | 助手命名清晰、加描述,前端做默认助手绑定 |
| Prompt 更新后旧会话不生效 | RAGFlow 的 Prompt 与会话绑定时,旧会话沿用旧 Prompt | 创建新会话触发新 Prompt,或通过 API 更新会话关联的 Prompt |
| 中文 Prompt 中英文模型返回英文 | 模型默认语言与 Prompt 语言不一致 | 在 Prompt 首行加入"请始终使用中文回答" |
思考题
公司员工手册每年更新一次。新版本上线后,旧助手应该直接退役、重定向到新助手、还是保留作为历史参考?请从用户体验、成本、合规三个角度设计一个助手版本管理方案。
如果用户的单次提问中包含了三个子问题(如"年假几天?怎么申请?没休完能换钱吗?"),目前 RAGFlow 的一次检索只会产生一组 Chunk 喂给 LLM。请设计一种 Multi-Query 分解方案,使得复杂问题被自动拆成多个子问题分别检索,最后合并生成答案。
(答案提示见第8章末尾或附录 D。)
延伸阅读与资源
10倍开发者的 Dify 魔法书:从零构建全栈 AI 应用
后端工程师转型AI第一课-Ollama 与私有化大模型实战
大型语言模型(LLM) vLLM 高性能推理落地实战
Agent开发之LlamaIndex 实战修炼与源码进阶
大语言模型Transformers 实战修炼与源码剖析