行业信息助手 — 测试题答案
请先独立作答,再查阅此文件!答案文件对应 [[行业信息助手_学习笔记]] 第十节测试题。
基础概念(理解层)
题 1:RAG 与纯 LLM 问答的本质区别
核心区别:
| 对比项 | 纯 LLM | RAG |
|---|---|---|
| 知识来源 | 训练时的参数记忆 | 外部检索的实时文档 |
| 私有数据 | 无法访问 | 可以访问 |
| 知识更新 | 需要重新训练 | 更新文档库即可 |
| 幻觉风险 | 较高(无依据瞎编) | 较低(有原文依据) |
本项目 RAG 涉及的模块:
document_service.py— 文档解析与分块(PDF/DOCX → 文本片段)embedding_service.py— 文本片段 → 向量(调用 DashScope Embedding API)milvus_service.py— 向量存储与相似度检索retrieval_service.py— 混合检索(向量检索 + 关键词),支持 Reranker 重排序chat_service.py/chat_service_v2.py— 将检索结果拼入 prompt 发给 LLM
题 2:为什么需要 6 个 Agent
原因:单一 LLM 的局限性
一次 LLM 调用需要同时完成:搜索判断、信息提取、代码编写、逻辑写作、质量评估——这些任务对模型的提示词、输出格式、温度参数需求截然不同,放在一起会互相干扰,质量下降。
专业分工的好处:
- 提示词专精:每个 Agent 的 system prompt 针对单一任务优化
- 参数调优:DataAnalyst 用低温(0.3)保证数据准确,LeadWriter 用高温(0.7)保证文章流畅
- 并行潜力:搜索和数据提取可以并行执行
- 质量可控:CriticMaster 独立审核,避免「自己写自己评」的偏差
- 职责清晰:出错时容易定位到哪个 Agent 的问题
题 3:SSE vs WebSocket,为什么选 SSE
区别:
| 对比项 | SSE | WebSocket |
|---|---|---|
| 方向 | 单向(服务端 → 客户端) | 双向 |
| 协议 | HTTP/1.1 | 独立协议(需握手升级) |
| 重连 | 浏览器自动重连 | 需手动实现 |
| 复杂度 | 简单 | 较复杂 |
| 适用场景 | 推送通知、进度流 | 实时聊天、游戏 |
本项目选 SSE 的理由:
深度研究是单向流——用户发起请求后,只需要接收服务端的进度更新,不需要在研究过程中向服务端发消息。SSE 恰好满足这个场景,实现更简单,且 FastAPI 原生支持 StreamingResponse,无需额外依赖。
题 4:Checkpoint 解决的问题与双状态设计
解决的问题: 深度研究耗时可能长达数分钟。网络中断、浏览器关闭或服务器重启都会导致研究进度全部丢失,用户不得不重新开始。
两种状态分开存储的原因:
| 状态 | 字段 | 内容 | 恢复时用途 |
|---|---|---|---|
| 后端状态 | state_json | 已收集的事实、搜索结果、各 Agent 输出 | 让 LangGraph 从中断的节点继续执行 |
| 前端状态 | ui_state_json | 进度条、已展示的中间结果、搜索关键词列表 | 恢复页面显示,让用户看到之前的进展 |
分开设计的原因:后端状态是执行逻辑的依据,前端状态是展示的依据,二者结构和用途不同。如果混在一起,后端 Agent 需要了解 UI 细节,违反关注点分离原则。
架构分析(分析层)
题 5:完整深度研究请求的数据流
| |
题 6:不同 Agent 温度参数的设计原因
温度(temperature)的含义: 控制 LLM 输出的随机性。温度越低,输出越确定、保守;温度越高,输出越多样、有创意。
| Agent | 温度 | 任务性质 | 为什么这个温度 |
|---|---|---|---|
| DataAnalyst | 0.3(低) | 提取数字、验证事实 | 数据提取需要准确性,不能「创意性地」修改数字,低温减少幻觉 |
| CodeWizard | 0.3(低) | 生成 Python 代码 | 代码必须语法正确、逻辑严谨,高温会产生莫名其妙的代码 |
| DeepScout | 0.5(中) | 搜索策略、关键词生成 | 需要一定创意来发散搜索角度,但也不能太离谱 |
| CriticMaster | 0.5(中) | 质量评审 | 评审需要客观,但也需要一定灵活性来识别不同类型的问题 |
| ChiefArchitect | 0.7(高) | 规划大纲 | 大纲设计需要创意和发散性思维,不同研究问题需要不同结构 |
| LeadWriter | 0.7(高) | 撰写报告 | 写作需要流畅自然的语言,低温会导致文章生硬重复 |
题 7:Redis 和 Milvus 的职责,能否互换
Redis 的职责:
- 缓存频繁查询的数据(新闻、搜索结果)
- 存储「取消研究」标志位(
cancel_flag:{session_id}) - 用户 Session 缓存
- 本质:键值缓存,适合存小型结构化数据,读写极快
Milvus 的职责:
- 存储文档的向量表示(高维浮点数组)
- 执行向量相似度搜索(ANN 近似最近邻)
- 本质:向量数据库,专为高维向量的相似度计算优化
能否互换?不能。
- Redis 不支持高维向量的相似度检索(没有 ANN 索引)
- Milvus 不适合做普通键值缓存(查询方式是向量相似度,不是精确键查找)
- 两者解决的是完全不同的问题
题 8:为什么提供 GET 版本的研究接口
GET /research/stream 的存在原因:
- 浏览器测试便利:在浏览器地址栏直接输入 URL 就能测试 SSE 流,不需要 Postman 或代码
- EventSource 限制:浏览器原生的
EventSourceAPI 只支持 GET 请求,如果前端想用原生 EventSource(而非 fetch + ReadableStream),必须提供 GET 接口 - 开发调试:GET 接口方便开发者快速验证 SSE 连接是否正常
补充本项目前端可能使用
fetch+ReadableStream来消费 POST 的 SSE,GET 版本主要是调试用途。
代码理解(应用层)
题 9:新增 LegalAnalyst Agent 需要修改的文件
至少需要修改以下 5 个位置:
1. 新建 Agent 文件
backend/app/service/deep_research_v2/agents/legal_analyst.py
| |
2. agents/__init__.py
| |
3. config/llm_config.py
| |
4. service/deep_research_v2/state.py
| |
5. service/deep_research_v2/graph.py
| |
题 10:用 Valtio 设计研究进度状态
| |
| |
在 React 组件中使用 useSnapshot 订阅:
| |
扩展思考(评估层)
题 11:迭代次数改为 5 可能带来的问题
潜在问题:
成本爆炸:每次迭代调用 DeepScout(搜索 API)+ DataAnalyst + LeadWriter(16000 token)+ CriticMaster。5 轮迭代的 token 消耗和 API 费用可能是 1 轮的 4-5 倍。
时间过长:每轮至少几十秒,5 轮可能需要 5-10 分钟,用户体验极差,且 HTTP 连接超时风险大。
收益递减:CriticMaster 的评分标准是 LLM 给的,本身有随机性。第 2-3 次迭代的改进可能边际效益很小,第 4-5 次甚至可能因为「过度修改」质量下降(LLM 漂移问题)。
无限循环风险:如果 CriticMaster 评分标准不稳定(比如对同一报告时而 5.5 时而 6.5),在阈值附近来回震荡,可能把 5 次迭代全部用光。
内存压力:每次迭代累积的 state(搜索结果、事实列表)越来越大,可能超出 LLM 上下文窗口或服务器内存(已有
MEM_LIMIT=8G限制)。
建议的改进方案: 保留迭代次数上限,但加入「改进量阈值」:如果两次评分差 < 0.5,即使未达到 6.0 也提前终止,避免无效迭代。
题 12:stock_mapping.py 硬编码的局限与改进方案
硬编码的局限性:
- 维护困难:新上市公司、公司改名、退市需要手动更新代码并重新部署
- 覆盖不全:目前只有约 90 家公司,A 股 5000+ 家公司无法全覆盖
- 无版本管理:不知道映射数据是什么时候的,是否过时
- 无容错:公司名称有多种写法(“华为” vs “华为技术”),硬编码无法处理模糊匹配
更好的设计方案:
方案一:数据库表 + 定期同步
| |
- 通过股票数据 API(如聚合数据)每天定时同步全量数据
- 查询时走数据库,支持模糊匹配(
LIKE或全文检索)
方案二:调用专业金融 API
- 实时查询第三方股票数据服务(如通达信、同花顺 API)
- 不需要本地维护映射,始终最新
- 缺点:依赖外部服务,有 API 成本
方案三:LLM 辅助 + 缓存
- 用 LLM 识别公司名称并返回股票代码
- 结果缓存到 Redis,下次直接用
- 适合长尾场景(小众公司)