AI 产品拆解案例:Manus、Cursor、DeepSeek 与 Character.AI

困难 🔴AI 学习
15 个标签
预计阅读时间:65 分钟
ManusCursorDeepSeekCharacter.AI上下文工程MoEKV Cache系统工程RAG客服机器人内容审核金融风控Text-to-SQL智能推荐落地案例

AI 产品拆解案例:从 Manus、Cursor、DeepSeek 到 Character.AI

AI 产品的竞争力,早就不只取决于"用了哪个大模型"。同一个基座模型,不同团队做出来的产品体验可以有数量级的差距——差距往往藏在看不见的工程细节里:怎么组织上下文、怎么检索代码、怎么榨干每一分算力、怎么把推理成本压到极限。很多人以为做 AI 产品就是"选个好模型 + 写个好提示词",但当任务变成长达上百步的 Agent 流程、几十万行的代码库、千亿参数的训练、亿级用户的并发对话时,真正拉开差距的往往是模型调用之外的那一整套系统设计。本文选取 Manus、Cursor、DeepSeek、Character.AI 四个公开分享过工程实践的产品/团队,逐一拆解它们真正要解决的问题和背后的技术思路,帮助我们跳出"调 API"的视角,理解顶尖 AI 产品是怎么被"工程"出来的。

📌 为什么要研究这四个案例

在深入细节之前,先说明为什么这四个案例值得放在一起看。它们分别处在 AI 应用栈的四个不同层面,各自面对一种"看似只能靠更强模型解决、实际靠系统工程解决"的硬约束:

| 案例 | 所处层面 | 面对的硬约束 | 核心解法关键词 |

| --- | --- | --- | --- |

| Manus | 通用 Agent 应用 | 上下文窗口有限、长任务易跑偏 | 上下文工程、KV Cache、外部记忆 |

| Cursor | 编程助手应用 | 代码库远超窗口、延迟要求高 | 代码 RAG、增量索引、任务分层 |

| DeepSeek | 基座模型训练 | 算力与显存预算受限 | MoE、MLA、FP8、强化学习 |

| Character.AI | 海量对话服务 | 显存里 KV Cache 爆炸 | GQA/MQA、滑动窗口、缓存压缩 |

把它们串起来看,会发现一条清晰的主线:AI 产品的天花板,往往不在模型本身,而在"模型之外的系统工程"。

🧭 Manus 拆解:顶尖 Agent 的上下文工程

核心工程挑战

Manus 是一个通用 Agent 产品,需要完成"订机票""做研究报告""写代码"这类需要几十甚至上百步工具调用的长任务。团队面临一个直接的选择:要不要为 Agent 场景专门训练或微调一个模型?据公开分享,Manus 团队判断基座模型的迭代速度远快于自研模型的速度——押注在自研模型上,很可能"模型一升级,之前的努力就作废"。于是他们把工程重心放在了如何把现成的大模型用到极致,这套方法论后来被称为"上下文工程"(Context Engineering):不改模型参数,只是让传给模型的上下文(系统提示、历史记录、工具结果、外部记忆)组织得足够好,同样可以做出体验一流的 Agent。

打个比方:如果模型是一位能力很强但记性有限的专家顾问,上下文工程做的事情就是"如何把资料摆在他面前"——同样一位顾问,桌上资料摆得清爽有序还是杂乱堆叠,产出质量天差地别。上下文工程研究的正是这门"摆资料"的手艺。

技术思路:让上下文为长任务服务

1. 保持上下文前缀稳定,把 KV Cache 命中率当第一指标

Agent 每一步都要把此前所有历史重新喂给模型,上下文会随任务推进越来越长。如果每一步 prompt 的前缀发生哪怕很小的变化(比如往系统提示里插入当前时间戳),都会让推理引擎的前缀缓存失效,导致该请求要重新计算全部上下文——延迟和成本都会显著上升。所以一个朴素但极其重要的原则是:系统提示尽量只做"追加",不做"编辑";序列化 JSON 时保证字段顺序稳定;能不放进前缀的动态信息(如时间戳)就不要放。

textCode
反例:每一步都在 system prompt 里塞入当前时间
"当前时间: 2026-07-02 10:23:11..." → 前缀每步都变 → 缓存命中率归零

正例:把时间戳放进普通消息或工具返回结果里
system prompt 保持完全不变 → 前缀持续命中缓存 → 单步延迟和成本大幅下降

这一点为什么这么重要?因为主流推理引擎(如 vLLM、SGLang)都支持"前缀缓存(Prefix Caching)":如果两次请求的前缀 token 完全一致,第二次就能直接复用第一次算好的 KV Cache,跳过这部分的重新计算。在 Agent 场景里,系统提示 + 工具定义往往有几千 token 且每步都相同,前缀命中与否直接决定了单步延迟能差好几倍。很多厂商的缓存命中 token 还享有大幅折扣(例如缓存读取价格可低至常规输入价的十分之一),所以缓存命中率同时影响体验和账单。

pythonCode
# 反面示范:破坏前缀稳定性
def build_prompt_bad(tools, history):
    system = f"你是助手。当前时间:{datetime.now()}"  # 每步都变,缓存必失效
    return [{"role": "system", "content": system}] + history

# 正面示范:前缀完全稳定,动态信息下沉到消息体
STABLE_SYSTEM = "你是助手。可用工具:{tools}"  # 模板固定
def build_prompt_good(tools, history, now):
    system = STABLE_SYSTEM.format(tools=tools)   # 只要工具集不变,前缀就不变
    # 时间戳作为一条普通 user 消息放在尾部,不污染前缀
    return [{"role": "system", "content": system}] + history + [
        {"role": "user", "content": f"[当前时间] {now}"}
    ]

2. 把长期状态写到外部文件系统,而不是塞满上下文窗口

长任务的中间产物(网页抓取结果、代码文件、日志)体积很大,如果全部塞进上下文,很快会超出窗口上限,还会拖慢推理、稀释模型的注意力。比较可行的做法是让文件系统本身成为"外部记忆":Agent 把大段内容写入文件,上下文里只保留文件路径和摘要,需要时再读回来。这相当于把"几乎无限的记忆"下放给了廉价的存储,而不是昂贵又有限的上下文窗口。

pythonCode
# 把抓取到的大段网页内容写入文件,上下文里只留引用
def store_observation(agent, url, raw_html):
    path = f"/workspace/cache/{hash(url)}.html"
    write_file(path, raw_html)                       # 大内容落盘
    summary = agent.llm.summarize(raw_html[:8000])   # 只对开头做摘要
    # 回写进上下文的只有路径 + 摘要,几十个 token 而非几万个
    return f"已抓取 {url},存于 {path}。摘要:{summary}"

def recall_when_needed(agent, path, query):
    """真正需要细节时再把文件读回来,按需注入"""
    content = read_file(path)
    return agent.llm.extract(content, question=query)

3. 让工具调用的失败信息留在上下文里,而不是悄悄擦掉重试

很多实现出于"美观"会把失败的工具调用从历史里删掉,只保留成功的部分。但这样做的代价是模型会反复犯同样的错误——因为它根本看不到失败的证据。把报错信息、异常堆栈原样保留在上下文中,其实是在给模型提供负样本,让它有机会在后续步骤里"看到教训并绕开它",这是一种隐式的在线学习信号。

textCode
反例:只保留成功轨迹
Step 1 (成功) → Step 2 (成功) → ...   模型看不到"哪条路走不通",可能重复踩坑

正例:失败也保留
Step 1: call api_x → Error: 404 not found
Step 2: call api_y → 成功   模型"看到"了 api_x 不可用,主动改用 api_y

4. 用"屏蔽"而不是"删除"来管理工具集

随着 Agent 可用工具越来越多,动态增删工具定义同样会破坏上下文前缀的稳定性(工具定义通常出现在提示靠前的位置)。比较稳妥的做法是让工具定义本身保持不变,通过模型输出层面的掩码来限制当前步骤可选的工具子集,而不是在提示里反复增删工具描述。

