这次分享一下对话与推荐系统、Function Calling、Grep相关知识。

1. 对话系统

核心定义:对话系统是通过自然语言实现人机交互的人工智能系统,旨在模拟人类对话能力。

1.1 常见对话系统

对话系统主要分为任务导向型、开放域闲聊型、混合型三种类别。

a. 任务导向型对话系统

  • 目标: 帮用户解决具体问题、达成特定任务(如:订机票、查天气、预约会议、导航)。

  • 特点: 追求高效、精准、短路径。用户希望用最少的对话轮数把事情办成。

  • 核心机制: 槽位填充(Slot Filling)。比如订机票,系统必须集齐“出发地”、“目的地”、“出发时间”这几个核心信息(槽位),缺了就得主动追问。

b. 开放域闲聊型对话系统

  • 目标: 情感陪伴、娱乐、闲聊(如:小冰、早期的 Siri 闲聊模式)。

  • 特点: 追求上下文连贯性、趣味性、共情能力。没有明确的终点,对话轮数越多越好。

  • 核心机制: 开放域生成(Open-Domain Generation),更依赖于模型对宏大知识库和人类情感的建模。

c. 混合型对话系统

  • 目标: 结合任务导向型系统和闲聊型系统的优势,既能高效完成特定任务,又能进行自然流畅的开放式对话,并实现两种模式间的无缝切换。。

  • 特点: 高智商(完成任务)与高情商(情感陪伴、丝滑过渡)有机结合,让机器的表现更像一个真正的“人”。

  • 核心机制: 采用多模块架构:任务模块处理结构化请求,对话模块管理开放话题,上下文理解模块确保交互连贯性。使系统响应准确率提升40%以上(对比单一模式系统)。

1.2 任务型对话核心模块

在 LLM(大语言模型)爆发之前,工业界最成熟、架构最清晰的是任务型对话系统。它像一条精密的流水线,由四个核心模块串联而成:

  1. 自然语言理解 (NLU, Natural Language Understanding)

    • 输入: 用户的原始文本(文本或语音转文字)。

    • 输出: 结构化的语义表示。它主要干两件事:

      • 意图识别 (Intent Detection): 搞清楚用户想干嘛。比如输入“帮我买一张明天去北京的机票”,意图就是 book_flight

      • 槽位填充: 抠出关键信息。比如提取出 date="明天"destination="北京"

  2. 对话状态跟踪 (DST, Dialogue State Tracking)

    • 作用: 系统的“短期记忆”。在多轮对话中,用户的信息是陆陆续续给出的,甚至会中途修改。DST 的任务就是把当前轮次的新信息和历史记忆整合起来,维护一个最新的“全局状态字典”。

  3. 对话策略选择 (DP, Dialogue Policy)

    • 作用: 系统的“决策大脑”。基于 DST 提供的当前状态,决定系统下一步该做出什么动作(Action)

    • 决策示例:

      • 如果信息不全\rightarrow 执行“追问”动作(“请问您从哪里出发?”)。

      • 如果信息齐全\rightarrow 执行“调用外部API/数据库查询”动作。

      • 如果查到了结果\rightarrow执行“向用户展示结果”动作。

  4. 自然语言生成 (NLG, Natural Language Generation)

    • 作用: 把大脑做出的抽象动作决策,翻译成人类能听懂的自然语言文本。传统上多使用模板(如:"已为您找到从{NLU.departure}到{NLU.destination}的机票..."),现在更多使用生成式模型。

小结: NLU(听懂)\rightarrowDST(记住) \rightarrow  Policy(决策)\rightarrow  NLG(表达)。这就是经典的 Pipeline(流水线)架构。

1.3 基于LLM的对话系统

大语言模型(如GPT-3/4)通过1750亿参数实现上下文理解,替代了传统基于规则和模块化的NLP处理方式。

核心优势:零样本学习能力使LLM无需领域数据微调即可完成对话任务,相比传统方法节省90%开发周期。

