03-M3-基础RAG-检索大模型让系统开口回答

检索 + 大模型,让系统「开口回答」:基础 RAG 落地实测(M3)

系列 :城市管理 Agentic RAG ------ 从零搭建城市管理问答系统

本篇 :M3 · 基础 RAG(实测版)

源码https://gitee.com/Chester_Xue/city-agentic-rag


一、M2 之后还差什么?

M2 做完,系统会「找」了:问「暴雨」能捞回 3 段相关文本。但捞回来的是碎片,不是人话:

text 复制代码
气象局 预警等级.txt: 橙色:3小时内降雨量将达50毫米以上...
应急局 防汛应急响应预案.txt: Ⅲ级:暴雨红色预警,或城区内涝积水点超过 20 处...

市民要的是:「暴雨橙色预警怎么响应? 」------一句完整、有条理、带出处的回答。这就是 M3:检索增强生成(RAG)

上一版 M3 文章是设计稿(当时代码还没写)。现在代码落地了,这篇是实测版:三个文件、一个闭环、两条真实验收问答,全部真实运行输出。

二、RAG 在干什么(大白话版)

把大模型想象成一个知识渊博但记性差的专家 。你直接问他,他可能凭印象瞎说(这就是「幻觉」)。RAG 的做法是:先查资料,把资料塞给他,让他照着资料答

text 复制代码
你的问题
   │
   ▼
[检索] 从 Chroma 捞出最相关的 3 段资料(M2 已就绪)
   │
   ▼
[拼接] 问题 + 资料 + 指令 = Prompt
   │
   ▼
[生成] 大模型照着资料回答,标注引用来源

关键点:大模型只负责「组织语言」,不负责「回忆知识」------知识来自检索到的真实资料,从根上抑制幻觉。

三、落地:三个文件,一个闭环

1. retriever.py:把「能查」包装成「好查」

M2 的 db.query() 已经能查了,M3 包一层更语义化的接口,顺便把「来源标注」格式化好:

python 复制代码
def retrieve(query: str, top_k: int = 3,
             dept_filter: str | None = None) -> list[dict]:
    """检索与 query 最相关的文本块。"""
    db = ChromaVectorDB()
    return db.query(query, top_k=top_k, dept_filter=dept_filter)

def format_context(hits: list[dict]) -> str:
    """把检索结果拼成 Prompt 上下文,每段前标注来源。"""
    blocks = []
    for i, hit in enumerate(hits, 1):
        meta = hit.get("metadata", {})
        dept = meta.get("dept", "未知部门")
        fname = meta.get("file", "未知文件")
        blocks.append(f"[{i}] (来源:{dept}/{fname})\n{hit['content']}")
    return "\n\n".join(blocks)

两个细节:

  • dept_filter 参数是为 M4 的 Agentic 路由预留的通道------路由判断「这是气象问题」,检索就只查气象,省得拿民政的资料凑数。
  • format_context 给每段资料贴上「来源:部门/文件名」的标签,大模型回答时就能直接引用------这是让回答可溯源的关键一步

2. generator.py:LLMClient,可替换的大模型客户端

大模型接入做成一个类,配置全部走 .env(项目 M0 就留好了位子):

python 复制代码
class LLMClient:
    def __init__(self, api_type: str = "deepseek"):
        self.api_type = api_type          # 可扩展: openai / qwen / claude
        self.api_key = os.getenv("API_KEY", "").strip()
        self.base_url = os.getenv("API_BASE_URL", "https://api.deepseek.com/v1")
        self.model_name = os.getenv("MODEL_NAME", "deepseek-chat")
        if not self.api_key:
            raise ValueError("未配置 API_KEY:请复制 .env.example 为 .env 并填入 API Key")

    def chat(self, prompt: str, temperature: float = 0.3,
             max_tokens: int = 1024) -> str:
        """调用大模型补全对话。"""
        from openai import OpenAI
        client = OpenAI(api_key=self.api_key, base_url=self.base_url)
        resp = client.chat.completions.create(
            model=self.model_name,
            messages=[{"role": "user", "content": prompt}],
            temperature=temperature,
            max_tokens=max_tokens,
        )
        return resp.choices[0].message.content.strip()