pythonCode
# 工具定义始终全量保留在前缀里(前缀稳定),
# 通过约束解码/logit 掩码限制本步"可选"的工具,而非删改定义
ALL_TOOLS = ["search", "read_file", "write_file", "run_code", "send_email"]

def allowed_tools_this_step(state):
    if state.phase == "research":
        return {"search", "read_file"}      # 研究阶段禁用写和发邮件
    if state.phase == "delivery":
        return {"write_file", "send_email"}
    return set(ALL_TOOLS)
# 前缀里 ALL_TOOLS 定义永远不变 → 缓存持续命中;只是在采样层屏蔽不该用的

5. 故意保留一些多样性,避免陷入"复读"式的模式坍缩

如果上下文里全是高度相似、模板化的历史动作,模型容易陷入模仿最近动作的惯性,即使当前情境已经不再适用。适度在历史记录的表述上引入变化,有助于防止这种"少样本模仿"带来的僵化和跑偏。

6. 把系统提示拆成"稳定层"和"易变层"

一个常见的误区是把整个系统提示当成一整块随意拼接的文本。更稳健的做法是把它拆成两层:一层是几乎不变的"身份与工具说明"(模型是谁、能用哪些工具、输出格式约定),放在最前面,长期保持字节级不变,最大化命中前缀缓存;另一层是随任务推进而变化的"当前状态"(已完成的步骤、当前子目标、最新的观察结果),放在提示的尾部,只在这一层做更新。这样即使任务状态频繁变化,前面几千 token 的稳定部分依然可以持续复用缓存。

textCode
[稳定层:身份、工具schema、输出格式约定]   ← 几乎不变,长期命中缓存
--------------------------------------
[易变层:当前任务状态、最近观察、下一步计划]  ← 每步都可能更新

7. 让计划显式地留在上下文里,对抗长任务的"注意力漂移"

任务步数一多,模型容易忘记最初的目标,或者被中间某一步的细节带偏。把当前的整体计划、已完成事项、待办事项显式地写成一小段结构化文本,反复出现在提示中,相当于不断地把模型的注意力"拉回"到主线任务上,减少长任务中期跑题的概率。

textCode
# 每一步都把这段"待办清单"重新贴到上下文尾部,充当注意力锚点
[任务目标] 生成一份 Q2 竞品分析报告
[已完成] ✓ 收集 3 家竞品官网信息  ✓ 提取定价策略
[进行中] ▶ 对比功能矩阵
[待办]   ☐ 撰写结论  ☐ 导出 PDF

8. 小结:上下文工程的通用原则清单

把以上做法抽象一下,可以归纳出一份适用于绝大多数 Agent 产品的通用原则清单:

优先保证上下文前缀的稳定性,把缓存命中率当作和准确率同等重要的指标;
长期状态尽量外置到文件系统或数据库,上下文里只保留引用和摘要;
工具调用失败的痕迹要留下来,它是模型免费的纠错样本;
工具集的变化用掩码控制,而不是频繁编辑提示本身;
历史记录保留适度多样性,防止模型陷入机械模仿;
系统提示分层设计,稳定部分和易变部分物理隔离;
长任务中显式维护并反复呈现当前计划,对抗注意力漂移。

这些原则并不依赖某个具体模型的私有能力,任何团队在调用公开的大模型 API 构建自己的 Agent 产品时,都可以直接照搬这套思路——它本质上是一套"如何跟大模型高效协作"的工程规范,而不是某个厂商独有的秘密武器。

对我们的启发

不是所有产品竞争力都要靠"自研更强的模型"来获得;把现有模型的上下文组织好——缓存友好、记忆外置、保留错误信号、结构稳定——本身就是一种可以被系统化、可以被持续打磨的工程能力,甚至是决定 Agent 产品体验上限的关键变量。

💻 Cursor 拆解:AI 怎么读懂你的整个代码库

核心工程挑战

写代码时,模型只有理解了整个项目的架构、跨文件的调用关系、团队约定的写法,给出的建议才靠谱。但一个中大型代码库随便就是几十万行,远远超出任何大模型的上下文窗口;即使窗口足够大,把整个仓库塞进去成本也高得不现实,而且真正相关的信息往往只是仓库中很小的一部分。更棘手的是,编程助手不像离线的文档问答工具,它要嵌入在编辑器里实时响应用户的每一次按键和每一次提问,对"新鲜度"和"延迟"都有很高要求——代码改了一秒钟前,助手就应该知道;用户按下 Tab 键,补全建议不能让人等出割裂感。真正的挑战不是"模型够不够聪明",而是"怎么在几十万行代码里,又快又准地把当前任务真正相关的那几百行找出来,并且延迟要低到能跟得上手速"。

做个量级对比:一个 50 万行的代码库,按平均每行 10 个 token 估算约 500 万 token,即使是 100 万 token 窗口的模型也装不下十分之一;就算装得下,一次请求的输入费用也会高到无法接受。所以"检索出真正相关的那一小部分"不是优化项,而是必需项。

技术思路

1. 代码版 RAG:对代码库做语义索引和检索

和文档 RAG 类似的思路被搬到了代码场景:把代码库按函数、类、文件等粒度切分,构建语义向量索引;编辑或提问时先检索出相关的代码片段,再把这些片段和用户的当前上下文一起喂给大模型。区别在于代码有更强的结构性——检索不能只看文本相似度,还要能捕捉"这个函数调用了谁""这个类型在哪里被引用"这类关系,否则很容易检索到字面相似但语义无关的片段。另外,通用文本的嵌入模型并不天然适合代码——变量命名、缩进、语言关键字的分布规律和自然语言差异很大,因此专门针对代码语料训练或微调嵌入模型,往往能显著提升检索的准确率。

pythonCode
# 代码库语义索引的简化流程
def build_code_index(repo_path, embed_model, vector_db):
    for file in walk_source_files(repo_path):
        # 关键:按语法单元(函数/类)切分,而不是按固定行数硬切
        for chunk in split_by_ast(file):        # 每个 chunk 是一个完整函数/类
            metadata = {
                "path": file.path,
                "symbol": chunk.symbol_name,
                "calls": chunk.callees,          # 记录它调用了谁
                "language": file.language,
            }
            vector_db.add(embed_model.encode(chunk.code), metadata)

def retrieve_context(query, cursor_file, embed_model, vector_db, budget=6000):
    hits = vector_db.search(embed_model.encode(query), k=30)
    # 排序时综合语义相似度 + 与当前文件的调用距离 + 文件新鲜度
    ranked = rerank(hits, cursor_file)
    return pack_within_budget(ranked, token_budget=budget)  # 按预算截断

2. 用自研小模型处理延迟敏感的子任务

不是所有环节都要调用最强的大模型。像"光标处该补全什么代码"这种任务,用户对延迟极其敏感(几百毫秒的等待就会打断心流),如果每次都请求一个慢而贵的大模型并不划算。比较现实的路径是针对这类高频、模式相对固定的子任务,训练或蒸馏一个专用的小模型,让它在低延迟链路上完成,把大模型留给真正需要深度推理的场景,比如多文件重构、复杂需求理解。

textCode
用户操作                      处理方式
-------------------------  ----------------------------
输入下一个 token 的补全       延迟敏感 → 轻量小模型,就近部署
"帮我重构这个模块"            复杂度高 → 大模型 + 代码检索上下文
"这个报错是什么原因"           需要跨文件定位 → 检索 + 大模型综合分析

这种"任务分层、模型分级"的思路,本质上和 CPU 里的多级缓存是一个道理:高频、简单的请求用又快又便宜的路径处理,低频、复杂的请求才动用又慢又贵的重型资源。用一个统一的路由层来决定每个请求走哪条链路:

pythonCode
def route_request(request):
    if request.type == "inline_completion":
        return small_model.complete(request)        # 毫秒级,本地/边缘
    elif request.type in ("refactor", "explain_bug"):
        ctx = retrieve_context(request.query, request.file)
        return big_model.chat(request, context=ctx)  # 秒级,云端大模型
    else:
        return big_model.chat(request)

3. 结合静态分析和 AST,让检索"懂代码"而不只是"懂文本"

纯文本向量检索容易被变量名、注释这类表层信号误导,抓不住"谁调用了谁""这个符号定义在哪"这种结构信息。把静态分析、抽象语法树(AST)解析和语义索引结合起来,可以让检索系统理解代码的调用图、类型关系、作用域,从而在补全和问答时提供更贴合项目实际结构的上下文,而不是简单的字符串相似匹配。

4. 增量索引:编辑一行代码,不必重新扫描整个仓库

真实开发场景中,代码库几乎每分钟都在变化。如果每次改动都要重新对全仓库做一遍语义索引,成本和延迟都无法接受。更实际的做法是做增量索引:只对发生变化的文件或函数重新计算向量、更新索引条目,未改动的部分继续复用已有索引。索引系统的更新速度,直接决定了 AI 编程助手"感知"到最新代码的时效性。

pythonCode
# 监听文件变更,只对 diff 涉及的符号做增量更新
def on_file_saved(path, index):
    old_symbols = index.get_symbols(path)
    new_symbols = parse_symbols(path)
    changed = diff_symbols(old_symbols, new_symbols)
    for sym in changed.removed:
        index.delete(sym.id)
    for sym in changed.added_or_modified:
        index.upsert(sym.id, embed(sym.code), sym.metadata)
    # 未变动的绝大多数符号完全不动,全量重建 O(N) 降为增量 O(变更量)

5. 检索结果的排序与上下文预算分配

即使检索系统召回了一批相关代码片段,也不能不加选择地全部塞进提示——上下文窗口本身是有限资源,还要给对话历史、当前文件内容留出空间。需要有一层排序和裁剪逻辑:综合语义相关性、代码调用距离、文件的新旧程度等信号给候选片段打分,按预算截断,优先保留信息密度最高的片段,而不是简单按相似度分数一刀切。

6. 多信号融合:光有向量检索还不够

只依赖向量相似度的检索,遇到"函数名完全一致但语义描述不同"或者"用户直接引用了某个符号名"这类情况时,效果反而不如传统的关键词匹配。更成熟的系统会把向量检索、关键词检索、最近编辑过的文件、当前打开的标签页等多种信号融合起来做综合排序,而不是单一依赖某一种检索方式,这也是为什么"看起来简单的代码搜索"背后往往是一套相当复杂的融合排序系统。

pythonCode
def fused_score(candidate, query, editor_state):
    return (
        0.45 * candidate.vector_similarity          # 语义相似
        + 0.25 * bm25_keyword_score(candidate, query)  # 关键词精确匹配
        + 0.15 * recency_bonus(candidate, editor_state.recent_files)
        + 0.10 * open_tab_bonus(candidate, editor_state.open_tabs)
        + 0.05 * call_graph_proximity(candidate, editor_state.cursor_symbol)
    )

7. 护城河不只在模型本身,也在工程系统设计

不同 AI 编程工具背后可能用的是同一批基座大模型,但体验差距很大——差距往往来自索引更新是否够快(改一行代码要不要重建全库索引)、检索排序是否精准、延迟敏感任务有没有专门优化、和编辑器的交互设计是否顺手。这些都不是"换个更强的模型"能直接解决的问题,而是纯粹的系统工程能力,这也是为什么简单封装模型接口和真正做深工程的产品,体验差距会越拉越大。

对我们的启发

面对"上下文太大装不下"的问题,答案往往不是等更大的窗口,而是构建一套检索加结构分析的系统,把无限的信息裁剪成模型真正需要的那一小部分;同时要做好任务分层——不是每个任务都值得用最贵的模型去处理,索引和检索系统的实时性往往比模型本身的能力上限更影响日常体验。

🧮 DeepSeek 拆解:用几分之一的钱训出顶尖模型

核心工程挑战

训练和运行千亿甚至万亿参数级别的大模型,算力和显存都是巨大的硬约束。行业里长期存在一种朴素假设:模型效果基本由"堆了多少算力、用了多少数据"决定,资源越多的团队天然占优。据公开分享,DeepSeek 团队在算力资源相对受限的条件下,依然要做出能对标一线水准的模型,问题就变成了:能不能通过架构和训练方法上的系统性创新,用远低于"堆算力"路线的资源投入,换到接近甚至持平的模型能力?这要求团队不能只在某一个环节做优化,而是要把模型架构、注意力机制、数值精度、训练范式当作一个整体来协同设计,任何一个环节的浪费都会被资源约束放大。

技术思路

1. MoE(混合专家架构):参数量大,但每次只激活一部分

传统稠密模型每次前向传播要用到全部参数,参数越多,算力开销越大。MoE 把网络中的前馈层换成多个"专家"子网络,每个 token 只经过一个路由器挑选出的少数几个专家,而不是全部专家参与计算。这样模型总参数量可以做得很大、表达能力强,但实际参与计算的参数量只是其中一小部分,从而用远低于同等规模稠密模型的算力开销,获得接近大模型的效果。

textCode
稠密模型:  输入 → 全部参数参与计算 → 输出   (参数量 = 计算量)
MoE 模型:  输入 → 路由器 → 激活少数专家 → 输出 (参数量 >> 实际计算量)

用一个团队协作的类比:稠密模型像"每个问题都让全公司所有人一起开会讨论",人越多会越低效;MoE 像"路由前台根据问题类型,只叫来最对口的两三位专家处理",公司总人数(总参数)可以很多,但每个问题实际动用的人力(激活参数)很少。DeepSeek-V3 总参数达数千亿,但每个 token 实际激活的参数只是其中一小部分,这正是它训练与推理成本远低于同等能力稠密模型的关键。

pythonCode
# MoE 前馈层的简化逻辑(伪代码)
class MoELayer:
    def __init__(self, num_experts=256, top_k=8):
        self.experts = [FeedForward() for _ in range(num_experts)]
        self.router = Router(num_experts)
        self.top_k = top_k

    def forward(self, token):
        # 路由器为每个 token 打分,只选 top_k 个专家
        scores = self.router(token)
        chosen = topk(scores, self.top_k)         # 只激活 8/256 个专家
        out = 0
        for idx, weight in chosen:
            out += weight * self.experts[idx](token)
        return out
    # 总参数 = 256 个专家,但每 token 只计算 8 个 → 算力约为稠密的 1/32

2. MLA(多头潜在注意力):压缩 KV Cache,省显存

标准多头注意力在推理时需要为每个 token 缓存所有注意力头的 Key/Value,序列越长、并发越多,显存占用越夸张。MLA 的思路是先把 Key/Value 投影到一个低维的"潜在空间"再进行缓存,需要时再还原展开,用一次低维压缩换取显著更小的缓存体积,从而在同样的显存预算下支持更长的上下文或更高的并发。可以把它类比成把一份高分辨率图片先压缩成一个小体积的文件存起来,等真正需要显示的时候再解压还原——存储的是压缩后的紧凑表示,而不是原始的完整数据,只要压缩和解压的映射设计得当,效果损失可以控制在很小的范围内。

3. FP8 低精度训练:用更粗的数值精度换更高的训练吞吐

训练时数值精度越高,单次计算的硬件开销越大。把关键计算路径从常见的 16 位精度进一步降到 8 位浮点(FP8),单位时间能处理的计算量明显提升,训练同样规模的模型所需的时间和资源随之下降。低精度最大的风险是数值溢出或梯度不稳定,因此需要配合精细的缩放策略和混合精度设计,在关键的敏感环节保留更高精度,来保证训练稳定收敛,而不是简单粗暴地把所有数字都截断成 8 位。