特性 传统 Pipeline 架构 LLM Agent 架构(端到端/大模型)
构建复杂度 高。需要分别训练 NLU、DST 模块,模块间存在误差传递(NLU 错了,后面全盘皆输)。 低。通常一个大模型(End-to-End)直接搞定输入到输出。
语义理解能力 弱。极度依赖人工定义的规则或有限的训练标签,遇到非规范表达容易崩。 极强。具备强大的泛化和推理能力,能轻松理解复杂的长句、隐喻和上下文。
工具调用 (Tool Using) 刚性。需要工程师硬编码 API 的触发条件和参数绑定。 弹性。通过 Function Calling(函数调用) 或 ReAct 框架,大模型能自己判断何时该查数据库、如何拼接参数。
可控性与确定性 极高。行为完全在预设的业务逻辑和模板内,不会胡言乱语。 较低。存在**幻觉(Hallucination)**风险,可能会生成看似合理但完全错误的内容。

现代 LLM 对话系统的核心:Agent 架构

现在的对话系统不再单纯依靠问答树,而是演变成了 AI Agent(智能体)。它的公式通常是:

Agent=Large Language Model (LLM)+Memory (记忆)+Tools (工具/RAG)+Planning (规划能力)

  • Memory(记忆): 利用向量数据库或特定 Window 机制管理长短期记忆,取代了传统的 DST。

  • Tools(工具): 结合 RAG(检索增强生成) 动态调取外部 PDF 文档、本地知识库或第三方 API,解决大模型的幻觉和时效性问题。

2. 推荐系统

系统定义:推荐系统是信息过滤系统的子集,通过算法预测用户对物品的“偏好“或“评分“,广泛应用于电商、流媒体等领域。

核心任务:是解决信息过载:从海量的商品、视频或文章中,精准挑选出用户当前最感兴趣的极少部分内容,呈现在用户的屏幕上。

2.1 推荐系统的三大经典演进范式

推荐系统的技术发展,本质上是不断挖掘“用户(User)”与“物品(Item)”之间关联的过程。

范式一:基于协同过滤(Collaborative Filtering, CF)——“人以群分,物以类聚”

这是推荐系统最经典的基石算法,分为两种思路:

  • 基于用户的协同过滤 (User-CF): 发现和你口味相似的“互联网搭子”。系统发现你和用户 A 都喜欢看科幻片和动作片,最近用户 A 追了一部新出的悬疑片,系统就会把这部悬疑片也推荐给你。

  • 基于物品的协同过滤 (Item-CF): 发现物品之间的绑定关系。系统发现喜欢看《星际穿越》的人,通常也极大概率会看《盗梦空间》,如果你刚刚看了前者,系统就会无脑推送后者(电商的“经常一起购买”也是这个原理)。

范式二:基于内容/特征的推荐(Content-Based / Deep Learning)——“打标签”

协同过滤有个致命缺点:如果是一个全新上架的商品,没有任何人买过它,它就永远无法被推荐(冷启动问题)。 于是系统开始拆解物品和用户的特征:

  • 物品特征: 这是一部“动作、硬汉、科幻、2026年上映”的电影。

  • 用户特征: 这是一个“男性、25岁、程序员、喜欢吴京”的用户。

  • 深度学习时代(如经典的 Wide & DeepDeepFM 模型),算法把成百上千个这样的特征组合起来,丢进神经网络,预测用户点击这个物品的概率(CTR 预估)。

范式三:大模型时代的语义与图推荐(LLM / Graph-based)——“深层理解”

现在的推荐系统开始引入知识图谱(Knowledge Graph)和大语言模型(LLM)。系统不再死板地匹配标签,而是能理解:因为你喜欢“赛博朋克”,所以不仅会喜欢《赛博朋克2077》游戏,还可能对科幻小说《仿生人会梦见电子羊吗?》感兴趣。

3. Function Calling

核心:它是大模型厂商(如 OpenAI、Anthropic、Google 等)在底层对模型进行专门的微调(Fine-tuning),让模型建立了一种强烈的意识——“当用户提问超出我的能力时,我不应该瞎编,而是应该输出一套极其标准、严谨的 JSON 格式参数,交给后台代码去执行。”

有了 Function Calling,大模型不仅能陪你聊天,还能在需要的时候,自己决定去调用外部的代码、API 或数据库,获取真实数据或执行具体任务

