企业Agent实战搭建全流程1/3——药企竞品情报自动化

这是"药企AI Agent落地三部曲"的第1篇。三篇分别解决:
-
本篇:文献调研与竞品情报——信息淹没,人工整理效率极低
-
第2篇:临床方案与注册文件——长文档修改联动,格式校验耗时
-
第3篇:跨部门数据孤岛——多部门数据不通,协作靠手工转格式
本三部曲完整记录了从单Agent逐步扩展到多Agent协作系统的过程。所有工具全线开源,可私有化部署。
1. 问题描述
先说痛点。
在药企做过竞品情报的人应该都有经历:每周花20多个小时在文献筛选和整理上,真正坐下来做分析判断的时间不到5小时。大部分时间都耗在"找"这个动作上——翻PubMed、刷ClinicalTrials.gov、盯竞品官网、查财报、看会议摘要……
几个数字比较说明问题:
-
竞品动态追踪靠人工,平均滞后2周。等你看到人家发了新闻稿,其实临床数据早就在会议摘要或者SEC filing里了
-
一份竞品周报花4小时制作,其中至少3小时是花在找数据、对齐口径、格式调整上
-
同一段数据在不同报告里重复整理。临床部的报告、注册部的报告、BD的报告,各写各的,数据来源都不一样
-
每周检索文献200+篇,真正有用的不超过20篇,但是你还得一篇篇点进去看
再说说硬约束,药企场景跟互联网场景最大的区别就在这儿:
-
数据不能出内网。患者数据、临床数据、商业分析——这些碰都不能碰外部API。用GPT-4o?数据过境的合规风险你扛不住
-
必须满足GxP合规。GxP不只是个标签,它意味着你得有审计追踪、版本控制、数据溯源
-
已有PostgreSQL基础设施。核心业务跑在PG上面(LIMS、CTMS、eTMF),不可能为了一个AI项目单独搞一套新的数据库集群,运维团队会把你骂死
-
全线需要开源可私有化部署。自主可控不是喊口号,是药企IT部门审批的红线。不开源?不部署在内网?直接否掉
说白了,你要在"数据不出内网 + GxP合规 + 复用现有基础设施 + 全线开源"这四个约束条件下,搭一套能干活的AI系统。
我之前帮另一家药企评估过一套商业化方案——某大厂的"智能知识管理平台",功能演示确实炫酷。但是一问到数据部署,对方说"可以部署私有云版本",再一问价格,光授权费就够买三台GPU服务器了。更关键的是,它的文档解析引擎不支持药企常用的双栏排版论文格式,改都没法改。这种"黑盒"方案在药企根本过不了合规审查。
所以我给自己定了个原则:所有组件必须开源、可审计、可二次开发。出了问题自己能改,不用等供应商排期。
2. 方案设计
先说一个我踩过的最大的坑:Agent的核心不是对话能力,是工作流能力。
很多人一上来就想"做一个AI聊天机器人,能回答医药问题就行了"。这是把Agent当搜索引擎用。真正有价值的Agent,是一条完整的工作链:检索→解析→结构化→生成→学习。每一步都能自动化,串起来才是"做情报",而不只是"搜信息"。
按这个思路,我把整个系统拆成了几个模块,每个模块选一个最合适的开源方案。
技术选型
|
组件 |
选型 |
开源协议 |
选择理由 |
考虑过但没选 |
|---|---|---|---|---|
|
Agent平台 |
Dify.ai |
开源(GitHub 90k+ Star) |
原生支持多Agent编排和Workflow,支持私有化Docker部署 |
Langflow(可视化好但是多Agent支持弱)、Flowise(轻量但不适合企业级) |
|
向量数据库 |
PGVector |
PostgreSQL扩展(开源) |
药企已有PostgreSQL基础设施,加个扩展即可 |
Milvus(功能更强但是需要独立部署运维,对这个规模偏重) |
|
LLM底座 |
Qwen3.5(35B-A3B) |
Apache 2.0 |
最新开源旗舰,Apache 2.0,中文能力顶级,MoE架构推理高效,可私有化部署 |
GPT-4o(数据出境合规风险)、GLM-4(中文略逊Qwen) |
|
记忆层 |
Mem0(开源版) |
Apache 2.0 |
分层记忆,Dify原生集成 |
LangChain Memory(功能弱)、自建(开发成本高) |
|
重排序 |
BGE-Reranker-v2-m3 |
MIT |
BAAI开源,中英文检索精度优秀 |
Cohere Rerank(商业API,数据出境风险) |
|
文档解析 |
pdfplumber + MinerU |
开源 |
MinerU专攻学术论文双栏排版,上海AI Lab开源 |
Unstructured.io(通用性强但是学术论文解析不如MinerU精准) |
|
文档存储 |
RustFS |
开源 |
分布式对象存储,S3兼容,私有化部署 |
MinIO(开源优秀但是核心维护团队在海外) |
|
报告生成 |
Jinja2 + python-docx |
开源 |
模板引擎+Word输出 |
— |
GPU预算升级建议:如果GPU资源充足,可以把Qwen3.5-35B-A3B升级到Qwen3.5-397B-A17B(总参数397B,激活参数17B),中文理解和长文本能力更好,不过需要4×A100 80G起步。初期建议先上35B-A3B,跑通流程再说。
为什么选Dify不选Langflow?Dify的Workflow编排能力是真的强,条件分支、并行节点、循环——这些在复杂的检索工作流里都是刚需。Langflow可视化确实好看,但是一旦涉及多Agent编排就不太够用了。
为什么用PGVector不单独上Milvus?因为药企已经有跑着的PostgreSQL了。加个扩展就能做向量检索,不需要额外部署一套新系统。800篇文献的向量规模,PGVector完全够用,杀鸡不用牛刀。
Qwen3.5-35B-A3B怎么样?MoE架构,总参数35B,但是激活参数只有3B。这意味着什么?推理的时候只需要跑3B的参数,速度很快,1-2张A100 40G就能跑得很流畅。显存占用大概在70GB左右(因为要加载全部权重,这个后面Step 2详细说)。初期建议先上这个,够用。
架构图