textCode
精度对比(占用与吞吐是相对关系,仅示意)
FP32:4 字节/数  精度最高  显存占用最大  吞吐最低
FP16/BF16:2 字节/数  主流训练精度
FP8:1 字节/数  显存占用最小  吞吐最高  但需精细缩放防溢出

4. 纯强化学习训练推理能力:绕开海量人工标注的推理链

让模型学会"一步步推理",传统做法依赖大量人工标注的详细解题过程。据公开分享,DeepSeek 探索了一条更依赖强化学习、较少依赖人工标注推理过程的路径:让模型通过大量"是否得到正确答案"这类可验证的奖励信号自主探索、自我改进推理策略,而不必先由人类手把手示范每一步该怎么想。这大幅降低了对昂贵的人工标注推理数据的依赖。

textCode
传统路径:人工标注详细解题步骤 → 监督微调 → 模型模仿标注的推理过程
RL 路径: 只提供"答案对不对"的奖励信号 → 模型自主探索 → 逐步学会有效的推理策略

这条路径之所以巧妙,在于它抓住了数学题、代码题这类任务的一个特点:过程难以标注,但答案容易验证(数学题有标准答案,代码题能跑测试用例)。既然验证便宜,就让模型自己去大量试错,用"答案对不对"当作奖励反向优化,模型会自发学出诸如"先验算""发现矛盾就回溯"这类推理行为,而这些行为没有一条是人类手把手教的。

5. 知识蒸馏:把大模型的推理能力"教"给小模型

在用强化学习训练出具备较强推理能力的大模型之后,还可以把这类大模型生成的高质量推理过程作为训练数据,去蒸馏训练参数量小得多的模型。这样一来,小模型也能在特定任务上表现出接近大模型的推理水平,同时保留小模型部署成本低、推理速度快的优势,形成"大模型负责探索出好的推理路径,小模型负责低成本地复用这些路径"的分工。

textCode
大模型(RL 训出强推理)
    │  生成大量高质量"带推理过程"的样本
    ▼
小模型(在这些样本上微调)→ 以远低的部署成本获得接近大模型的推理表现

对我们的启发

DeepSeek 这几项技术看似分散——架构、注意力机制、数值精度、训练范式,但共同的主线是同一个思路:当硬件或预算不是你的优势时,就把每一处能够"用系统工程弥补差距"的环节都做到极致,不是单点突破,而是从架构设计到训练策略的全链路协同优化。对于资源有限的团队,这个案例的价值不在于照搬某个具体架构,而在于它示范了一种思维方式:先搞清楚成本主要花在哪几个环节,再逐一寻找该环节里"用工程手段换资源"的空间。

💬 Character.AI 拆解:海量陪聊的成本怎么压下来

核心工程挑战

陪聊类产品的典型特征是:用户数量巨大、对话轮次很长、并发规模高,而且用户对"角色记得住之前聊过什么"有很强的期待,这意味着单次对话的上下文往往比一般问答场景长得多。这类场景推理成本的真正瓶颈,往往不是"算力不够",而是显存里堆积的 KV Cache——每一路对话都要为已经生成过的所有 token 保留 Key/Value 缓存,对话越长、并发用户越多,缓存占用就越大,超过显存容量后要么拒绝新请求,要么被迫压缩批量大小、拖慢整体吞吐。单纯增加 GPU 数量只能线性提升算力,却无法从根本上改变"每路对话占用的缓存随长度线性增长"这个结构性问题。据公开分享,Character.AI 在这个方向上做了大量压缩 KV Cache 的工程优化,目标就是在同样的硬件上撑起数量级更大的并发对话。

为什么 KV Cache 是瓶颈?可以这样理解:模型每生成一个新 token,都要"回看"前面所有 token 的 Key/Value 才能算出注意力。为了不每步都重算,就把这些 Key/Value 缓存在显存里。于是每多一个用户、每长一轮对话,显存里就多压一份缓存。当成千上万路长对话并发时,KV Cache 会挤占掉本该用来跑更大 batch 的显存,成为真正卡住吞吐的地方。

技术思路

1. MQA/GQA:让多个注意力头共享 Key/Value

标准多头注意力(MHA)里每个头都有自己独立的 Key/Value,头数越多缓存越大。多查询注意力(MQA)让所有头共享同一份 Key/Value,只有 Query 保持独立;分组查询注意力(GQA)是介于两者之间的折中方案——把头分成若干组,组内共享一份 Key/Value。这样在几乎不损失效果的前提下,把 KV Cache 体积压缩到原来的几分之一甚至十几分之一。

textCode
MHA: 头1(K1,V1) 头2(K2,V2) 头3(K3,V3) 头4(K4,V4)   → 缓存 = 4 份 KV
GQA: [头1,头2]共享KV_A   [头3,头4]共享KV_B          → 缓存 = 2 份 KV
MQA: 头1,头2,头3,头4 共享同一份 KV                   → 缓存 = 1 份 KV

以一个 32 头的模型为例:MHA 需要缓存 32 份 KV;改成 8 组的 GQA,缓存直接降到 8 份,压缩到原来的 1/4;极端的 MQA 只留 1 份,压缩到 1/32。而实验普遍表明,GQA 在压缩显存的同时几乎不损失模型质量,这也是它成为当今主流大模型标配的原因。

2. 滑动窗口注意力:只保留最近一段上下文的缓存

陪聊场景里,很多时候模型真正需要"精确记住"的只是最近若干轮对话,而不是从会话开始的全部历史。滑动窗口注意力只在一个固定大小的窗口内做完整的注意力计算和缓存,超出窗口的旧 token 被丢弃或降级处理,缓存大小不再随对话长度无限增长,而是被限制在一个可控的常数范围内。

textCode
对话长度:  [t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 ...]
滑动窗口(4):              [ t7 t8 t9 t10 ]  ← 只对窗口内做完整注意力
                          缓存大小恒定,不随对话变长而膨胀

3. 跨层共享 KV Cache:多层复用同一份缓存

Transformer 通常每一层都有自己独立的 Key/Value 缓存,层数越多、总缓存量越大。让相邻的多个层共享同一份 KV Cache,可以在增加模型深度的同时,不让缓存体积随层数线性膨胀,用较小的额外显存代价获得更深的网络。

4. 量化与连续批处理:从缓存体积到整体吞吐的全链路优化

除了从注意力结构层面压缩缓存,把 KV Cache 本身用更低精度(比如 int8 甚至更低)存储,也能在几乎不影响回复质量的前提下进一步减少显存占用。与此同时,服务层面常见的连续批处理(continuous batching)让不同用户的请求可以在生成过程中动态加入或退出同一批次,而不必等一整批都生成完才能处理下一批,从而把显存和算力的利用率维持在高位。缓存压缩解决"能不能装下更多对话",连续批处理解决"能不能把资源用满",两者结合才能真正把单位成本打下来。

textCode
静态批处理:  [====请求A====][请求B短却要等A结束]  GPU 常有空转
连续批处理:  请求随时加入/退出同一批次,谁完成谁走,空位立即补新请求
              → GPU 利用率持续维持高位,吞吐显著提升

5. 长期记忆与会话窗口的取舍

对陪聊场景而言,用户往往希望"角色记得住我",这和滑动窗口"只保留最近上下文"存在天然张力。工程上的折中思路是把两者分层:短期的、逐字精确的上下文交给注意力机制和 KV Cache 处理,而跨会话的长期记忆(用户的偏好、过往关键事实)则单独抽取、总结后存放到外部存储中,在新会话开始时以精简摘要的形式重新注入,而不是让原始的长对话历史一直占用宝贵的缓存空间。

pythonCode
# 会话结束时抽取长期记忆,下次会话开头再注入
def on_session_end(user_id, transcript, memory_store):
    facts = extract_key_facts(transcript)   # "喜欢猫""是程序员""上次在聊旅行计划"
    memory_store.upsert(user_id, facts)

