一、项目背景与目标

本项目是一个为期 1–3 个月的 RAG 技术验证项目,目标是回答两个问题:

  • 这套架构能否支撑股票分析场景——财报、研报、公告、新闻的文本问答,带出处返回,按公司代码和用户权限过滤。
  • 多模态入库在 VPS 上跑不跑得动——音频/视频转录的耗时、内存占用、以及对并发检索的影响。

架构分工:WordPress 负责会话管理和前端(每个会话一个父 post,每轮对话以子 post 保存),Python/LangGraph 服务负责检索与 RAG 逻辑,One API 作为 LLM 网关。这样可以复用旧 golforfun 项目的拆条、硬过滤、DeepSeek JSON 解析等积累,WordPress 只承担它擅长的部分。

二、核心架构决策

2.1 会话记录与索引入库分离

WordPress 侧保存会话 post,但会话本身也要再做一次索引入库,否则“以后问之前怎么聊的”无法实现。会话 chunk 不按语义切分对话文本,而一问一答作为一个单元入库,metadata 带 session_id、user_id、turn_index、timestamp。太短的问题(<5 字)和纯寒暄不入库,避免稀释检索质量。

写入时机采用异步批量:WordPress 保存 post 后通知 Python 服务,Python 在后台队列批量 embedding 后写入 Chroma,不阻塞用户响应。

2.2 会话库与文档库分开

会话 chunk 和文档 chunk 的 embedding 分布不同,混在一个 collection 里相似度分数可比性下降。建议分开两个 collection,检索时同时查、合并结果,并在 prompt 里区分“知识库内容”和“该用户的历史对话”。

2.3 权限过滤在检索时完成

权限按“注册用户 / 游客”两档划分,保留按角色细分的扩展点。核心做法是在向量库查询时传入 metadata filter,而不是先检索再在应用层过滤——后过滤方式下,模型可能已经“看到”了无权访问的内容。

Chroma 的 where 条件可以做到这一点。封装一个 build_access_filter(user_context) 策略函数,当前只返回 public / public + registered 两种过滤表达式,以后加角色时只改这个函数。WordPress 中转层要传递足够的身份信息(user_id + role),哪怕现在 Python 侧暂时不用,以后启用角色过滤时不需要改 WordPress 调用代码。

三、不规则格式的拆分策略

高尔夫规则 PDF 有固定格式,可以用正则按子条款拆分。新项目的信息形式不规则,核心思路要从“按格式拆”转向“按语义拆”,每种来源单独设计管道。

来源 拆分方式 出处元数据
PDF(不规则) py-chunks 的 section 模式按标题分组;或语义拆分(相邻句 embedding 相似度,话题切换处断开) 页码 + 源文件 URL
图片 多模态模型生成描述文本,描述作为一个 chunk 入库 图片 URL 或文档内位置
网址 ChromeRAG 抽正文为 Markdown,按 heading 拆,embedding 前把标题路径前置 页面 URL
会话 一问一答为一个单元,不按句子切 session_id + turn_index
视频 ffmpeg 抽关键帧 + faster-whisper 转录,按时间窗口合并 video_url + start_time/end_time
音频/会议 faster-whisper 转录,按时间窗口切 chunk audio_url + start_time/end_time

所有管道归一化成统一结构:chunk_text(用于 embedding)+ metadata(source_type、source_url、page_or_position、session_id 等)。检索时混排,通过 source_type 过滤或在返回时根据 source_url 拼出处链接。

四、完全免费的技术选型

要求方案完全免费且 VPS 能跑,各组件选型如下:

组件 方案 许可证
语音转写 faster-whisper(CPU + int8,base/small 模型) MIT
文本 embedding bge-small-zh-v1.5(本地) 免费
向量库 Chroma(本地持久化) Apache 2.0
视频抽帧 ffmpeg / OpenCV 免费
LLM One API 背后的模型 取决于渠道

faster-whisper 基于 CTranslate2,比原版 Whisper 快约 4 倍,内存占用更低。base 模型 CPU 内存约 500MB,small 约 1–2GB,均在 VPS 承受范围内。内置 Silero VAD 自动过滤静音段,避免 Whisper 在长时间静音时产生幻觉。

视频方案的关键取舍:视觉部分只做关键帧展示,不做原生视频 embedding。视频检索以转录文本为主,关键帧作为出处展示。纯视觉无语音的视频暂时接受“检索不到”,等文本管道跑稳再考虑加视觉描述。

五、旧项目可复用的经验

来自 golforfun 项目的踩坑清单,在新项目中仍然适用:

  • DeepSeek 通过 One API 不支持 with_structured_output,所有结构化输出改成“prompt 要求纯 JSON + 手动 json.loads”。需要一个容错解析函数:直接解析失败则剥离 markdown 代码块再解析,再失败返回兜底文案。
  • timeout=300 必须加,DeepSeek 响应 30 秒–2 分钟。
  • One API 上游随机 429 不是代码问题,One API 会自动重试其他渠道。
  • proxy_read_timeout 300s 在 nginx 反代里必须加,默认 60s 会在模型响应前超时。
  • Restart=on-failure + TimeoutStartSec=120,避免模型加载期间被 systemd 误杀。