整个系统就一个Agent,干一件事:把散落在各处的文献和情报数据收集起来、解析、结构化、生成周报,然后记住用户喜欢什么。不复杂,但是每一步都跑通了。
3. 实施过程

下面按天拆解。所有命令和代码都可以直接复制跑。
Step 1:Dify环境搭建(Day 1)
Dify用Docker部署,十分钟就能跑起来。
# 克隆Dify仓库
git clone https://github.com/langgenius/dify.git
cd dify/docker
# 复制环境配置文件
cp .env.example .env
# 编辑.env,改几个关键参数:
# SECRET_KEY=你随机生成的密钥(建议用 openssl rand -hex 32)
# DB_PASSWORD=设个强密码
# REDIS_PASSWORD=同上
# LOG_LEVEL=WARNING(默认INFO,日志量太大,排查问题的时候再改成DEBUG)
# MAX_WORKERS=4(Celery Worker并发数,文献量大的时候可以调高到8)
# WEB_API_RATE_LIMIT=60(API限流,防止被刷爆)
# 启动所有服务
docker compose up -d
启动完访问 http://你的内网IP/install,完成初始化设置,创建管理员账号。整个过程跟装个WordPress差不多。
踩坑记录
坑1:Docker镜像拉取很慢或者直接超时。内网环境没有外网访问权限,docker compose pull直接卡死。解决办法是提前在内网的镜像仓库(Harbor)里把Dify的几个镜像同步好,或者找运维要离线安装包(tar包),用 docker load 手动导入。别到了现场才发现拉不了镜像,那就尴尬了。
坑2:启动之后浏览器访问不了。最常见的原因有两个:一是防火墙没开端口,firewall-cmd --add-port=80/tcp --permanent 然后 firewall-cmd --reload;二是SELinux阻止了Docker的网络访问,临时测试可以用 setenforce 0 关掉,生产环境建议配置SELinux策略而不是直接关。
官方文档:[Dify Docker部署指南](https://docs.dify.ai/getting-started/install-self-hosted/docker-compose)
Step 2:Qwen3.5模型部署(Day 1-2)
这一步需要有GPU的服务器。我用的是vLLM做推理服务,部署完暴露一个OpenAI兼容的API接口,Dify直接对接。
# 安装vLLM(注意版本,必须>=0.6.0才支持Qwen3.5的MoE架构)
pip install vllm>=0.6.0
# 启动Qwen3.5-35B-A3B推理服务(MoE架构,1-2张A100 40G即可)
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen3.5-35B-A3B \
--host 0.0.0.0 \
--port 8000 \
--tensor-parallel-size 2 \
--gpu-memory-utilization 0.9 \
--max-model-len 32768
几个关键参数说明一下:
-
--gpu-memory-utilization 0.9:默认是0.9,意思是允许vLLM用90%的显存。如果你的GPU还要跑别的服务,需要调低这个值,比如设0.7
-
--max-model-len 32768:最大上下文长度。这个值要根据显存来调,32K在双卡A100 40G上是跑得动的。如果发现OOM,先试24576或者16384
-
--tensor-parallel-size 2:张量并行数,用几张卡就设几
跑起来之后,去Dify后台:设置→模型供应商→添加OpenAI-API兼容→填入地址 http://内网IP:8000/v1。模型名称填 Qwen/Qwen3.5-35B-A3B。
测一下连通性:
curl http://内网IP:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"Qwen/Qwen3.5-35B-A3B","messages":[{"role":"user","content":"你好"}]}'
返回正常JSON就说明没问题了。
踩坑记录
坑1:MoE模型的显存占用跟你想的不一样。Qwen3.5-35B-A3B虽然激活参数只有3B,推理速度接近3B模型,但是显存占用不能按3B来算——全部35B的权重都得加载到显存里,实际占用大概70GB+。也就是说,如果你只有2×A100 40G(总共80GB),留给KV Cache的空间只有10GB左右,长文本场景可能会OOM。解决办法:要么降 --max-model-len,要么用3张卡分摊。
坑2:vLLM版本兼容性。确保vLLM版本 >= 0.6.0,老版本(比如0.4.x)不识别Qwen3.5的MoE架构,启动直接报 Model architecture not supported 的错误。安装完之后先用 pip show vllm 确认一下版本号。
官方文档:[vLLM文档](https://docs.vllm.ai/)
[Qwen模型文档](https://qwenlm.github.io/)
Step 3:PGVector向量数据库配置(Day 2)
不用额外部署任何东西,直接在你现有的PostgreSQL里加个扩展。
# 安装PGVector扩展(根据你的PG版本选对应包名,注意大版本要匹配)
sudo apt install postgresql-15-pgvector
# 连接你的数据库,执行以下SQL
psql -U your_user -d your_database
-- 启用PGVector扩展
CREATE EXTENSION IF NOT EXISTS vector;
-- 创建文献向量表
-- 注意维度要跟embedding模型匹配!Qwen3.5配bge-m3的话维度是1024,不是1536
CREATE TABLE document_embeddings (
id SERIAL PRIMARY KEY,
content TEXT,
metadata JSONB,
embedding vector(1024) -- bge-m3输出1024维向量
);
-- 创建向量索引,加速余弦相似度检索
-- 注意:ivfflat索引需要先有数据才能建!空表建索引会报错
-- 建议先灌一批数据再建索引,或者改用HNSW索引(支持空表建索引,但是构建更慢)
CREATE INDEX ON document_embeddings
USING hnsw (embedding vector_cosine_ops);
然后在Dify后台配置外部知识库连接,填入你的PostgreSQL连接信息。这样Dify的Agent就能直接检索你库里的文献了。
踩坑记录
坑1:PGVector版本要跟PostgreSQL大版本匹配。PG15装PG14的vector扩展会报错 could not open extension control file,因为控制文件路径是按大版本分的。安装之前先 psql --version 确认PG版本,然后装对应的pgvector包。
坑2:ivfflat索引在空表上建不了。第一次跑建表SQL顺手建索引的话会直接报错。解决办法:先灌一批数据进去再建ivfflat索引,或者像上面SQL里写的,改用HNSW索引。HNSW支持空表建索引,好处是后面新增数据不需要重建索引,坏处是初次构建比ivfflat慢不少。800篇文献的规模,两种索引差别不大,看个人偏好。
官方文档:[PGVector](https://github.com/pgvector/pgvector)
Step 4:文档解析流水线搭建(Day 2-3)
这一步我踩了不小的坑。
一上来我用pdfplumber解析论文PDF,结果双栏排版的论文表格直接乱成一锅粥。测了50篇论文,表格解析准确率只有67%。后来换上了MinerU(上海AI Lab开源的项目,专攻学术论文解析),准确率直接拉到89%。
但是MinerU也不是万能的,大概5%的复杂表格(合并单元格特别多那种)还是会错位。最后定下来的方案是两个一起用:MinerU为主,pdfplumber补刀。
# 安装MinerU(注意full版本会拉很多依赖,包括paddleocr)
pip install magic-pdf[full]
# 安装pdfplumber作为补充
pip install pdfplumber
from magic_pdf.data.data_reader_writer import FileBasedDataWriter, FileBasedDataReader
from magic_pdf.data.dataset import PymuDocDataset
# 读取PDF
reader = FileBasedDataReader('')
pdf_bytes = reader.read('paper.pdf')
ds = PymuDocDataset(pdf_bytes)
# 解析为Markdown(表格、公式结构都保留)
md_content = ds.apply(
PdfFlashParsePipeline(),
ImageWriter('/tmp/images'),
)
解析完的Markdown不是直接就能用的。页眉页脚要清掉,引用格式要统一,参考文献要剥离——这些预处理做完再向量化入库。别问我怎么知道的,第一次入库的数据检索出来全是页眉和页码,差点以为自己搞了个假系统。
踩坑记录
坑1:MinerU需要额外的GPU显存。MinerU内部用了布局检测模型(基于PaddleOCR),需要额外的2-4GB显存。如果跟LLM推理服务跑在同一张卡上,需要预留这部分显存。建议把MinerU的任务放到非高峰时段批量跑,或者单独用一张卡跑解析,不要跟vLLM抢显存。另外 magic-pdf[full] 在离线环境安装的时候,需要提前准备好paddleocr等依赖的whl包,不然装到一半卡住就很烦。
坑2:解析速度比想象的慢。MinerU解析一篇10页的论文大概需要30-60秒(GPU模式),800篇文献的初始入库需要预留半天时间。别把初始入库安排在上线前一天,老老实实提前两三天开始跑。如果CPU模式的话更慢,一篇可能要5-10分钟。
官方文档:[MinerU](https://github.com/opendatalab/MinerU)
Step 5:Dify Workflow配置——检索+摘要(Day 4-5)
这是Agent的核心工作流。在Dify里用Workflow编排,搭了一条这样的流水线:
开始节点 → 接收用户输入(竞品名称/靶点/关键词)
并行检索节点(三路同时跑):
-
PubMed E-utilities API(通过HTTP请求节点)
-
Semantic Scholar API
-
内部知识库(PGVector)
重排序节点 → BGE-Reranker对合并结果排序
摘要生成节点 → Qwen3.5按竞品维度组织输出
结束节点 → 返回结构化摘要
BGE-Reranker的部署也在这一步完成。用Xinference起一个推理服务就行:
# 安装xinference(建议>=0.10.0,低版本可能找不到bge-reranker-v2-m3)
pip install xinference>=0.10.0
# 启动Reranker服务
xinference-local --host 0.0.0.0 --port 8001
# 在xinference web UI中启动 bge-reranker-v2-m3 模型
# 或者直接命令行
xinference launch --model-name bge-reranker-v2-m3 --model-type reranker
Dify的Workflow里用HTTP请求节点调用这个Reranker接口,对三路检索的合并结果做二次排序。实际跑下来效果很明显——不加重排的时候,Top 10结果里大概6-7篇是相关的;加上之后能到9篇以上。
有一个配置细节要注意:Dify Workflow里HTTP请求节点的超时时间要调大一些,默认值太短了。PubMed API响应慢的时候可能要10-20秒才返回,建议把超时设到30秒。不然高峰期检索直接超时失败,报错信息还不太明显,排查了半天才发现是超时问题。
这里有两个坑说一下:
第一个坑:PubMed API返回的摘要太短了,基本就是一两句话,根本不够做分析。解决办法是通过DOI去抓CrossRef的全文摘要,信息量翻了好几倍。如果CrossRef也没有全文摘要,退而求其次去Semantic Scholar拿 enriched abstract。两条路都试了,基本上90%以上的论文都能拿到足够的摘要信息。
第二个坑是多源检索的去重。PubMed和Semantic Scholar有重叠,同一个论文可能两边都检索到。我用DOI+标题双重匹配做去重,先匹配DOI(精确),DOI缺失的再用标题模糊匹配。标题匹配有个细节:要先把标题统一转成小写、去掉标点,不然"Phase III"和"Phase 3"会匹配不上。
另外还有一个容易忽略的问题:三路检索返回的结果格式不统一。PubMed返回的是XML格式,Semantic Scholar返回JSON,内部知识库返回的又是另一种结构。在做重排之前,必须先写一个适配层把格式统一掉——统一成 (title, abstract, source, doi) 这样的标准结构,然后再喂给Reranker。这个问题一开始没处理好,Reranker直接报错了,因为输入格式不对。
踩坑记录
坑1:Xinference版本太低找不到模型。如果xinference版本低于0.10.0,xinference launch 启动bge-reranker-v2-m3的时候会报模型不存在的错误。升级一下就好:pip install --upgrade xinference>=0.10.0。
坑2:三路检索结果格式不统一。这个问题上面提了,再多说一句——PubMed的XML解析用Python的 xml.etree.ElementTree 就行,不需要额外装库。关键是把所有来源的字段名对齐,不然后面重排和生成都会出问题。这个问题后面单独展开说,第2篇会讲到多数据源适配的通用做法。
官方文档:[Dify Workflow指南](https://docs.dify.ai/guides/workflow)
[FlagEmbedding(BGE-Reranker)](https://github.com/FlagOpen/FlagEmbedding)
Step 6:竞品周报自动生成(Day 6-8)
检索完了得输出。我一开始犯了一个经典错误:直接让Qwen3.5写"文献综述"。结果生成的周报又长又学术,根本没人看。
后来调整了prompt方向——"写给决策者看的简报,每个信息点不超过2行"。效果马上就出来了。
周报模板用Jinja2定义,最终用python-docx输出Word文档:
from docx import Document
from docx.shared import Pt, Inches
from docx.enum.text import WD_ALIGN_PARAGRAPH
# ========== 样式预定义(很重要,不然默认样式很丑)==========
def setup_styles(doc):
"""定义Word文档样式,这一步不能省"""
# 设置正文字体
style = doc.styles['Normal']
font = style.font
font.name = '微软雅黑'
font.size = Pt(10.5)
# 设置标题样式
for i in range(4):
heading_style = doc.styles[f'Heading {i}']
heading_style.font.name = '微软雅黑'
heading_style.font.color.rgb = None # 用默认黑色,别搞花里胡哨的
# 设置列表样式
list_style = doc.styles['List Bullet']
list_style.font.name = '微软雅黑'
list_style.font.size = Pt(10.5)
list_style.paragraph_format.space_after = Pt(4) # 段后间距,别挤在一起
# Jinja2周报模板
template = """
# 竞品情报周报 - {{ week_range }}
## 1. 管线进展
{% for item in pipeline_updates %}
- **{{ item.company }}** {{ item.drug }}: {{ item.update }}
{% endfor %}
## 2. 临床数据
{% for item in clinical_data %}
- **{{ item.trial_id }}** {{ item.phase }}期 | {{ item.indication }}
- 主要终点: {{ item.primary_endpoint }}
- 结果: {{ item.result }}
{% endfor %}
## 3. 专利动态
{% for item in patent_updates %}
- **{{ item.company }}**: {{ item.detail }}
{% endfor %}
## 4. 合作交易
{% for item in deals %}
- **{{ item.company_a }}** × **{{ item.company_b }}**: {{ item.detail }}
{% endfor %}
"""
# 用python-docx输出Word
doc = Document()
setup_styles(doc) # 先设样式
doc.add_heading('竞品情报周报', 0)
for section in report_data:
doc.add_heading(section['title'], level=1)
for item in section['items']:
doc.add_paragraph(f"{item['company']}: {item['detail']}", style='List Bullet')
doc.save('weekly_report.docx')
踩坑记录
坑1:Jinja2模板里的中文在python-docx里可能显示乱码。确保模板文件和docx的编码都统一为UTF-8。如果模板文件是用Windows记事本编辑保存的,可能是GBK编码,读进来就乱码了。用VS Code或者任何正经编辑器打开,另存为UTF-8格式就行。
坑2:让LLM按模板生成结构化数据的时候,输出格式经常不稳定。有时候多一个字段,有时候少一个字段,偶尔还会自己加一个"总结"板块。这个问题一两句话说不清楚,后面第2篇会单独展开说怎么用Pydantic做格式校验,确保LLM输出的JSON严格符合模板定义的结构。这里先记住一个原则:不要相信LLM的输出格式,一定要加校验。
官方文档:[python-docx](https://python-docx.readthedocs.io/)
Step 7:接入Mem0记忆层(Day 9-10)
这一步是让Agent从"工具"变成"助手"的关键。
Mem0能记住用户的偏好。比如"重点关注PD-L1和ADC方向的竞品动态"、"核心竞品公司是阿斯利康、罗氏、默沙东"。Agent下次检索的时候就会自动过滤无关文献,优先推送用户关心的内容。
# 部署Mem0
git clone https://github.com/mem0ai/mem0.git
cd mem0
pip install -e .
这里有一个非常重要的配置:Mem0默认会调用OpenAI的API做记忆提取。如果不改配置,你的数据就直接走外网了,药企数据出境这个事就不用我多说了吧。必须把config指向本地部署的模型:
# config.yaml
llm:
provider: openai
config:
model: "qwen3.5-35b-a3b"
base_url: "http://内网IP:8000/v1"
# ↑ 必须指向本地vLLM,绝对不能走OpenAI
embedder:
provider: openai
config:
model: "bge-m3"
base_url: "http://内网IP:8001/v1"
# 记忆类型选择
# 默认用vector(向量检索),对于偏好类信息够用了
# 如果涉及复杂的事实关系(比如"阿斯利康的PD-L1管线跟默沙东有合作"这种),
# 可以用graph模式,但是需要额外配置图数据库,初期不建议折腾
memory_type: vector
# 记忆检索相关度阈值
# 默认0.5,对中文场景偏高,建议调到0.3-0.4
# 不然很多相关的记忆检索不出来
threshold: 0.35
from mem0 import Memory
m = Memory()
# 记录用户偏好
m.add("重点关注PD-L1和ADC方向的竞品动态", user_id="researcher_1")
m.add("核心竞品公司:阿斯利康、罗氏、默沙东", user_id="researcher_1")
# 检索时自动结合记忆
results = m.search("最新临床数据", user_id="researcher_1")
接上Mem0之后,Agent推送的文献相关度明显提升。之前推10篇大概6篇有用,现在8篇都有价值。
踩坑记录
坑1:Mem0默认调OpenAI API做记忆提取。这个坑再强调一次——Mem0的config里如果不显式指定 base_url 指向本地模型,它会默认调OpenAI的API。数据直接出境,合规风险巨大。部署完之后第一件事就是检查网络请求,确认没有任何外网调用。
坑2:记忆检索的相关度阈值。默认阈值是0.5,对中文场景偏高,很多相关的记忆检索不出来。建议调到0.3-0.4之间。我最后用的是0.35,既能召回相关记忆,又不会引入太多噪音。如果你的用户群体比较大、记忆条目很多,可能需要根据实际效果微调。
官方文档:[Mem0](https://docs.mem0.ai/)
Step 8:RustFS文档存储(Day 10)
最后把文档存储也搭好。RustFS是个开源的分布式对象存储,S3兼容接口,Docker一行就起来了。
# Docker部署RustFS
# 注意:必须通过环境变量设置access key和secret key,
# 不然默认匿名访问,生产环境绝对不能这样
docker run -d \
--name rustfs \
-p 9000:9000 \
-p 9001:9001 \
-e RUSTFS_ACCESS_KEY="your-access-key" \
-e RUSTFS_SECRET_KEY="your-secret-key" \
-v /data/rustfs:/data \
rustfs/rustfs:latest
# 创建存储桶(需要配置好认证信息)
aws s3 mb s3://reports \
--endpoint-url http://内网IP:9000 \
--region us-east-1
# 周报自动上传
aws s3 cp weekly_report.docx s3://reports/2026-W28/ \
--endpoint-url http://内网IP:9000
生成完的周报自动传到RustFS里,按"年份-周数"分目录存放。谁、什么时候、生成了一份什么报告,全都有记录。GxP审计追踪?这不就来了嘛。
踩坑记录
坑1:boto3连接RustFS的签名问题。RustFS的S3兼容接口跟AWS S3有细微差异,用Python的boto3客户端连接的时候,需要显式设置 signature_version='s3v4',不然上传大文件(比如几十MB的报告)会报签名错误。代码里加一行配置就行:
```python
import boto3
from botocore.config import Config
s3_client = boto3.client(
's3',
endpoint_url='http://内网IP:9000',
awsaccesskey_id='your-access-key',
awssecretkey_id='your-secret-key',
config=Config(signature_version='s3v4')
)
```
坑2:RustFS本身不做跨节点备份。单节点部署的话,硬盘挂了数据就没了。建议配合rsync做定期备份到另一台机器,写个crontab每天凌晨跑一次。生产环境别偷懒,数据丢了可没人帮你。
官方文档:[RustFS](https://rustfs.com/docs/)
4. 运行结果

部署前后对比,用数据说话:
|
指标 |
部署前 |
部署后 |
变化 |
|---|---|---|---|
|
竞品周报生成时间 |
4小时 |
30-40分钟 |
↓83-85% |
|
竞品动态追踪延迟 |
平均2周 |
2-3天获取 |
↓75-80% |
|
每周文献整理耗时 |
20h |
6-8h |
↓60-70% |
|
文献检索相关度 |
人工筛选~75% |
Agent检索85-88%* |
↑10-13% |
|
周报覆盖信息量 |
3-5个竞品 |
10+个竞品 |
↑200% |
文献检索相关度数据说明:85-88%这个数据是基于200篇文献的抽样测试,Agent检索结果经人工逐篇复核后统计的相关率。不是Agent自动判定的,是真有人一篇篇看过了。
最让我意外的变化是周报覆盖信息量。之前人工只能盯3-5个核心竞品,因为精力有限。现在Agent同时跑10+个竞品的动态,人只需要看Agent整理好的简报做判断。覆盖面翻了2倍,人反而更轻松了。
数据方法论说明:以上数据取4周实际运行的每周平均值。测试环境为2×A100 40G + 64GB内存,文献库规模800篇。不同硬件配置和文献规模下数据会有差异。比如文献库扩到2000篇以上,检索延迟和解析时间都会上升,GPU显存也可能成为瓶颈。
资源消耗方面:
-
GPU:Qwen3.5-35B-A3B推理需要1-2×A100 40G(MoE架构,显存占用约70GB)
-
存储:800篇文献解析后约15GB(含向量数据)
-
内存:Dify + Mem0 + BGE-Reranker合计约16GB
说实话这个成本在药企IT预算里不算高。一台带双卡的GPU服务器就能跑起来,比买一套商业情报数据库便宜多了。
5. 总结与下一步
踩坑总结
10天跑下来,总结几个经验:
-
文档解析是地基。MinerU比pdfplumber更适合学术论文,但是两者互补使用效果最好。解析质量直接决定后面所有环节的产出质量。垃圾进垃圾出,这话在Agent场景里尤其准。
-
复用已有基础设施。PGVector不需要额外部署新系统,药企已经有PG了就直接加扩展。别动不动就想"上一套新的向量数据库",运维成本和稳定性都得考虑。
-
记忆层是Agent从"工具"变成"助手"的关键。没有Mem0的时候,Agent就是个检索工具,每次都从头来。接上之后,Agent开始记住你关心什么、不关心什么,推送越来越精准。这个差别很大。
-
报告模板决定输出质量。prompt写得再好,不如模板定义得清晰。把"生成一份竞品周报"改成"按管线进展、临床数据、专利动态、合作交易四个板块,每条不超过2行",输出质量直接上了一个台阶。
-
多源检索一定要做去重和格式适配。PubMed和Semantic Scholar的重叠率比想象的高,不去重的话周报里同一条信息出现两次,很丢人。而且各家返回的数据格式不一样,必须先统一格式再做后续处理。
核心洞察
做了这个项目之后我最大的感受是:Agent的价值不是替你检索,是替你"做情报"。
检索只是第一步。结构化、判断、呈现、学习——这些串起来才是完整的工作流。很多药企也上了AI工具,但是效果不好,问题不在工具本身,在于只做了"点"没做"链"。弄个ChatGPT接口让大家问答,这不叫Agent,这叫换了个搜索引擎。
真正的Agent是一条链:它知道去哪找、找到了怎么解析、解析完怎么结构化、结构化完怎么呈现给对的人、呈现完还能记住下次该怎么做得更好。每一步都自动化,串起来才有价值。
还有一点想说的:不要追求一步到位。我见过太多项目死在"等我们把架构设计完美了再上线"这句话上。我的做法是先跑通最小可用版本——哪怕周报格式丑一点、覆盖范围小一点,先让业务方用起来。用起来之后反馈自然就来了,迭代方向也就清楚了。从Day 1到Day 10,这套系统经历了三版大改,每一版都是根据实际使用反馈调的。
另外一个心得:药企的人其实很愿意接受新工具,前提是这个工具真的能帮他们省时间。不要跟业务部门讲什么"AI赋能"、"数字化转型"这种空话。你就跟他说"这个周报以后半小时就能出,你以前花4个小时"——他立马就愿意试了。
系列预告
文献情报解决了,但是药企的痛点远不止这一个。100多页的临床方案改一个入组标准,样本量、统计方法、安全性章节要联动修改,手动检查交叉引用平均遗漏8-12处。
下一篇,我们用同一套架构,给Agent加上长文档理解和参数联动能力。
本系列基于真实行业痛点,技术方案基于开源工具和通用架构设计,可迁移至真实场景。文中涉及的工具选型仅代表个人实践偏好,不构成商业推荐。
更多推荐



所有评论(0)