def on_session_start(user_id, memory_store):
    facts = memory_store.get(user_id)
    # 用一小段摘要注入,而非把上次几千 token 的原始对话全搬过来
    return f"[关于该用户的已知信息] {summarize(facts)}"

对我们的启发

面向"海量并发、长对话"的产品,推理优化的重点应该先看 KV Cache 这个瓶颈,而不是一味追求更快的芯片或更大的批量;注意力机制层面的共享和裁剪(GQA、滑动窗口、跨层共享)往往比单纯堆硬件更划算,也更能直接决定产品能不能在成本可控的前提下撑住用户规模。

🌟 总结:四个案例的共同主线

这四个拆解看似分属不同领域——Agent、编程助手、基座模型训练、对话产品,但可以看到同一条主线:顶尖 AI 产品的竞争力,很大一部分来自"模型之外"的系统工程能力。Manus 把功夫下在上下文组织上,Cursor 把功夫下在代码检索和任务分层上,DeepSeek 把功夫下在架构和训练效率上,Character.AI 把功夫下在推理阶段的缓存压缩上。四者面对的约束不同——有限的上下文窗口、有限的检索延迟、有限的算力预算、有限的显存容量,但解法的思路是相通的:先精确定位真正的瓶颈在哪里,再针对瓶颈设计专门的系统,而不是简单地寄希望于"换一个更强的模型"就能一劳永逸。

把四个案例的方法论浓缩成一张对照表,方便日后做自己的 AI 产品时对号入座:

| 案例 | 真正的瓶颈 | 关键技术手段 | 可迁移的通用原则 |

| --- | --- | --- | --- |

| Manus | 上下文窗口有限、长任务跑偏 | 前缀缓存、外部记忆、错误留痕、计划锚点 | 把上下文当作可精心设计的资源来经营 |

| Cursor | 代码库巨大、延迟敏感 | 代码 RAG、AST 分析、增量索引、任务分层 | 用检索裁剪信息,用分层匹配成本 |

| DeepSeek | 算力与显存预算受限 | MoE、MLA、FP8、RL 训推理、蒸馏 | 全链路协同,逐环节用工程换资源 |

| Character.AI | KV Cache 撑爆显存 | GQA/MQA、滑动窗口、跨层共享、量化+连续批处理 | 先找准瓶颈资源,再针对性压缩 |

理解这些原理,比单纯记住"调哪个 API"更能帮助我们判断一个 AI 产品的真实工程含金量,也更能指导我们在做自己的 AI 应用时,把有限的资源投入到真正决定体验和成本的地方。一句话概括这四个案例给从业者的最大启示:模型是买得到的,系统工程能力是买不到的,后者才是真正的护城河。

🏢 案例五:企业 RAG 知识库落地——把 5 万份内部文档变成可问答的专家

背景

某中型 SaaS 公司客服和售前团队每天要在 5 万多份 PDF/Confluence/工单里翻答案,平均一次查资料耗时 6 分 20 秒,新人上手周期长达 3 个月。目标是做一个内部问答系统,把"找答案"从分钟级压到秒级,同时答案必须可溯源(给出引用出处),否则法务不批准上线。

方案与原理

核心是检索增强生成(RAG):先把文档切块、向量化入库,提问时检索相关片段,再让大模型基于片段生成答案。难点不在"能跑通",而在"准确率能不能上线"。原始 naive RAG 的首版命中率只有 58%,远达不到可用标准。团队做了四轮迭代:分块策略优化、混合检索、重排序、以及查询改写。

架构

textCode
文档源(PDF/Confluence/工单)
   │  1) 解析 + 语义分块(父子分块)
   ▼
预处理管线 ──► 向量库(Milvus) + 全文索引(Elasticsearch)
   │
用户提问 ──► 2) 查询改写(HyDE/多查询) ──► 3) 混合检索(向量+BM25)
   │                                        │
   ▼                                        ▼
4) Rerank(bge-reranker) ──► Top-5 片段 ──► 5) LLM 生成 + 引用标注 ──► 答案

关键代码:父子分块 + 混合检索 + 重排

pythonCode
# 父子分块:小块用于精确检索,命中后返回父块给 LLM 提供完整上下文
def build_parent_child_chunks(doc):
    parents = split_by_heading(doc, max_tokens=1200)   # 父块:一个小节
    records = []
    for p in parents:
        pid = new_id()
        for child in split_fixed(p.text, size=256, overlap=40):  # 子块:精确匹配单元
            records.append({
                "child_id": new_id(),
                "parent_id": pid,
                "child_text": child,       # 入向量库的是子块
                "parent_text": p.text,     # 交给 LLM 的是父块
                "source": doc.path,
                "page": p.page,
            })
    return records

def hybrid_search(query, k=30):
    vec_hits = milvus.search(embed(query), top_k=k)        # 语义召回
    kw_hits = es.search(query, top_k=k)                    # 关键词召回(擅长专有名词/编号)
    fused = rrf_fuse(vec_hits, kw_hits, k_const=60)        # 倒数排序融合
    return fused[:k]

def answer(query):
    hits = hybrid_search(query, k=30)
    reranked = bge_reranker.rank(query, hits)[:5]          # 精排只留 Top-5
    parents = dedup([h.parent_text for h in reranked])     # 去重父块
    ctx = "

".join(parents)
    prompt = (
        "只依据以下资料回答,并在每句话后用[序号]标注出处;"
        "资料中找不到就回答'资料中未提及'。

资料:
" + ctx
    )
    return llm.chat(prompt, query)   # 强约束:禁止编造 + 强制引用

指标(迭代对比)

| 版本 | 关键改动 | 检索命中率 | 答案准确率 | 平均延迟 | 幻觉率 |

| --- | --- | --- | --- | --- | --- |

| V1 naive | 固定 512 分块 + 纯向量 | 58% | 61% | 1.2s | 14% |

| V2 | 父子分块 | 71% | 73% | 1.4s | 9% |

| V3 | 加混合检索(BM25) | 83% | 82% | 1.6s | 6% |

| V4 | 加 Rerank + 查询改写 | 91% | 89% | 2.1s | 2.3% |

上线后单次查资料耗时从 380 秒降到 18 秒,新人上手周期从 3 个月压缩到 3 周,客服团队年节省工时约 1.1 万小时,折合人力成本约 82 万元/年;系统年运营成本(向量库+LLM调用)约 14 万元,ROI 约 5.8 倍。

常见坑

分块太大导致检索精度差,太小导致上下文断裂:父子分块是性价比最高的折中。
只用向量检索,遇到产品型号、错误码这类专有名词召回极差,必须叠加 BM25。
不做 Rerank:向量库 Top-30 里正确片段常排在 15 名开外,直接截 Top-5 会漏。
文档更新后忘记增量重建索引,答案引用到已废弃的旧版文档,酿成事故。

最佳实践

强制引用 + "找不到就说找不到"的 prompt 约束,是把幻觉率从两位数压到 2% 的关键。评估要建离线测试集(至少 200 条人工标注问答对),每次改动跑回归,不能靠"感觉变好了"。

🎧 案例六:客服机器人——从"人工兜底"到"自动解决 68%"

背景

电商平台日均咨询量 12 万条,人工客服 200 人仍然排队严重,高峰期用户平均等待 8 分钟。目标:用 AI 自动解决高频重复问题(退换货、物流、发票),把人力释放给复杂投诉。

方案与原理

不是简单接一个聊天模型,而是"意图识别 + RAG + 工具调用 + 人工兜底"的组合。核心难点是"敢不敢自动执行操作"——比如自动发起退款。方案用置信度阈值分流:高置信度自动处理,低置信度转人工,中间态先给建议由用户确认。

架构

textCode
用户消息 ──► 意图分类(28类) ──► 置信度判断
                                  ├─ >0.9  自动处理(RAG答疑/调用退款API)
                                  ├─ 0.6~0.9 给出建议+按钮确认
                                  └─ <0.6  转人工(带对话摘要)