选型说明:

  • 为什么 DeepSeek :走 OpenAI 兼容协议,openai 库改个 base_url 就能用;中文能力强、价格便宜,原型阶段首选。
  • 为什么温度设 0.3:问答场景要「稳」不要「飘」,低温度减少发散。
  • 为什么构造时就校验 key.env 没配好,启动第一秒就报错,而不是跑十轮问答才在中间炸掉。

.env 长这样(API_KEY 填你自己的,gitignore 已忽略,不会提交):

text 复制代码
API_KEY=sk-your-key-here
API_BASE_URL=https://api.deepseek.com/v1
MODEL_NAME=deepseek-chat

3. Prompt 模板:RAG 的效果一半在 Prompt

模板只有四行,两条硬约束:

text 复制代码
你是城市应急分析助手。根据以下从各部门检索到的资料回答问题。
必须标注引用来源(部门+文件名)。信息不足请明确告知。

参考资料:
{context}

问题:{question}
  • 必须标注引用来源------回答自带出处,可溯源、可核验,用户能自己点开原文核对
  • 信息不足请明确告知------防止模型硬编造,这是 M5「答案反思」的前置习惯

4. cli.py:把链路串起来

bash 复制代码
python -m src.cli
text 复制代码
====================================================
  城市应急 Agentic RAG · 基础问答(M3)
  输入问题开始问答,输入 exit/退出 结束
====================================================

❓ 问题 > 

输入 → 检索 → 拼 Prompt → 生成 → 打印(含引用),一个最简单的可用闭环。三条退出命令 exit / quit / 退出 随便用。

四、真实验收:两条问答,全部带来源

推进表 M3 的验收标准是两条跨部门问答,我们逐个实测。

验收 1:问民政问题

复制代码
❓ 问题 > XX街道独居老人有多少?

检索侧命中(真实输出):

text 复制代码
[0.7603] 民政局/弱势群体统计.txt
[0.6438] 民政局/养老服务设施.txt
[0.5048] 民政局/低保救助政策.txt

前 3 条全来自民政局,没有一条串到别的部门。生成侧回答(节选):

text 复制代码
💬 回答:
XX街道独居老人为 156人(来源:民政局/弱势群体统计.txt)。
若您需要其他街道或更近期的数据,当前资料不足,建议补充更新信息。

📎 引用来源:
   - 民政局/弱势群体统计.txt
   - 民政局/养老服务设施.txt
   - 民政局/低保救助政策.txt

返回民政数据 + 来源,验收通过。

验收 2:问气象/应急问题

复制代码
❓ 问题 > 暴雨橙色预警怎么响应?

检索侧命中(真实输出):

text 复制代码
[0.7419] 气象局/预警等级.txt
[0.7125] 应急局/防汛应急响应预案.txt
[0.6494] 气象局/预警信号发布规范.txt

生成侧回答(节选):

text 复制代码
💬 回答:
根据现有资料,暴雨橙色预警的响应要求如下:

1. 响应启动条件:气象部门发布暴雨橙色预警时,启动Ⅳ级应急响应
2. 具体响应动作:
   - 各成员单位到岗值班
   - 抢险队伍集结待命
   - 避难场所开放
   - 危险区域群众转移(橙色及以上强制转移)
3. 预警发布渠道:市气象台官方微博、微信公众号、应急广播、短信平台
   (依据[1]气象局/预警等级.txt、[2]应急局/防汛应急响应预案.txt、
   [3]气象局/预警信号发布规范.txt)

信息不足说明:资料未明确橙色预警对应的具体降雨量数值、强制转移的
具体人数范围,需结合原始文件核对。

📎 引用来源:
   - 气象局/预警等级.txt
   - 应急局/防汛应急响应预案.txt
   - 气象局/预警信号发布规范.txt

返回气象数据 + 来源,验收通过。

注意回答末尾的「信息不足说明 」------这是 Prompt 硬约束在起作用:模型主动承认资料里没写清楚的东西,而不是编一个数字糊弄你。「不编」比「答对」更难,RAG 的价值就在这里。