3.1 流程闭环

Function Calling 并不是大模型“自己去运行代码”,它是一个大模型与你的后台服务器(代码环境)来回交接的四步闭合回路。

我们用一个“查询特定城市天气”的例子来拆解这个过程:

第一步:定义工具,扔给大模型

作为开发者,你需要先写好一个查询天气的本地函数 get_current_weather(location, unit)。然后,你用 JSON Schema 的格式把这个函数的“说明书”和用户的提问一起打包发给 LLM。

告诉 LLM: “我有这个工具,名字叫 get_current_weather,它需要一个参数叫 location。现在用户说:‘帮我看看北京今天热不热?’”

第二步:大模型决策,返回“参数解构”

LLM 读了用户的输入,又看了工具说明书,发现自己算不出北京现在的温度,但工具可以。于是它中止文本生成,不返回聊天普通文本,而是返回一个标准的 JSON 结构:

{
  "name": "get_current_weather",
  "arguments": "{\"location\": \"Beijing\"}"
}

第三步:本地代码执行工具

你的后台服务器接收到了 LLM 返回的这个 JSON。你的代码解析它,并真正跑了一行代码get_current_weather("Beijing")。 函数向气象局 API 请求,拿到了真实结果:{"temperature": "32°C", "weather": "Sunny"}

第四步:把结果喂回大模型,生成人话

你把这个真实的天气数据(Context)再次打包发给 LLM。LLM 这下心里有底了,它结合这个真实数据,用自然语言回复用户:

LLM 回复: “北京今天是个大晴天,温度有 32°C 呢,体感还是挺热的,出门记得防晒哦!”

3.2 常见问题与解决方案

难点一:工具太多导致模型“眼花”(工具过载与上下文爆炸)

  • 痛点: 比如在一个企业 ERP 系统中,可能有几百个 API(查库存、批假、报销等)。如果你把几百个工具的 JSON Schema 一股脑全塞进 Prompt 里,会导致:1. Token 费用飙升;2. 大模型注意力分散,极容易选错工具或传错参数。

  • 工业级解法:工具检索(Retrieval-Augmented Tool Selection) 不直接传所有工具。在最前端架设一个轻量级的向量数据库(向量模型如 bge、text-embedding)。把几百个工具的描述做成向量。用户说话后,先去向量库里检索出最相关的 3~5 个工具,然后动态拼进 Prompt 发给大模型。

难点二:大模型“胡言乱语”(幻觉调用与参数格式错误)

  • 痛点: 大模型有时会凭空捏造一个不存在的参数,或者把本来需要传入数字 26 的地方传成了字符串 "二十六度",导致后台 Python 代码直接抛出 KeyErrorTypeError 崩溃。

  • 工业级解法:Pydantic 强校验与结构化输出(Structured Outputs)

    在代码中绝对不能直接用 json.loads() 裸奔。工业界通常配合 Pydantic 库。现在主流的大模型接口(如 OpenAI)支持 response_format={"type": "json_schema", ...}。在本地接收到数据后,必须通过 Pydantic 模型的 ValidationError 捕获异常。如果出错,自动写一个 Error Prompt 弹回给大模型让它自纠(“你刚刚传的参数类型不对,请重新生成”),通常 1 次 Self-Correction 就能修复。

难点三:用户话没说全(反向追问机制)

  • 痛点: 工具 get_weather(location) 要求必须有 location 字段。但用户只说:“帮我查查天气。”这时候大模型无法调用工具。

  • 工业级解法:槽位检查与意图锁死(Slot Filling Loop) 利用大模型强大的理解力,在 System Prompt 中硬性规定:“如果用户没有提供必填参数,你绝对不能调用工具,而必须向用户反向提问,引导其说出缺失的参数。”或者在后台使用有限状态机(FSM)拦截,当大模型未能触发 Tool Call 且缺少必要参数时,由业务代码强制下发追问文本。

4. Grep与RAG