关键代码:置信度分流 + 工具调用

pythonCode
def handle_message(msg, session):
    intent, conf = intent_classifier(msg, session.history)
    if conf < 0.6:
        return escalate_to_human(session, reason="低置信度",
                                 summary=summarize(session.history))
    if intent == "refund" and conf >= 0.9:
        order = get_order(session.user_id, msg)
        if order.refundable and order.amount <= 500:   # 小额自动退
            do_refund(order.id)
            return f"已为订单 {order.id} 发起退款,1-3 个工作日到账。"
        return escalate_to_human(session, reason="大额退款需人工审核")
    if intent in ("logistics", "invoice", "faq"):
        ctx = rag_retrieve(msg, top_k=5)
        ans, grounded = llm_answer_with_check(msg, ctx)  # 校验答案是否有据
        if not grounded:
            return escalate_to_human(session, reason="无可靠依据")
        return ans
    return escalate_to_human(session, reason="未覆盖意图")

指标

| 指标 | 上线前(纯人工) | 上线后(AI+人工) |

| --- | --- | --- |

| 自动解决率 | 0% | 68% |

| 平均首响时间 | 8 分钟 | 3 秒 |

| 用户满意度(CSAT) | 82% | 88% |

| 单次咨询成本 | 4.2 元 | 0.9 元 |

| 人工客服规模 | 200 人 | 95 人(转复杂问题) |

日均 12 万咨询,自动解决 8.2 万条,单条成本从 4.2 元降到 0.35 元(纯AI部分),年节省约 3400 万元。误操作率(错误退款/错误答复)控制在 0.4% 以内,靠的是金额阈值 + 校验兜底。

常见坑

一上来就让 AI 全自动执行退款/改地址等写操作,出一次错就是资损,务必设金额/风险阈值。
意图分类只用单轮消息,忽略上下文,导致"那我要退货"这种指代识别不了。
转人工时不带对话摘要,用户被迫从头再说一遍,满意度暴跌。

最佳实践

写操作分级授权,只读答疑放开、小额写操作自动、大额/高风险强制人工。转人工必带结构化摘要(用户诉求+已尝试方案+订单信息)。

👨‍💻 案例七:代码助手——团队内网私有化编程助手

背景

一家金融公司因合规不能用公有云代码助手,需在内网私有化部署,服务 300 名工程师。约束:代码不能出内网、单次补全延迟需低于 400ms、要理解公司自研框架。

方案与原理

基座用开源代码模型(如 32B 级)私有部署,补全走蒸馏后的 7B 小模型保延迟,问答/重构走大模型 + 代码 RAG。针对自研框架做了持续预训练(在内部代码语料上增量训练),让模型认识内部 API。

架构

textCode
IDE 插件 ──► 网关(鉴权/限流)
              ├─ 补全 ──► 7B 蒸馏模型(GPU本地,FP8) ──► <400ms
              └─ 问答/重构 ──► 代码RAG检索 ──► 32B模型 ──► 秒级
内部代码库 ──► 增量索引(AST切分) ──► 向量库

关键代码:延迟预算控制的补全

