公司动态

AI重构调研:用RAG+Agent搭建智能调研助手

📅 2026/8/29 6:49:13
AI重构调研:用RAG+Agent搭建智能调研助手
AI 对调研行业的冲击已经不再停留在概念层面。近期业内流传最广的一个对比是一个只有 60 人左右的 AI 团队估值做到 20 亿美元而一家拥有 4 万人规模的调研行业巨头市值只有 34 亿美元。无论具体数字在不同时间、不同报道中如何变化这种对比都指向同一个信号调研生意的成本结构正在被 AI 重写。这里的“调研”不是单指问卷调查而是市场调研、行业研究、竞品分析、投研报告、咨询报告等一系列以“收集信息、分析信息、形成判断、产出可读结论”为核心的工作。传统模式下这类业务拼的是人头研究员找资料、分析师做表格、顾问写报告流程长、成本高、交付慢。大模型出现之后其中大量环节变成了可编程的自动化任务“小团队高估值、大团队低市值”的反差正是由此而来。下文先解释为什么 AI 能重构调研行业再带读者用一个 RAG Agent 的最小工程搭出一个“输入主题、输出带引用调研报告”的 AI 调研助手。你会看到它涉及哪些组件、每个参数怎么调、结果怎么验证、报错怎么排查以及真正投入生产时还要补哪些能力。学完这套思路可以把它迁移到行业知识库、竞品监控和报告生成等场景。1. 先看一条主线调研行业为什么会被 AI 重做1.1 传统调研的核心成本是人一份典型的行业调研报告从需求到交付通常要经过这些环节需求沟通确认客户到底要回答什么问题、报告给谁看。案头研究从研报、年报、政策文件、新闻、展会资料中找事实和数据。访谈与问卷补充公开资料里没有的信息。数据清洗与交叉验证核对多个来源的冲突数据。报告撰写把事实、观点、结论组织成结构清晰的文档。汇报与修订根据客户反馈反复修改。在这个流程里大部分时间押在人身上。一个资深分析师能同时跟进的专题数量有限一份报告从启动到交付往往以周为单位。更关键的是传统专业服务公司的收入模型近似“收入 人数 x 人均单价”当人员规模扩大时管理成本、沟通成本、培训成本会同步上升。这就是为什么拥有几万人的巨头仍然很重要但它的增长方式已经越来越接近“线性增长”很难出现指数级扩张。一旦某种技术能把流程里的若干人工环节压缩成程序调用这个行业的单位产出就会被重新定价。1.2 AI Agent 把“人力流水线”变成“自动化工作流”大模型刚出现时很多人以为“调研 AI”就是聊天框里问一句模型背一段知识出来。这种方式在简单问答里成立但应付不了真实调研。真实调研有三个特点信息是新鲜的、结论是要有出处的、报告是长篇结构化内容。这三个特点决定了单次 Prompt 不够需要的是一个能规划任务、调用检索工具、阅读材料、分节写作、最后复核引用的执行体。这个执行体就是 AI Agent智能体。在工程上Agent 的核心不是某一个模型而是一个循环模型生成下一步动作程序执行动作并把结果返回给模型模型再根据结果决定下一步。放到调研场景里动作就是“拆解问题”“检索资料”“阅读片段”“写一节报告”。环节传统调研AI 调研工作流拆解主题分析师人工拆分Agent 按主题生成子问题收集资料人力检索、下载、筛选检索器从知识库召回片段阅读与提炼人工阅读全文模型基于召回片段提炼报告撰写顾问逐节写作模型按章节生成初稿引用核验人工回查原文强制引用编号人工抽检从这个表能看出AI 调研并没有完全取消人而是把人的工作从“执行”提升到“定义流程、审查结果、处理例外”的层面。这也是为什么一个小团队就能构造出过去需要大量分析师才能完成的服务。1.3 估值对比反映的是单位产出能力的变化回到那个估值对比。一个 60 人团队做到 20 亿美元估值一个大几千人乃至几万人团队市值只有几十亿美元背后并不只是资本市场的情绪波动而是按“单位人力可支撑的产值”重新定价。对一个 AI 驱动的调研产品来说新增一个客户的边际成本接近零知识库已经搭好检索和生成都是程序调用报告初稿由模型自动生成。而传统调研公司每接一个新项目都要重新投入分析师的工时。这种成本结构差异会直接体现在估值倍数上。这个判断也有另一面AI 调研公司的估值高不等于它已经稳定盈利。小团队的优势是资本效率高劣势是客户信任、行业数据和交付质量需要时间积累。所以正确的理解不是“传统巨头要完蛋”而是“谁把 AI 工作流真正嵌入调研闭环谁就能以更低的成本提供同等质量的交付”。2. 拆开看支撑“小团队高估值”的三类核心技术2.1 RAG让模型基于证据回答而不是背答案RAGRetrieval-Augmented Generation检索增强生成是调研系统的地基。它的思想很直接模型不直接回答而是先从知识库里检索出与问题相关的文档片段再把片段放进 Prompt让模型基于这些材料生成回答。为什么调研场景必须用 RAG第一模型训练数据有截止时间而行业资料永远在更新。昨天发布的政策、上一季度的财报模型不可能靠“记忆”准确回答。第二调研报告必须给出来源。RAG 可以在检索时保留每个片段来自哪份文档的 metadata最终生成的报告才能标出“这段结论出自哪个来源”。第三企业内部的调研场景往往涉及私有资料这些资料不可能进入公开模型训练集。RAG 让模型把私有文档当作“短期记忆”只在检索命中时使用从而在合规边界内完成信息提炼。最简架构如下原始文档 - 切分 - 向量化 - 写入向量库 用户问题 - 向量化 - 相似度检索 - 召回片段 - 拼接 Prompt - 模型生成工程里最容易出问题的地方是第一段文档切分和向量化。切得太碎单块信息不完整切得太长检索噪声大还浪费 token。后面的章节会专门讲参数。2.2 Agent把流程从一次性问答变成多步执行RAG 只是“检索 生成”还不能叫 Agent。真正的调研工作流需要多步执行根据调研主题拆解出若干子问题。对每个子问题分别检索。汇总检索材料。按报告结构分节写作。复核引用和事实冲突。如果把这五步写在一个 Prompt 里模型经常会在长上下文里丢失重点而且无法定位是哪一步出了问题。Agent 方案的价值在于每步是一个独立节点节点的输入输出都有明确结构中间任何一步失败都可以单独调试。实现方式有很多Python 生态里常用 LangGraphJava 团队可以用 Spring AI 的 Agent 能力自己写也不难本质上就是按状态机顺序调用函数。关键是架构要对规划、检索、写作、复核要分离。2.3 评测与反馈没有评测调研 AI 只会越调越乱小团队能跑得快的另一个原因是它们通常把“评测集”当核心资产。AI 调研系统最危险的问题是“看起来很有道理实际是编的”。如果不建立评测集每次改 Prompt、换模型、调参数都不知道是变好了还是变坏了。评测集不需要一开始很大。先准备 20 到 30 个真实调研问题每个问题配好标准答案和参考来源然后跑一轮系统记录三件事检索阶段有没有召回正确答案。生成阶段有没有忠实使用召回内容。结论和数据有没有和原文冲突。后面会给出具体的评价维度和一个反面案例。3. 搭建一个最小可运行的行业调研 Agent3.1 环境准备与依赖下面的示例用一个最小闭环说明思路收集几份行业报告入库后输入一个调研主题系统自动输出带来源编号的报告章节。建议环境依赖项版本建议说明Python3.11 及以上类型提示和异步语法支持更完整langgraph0.2 及以上编排调研工作流langchain-openai0.2 及以上调用 OpenAI 兼容接口langchain-community0.3 及以上加载 PDF、文本langchain-chroma0.1 及以上向量库持久化pypdf5.0 及以上解析 PDF先创建依赖文件mkdir research-agent cd research-agent python -m venv .venv source .venv/bin/activate# requirements.txt langgraph0.2.0 langchain-core0.3.0 langchain-openai0.2.0 langchain-community0.3.0 langchain-chroma0.1.0 chromadb0.5.0 pypdf5.0.0 pyyaml6.0.0安装pip install -r requirements.txt示例代码默认调用 OpenAI 兼容接口需要设置环境变量export OPENAI_API_KEY你的密钥如果你希望完全本地化可以使用 Ollama 加载开源模型然后把语言模型地址指向本地服务。只要接口兼容 OpenAI 格式下面的代码基本不需要大改。注意示例代码用于说明实现思路实际落地前要确认依赖版本和模型接口是否与当前环境一致。3.2 项目目录设计research-agent/ ├── requirements.txt ├── sources/ # 放原始 PDF、MD、TXT ├── ingest_docs.py # 文档入库 ├── research_agent.py # 调研工作流 └── data/ └── chroma/ # 向量库持久化目录sources/放可以公开获取的行业报告、年报摘要、政策文件。入库脚本会把它们切分成块转成向量写入data/chroma/。调研工作流则读取向量库完成检索和生成。3.3 文档入库切分、向量化、写入向量库# ingest_docs.py import os from langchain_community.document_loaders import PyPDFLoader, TextLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_openai import OpenAIEmbeddings from langchain_chroma import Chroma PERSIST_DIR ./data/chroma CHUNK_SIZE 800 CHUNK_OVERLAP 120 def load_docs(source_dir: str): docs [] for root, _, files in os.walk(source_dir): for name in files: path os.path.join(root, name) if name.endswith(.pdf): # PyPDFLoader 返回的是按页拆分的 Document loader PyPDFLoader(path) docs.extend(loader.load()) elif name.endswith((.txt, .md)): loader TextLoader(path, encodingutf-8) docs.extend(loader.load()) return docs def main(): raw_docs load_docs(./sources) splitter RecursiveCharacterTextSplitter( chunk_sizeCHUNK_SIZE, chunk_overlapCHUNK_OVERLAP, separators[\n\n, \n, 。, , , ], ) chunks splitter.split_documents(raw_docs) print(floaded {len(raw_docs)} docs, generated {len(chunks)} chunks) embeddings OpenAIEmbeddings(modeltext-embedding-3-small) vector_store Chroma.from_documents( documentschunks, embeddingembeddings, persist_directoryPERSIST_DIR, ) print(fvector store ready at {PERSIST_DIR}) if __name__ __main__: main()这段代码里最重要的不是加载而是切分。RecursiveCharacterTextSplitter会按优先级依次尝试分隔符先按段落\n\n再按换行再按句号、分号、逗号。对中文文档来说把中文标点加进separators很关键否则切分会在英文空格处硬切把完整句子拦腰截断。CHUNK_OVERLAP用于保留相邻片段之间的上下文。比如一段话跨越了两个块没有 overlap 的话后一个块会丢失前文主语。3.4 调研工作流规划、检索、写作、复核下面用一个带状态的图来编排流程。每个节点只做一件事# research_agent.py import json from typing import TypedDict from langchain_chroma import Chroma from langchain_core.prompts import ChatPromptTemplate from langchain_openai import ChatOpenAI, OpenAIEmbeddings from langgraph.graph import END, StateGraph class ResearchState(TypedDict): topic: str questions: list documents: list report: str llm ChatOpenAI(modelgpt-4o-mini, temperature0.3) vector_store Chroma( persist_directory./data/chroma, embedding_functionOpenAIEmbeddings(modeltext-embedding-3-small), ) def plan_questions(state: ResearchState): prompt ChatPromptTemplate.from_template( 你是行业研究员。请针对调研主题拆解出 3 到 5 个必须回答的子问题。\n 主题{topic}\n 只输出 JSON 数组例如 [子问题1, 子问题2]不要输出多余文字。 ) resp llm.invoke(prompt.format(topicstate[topic])) # 实际项目建议使用 llm.with_structured_output(Schema) 做结构化解析 questions json.loads(resp.content) return {questions: questions} def retrieve(state: ResearchState): documents [] for q in state[questions]: docs vector_store.similarity_search_with_score(q, k4) for doc, score in docs: documents.append( { question: q, content: doc.page_content, source: doc.metadata.get(source, unknown), score: round(score, 4), } ) return {documents: documents} def write_report(state: ResearchState): context \n---\n.join( f[问题] {d[question]}\n[来源] {d[source]}\n[内容] {d[content]} for d in state[documents] ) prompt ChatPromptTemplate.from_template( 你是一名行业研究员。请根据以下检索材料撰写一节调研报告。\n 要求\n 1. 只使用检索材料中的事实不要补充外部知识。\n 2. 每个观点后标注参考来源编号。\n 3. 如果材料不足明确写“材料不足需要补充调研”。\n\n 检索材料\n{context}\n\n 调研主题{topic}\n 请输出 Markdown 格式的报告章节。 ) resp llm.invoke(prompt.format(contextcontext, topicstate[topic])) return {report: resp.content} def build_graph(): graph StateGraph(ResearchState) graph.add_node(plan, plan_questions) graph.add_node(retrieve, retrieve) graph.add_node(write, write_report) graph.set_entry_point(plan) graph.add_edge(plan, retrieve) graph.add_edge(retrieve, write) graph.add_edge(write, END) return graph.compile() if __name__ __main__: app build_graph() result app.invoke( { topic: 新能源汽车动力电池行业竞争格局, questions: [], documents: [], report: , } ) print(result[report])运行方式python ingest_docs.py python research_agent.py正常结果是一段 Markdown 报告其中每条核心结论后面带有来源编号。如果某个子问题没有检索到足够材料报告里会出现“材料不足需要补充调研”的提示而不是让模型硬编。这个示例只包含三个节点但它已经具备了一个 Agent 的最小特征有状态、有步骤、每步可单独检查和调试。正式产品还可以加入“复核”节点把生成内容再次检索比对标记出引用可疑的句子。4. 关键参数怎么调分块、检索和生成4.1 文档切分参数参数示例值作用调整影响chunk_size800 字符每个文本块的大小调大上下文更完整但检索噪声高调小更精准但片段可能缺失上下文chunk_overlap120 字符相邻块重叠长度避免关键句子被截断过大增加冗余存储separators\n\n、\n、。、空格切分优先级中文文本必须包含中文标点否则会在错误位置切断切分没有绝对标准需要结合文档类型试。年报、研报这类正式文档可以适当调大chunk_size政策文件里定义条款密集可以调小保证每块只包含少量条款。一个常见误区是追求“切得干净”。实际上块之间的语义重叠是正常且必要的关键是控制重叠比例。建议重叠量控制在块大小的 10% 到 20%比如 800 字符的块配 80 到 160 字符的重叠。4.2 检索参数参数示例值作用调整影响k4每个子问题召回片段数调大覆盖更广但无关片段增多调小结果更聚焦但可能漏信息score_threshold可选相似度阈值低于阈值的片段直接丢弃降低幻觉风险search_typesimilarity检索方式可换成 mmr 增加结果多样性避免多个片段重复同一内容similarity_search_with_score返回的 score 在 Chroma 里是距离值越小表示越相似。不同向量库对分数的定义不同使用前要先确认语义。实际项目中一个问题拆成 5 个子问题每个子问题取 4 条片段最终会拼接 20 条片段进 Prompt。如果大量片段彼此重复可以改用 MMR 检索或者先聚类去重再进入生成节点。4.3 生成参数参数示例值作用调整影响temperature0.3生成随机性调研建议 0.2 到 0.4调高容易创意发挥也容易编造max_tokens2000单次输出上限过小导致报告截断过大浪费 token 且长文容易跑偏modelgpt-4o-mini模型选择初稿用快模型事实密集章节可替换更强模型调研场景和写诗、聊天不一样输出的核心指标是忠实度不是文学性。温度超过 0.7 之后幻觉风险会明显上升。更可靠的做法是在 Prompt 里强制模型“只使用检索材料”并且要求“每条结论后面标注来源编号”。4.4 提示词里必须要求结构化输出上面代码里拆解子问题时使用了“只输出 JSON 数组”的 Prompt。这种做法简单但解析容易失败模型可能输出多余的 Markdown 标记或解释文字。更稳的方案是使用with_structured_output。以 Pydantic 模型定义输出结构from pydantic import BaseModel class ResearchQuestions(BaseModel): questions: list[str] structured_llm llm.with_structured_output(ResearchQuestions) result structured_llm.invoke(适合调研新能源汽车动力电池行业的子问题) print(result.questions)这样模型输出会直接映射到对象省去json.loads的解析和异常处理。报告生成阶段也可以定义“结论、来源编号、置信级别”这样的结构化字段便于后续二次处理。5. 怎么验证调研结果靠谱5.1 先做人工判读自动指标再多也替代不了人工抽检。每一份生成的报告至少按下面清单过一遍每个核心结论是否能在检索材料里找到对应原文。来源编号对应的文档是否真实存在。数字、日期、百分比是否和原文完全一致。是否混入了模型自身记忆里的知识而不是仅用检索材料。材料不足的地方模型是否明确承认不足。整段报告是否存在“第一句正确第二句推导出错误结论”的情况。其中第 4 点最容易忽略。模型哪怕在 Prompt 里被要求“只使用检索材料”依然可能把训练时见过的知识不自觉写进报告。抽检时要把每一句和数据相关的表述回查原文。5.2 自动评估指标人工抽检适合小批量迭代阶段要配合自动指标。层次指标说明检索recallk正确答案是否出现在召回的前 k 条里检索MRR第一个正确答案排在第几位生成忠实度生成的句子能否由检索片段支持生成相关性生成内容是否对应该调研问题Python 生态里可以用 RAGAS 这类工具快速计算部分指标也可以自己写一个简化版对评测集中的每个问题把生成结果里的句子与检索片段做语义相似度低于阈值就标记为“可能无依据”。5.3 一个典型错误输出示例下面是一个常见的错误输出2025 年全球储能市场规模预计达到 3800 亿美元同比增长 25%其中中国市场份额超过 50%。如果检索材料里写的是“2023 年中国储能市场新增装机规模达到 XX GWh”那么这句输出就是明显错误。模型把训练记忆里的“预测数字”塞进了报告同时没有给出来源编号。这种现象在行业报告评测里非常常见。修复方式不是只换更强的模型而是从三个方向同时下手Prompt 明确“每个数字必须带来源编号”。检索时提高片段相关性要求用 score_threshold 过滤低质量片段。增加复核节点让第二次调用模型检查“报告中的数字是否能在检索材料中找到”。5.4 学习环境与生产环境的边界能力学习环境生产环境配置环境变量写死配置中心外置按环境隔离向量库本地 Chroma独立向量数据库服务支持多实例日志print结构化日志记录每次检索和生成的 trace权限本机访问按部门、数据等级做访问控制监控无记录 token 消耗、耗时、失败率、幻觉抽检率模型单模型多模型路由初稿和复核分层回滚无提示词、参数、知识库版本化支持快速回滚生产环境里“能跑通”和“能交付”是两件事。调研系统的交付物要进入客户报告必须保留完整的溯源链路哪个问题检索了哪些片段、模型基于哪些片段写了哪句话这些都要能查。6. 常见问题排查检索不到、幻觉、引用错位6.1 检索不到相关内容现象某个问题明明知识库里有对应文档但报告里仍然写“材料不足”。排查链路先确认文档是否真正入库。运行python ingest_docs.py看打印的 chunks 数量。用向量库直接做一次相似度搜索确认问题本身能否命中。检查 embedding 模型是否一致。入库用text-embedding-3-small查询也必须是同一个模型。检查切分是否把关键信息切碎。如果关键数据正好落在两个块的边界可能两边都不完整。如果问题表述和文档语言差异较大尝试先改写问题把口语化提问转成与文档一致的术语。# 单独调试检索 query 动力电池行业集中度 docs vector_store.similarity_search_with_score(query, k5) for doc, score in docs: print(score, doc.page_content[:80], doc.metadata.get(source))这一步能快速判断是入库问题、embedding 问题还是检索参数问题。6.2 报告出现幻觉现象报告里出现来源编号但编号对应的片段里根本没有这句话。常见原因温度设置过高。Prompt 里没有强调“不能补充外部知识”。检索片段本身与问题无关模型为了完成任务强行编造。拼接的检索材料过长模型在长上下文里丢失了引用约束。处理建议把 temperature 降到 0.3 以下。在生成节点加入“如果片段不相关必须明说材料不足”。对检索结果先做一轮相关性过滤低于阈值的片段不进入 Prompt。生成后追加复核节点把报告句子逐句与检索片段比对输出“有依据 / 无依据”。6.3 长报告写不完或格式损坏现象报告写到一半被截断或者 Markdown 表格缺了一列。原因通常是max_tokens设置太小或者一次让模型生成了过长的内容。大模型在超长输出时质量和稳定性都会下降。推荐做法是分节生成先让规划节点输出报告的大纲再对每个 H2 章节单独调用一次生成最后拼接。这样每一段输出短受控性好也便于在某一节失败时单独重试。6.4 token 成本和性能问题现象一次调研要拆 5 个问题、检索 20 条片段、生成几千字调用量和 token 消耗都很大。优化方向对相同主题的问题做缓存命中缓存就直接复用检索结果。多个子问题共享的检索片段去重后再进 Prompt。初稿用便宜快速的模型只在事实核验时用更强的模型。向量库放到独立服务避免每次启动程序都重新加载 embedding 模型。7. 落地调研 AI 的实践清单与扩展方向7.1 哪些环节 AI 化优先级高业务环节AI 化优先级原因资料收集高检索和抓取可以完全程序化信息提炼高LLM 擅长从长文档提炼要点报告初稿高结构化输出已经比较成熟数据交叉验证中依赖规则和人工配合专家访谈低需要人际信任和专业判断关键结论判断低责任在人不能甩给模型一个稳妥的落地顺序是先用 AI 做“资料收集 信息提炼 初稿”保留“人审 修订 最终定稿”。跑通之后再把复核节点逐步自动化。7.2 可复用清单从需求到交付报告明确报告的读者和决策用途决定信息密度和结论粒度。圈定知识库语料范围确认来源合规。建立 20 到 30 条问题的评测集先跑基线。定义引用规范每条结论必须能回查到原文片段。先小范围试点输出报告必须有人工签名确认。记录失败案例把常见幻觉写进评测集。提示词、向量库、模型版本全部纳入版本管理支持回滚。每次改动只调一个变量对比评测集指标不要同时改多个参数。这套清单适合团队内部知识库也适合对外交付的调研产品。核心思想是AI 调研系统不是一次搭完的而是靠评测集和失败案例持续迭代出来的。7.3 扩展方向多智能体、领域模型、AI 编程辅助如果已经跑通最小系统可以按下面顺序扩展多智能体协作把规划、检索、写作、复核拆成多个 Agent每个 Agent 有自己的工具和提示词。规划 Agent 负责拆题资料 Agent 负责检索与去重写作 Agent 负责分节生成复核 Agent 独立检查引用。这种架构更接近真实团队的职责拆分。领域知识增强对特定行业微调一个小模型作为专用工具不替代主模型只承担术语理解、实体抽取这类专项任务。Java 技术栈如果团队以 Java 为主可以调研 Spring AI 对向量库、模型调用和 Agent 编排的支持减少引入 Python 服务带来的维护成本。开发效率这类系统的开发本身就适合用 AI 编程工具辅助。用 Cursor 这类 AI Coding 工具生成样板代码、补测试、查文档可以把搭建成本压得很低。这也反过来解释了一个现象为什么现在 60 人的团队能做出过去 600 人团队都不一定做得完的产品。对新手有效的练习方式不是一开始就学 LangGraph 的全部概念而是先找 20 份真实行业报告按本文的步骤搭一个最小系统跑通“输入主题、输出带引用的报告”全流程然后反复用评测集折磨它。等你能说清楚每个参数变化对输出的影响再进入多 Agent 和部署层面会顺畅得多。