grep 是 Linux 操作系统中一个经典的命令行工具,它的全称是 G_lobal Regular E_xpression P_rint

  • 工作原理: 它像一个拿着放大镜的质检员,从头到尾、逐行扫描你指定的文件。只要某一行文本完全包含你输入的字符串(或符合你写的正则表达式),它就把这一行抓取出来打印到屏幕上。

  • 核心特点: * 死板但绝对精准: 它不理解文字的意思。你搜 "apple",它能找到苹果公司的 iPhone,也能找到冰箱里的苹果,但它绝对找不到 "iPhone" 或 "fruit"(除非你明确写了这些词)。

    • 速度极快,无需训练: 直接在底层对二进制或文本流进行匹配,不需要高昂的算力,也不需要建索引。

特性维度 Grep (传统字面检索) RAG (AI 语义检索增强)
匹配机制 字面匹配 / 正则表达式。必须一模一样(或符合规则模式)。 语义匹配(Embedding 向量空间距离)。理解概念、同义词和上下文。
对模糊问题的处理 完全无能为力。输入“心情不好看点啥”,若文档里没有这几个字,就查不到任何东西。 游刃有余。能理解“心情不好”代表需要安慰,从而检索出治愈系电影或心理疏导文档。
输出结果 原始文本行。搜到什么就原封不动地吐出哪几行。 定制化自然语言。把分散在各个角落的知识融会贯通,直接给出一篇条理清晰的总结回答。
系统复杂度 极低。一个单机命令,几毫秒就能搞定。 。包含文档切片、向量化(Embedding)、向量数据库、大模型推理(LLM)等多个环节。
数据更新成本 零成本。文件改了,重新跑一次 grep 就能拿到最新结果。 有成本。文档更新后,需要重新切片并计算向量(Upsert 到向量数据库)。

在RAG的混合检索中对于专有名词的检索常常用Grep/BM25技术,精准锁定文档位置。

为什么现在的代码agent都是用Grep去检索?

1. 速度差异:Grep 是毫秒级,向量 RAG 是秒级

在编写代码时,开发者对延迟(Latency)的容忍度极低。AI 助手必须在几百毫秒内给出提示,否则就会打断开发者的思路。

  • 如果用向量 RAG: 当你在编辑器里写了一句代码,AI 想要寻找相关上下文。它需要先将你当前的代码片段发送给 Embedding 模型(消耗网络请求时间,约 50-200ms),然后再去向量数据库里进行数学计算(距离检索)。整个过程下来,通常需要几百毫秒甚至几秒

  • 如果用 Grep(如 ripgrep): ripgrep 是用 Rust 编写的,它利用了多线程、内存映射(Mmap)和高级正则表达式引擎。它可以在几毫秒内,直接在本地榨干 CPU,横扫你整个包含几万个文件的本地项目工程。 这种瞬时响应的速度,向量检索在单机环境下根本无法比拟。

2. 核心痛点:代码是“绝对精确”的,语义模糊会引发灾难

这正是 Grep 的“死板”在代码领域变成“神技”的核心原因。代码是一门高度崇拜符号和精确性的语言。

  • 专有名词与调用链: 假设你在重构代码,想要找到定义了 userService.getUserById_v2 的地方。

    • 向量 RAG: 它通过语义理解,可能会觉得 userService.fetchUser(id) 或者 adminService.getUser 和你的目标“语义很近”,从而把它们作为上下文捞出来喂给大模型。

    • 大模型拿到错误上下文: 就会开始瞎编,生成错误的方法调用。

    • Grep: 字符严格匹配。有就是有,没有就是没有。它能百分之百精准地帮你把所有出现过 getUserById_v2 的行和文件找出来。对于重构、跳转定义、查找引用,字面匹配是绝对的刚需。

3. 动态代码库的“实时性”要求

你的本地代码库是动态的、随时在变的。你可能刚删了一行函数,又新写了一个类。

  • 如果用 RAG,这意味着每一次你敲击键盘修改代码,系统都需要在后台对修改的文件重新切片、重新跑 Embedding、重新更新向量数据库。这种高频的增量更新会消耗大量的本地 CPU 和内存。

  • 而 Grep 不需要任何“预训练”或“索引建立”,它是直接读取当前磁盘上的最新文件。你刚改完代码,Grep 下一毫秒就能搜到最新结果。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