五、踩过的坑

坑 1:API Key 是拦路虎

.env.example 从 M0 就有,但 API_KEY 一直是占位符。M3 开工第一件事就是填真 key------没有它,生成环节整个跑不起来。教训:M0 就把 key 申请好,别拖到里程碑当天。

坑 2:模型偶尔「过度谨慎」

验收 1 第一次跑,模型开头先说「无法直接查询到 XX 街道独居老人的具体人数」,绕了一大圈最后才给出 156 人。数据明明完全匹配,它却先自我怀疑。这是大模型「过度对齐」的典型表现------prompt 里把「必须基于资料直接回答」写得再明确一点,或者把检索分数(0.76 已经很高)作为置信度提示给它,能缓解。这个问题 M5「答案反思」阶段会系统处理。

坑 3:别小看格式化的细节

format_context 里给每段加 [1] (来源:部门/文件名) 前缀,看起来只是排版,实际是让模型回答时能指着编号说「依据1------引用格式立刻规范了。Prompt 工程的胜负手往往在这些细节里。

六、为 M4/M5 铺的路

M3 是「地基」模块,几个设计决策直接服务后面的 Agentic 阶段:

  • retrieverdept_filter 参数 → M4 路由的落点:路由说「这是气象问题」,检索就只查气象
  • Prompt 里「标注来源」的硬约束 → M5 反思时对照核查的依据:回答和引用逐条比对,编没编一目了然
  • LLMClient(api_type=...) 可替换 → 换模型不改业务代码,二期接国产大模型零成本

七、下一步

系统现在能「查」能「答」了,但有个尴尬:问「暴雨橙色预警怎么响应」,它把气象、民政、热线的资料全捞出来一起答------检索是盲目的,没有「意图」概念。下一步 M4:Agentic 路由,让系统先判断「这是个气象问题」,再决定查哪个部门。

下一篇 :[M4 Agentic 路由:让每个问题找到对的部门](#M4 Agentic 路由:让每个问题找到对的部门)

上一篇 :[M2 切分与向量化:让机器「看懂」城市知识](#M2 切分与向量化:让机器「看懂」城市知识)

系列目录 :[城市管理 Agentic RAG ------ 从零搭建城市管理问答系统](#城市管理 Agentic RAG —— 从零搭建城市管理问答系统)


📚 想了解更专业的内容? 本文是项目实战记录。如果你对 RAG 的原理、Prompt 工程技巧、大模型 API 接入的完整方案感兴趣,欢迎访问我的 CSDN 专栏 喵本喵叁肆的 Agentic RAG 实战专栏 阅读完整的技术博客系列(含可运行代码、架构图与验收标准)。

相关推荐
九硕智慧建筑一体化厂家4 天前
从照明节能到碳排可见:能碳平台助力机房迈向 “零碳” 未来
运维·笔记·智慧城市
龙亘川6 天前
综合执法一体化数字管控平台介绍
大数据·人工智能·安全·智慧城市
招风的黑耳6 天前
我国牵头制定智慧城市国际标准发布:从“跟跑“到“领跑“,意味着什么?
人工智能·智慧城市
九硕智慧建筑一体化厂家7 天前
数字化管控落地,直流照明构筑安全照明体系
运维·人工智能·笔记·安全·智慧城市
九硕智慧建筑一体化厂家8 天前
打破系统孤岛!IBMS 一体化平台,构建智慧楼宇全域管控体系
运维·网络·人工智能·笔记·智慧城市
leoZ23110 天前
Vue3 还原一个企业级后台-01-项目背景与选题
图像处理·人工智能·chatgpt·智慧城市·边缘计算·openvino·dreamfusion
2601_9628601510 天前
智慧城市企业 2027 排期:LEAP East 香港站同时验证亚太与沙特需求
人工智能·智慧城市
九硕智慧建筑一体化厂家10 天前
交通基础设施直流照明系统,隧道与轨道交通照明新方案
运维·人工智能·智慧城市
九硕智慧建筑一体化厂家11 天前
从电费支出到碳资产收益,直流照明如何让商业楼宇实现柔性用能
运维·人工智能·智慧城市