关键词硬过滤的思路可以借鉴,但股票场景没有“规则 17”这种稳定分类号,改为按 ticker、report_date、source_type 做 metadata 过滤。同义词扩展仍然有用(“茅台”“600519”“贵州茅台”指同一家公司)。

高尔夫规则那套“关键词→规则号硬过滤”不能照搬,但封装成策略函数的思路可以延续:检索逻辑集中在一处,便于后续调整。

六、需要补充的工程细节

6.1 数据生命周期

验证期数据量不大,复杂的删除/更新机制可以降级,但建议每个 chunk 的 metadata 带 source_id 和 content_hash,为以后的去重和删除留好口子。同一份 PDF 重新上传时用 content_hash 判断是否重复入库。fetched_at 字段用于以后按时间衰减排序。

6.2 检索质量可观测

黄金测试集是短期项目里投入产出比最高的一件事。第一周就建 10–20 条测试集(问题 + 期望命中的文档/片段 + 期望答案要点),每次改 chunk 策略或过滤逻辑后跑一遍看命中率变化。不需要上 RAGAS 这类框架,一个 Python 脚本就够。没有它,一个月后无法回答“这个方案到底行不行”。

股票场景的测试集围绕“某公司某报告期的某指标是多少”“最近某公司有什么利空新闻”“某条款在财报的哪一页”,K 线相关问题不放进来——它们属于另一个目标。

6.3 trace_id 从第一天就加

几乎零成本,但价值很大。验证期会频繁改 prompt、改检索逻辑,出问题时需要快速判断“是检索没召回,还是召回对了但生成错了”。trace_id 透传到 WordPress 和 One API 日志,可以一条链路查到底。

6.4 并发与资源

视频/音频转录是 CPU 密集型任务。需要给转录任务设并发上限(比如同时只处理 1 个),其余排队,否则会拖慢检索响应。验证目标二时,要测“转录进行中检索是否受影响”,而不只是“单个转录能不能跑完”。

6.5 三个必须实测的数

验证“多模态入库在 VPS 上跑得动”,需要实测:

  • 单次转录耗时:10 分钟音频用 faster-whisper base 跑一遍,记录实际耗时。超过视频长度 2–3 倍则体验差,需降模型或加队列。
  • 峰值内存:转录时 free -h 看占用,接近 VPS 上限则并发会 OOM。
  • 对检索响应的影响:转录进行时发一个已缓存的问题,看检索是否明显变慢。

七、实施顺序建议

  • 第 1 周:PDF + 会话管道跑通,建 10–20 条股票场景黄金测试集,测命中率。验证目标一的核心部分。
  • 第 2 周:加 URL(新闻/公告)入库,扩展测试集。用 10 分钟音频跑 faster-whisper,记录耗时、内存、并发影响三个数。验证目标二的音频部分。
  • 第 3 周:如果音频跑得动,加视频(抽帧 + 转录),测同样的三个数。如果音频已吃力,视频先不做,目标二降级为“音频入库跑得动”。
  • 第 4 周起:根据前两周数据决定是否继续扩展。目标一命中率不理想时,优先调检索策略,而不是急着加新来源。

八、两个目标的预判

目标一大概率能达成。文本 RAG 在财报/新闻场景有大量先例,旧 golforfun 项目也证明了单来源管道可复现。风险在于股票术语比高尔夫规则更杂,同义词扩展和 metadata 过滤的设计会更费劲。

目标二大概率“音频跑得动、视频勉强跑得动”。faster-whisper base 在 CPU 上处理 10 分钟音频通常在 1–3 分钟量级,VPS 能承受;视频多了抽帧和关键帧存储,处理时间和磁盘占用都会上升,但 1–3 个月的数据量不会撑爆。真正的风险是并发——多个用户同时上传会占满 CPU,拖慢检索。

结论:两个目标都要当作独立验收项,分别设测试集和实测指标。目标一看命中率,目标二看转录耗时、峰值内存、并发影响三个数。不要用“整体感觉还行”代替具体结论。

九、明确不做的部分

  • K 线图形态分析:需要视觉编码器或专门的视觉检索方案,不塞进文本 RAG。K 线图前端展示,AI 分析基于代码计算的技术指标 + RAG 检索的文本信息(新闻、研报观点),两者在 prompt 里合并。
  • 实时行情计算:向量检索不适合精确数值计算,交给代码或行情 API。
  • 按角色细分的权限:验证期用“注册/游客”两档足够,build_access_filter 已保留扩展点。
  • 复杂删除/更新/审计日志:验证期数据量小,手动清理够用,留好 metadata 字段即可。

一句话总结: 这套架构能支撑股票分析场景里的“信息检索与问答”层,不能支撑“量化计算与图表识别”层;多模态入库在 VPS 上音频可行、视频勉强可行,关键验证指标是转录耗时、峰值内存和并发影响三个数。1–3 个月的验证期,先跑通 PDF + 会话,再加 URL 和音视频,用黄金测试集和 trace_id 保证每一步都有可验证的产出。