本稿 · 2026-04-26 · 同专栏 第 3 篇(骨架 可成稿后去「骨架」二字或另存
-发布版。)
系列位置:第 2 篇已讲本机能转(向量、重排、
llama-server三进程、约 8k)、不重复长命令与nvidia-smi教程。本篇进应用层:话从哪来、为什么这样接框架。
硬件/进程假设(承接第 2 篇):读者已知 对话走 GPU 进程、向量/重排走 CPU 进程 的分工;下文只写请求进业务层之后的顺序。
1. 引子:第 2 篇「能转」,这篇「话从哪来」
先把「能转」验到底。 多进程、三端口起来之后,在接业务代码前,用第 2 篇里的 curl 把对话 / 向量 / 重排各打一条最小请求,确认没接错层、没串端口——这步不偷懒,后面排 bug 会省很多口水。
进了 FastAPI 之后,也先别急。 本项目的习惯是:路由里把「捞到了什么、候选长什么样」先打日志或 print 自检,确认时间窗、过滤、是否走向量/重排和脑子里的顺序一致,再把这段东西塞进主模型。一上来就端到端出字,出了问题很难分清是召回坏了还是生成坏了。
下面才谈:在强业务里,LangChain / LangGraph 我实际用哪一截、为什么没上全家桶。
2. 工作站里实际用到了什么:LangChain、LangGraph 各一截
先落对照表,再补三句人话(强业务里 LC 还能吃哪几块、为什么我这儿只包一层、Graph 在全文里占多少篇幅)。
2.1 对照表
| 在做什么 | 与「全家桶 RAG 代理」的距离 | |
|---|---|---|
| LangChain(本项目) | 如 RunnableLambda 等薄封:invoke(入参) → dict;实验路由统一 I/O 形状 | 不是把记忆 / 全链路检索外包给「链式代理」;强业务状态与策略仍在自己代码里 |
| LangGraph | 两档:(1)实验向:context_langgraph 一类线性图,捞消息 → 过滤 → 排序 → 打日志;(2)主链路:与 FastAPI 绑定的 chat 入口 上接「门禁、条件澄清、记忆召回、生成」等一整张图(具体 path 以你服务为准,不必与本文逐字一致) | 全链路这档才和 FastAPI + 流式/对照版 放在同一层谈;篇幅上,Graph 集中在「要编排、要条件边、要可观测」的主链路与实验两条线,不是全仓库到处塞 StateGraph |
与「主链路」的切分(一句):在接有状态的上下文管理、走完整对话闭环之前,我单开了路由,只测召回(时间窗、候选、向量/重排是否进 prompt 等)——不参与带门禁与生成的主链;排障、对拍时别和主链日志混成同一条结论。
2.2 强业务里,LangChain 还能用的通常是什么、为什么我只「包一层」
- 能用的、且相对稳的是「形状与胶水」:Runnable、统一
invoke出参、和 HTTP/自家ContextStore的衔接;适合做实验路由、对拍时少写样板代码。 - 刻意少用的是「全家桶语义」:Retriever、Memory、LCEL 全家桶不是不用,而是——一旦会话策略、候选池、门禁、prompt 模板都要按产品改,绑死在 LC 的默认抽象上,后面拆的成本往往高于一开始自己管
get_context_candidates一类函数。所以本项目是:业务数据与规则自管,LC 只当薄封与可替换的编排壳**。 - 若你的目标是图快、先上线、不在乎后期换骨架:完全可以直接上 LangChain 全家桶(Retriever、Memory、现成 RAG/Agent 例),用可接受的耦合换时间;本文路线是另一种取舍:多写一点自管,换后面改需求时不那么疼。
2.3 LangGraph 在本项目里占几成(人话)
- 就两块图:主链全路 + 实验小线性,没有「一张图画满全仓」。
- 测试/对拍还会多用 LangGraph:按节点打日志、一次性能看清,比一坨
print好对齐;不因此等于「全业务都图驱动」。
3. 「捞上下文」的主逻辑(含门禁、时间窗、重排前的一口坑)
不写 第 2 篇已经讲过的 端口、curl;第 2 篇 解决的是:向量 / 重排服务在 HTTP 上怎么喂;本篇解决的是:进业务之后,谁先谁后、进 prompt 之前要剃掉谁。
当前链路的实际顺序(和「有状态的上下文管理」同口径,可抽象成):
-
门禁
先判用户这轮能不能撑起一轮像样对话(信息够不够、是不是有效 turns)。不够就走澄清 / 不急着进主模型——这一点和「主窗口塞进一大段历史」是两条心流,后面还会用到。 -
时间窗 + 主上下文
和上文「只捞约 30 分钟内」一致:主窗口里的上下文也按同一时间策略收束,不是全历史无差别卷入。 -
候选
在窗内,用get_context_candidates一类 拉出「可能进 prompt」的消息(是否再 走向量/重排,看开关与业务)。 -
重排之前的一口坑(实战踩过)
门禁轮往往会在消息流里留下**「第一轮录入/筛选期」的输入**;进重排时若不把这条从候选里剔掉,它会和当前轮用户输入一起进 cross-encoder,排序会歪——所以:重排阶段要显式把「门禁录入那一条」筛出去,再和其余候选一起做 rerank。 -
门禁与「要不要再判一轮」
在相关度/策略允许时,门禁侧可以再判要不要补问或缩短卷入,不在本文展开成 if/else 表。 -
重排 → 拼进 prompt → 主推理
重排完,与当前轮用户消息、系统提示等 拼成主 prompt,交给 llama-server 对话进程 出字(第 2 篇 里那条「生成」的 HTTP)。
与 llama 三服务的关系(一句):业务层决定「捞谁、剃谁、以什么顺序进 prompt」;llama 只负责在约定端口上算 embedding / rerank / chat,不替你定 30 分钟、门禁、剔谁不剔谁。
还没做到的:换话题
「换话题」仍然卡着:业务规则还没到「像成熟产品主窗口」那样,能稳态地判新主题、断旧题、又不误伤刚聊一半的内容。后面若能解决,会专门写一篇讲这块;本文只记现状缺口。
产品上的参照(主观、非复刻)
如 豆包 一类主窗口的上下文感:按时间有选择地卷入、换话题能接上、整体像「老友」在聊——是我这边想逼近的体验方向;实现上 仍是自家会话状态 + 规则 + 模型,不是 贴牌同款算法。
外发若 需弱化具体产品名,可 改成「某类 主窗口式对话产品」。
阶段总结(承接上文、收束到「不展开」):§2~§3 只写到设计口径——门禁、时间窗、重排前剔人、进 prompt 的顺序;没 落到能让别人照抄的核心业务与字段,也无意教复刻。LangGraph 在工程里方便按节点对拍、打全日志;图/编排是壳,换话题、会话策略、谁覆盖谁 仍是业务自管,不 在这篇和官方文档争细节。
4. llama.cpp 与 vLLM:同一张卷子上,两道大题
这不是「谁更先进」——是 场景 和 你手头有几张卡、能忍受多重的进程栈 的事。
llama.cpp / llama-server 的省心,多半来自「轻、直、可预期」。 同一套工具 CPU、GPU 都能跑,对话进程 不必 和「吃满显存」划等号:本机小显存时往往 一进程 + 一坨 GGUF 就能把事办完,4B 一类量化档一进程 在 工作站/边缘 上常常就很能打;GGUF 直载、独立二进制 部署,没有一整条 Python 推理长栈 挂在耳边,延迟和占用 往往好看。和前面几篇的 三进程(对话 / 向量 / 重排分端口)也天然合拍——分进程、钱和电换体验 那条线不赘述。
vLLM 的强,在另一面: 起服务、占资源 往往就 重一档(典型是 在 GPU 上 做 高吞吐、批处理、PagedAttention/连续批 那一套;光把栈拉满就值得单独规划显存和运维)。HF 权重、PEFT、多 LoRA、类 OpenAI 服务 生态里跟它走的人多——多卡、集群、要当「推理中台」 时,文档和踩坑贴都厚。
| 你更可能卡在哪 | llama.cpp | vLLM |
|---|---|---|
| 本机/12G/抠门分进程 | 友好:CPU/GPU 可选,不默认「一启动就按生产集群胃口吃」 | 更适合 规划好 GPU 后上量 |
| 权重与日常姿势 | GGUF + 量化 为主,少折腾就能对话 | HF 管线、微调/多路 LoRA 一条线更顺 |
| 回复形状 | 本机 llama-server 在带 thinking 的 chat 模板上,常把最终答句与中间推理拆到 message.content / message.reasoning_content 等扩展字段 | 以当前 vLLM 版本行为为准,别默认和 llama 一侧字段名、分路逐字一致 |
OpenAI 兼容的一口防坑: 都宣称兼容 /v1/chat/completions,扩展字段(例如 reasoning_content、流式时 delta 分路)别默认全平台、全版本一码通——对接前看各自文档,联调时按实际 JSON 来。非流式 一次拿全;流式 按 SSE delta 分路各拼一截,一般不必 自研「从一整段里切 thinking 标签」的解析;客户端若只取 content,不等于 模型没走推理,可能是推理在另一路字段里。
5. 收束、现状与下一篇
这一套里最吃力的,往往在「本地怎么把环境摆平、服务怎么稳定起来」——部署、分进程、量化与端口、前期踩坑。那一步迈过去之后,线上(本机服务)在跑了,剩下的大头就是按自己业务写规则、接路由、调 prompt 与图编排,没有 一套放之四海而皆准的「业务模板」。
关于窗口与「算不算上线」:约 8k 上下文 在这套 12G / 三进程 的实验配置里,目前还不是我心目中的上线定稿——还会做更充分的测试;在后续迭代里,会优先试「换话题」相关的实验与规则(与 §3 里记的现状缺口同一战场),再 谈其它指标。
第 4 篇 在系列内是 「链路之外的一刀:语义压缩的独立实验(LlamaIndex + API)」——《链路之外的一刀:语义压缩的独立实验(LlamaIndex + API)》
在 候选已排好、进主模前 再谈 压缩/再摘要;不替代 本篇的「从会话里捞谁」。