1. 为什么我会动手做一个叫 hindsight 的项目
先交代一下背景。去年我们团队同时开了三个项目,每个项目都有完整的启动会、里程碑评审、结项汇报,但真正到了复盘环节基本就是走过场:拉个腾讯会议,谁负责哪块就报喜不报忧,老板做一个总结,然后PPT进归档夹,再也没人翻开过。最要命的是同一个坑,换个项目换批人照样踩。我当时脑子里的第一反应就是,能不能让大模型帮我们把"事后"这件事做得比人更靠谱。
所以就有了 hindsight。这个词在英文里就是"后见之明"的意思,平时说一个人 hindsight bias,通常带着点贬义,讽刺他事后诸葛亮。但我们做这个工具恰恰是要把"后见之明"变成一种可持续的生产力:把散落在聊天记录、工单、会议纪要里的零碎信息捞出来,按照时间线和主题重新组织,再用大模型提炼出决策依据、失控点、可复用经验。说白了,让 AI 当"复盘教练"。
平台选型上,我几乎没怎么纠结就选了 Dify。这个问题其实很好回答:我们团队没有专职的后端做 AI 服务治理,模型接入、向量检索、知识库管理、API 发布,这些活如果全部自研,没有两三个月根本跑不通。而 Dify 把 LLM 应用里的标准件——模型网关、RAG、工作流编排、外部 API 插件——都做成了开箱即用的模块。我们把精力主要花在数据清洗和提示词打磨上,而不是轮子重造。
适合看这篇内容的朋友,我大致归为三类:一是在用或者刚接触 Dify、想搞一个不止于"聊天机器人"的实际应用的人;二是团队里有复盘、质检、总结类需求,想用 LLM 落地但不知道从哪下手的人;三是好奇"知识库检索 + 工作流编排 + 输出结构化报告"这个套路如何真正串起来的人。下面讲的每一步都是我在真实环境下跑过的,不是概念推导。
2. 功能设计:先想清楚"喂进去什么"和"要吐出来什么"
2.1 数据源与三个核心输入
任何复盘工具,第一步都是数据。我在设计 hindsight 的时候,把数据源分成三类,每一类的处理方式都不一样。
第一类是 IM 群聊记录。团队项目群里的讨论是最鲜活的一手信息,但也是最脏的:表情包、@通知、红包链接、碎片化题外话全混在一起。我当时的处理办法是,用企业微信/飞书导出的会话记录,先做一轮正则清洗,把 100 字以内的寒暄、纯表情、系统通知直接过滤,剩下再按"主题-时间"切块。第二类是工单系统的历史记录,字段相对规整,重点是保留状态流转时间和处理人,这类数据对"哪一步拖延了"特别有说服力。第三类是会议纪要和决策文档,这是结构化程度最高的,但也最容易"报喜不报忧",所以我在提示词里特意要求模型优先交叉验证会议结论和群聊实际讨论。
有一个原则我觉得特别重要:不要把原始数据一股脑倒进知识库。我见过不少团队做类似工具时,图省事把所有聊天记录切个片就直接导入,结果模型经常被无关信息带偏,回答问题像在"翻旧账"。hindsight 的做法是,先给每一条记录打上"事件-决策-疑问-结论"的类型标签,只把带有决策或结论性质的片段推送进知识库。这个步骤表面上损失了一点信息量,实际上极大提升了检索精度。
2.2 输出报告的固定骨架
很多 AI 应用翻车,不是模型不行,是输出没有约束。hindsight 的输出报告我一开始就定了七个固定板块,每一个板块对应一个复盘维度:
关键事件时间线、当初的核心目标与当前结果对比、失控点与延迟原因、决策点回顾与备选方案、成本与资源投入分析、可复用经验清单、下一行动项。
为什么必须固定骨架?因为复盘报告是给人看的,如果每次生成的结构都不一样,读者每回都要重新适应排版,阅读成本直接抵消了 AI 节省的时间。而且固定结构还有一个好处:当模型输出偏离结构时,你能很快发现是检索环节出了问题还是提示词出了问题,排查效率高很多。后来我们前端页面干脆按这七个板块渲染成表单,用户不用读长文,直接看每一项的结果就行。
2.3 为什么选择"知识库 + 工作流"而不是纯对话
我承认,做一个纯对话式的"复盘助手"门槛最低——在 Dify 里创建一个聊天助手应用,写一段提示词,内置几个知识库,就能用。但实际跑完三个项目之后,我坚定地转向了工作流模式。
原因有两个。第一,复盘是要在同一个上下文里多次读取不同来源的信息的,聊天助手应用在对话过程中容易"忘掉"前面检索到的内容,而工作流可以通过节点变量明确传递,谁检索、谁引用、谁总结,每一步都有迹可循。第二,复盘报告需要结构化输出,聊天助手的自由问答模式很难保证每次都生成七段式报告,但工作流里的模板转换节点可以把大模型的输出强制压缩进固定格式里,做不到的宁可少输出也不要乱输出。
3. 在 Dify 里从零搭建 hindsight 的完整实操
3.1 创建应用类型:选工作流而不是聊天助手
进入 Dify 控制台之后,第一步就是"创建空白应用",类型选择"工作流"。这里我给新手一个提醒:工作流可以分为"编排"和"对话流"两种模式,hindsight 用的是纯编排模式,因为复盘报告是一次性生成,不需要多轮追问。如果你更希望用户在生成报告后继续追问细节,那就用对话流。我自己是先用编排打通核心链路,后续版本再用对话流加追问能力。
创建完应用后,我习惯在"编排"页面先画一遍整条链路:开始节点 → 知识检索节点 → LLM 节点 → 模板转换节点 → 结束节点。这一步不要省,画图的过程其实就是把你脑中的逻辑具象化,后面填参数才有据可依。
3.2 知识库的切片与索引配置
hindsight 的知识库我按项目名称分别建,比如"hindsight-demo-server"是一个独立知识库,"hindsight-crm"是另一个。不要把所有项目丢进一个大知识库,否则不同项目的上下文会相互污染,检索时经常把别家项目的记录翻出来,很尴尬。
切片大小是第一个要调的参数。Dify 默认的切片大小一般是 500 个 token 左右,但我实测下来,IM 聊天记录按 500 token 切,很容易把一个完整的决策讨论拦腰截断,最后模型只看到了"结论"看不到"论据"。我后来把分段长度调到 800 token,重叠长度 50,效果好了很多。不过要注意,这段经验只适用于对话类文本,如果是会议纪要这种本来就分条的文档,保持 400 token 反而更清晰,因为每一条纪要本身是自包含的。
索引方式我选的是"高质量",Embedding 模型用的是 text-embedding-v3。多说一句,Dify 里"高质量"索引和"经济"索引的区别,本质就是"先向量化再检索"和"文本关键词匹配"的区别,复盘对召回精度要求很高,必须在建库时就把 Embedding 算好,不能为了省那点 token 用经济模式。
3.3 知识检索节点的三个关键参数
知识检索节点是 hindsight 的命根子,我在这里踩过的坑最多。检索模式我最终选了"混合检索",也就是向量召回 + 关键词召回一起跑,再用 RRF(Reciprocal Rank Fusion)把两路结果合并排序。为什么不用单纯的向量检索?因为项目复盘里会出现大量专有名词、组件名、版本号,"flowchart-engine-v2"这种东西在语义上跟"流程图引擎"其实很接近,但向量模型不一定能在高维空间里把它俩拉得很近。混合检索能靠关键词这一路兜住精确匹配的需求。
TopK 我设为 8,Score 阈值设为 0.45。这里解释一下,TopK 是取多少个片段进模型,8 是一个性价比不错的选择:少了,背景信息不够,模型只能靠瞎猜;多了,无关片段挤占注意力,输出容易跑偏。Score 阈值是相关性过滤线,低于 0.45 的片段会被直接丢弃。需要注意的是,这个数值不是拍脑袋定的,而是我用三个项目的历史数据分别跑了一遍,观察哪些相关片段被错误丢弃之后得出来的。不同行业的业务语言差异很大,你要是做法律或医疗类复盘,术语集中,建议阈值调到 0.6 以上。
3.4 LLM 节点的模型选择与提示词约束
模型选择上,我在总结/提炼环节用的是 DeepSeek Chat(后来换了同架构的新版模型),temperature 固定 0.2,top_p 固定 0.4。做复盘报告最怕的是"创造性发挥",生成结果是给人做决策参考的,语义必须稳定,所以低温是最基本的原则。如果有条件,可以在总结环节单独接一个 context 较长的模型,比如 Kimi 或通义千问的超长上下文版本,方便容纳多次检索拼接后的长文本。
提示词部分我贴一个简化版模板,核心思路是"系统提示词定身份,用户提示词给材料":
系统提示词这么写:
你是一名资深项目复盘教练。你将收到来自不同信息源的复盘素材,格式为[来源编号]开头。你的任务是基于素材生成结构化复盘报告,严格遵守以下规则:只使用收到素材中的信息,禁止编造不存在的事实;如果某一板块在素材中找不到依据,明确写"素材不足,无法判断";不要对任何团队成员进行主观人格评价;输出必须使用 Markdown 格式,按 7 个固定章节组织。
用户提示词这边,把知识检索节点输出的所有片段拼进来,格式是"来源编号:内容",并在末尾写明本次复盘的项目名和时间范围。这个"来源编号"的细节非常关键,它让模型在输出报告时可以引用"【来源3】",后续用户核对原文时能直接跳转,可信度直接提升一个档次。
3.5 模板转换节点:把模型自由文本变成报告骨架
模板转换节点是很多人会忽略的一步,但它是 hindsight 输出稳定的终极保险。我的做法是,在 LLM 节点后面接一个模板转换节点,在它的 Jinja2 模板里预先写好七段式 Markdown 结构,把 LLM 输出中的关键字段用模板变量引用进来。比如"关键事件时间线"这一节,我要求 LLM 节点输出时只输出一个 JSON 对象,包含 timeline 数组、delay_causes 数组、reusable_experience 数组,然后模板转换节点负责把这些数组渲染成带标题的 Markdown 段落。
这么做的好处是,即便模型当时情绪不稳定,把 JSON 里的某些值写成了空数组,模板节点也能正常渲染出一个"当前板块无数据"的占位文字,避免整篇报告因为一个小字段的缺失而崩掉格式。如果你跳过模板节点直接输出 LLM 节点结果,我建议至少也要在提示词里强制要求"仅输出 Markdown,不要输出其他解释"。
3.6 调试与迭代提示词的三个步骤
Dify 工作流里每一步节点都可以单独运行,这是调试时最好用的功能。我的流程是三步:先用一个真实项目的全量数据跑一遍,看知识检索节点返回的片段是否贴合预期;再单独跑 LLM 节点,检查输出文本是否遵守了规则;最后整链跑通,看模板渲染出来的报告长什么样。
迭代提示词的时候,有一个非常容易犯的错:模型输出不符合预期就大改系统提示词,结果越改越乱。正确做法是每轮只改一个变量,而且要在测试集上对比输出。我自己专门准备了一个"三难一好"测试数据集:三个故意包含冲突信息的项目记录,一个正常项目记录,每次改完提示词都用这组数据回归一遍,确认不后退才切全量。
4. 从生成到消费:发布 API 与前端接入
4.1 发布为 API 与密钥管理
Dify 工作流调配稳定之后,右上角"发布"按钮可以把应用发布为一个可调的 API 服务。这里有一个容易忽略的点:Dify 会为应用的发布版本生成独立的 API 密钥,我建议每个环境用单独密钥,开发、测试、生产分开,避免某个实验性改动影响到线上用户。
调用工作流 API 和调用聊天助手 API 的协议是不同的,工作流 API 用的是 POST 请求,入参里传 workflow_id 和 inputs 字典。如果你像我一样把前端接在一个已有的内部系统里,前端拿到报告后可以直接用 JSON 序列化展示。我自己后来嫌前端开发成本高,索性在 Dify 的"知识库"侧做了个简易的只读页面,把每份生成的报告存成 Markdown 文件归档,按项目名和时间索引,效果反而比塞进一个复杂系统里更直观。
4.2 请求耗时的两个优化
超时是 hindsight 上线初期最头疼的问题。尤其当知识库切片较大、单次检索返回的片段较多时,LLM 节点动辄跑一两分钟,前端很容易超时重试,造成重复生成。我的解决办法是两个方向并行:一是把知识检索节点的 TopK 从 8 降到 6,同时把 Score 阈值微调到 0.5,牺牲一点召回率换响应速度;二是把 LLM 节点的 max_tokens 上限从 4000 砍到 2000,因为复盘报告七段只要求结论清晰,不需要长篇大论,2000 token 足够用完。
另外一个很实用的小技巧是,在生成请求里把流式开关关掉。工作流 API 默认支持流式输出,但我们的前端没有处理流式的逻辑,老老实实等完整结果更省心。如果你是给内部团队用,建议直接把 Dify 的调用超时时间调大,在网关层放宽到 180 秒,减少不必要的重试。
4.3 输出格式不稳定:靠"双保险"解决
上线三周后,有同事反馈偶尔报告里"可复用经验清单"是空白的。排查后发现,LLM 节点偶尔在 JSON 输出里用了中文逗号,或者把双引号写成“"”,导致模板转换节点解析失败。这个问题的根治办法是"双保险"。
第一层保险,是在系统提示词里加一句"输出必须是合法 JSON,不得使用中文引号或中文标点分隔属性"。但提示词约束不是 100% 可靠。第二层保险,是在 LLM 节点后面加一个 Python 节点或代码节点,用代码捕获异常并做修正:检测到解析失败就尝试剔除非法字符,再解析一次;如果仍然失败,就把 LLM 节点返回的原始文本原样放到报告末尾,标注"该板块为原始输出,未经格式化"。第二层保险虽然会让报告偶尔变得不那么美观,但至少保住了信息的完整性,用户不会被误导。
5. 踩坑记录与排查速查表
5.1 检索不到内容:先分清楚是"库的问题"还是"召回的问题"
hindsight 最常见的问题是用户输入项目名后,检索节点返回为空。遇到这种情况,我强烈建议先走一遍节点调试:单独运行知识检索节点,看输入 query 拼出来的是什么。如果 query 本身没问题,再检查知识库里到底有没有对应项目的文档。
还有一个比较容易忽视的原因:文档上传后需要等待嵌入建立完成。Dify 在"知识库-文档"页面里会显示索引状态,如果还是"待索引"那检索必然返回空。我把这个问题放进速查表里,因为太容易引发了:不是代码错了,是索引没建完。
5.2 检索到了相关片段但报告里没用上
这种情况在调试时更隐蔽。检索节点明明返回了 8 个片段,LLM 输出却一个都没引用。我排查之后发现,问题出在用户提示词的拼接顺序上:我把过长的项目背景描述放在了检索片段之前,模型注意力被背景信息占满,后面真正要紧的内容反而被忽略了。
解决方案是把检索片段放在用户提示词最前面,项目背景和问题描述放后面。这个顺序特别符合注意力机制的原理:模型读前面的内容会更认真。后来我又在每段片段前增加"来源编号"作为强标识,让模型在输出引用时能准确对应,这个操作直接把报告的"可信度"拉高了一大截。
5.3 知识库里重复内容过多,报告变得啰嗦
在复盘里,重复信息是正常的——一个决定可能在群聊里被反复确认过 3 次。但知识库不去重,检索的时候同一主题的 8 个片段可能 5 个都在说同一件事,模型就会很啰嗦。我后来在数据清洗阶段做了一个"语义相似度去重":用 Embedding 把每一条记录向量化,两两对比 cosine 相似度,超过 0.92 的只保留时间上最早的一条。这一步做完,生成报告的重复率下降非常明显,token 消耗也少了。
5.4 排查速查表
| 症状 | 可能原因 | 快速检查 | 解决办法 |
|---|---|---|---|
| 检索返回空 | 文档未索引 | 文档页面索引状态 | 等索引完成 |
| 检索返回空 | query 拼错 | 单跑检索节点 | 修开始节点变量映射 |
| 报告未引用片段 | 提示词顺序问题 | 检查用户提示词拼接顺序 | 检索片段放最前 |
| 报告内容啰嗦 | 知识库重复分片 | 查看片段相似度 | 清洗阶段做语义去重 |
| LLM 输出非 JSON | 中文标点/引号 | 单跑 LLM 节点看原始输出 | 提示词强约束 + 代码兜底解析 |
| 请求超时 | 片段多/生成长 | 看日志耗时分布 | 降 TopK、降 max_tokens |
| 模型编造事实 | 片段不足 | 看输出是否有"素材不足"字样 | 加规则要求必须声明缺失 |
6. 我实际跑完一轮后的几点体会
hindsight 这套东西运行到现在,最大的心得体会是:做工具的人太容易迷信"更好的模型"了。其实复盘效果好不好,七分取决于数据清洗与切片,两分取决于提示词约束,只有一分取决于模型参数。把聊天记录原样丢进知识库,换上再聪明的模型也救不回来。
给想复刻这个项目的人一个建议:先挑一个已经结束的小项目做试点,数据集控制在几百条以内,手工验证一遍报告质量,再决定是否扩大范围。不要一上来就把全公司的工单历史灌进去,那样只会得到一个连自己都看不懂的巨型知识库。
另外,我觉得 hindsight 后续还有很自然的扩展方向:接入飞书机器人,让用户在 IM 里直接对话生成周报;或者设置成定时任务,每个迭代结束自动拉取本迭代的工单和讨论记录生成复盘草稿。这些扩展在 Dify 里都不需要改动核心工作流,只要在触发端多接一个事件源就行。我下一步打算把"自动生成 → 人工确认 → 归档"这条闭环做成一个标准模板,让团队里非技术成员也能自己跑复盘。