pythonCode
def inline_complete(prefix, suffix, repo_ctx):
    # 用 FIM(填充中间)格式,把光标前后代码都给模型
    prompt = fim_format(prefix=prefix[-1500:], suffix=suffix[:500])
    # 只检索光标所在文件的 import 和最近编辑的 3 个符号,控制延迟
    ctx = quick_ctx(repo_ctx, budget_tokens=400)
    out = small_model.generate(
        ctx + prompt,
        max_new_tokens=64,       # 补全不要太长,短而准
        timeout_ms=350,          # 超时直接放弃,宁可不补也不卡手
        stop=["

"],
    )
    return out

指标

| 指标 | 数值 |

| --- | --- |

| 补全采纳率 | 31% |

| 补全 P95 延迟 | 380ms |

| 问答准确率(自研框架相关) | 74%(增量预训练前 41%) |

| 工程师日均节省编码时间 | 约 52 分钟 |

| 私有化硬件投入 | 8 张 GPU 卡,一次性约 120 万元 |

按 300 人、日均节省 52 分钟、人均时薪 120 元估算,年产能提升折合约 2700 万元,硬件+运维年成本约 200 万元。

常见坑

直接用通用代码模型不做增量预训练,对自研框架的补全经常"一本正经地瞎写"。
补全 max_tokens 设太大,模型生成一大段还常常跑偏,采纳率反而低。
检索上下文塞太多把延迟拖过 400ms,工程师觉得卡手直接关插件。

最佳实践

补全"宁短勿长、宁快勿全",超时就放弃。自研框架必须做增量预训练或至少构建高质量 few-shot 示例库。

📄 案例八:文档智能处理——发票/合同自动抽取

背景

财务共享中心每月处理 8 万张发票、合同,人工录入 12 个关键字段,错误率约 3%,2 人专职录入。目标:自动抽取结构化字段并校验。

方案与原理

传统 OCR + 规则模板对版式变化脆弱。改用多模态大模型直接"看图抽取",配合 JSON Schema 约束输出和业务规则校验(如金额=单价×数量、税率合法性)。

关键代码:结构化抽取 + 校验

pythonCode
SCHEMA = {
    "invoice_no": "str", "date": "str", "seller": "str",
    "buyer": "str", "amount": "float", "tax": "float", "total": "float",
}

def extract_invoice(image):
    prompt = (
        "抽取发票字段,严格输出 JSON,字段:" + str(SCHEMA) +
        ";看不清的字段填 null,不要猜测。"
    )
    raw = vlm.chat(image=image, text=prompt)
    data = json_parse_strict(raw)          # 解析失败则重试一次
    return validate(data)

def validate(data):
    issues = []
    if data["amount"] and data["tax"]:
        if abs(data["amount"] + data["tax"] - data["total"]) > 0.02:
            issues.append("金额+税额 != 总额")   # 业务规则兜底
    if not re.match(r"^d{8,20}$", data.get("invoice_no") or ""):
        issues.append("发票号格式异常")
    data["_needs_review"] = len(issues) > 0    # 有问题的转人工复核
    data["_issues"] = issues
    return data

指标

| 指标 | 人工 | AI 抽取 |

| --- | --- | --- |

| 单张处理耗时 | 45 秒 | 2.3 秒 |

| 字段准确率 | 97% | 96.2% |

| 需人工复核比例 | 100% | 11%(仅校验异常的) |

| 月人力 | 2 人 | 0.3 人(复核) |

月处理 8 万张,自动通过 89%,人工只看 11% 的异常件,人力从 2 人降到 0.3 人,年节省约 40 万元,投入(VLM 调用)年约 6 万元。

常见坑

让模型"猜测"看不清的字段,比留 null 更危险,务必约束"看不清填 null"。
只信模型输出不做业务规则校验,金额抽错会直接进账务系统。
版式极端(手写、盖章遮挡)的样本不转人工,追求 100% 自动化会翻车。

最佳实践

"模型抽取 + 规则校验 + 异常转人工"三段式,把自动化率和准确率解耦:模型负责覆盖大多数,规则负责兜底,人工负责长尾。

🛒 案例九:智能推荐——从协同过滤到 LLM 语义召回

背景

内容社区推荐点击率(CTR)长期卡在 4.2%,冷启动内容(新发布、小众)曝光不足,创作者流失。目标:提升 CTR 与内容多样性。

方案与原理

在传统召回(协同过滤+双塔)之外,新增一路 LLM 语义召回:用嵌入模型把内容和用户兴趣画像映射到同一语义空间,解决冷启动(新内容没有交互数据但有文本)。排序层用 LLM 生成的特征增强。

关键代码:多路召回融合

pythonCode
def recall(user):
    cf = collab_filter(user, n=200)             # 协同过滤:利用群体行为
    twin = two_tower(user, n=200)               # 双塔:泛化
    sem = semantic_recall(user_profile_emb(user), n=100)  # 语义:救冷启动
    merged = weighted_merge({
        "cf": (cf, 0.4), "twin": (twin, 0.35), "sem": (sem, 0.25),
    })
    return dedup(merged)[:300]

def semantic_recall(profile_emb, n):
    # 新内容一发布就入向量库,无需等交互数据积累,当天即可被召回
    return content_vecdb.search(profile_emb, top_k=n)

指标

| 指标 | 改造前 | 改造后 |

| --- | --- | --- |

| CTR | 4.2% | 5.1% |

| 冷启动内容 24h 曝光率 | 23% | 61% |

| 人均停留时长 | 18 分钟 | 21 分钟 |

| 创作者次月留存 | 44% | 53% |

CTR 提升 0.9 个百分点,在日活百万级的平台上意味着可观的广告与转化增量;冷启动曝光翻倍显著改善了创作者生态。

常见坑

全量替换成 LLM 召回,丢掉协同过滤的群体智慧,热门内容反而推不准。
语义召回权重给太高,推荐同质化(全是"相似内容"),多样性下降。
忽视推荐延迟,语义召回向量检索没做量化和 ANN 索引,P99 飙到 200ms 拖垮首屏。

最佳实践

LLM 语义召回是"补充路"而非"替代路",主要价值在冷启动和长尾;多路召回权重要 A/B 持续调优,别一把梭。

🛡️ 案例十:AI 内容审核——先大模型标注再蒸馏小模型

背景

UGC 平台日均新增内容 500 万条,需审核涉黄、涉暴、辱骂、广告导流等 9 类违规。纯人工审核成本高、标准不一致、审核员心理伤害大。

方案与原理

不能直接用大模型逐条审(成本和延迟都不允许 500 万/天)。策略是"大模型造标注数据 → 蒸馏出小模型跑线上 → 疑难样本回流大模型"。小模型扛住 99% 的量,大模型只处理小模型不确定的部分。

关键代码:置信度分层审核

pythonCode
def moderate(content):
    label, conf = small_classifier(content)     # 蒸馏小模型:毫秒级,扛全量
    if conf >= 0.95:
        return decide(label)                    # 高置信度直接判
    # 灰度区间交给大模型,量小可承受
    big_label, big_conf = big_model_moderate(content)
    log_hard_case(content, small=label, big=big_label)  # 回流,用于下轮蒸馏
    if big_conf >= 0.9:
        return decide(big_label)
    return route_to_human(content)              # 大模型也拿不准才上人工

指标

| 层级 | 承接比例 | 单条成本 | 延迟 |

| --- | --- | --- | --- |

| 小模型 | 94% | 0.0003 元 | 8ms |

| 大模型 | 5.5% | 0.02 元 | 600ms |

| 人工 | 0.5% | 1.5 元 | 分钟级 |

综合下来单条平均审核成本约 0.0045 元,500 万/天总成本约 2.25 万元/天;若全量走大模型则需 10 万元/天,成本降到约 1/4.4。违规召回率 96.8%,误杀率 1.2%。

常见坑

蒸馏数据只覆盖常见违规,对抗性变体(谐音、拆字、图文夹带)漏检严重,需持续对抗样本回流。
小模型置信度阈值定太低,大量样本涌向大模型,成本失控。
不设人工申诉通道,误杀导致创作者投诉激增。

最佳实践

"小模型跑量、大模型兜底、疑难回流蒸馏"形成飞轮,模型越用越准;对抗样本要专门运营,违规者永远在进化。

💰 案例十一:金融风控 AI——反欺诈实时决策

背景

支付机构面临盗刷、套现、洗钱等欺诈,传统规则引擎误杀率高(正常交易被拦)、且规则滞后于新型欺诈手法。目标:降低欺诈损失同时减少误杀。

方案与原理

不是让 LLM 直接判交易(延迟和可解释性都不满足),而是"梯度提升树(GBDT)实时打分 + 图神经网络识别团伙 + LLM 做案件调查辅助"。LLM 用在事后调查、可疑案件的关联分析和报告生成,而非实时链路。

关键代码:实时评分 + 图特征

pythonCode
def risk_score(txn):
    base_feats = extract_features(txn)          # 金额/频次/设备/地理位置
    graph_feats = graph_embedding(txn.account)  # 团伙特征:关联账户风险传播
    score = gbdt.predict(base_feats + graph_feats)   # 毫秒级
    if score > 0.9:
        return "block"                          # 高危拦截
    if score > 0.6:
        return "challenge"                      # 中危:短信验证/人脸
    return "pass"

def investigate(case):
    # LLM 只用于事后调查:关联多源数据,生成可疑点分析报告(非实时)
    evidence = gather_multi_source(case)
    return llm.analyze(evidence, template="反欺诈调查报告")

指标

| 指标 | 规则引擎 | AI 风控 |

| --- | --- | --- |

| 欺诈识别率 | 71% | 89% |

| 误杀率(正常被拦) | 2.8% | 0.9% |

| 决策延迟 | 30ms | 45ms |

| 新型欺诈响应周期 | 2 周(改规则) | 2 天(重训) |

| 案件调查耗时 | 4 小时/件 | 40 分钟/件(LLM辅助) |

年欺诈损失下降约 62%,按原损失 1.2 亿计约挽回 7400 万元;误杀率下降带来的用户体验改善减少了投诉与流失。

常见坑

想让 LLM 上实时链路做欺诈判断,延迟和可解释性(监管要求给出拒绝理由)都不达标。
忽视图特征,单账户看不出问题的团伙作案(养号、分散小额)漏网。
模型不定期重训,欺诈手法迭代后识别率断崖下跌。

最佳实践

实时链路用轻量可解释模型(GBDT/图),LLM 放在事后调查与报告;风控模型要有快速重训流水线应对新型欺诈。

🏥 案例十二:医疗辅助——病历结构化与辅助诊断建议

背景

医院门诊病历大量为自由文本,科研和质控需要结构化数据;医生希望有辅助提醒(如药物相互作用、漏诊风险)。约束:AI 只做辅助不做决策,一切建议需医生确认,且需极强的可溯源性。

方案与原理

病历结构化用微调后的医疗领域模型抽取(主诉、现病史、诊断、用药);辅助诊断走"检索医学知识库 + 大模型推理 + 强制引用指南来源"。绝不允许模型凭空给诊断,必须挂靠权威指南。

关键代码:可溯源的辅助建议

pythonCode
def clinical_assist(record):
    structured = med_extractor(record)          # 结构化抽取
    alerts = []
    # 药物相互作用:走规则库,不靠模型幻觉
    alerts += check_drug_interactions(structured["medications"])
    # 辅助提醒:检索指南 + 模型推理,必须给出指南出处
    guidelines = retrieve_guidelines(structured["diagnosis"])
    suggestion = llm.reason(
        structured, guidelines,
        constraint="每条建议必须标注来源指南名称和条款,无依据不输出"
    )
    return {
        "structured": structured,
        "alerts": alerts,
        "suggestion": suggestion,
        "disclaimer": "以上为AI辅助信息,最终诊断以医生判断为准",
    }

指标

| 指标 | 数值 |

| --- | --- |

| 病历结构化字段准确率 | 93.5% |

| 药物相互作用提醒覆盖率 | 98%(规则库) |

| 医生采纳辅助建议比例 | 41% |

| 单份病历结构化耗时 | 1.8 秒(人工约 6 分钟) |

| 漏诊提醒命中(回顾性评估) | 发现 7% 潜在漏诊 |

病历结构化把科研数据准备周期从数月压缩到数天;辅助提醒在回顾性评估中发现约 7% 的潜在漏诊或用药风险。

常见坑

让 LLM 直接给诊断结论,既有合规风险又有幻觉风险,必须定位为"辅助+需确认"。
药物相互作用这类确定性知识用模型判断,不如直接查权威规则库可靠。
不做可溯源,医生无法核实建议依据,采纳率极低且埋下法律隐患。

最佳实践

确定性知识走规则/知识库,开放性推理走 LLM 但强制引用权威来源;医疗场景 AI 的定位红线是"辅助不替代、建议不决策"。

✍️ 案例十三:营销文案生成——批量生产 + 品牌一致性

背景

品牌方每月需为上千个 SKU 生成商品文案、社媒帖子、投放素材,人工文案团队产能不足且风格不统一。目标:批量生成且保持品牌调性、合规(不夸大宣传)。

方案与原理

用大模型批量生成,关键在于"品牌知识注入 + 合规过滤 + A/B 效果回流"。把品牌调性、禁用词、卖点库沉淀成 prompt 资产和 RAG 知识库,生成后过合规检查,投放数据回流优化 prompt。

关键代码:品牌约束生成 + 合规过滤

pythonCode
BANNED = ["最", "第一", "国家级", "根治", "100%有效"]   # 广告法禁用/极限词

def gen_copy(sku):
    brand = retrieve_brand_kit(sku.brand)       # 调性/卖点/案例
    prompt = build_prompt(sku, brand, tone=brand.tone)
    candidates = [llm.generate(prompt, temperature=0.9) for _ in range(3)]
    valid = [c for c in candidates if compliance_ok(c)]
    if not valid:
        valid = [rewrite_to_compliant(candidates[0])]
    return rank_by_ctr_model(valid)[0]          # 用历史CTR模型预排序

def compliance_ok(text):
    return not any(w in text for w in BANNED) and not exaggerated(text)

指标

| 指标 | 人工 | AI 生成 |

| --- | --- | --- |

| 单条文案产出耗时 | 25 分钟 | 12 秒 |

| 月产能 | 约 800 条 | 约 5 万条 |

| 广告法违规率 | 1.5% | 0.2%(自动过滤) |

| A/B 投放平均 CTR | 3.1% | 3.4% |

| 文案团队规模 | 8 人 | 3 人(审校+策略) |

产能提升 60 倍,文案团队从执行转向策略与审校,合规违规率靠自动过滤降到 0.2% 以下,避免了潜在的广告法处罚风险。

常见坑

不做合规过滤,模型爱用"最""第一"等极限词,直接踩广告法红线。
品牌调性靠一句话描述,生成的文案千篇一律没有品牌辨识度,需构建卖点库和范例库。
生成即投放不做 A/B,白白浪费了可以回流优化的效果数据。

最佳实践

品牌资产(调性/卖点/禁用词/范例)沉淀成可复用的 prompt + RAG 资产;生成后必过合规,投放数据回流形成优化闭环。

📊 案例十四:数据分析 Agent——用自然语言查数据库

背景

业务方(运营、市场)不懂 SQL,每次取数要提需求给数据团队排期,平均等待 1.5 天。目标:让业务方用自然语言直接问数,Agent 自动生成 SQL、执行、出图。

方案与原理

Text-to-SQL 是核心,但裸用模型生成 SQL 准确率低(表结构复杂、字段歧义)。方案:Schema 检索(只给相关表) + few-shot 示例 + SQL 执行前校验 + 错误自修复(执行报错回喂给模型重写)。

关键代码:带自修复的 Text-to-SQL Agent

pythonCode
def nl_to_result(question, max_retry=2):
    tables = retrieve_relevant_tables(question)   # 只给相关表,避免schema过载
    examples = retrieve_similar_examples(question) # few-shot:相似历史问答
    sql = llm_gen_sql(question, tables, examples)
    for attempt in range(max_retry + 1):
        ok, err = validate_sql(sql)               # 语法+权限+防全表扫描校验
        if not ok:
            sql = llm_fix_sql(sql, err); continue
        try:
            rows = db.execute(sql, timeout=30)     # 执行超时保护
            return {"sql": sql, "data": rows, "chart": auto_chart(rows)}
        except DBError as e:
            sql = llm_fix_sql(sql, str(e))         # 执行报错自修复
    return {"error": "无法生成正确查询,请补充描述或联系数据团队"}

指标

| 版本 | 关键改动 | SQL 执行成功率 | 结果正确率 |

| --- | --- | --- | --- |

| V1 | 裸模型生成 | 52% | 44% |

| V2 | +Schema检索 | 71% | 63% |

| V3 | +few-shot示例 | 84% | 78% |

| V4 | +校验与自修复 | 93% | 87% |

取数等待从 1.5 天降到 30 秒,数据团队从"取数工具人"解放出来做数仓建设;业务方自助取数占比达 76%,数据团队临时需求量下降 70%。

常见坑

把全库几百张表的 schema 一次性塞给模型,token 爆炸且干扰生成,必须先做表检索。
不做 SQL 校验就执行,生成的全表扫描/删改语句可能拖垮库甚至误删数据(务必只读权限+防护)。
没有自修复,一次生成失败就返回错误,成功率上不去。

最佳实践

Text-to-SQL 必配"Schema 检索 + few-shot + 执行校验 + 错误自修复"四件套;数据库账号严格只读、加超时和扫描行数限制,把安全兜底做在系统层而非依赖模型自觉。

🧩 十个落地案例横向对比

把后面十个真实落地案例放在一起对比,可以看出跨行业的共性规律:

| 案例 | 核心技术 | 关键指标提升 | 最大的坑 | 通用启示 |

| --- | --- | --- | --- | --- |

| 企业RAG | 混合检索+Rerank | 命中率58%→91% | 只用向量检索漏专有名词 | 检索质量决定RAG上限 |

| 客服机器人 | 意图分流+工具调用 | 自动解决68% | 写操作不设阈值致资损 | 写操作分级授权 |

| 代码助手 | 蒸馏小模型+增量预训练 | 补全采纳31% | 不做增量预训练乱补 | 领域适配比通用能力重要 |

| 文档处理 | VLM抽取+规则校验 | 耗时45s→2.3s | 让模型猜看不清的字段 | 抽取+校验+人工三段式 |

| 智能推荐 | LLM语义召回 | 冷启动曝光23%→61% | 全量替换丢群体智慧 | LLM召回是补充非替代 |

| 内容审核 | 大模型蒸馏小模型 | 成本降至1/4.4 | 对抗变体漏检 | 小跑量大兜底疑难回流 |

| 金融风控 | GBDT+图+LLM调查 | 欺诈损失降62% | LLM上实时链路 | 实时用轻量LLM做事后 |

| 医疗辅助 | 领域微调+强制引用 | 结构化耗时6min→1.8s | 让LLM直接下诊断 | 辅助不替代建议不决策 |

| 营销文案 | 品牌约束+合规过滤 | 产能提升60倍 | 不过滤踩广告法 | 品牌资产沉淀为prompt |

| 数据Agent | Text-to-SQL自修复 | 成功率52%→93% | schema全塞+无校验 | 检索schema+执行兜底 |

贯穿所有案例的六条工程铁律

1.模型只是链路一环,真正决定成败的是"检索+校验+分流+兜底"的系统设计。
2.高频简单任务用小模型/轻量模型扛量,低频复杂任务才动用大模型,成本敏感场景尤其如此。
3.写操作、高风险决策必须设阈值和人工兜底,追求 100% 自动化往往在长尾翻车。
4.强制引用、强制校验、"找不到就说找不到"是压制幻觉最有效的三板斧。
5.建离线评估集跑回归,靠数据驱动迭代,不靠"感觉变好了"。
6.疑难样本回流再训练形成飞轮,模型越用越准,这是长期护城河。

一句话收束这十个案例:AI 落地不是"接个模型 API"那么简单,能不能上线、能不能盈利,取决于围绕模型搭建的那套工程系统——检索、校验、分流、兜底、评估、回流,每一环都不能少。