基于 LangChain + LangGraph 构建的 ReAct(Reasoning + Acting)智能体,以阿里通义千问(Qwen)大模型为核心,结合 RAG(检索增强生成) 技术,为"智扫通"品牌扫地机器人 / 扫拖一体机提供智能客服问答、个性化使用报告生成等服务。
前端使用 Streamlit 构建 Web 聊天界面,支持流式输出。
- 知识库问答 — 基于 RAG 技术,从产品手册、故障排除、维护保养、选购指南等知识文档中检索相关内容并生成精准回答
- 个性化使用报告 — 根据用户操作记录(模拟后端数据),自动生成月度使用情况报告与保养建议(Markdown 格式)
- 动态角色切换 — 通过中间件机制,智能体可在"客服助手"和"报告撰写师"两种角色间自动切换
- 环境感知 — 可查询天气信息(模拟),结合用户所在地给出环境相关建议
- 流式输出 — 前端支持打字机效果的流式响应,提升用户体验
- 文档自动去重 — 知识库文件通过 MD5 校验避免重复入库
┌──────────────────────────────────────────────┐
│ Streamlit UI (app.py) │
│ Web 聊天界面 · 流式输出 │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ ReactAgent (agent/react_agent.py) │
│ ┌─────────────────────────────────────┐ │
│ │ LangChain create_agent() │ │
│ │ ┌───────────────────────────────┐ │ │
│ │ │ System Prompt (动态切换) │ │ │
│ │ ├───────────────────────────────┤ │ │
│ │ │ 7 个 Tool 工具 │ │ │
│ │ │ rag_summarize · get_weather │ │ │
│ │ │ get_user_id · get_user_loc │ │ │
│ │ │ get_current_month │ │ │
│ │ │ fetch_external_data │ │ │
│ │ │ fill_context_for_report │ │ │
│ │ ├───────────────────────────────┤ │ │
│ │ │ 3 个 Middleware 中间件 │ │ │
│ │ │ monitor_tool · log_before_ │ │ │
│ │ │ model · report_prompt_switch │ │ │
│ │ └───────────────────────────────┘ │ │
│ └─────────────────────────────────────┘ │
└──────┬──────────────┬───────────────────────┘
│ │
▼ ▼
┌──────────────┐ ┌─────────────────────────────┐
│ RAG Service │ │ Model Factory │
│ (rag/) │ │ (model/factory.py) │
│ ┌──────────┐ │ │ ┌─────────────────────────┐ │
│ │ ChromaDB │ │ │ │ ChatTongyi (qwen3-max) │ │
│ │ 向量存储 │ │ │ ├─────────────────────────┤ │
│ ├──────────┤ │ │ │ DashScopeEmbeddings │ │
│ │ 文档切分 │ │ │ │ (text-embedding-v4) │ │
│ ├──────────┤ │ │ └─────────────────────────┘ │
│ │ 检索链 │ │ └─────────────────────────────┘
│ └──────────┘ │
└──────────────┘
projectAgent/
├── README.md # 项目技术文档(本文件)
├── .python-version # Python 版本声明 (3.13)
├── .env # 环境变量(API Key 等)
├── .gitignore # Git 忽略配置
├── pyproject.toml # 项目元数据与依赖声明
├── uv.lock # 依赖锁定文件
├── md5.txt # 已入库文档的 MD5 校验值(去重用)
│
├── app.py # 【入口】Streamlit Web 前端
├── main.py # 占位文件,打印 Hello from projectagent!
│
├── agent/ # ── 智能体层 ──
│ ├── react_agent.py # ReAct 智能体核心:创建 agent、流式执行
│ ├── chroma_db/ # 测试用 ChromaDB 实例(空)
│ └── tools/
│ ├── agent_tools.py # 7 个 @tool 工具函数 + CSV 数据加载
│ ├── middleware.py # 3 个 LangChain 中间件
│ └── chroma_db/ # 测试用 ChromaDB 实例(空)
│
├── config/ # ── 配置层(YAML)──
│ ├── agent.yml # 外部数据路径配置(records.csv)
│ ├── chroma.yml # ChromaDB 设置:集合名、分块参数、文件类型
│ ├── prompts.yml # 提示词文件路径配置
│ └── rag.yml # RAG 模型名称配置
│
├── data/ # ── 数据层 ──
│ ├── external/
│ │ └── records.csv # 用户使用记录(10 用户 × 12 月,共 120 行)
│ ├── 扫地机器人100问.pdf # 知识库:扫地机器人百问百答 (PDF)
│ ├── 扫地机器人100问2.txt # 知识库:扫地机器人百问百答 变体 2
│ ├── 扫拖一体机器人100问.txt # 知识库:扫拖一体机器人百问百答
│ ├── 故障排除.txt # 知识库:故障排查指南
│ ├── 维护保养.txt # 知识库:维护保养指南
│ └── 选购指南.txt # 知识库:选购指南
│
├── logs/ # ── 日志目录 ──
│ └── agent_YYYY-MM-DD_HH-MM-SS.log # 带时间戳的运行日志
│
├── model/ # ── 模型工厂 ──
│ └── factory.py # 抽象工厂模式:ChatModel + Embeddings 工厂
│
├── prompts/ # ── 提示词模板 ──
│ ├── main_prompt.txt # 系统提示词:客服助手模式
│ ├── rag_prompt.txt # RAG 检索链提示词模板(含 {input}、{context})
│ └── report_prompt.txt # 系统提示词:报告撰写模式
│
├── rag/ # ── RAG 检索增强生成层 ──
│ ├── rag_service.py # RAG 总结服务:检索 + 提示词 + 模型 + 解析器
│ ├── vector_store.py # Chroma 向量存储管理:文档加载、切分、MD5 去重
│ └── chroma_db/ # 【主】ChromaDB 持久化向量数据库
│ ├── chroma.sqlite3 # 向量元数据
│ └── 35bcfc11-.../ # 向量索引二进制文件
│
└── utils/ # ── 工具层 ──
├── config_handler.py # YAML 配置文件加载器
├── file_handler.py # 文件处理:MD5 哈希、类型过滤、PDF/TXT 加载器
├── logger_handler.py # 日志配置:控制台 + 文件双输出
├── path_tool.py # 项目根路径解析
└── prompt_loader.py # 提示词文件读取器
项目的启动入口。负责:
- 初始化
ReactAgent实例并缓存在st.session_state中(单例模式) - 渲染聊天界面(用户消息 + 助手回复)
- 调用
execute_stream()获取流式响应,逐字输出实现打字机效果 - 管理对话历史记录
仅输出 Hello from projectagent!,未参与实际应用逻辑。可能是项目初始化时的测试文件。
项目的核心逻辑模块。使用 LangChain 的 create_agent() 组装智能体:
- 模型:通过
model/factory.py获取ChatTongyi(qwen3-max)实例 - 系统提示词:从
prompts/main_prompt.txt加载 - 工具集:注册 7 个
@tool函数,覆盖 RAG 检索、天气查询、用户数据获取等 - 中间件:注册 3 个中间件实现工具调用监控、日志记录和动态提示词切换
- 运行时上下文:维护
{"report": False}上下文,用于控制角色切换 - 流式执行:
execute_stream()通过agent.stream(stream_mode="values")实现流式输出
定义智能体可调用的 7 个工具:
| 工具函数 | 类型 | 功能描述 |
|---|---|---|
rag_summarize(query) |
核心 | 调用 RAG 服务检索知识库并生成回答 |
get_weather(city, date) |
模拟 | 返回模拟的天气数据(温度、湿度、天气状况) |
get_user_id() |
模拟 | 返回模拟用户 ID(当前返回 "user_1001") |
get_user_location() |
模拟 | 返回模拟用户所在城市(当前返回 "北京市") |
get_current_month() |
模拟 | 返回当前月份("2025-01") |
fetch_external_data(user_id, month) |
核心 | 从 records.csv 查询用户指定月份的使用数据 |
fill_context_for_report() |
信号 | 空操作工具,作为触发报告模式的信号 |
还包含 generate_external_data() 函数,惰性加载并解析 records.csv 为嵌套字典结构:
{user_id: {month: {Feature, CleaningEfficiency, Consumables, Comparison, Time}}}三个 LangChain 中间件,以装饰器形式注册:
| 中间件 | 装饰器 | 作用 |
|---|---|---|
monitor_tool |
@wrap_tool_call |
包装每个工具调用:记录输入/输出日志;检测到 fill_context_for_report 被调用时将 context["report"] 设为 True |
log_before_model |
@before_model |
每次 LLM 调用前记录当前消息数量 |
report_prompt_switch |
@dynamic_prompt |
动态提示词切换器:当 context["report"] == True 时,将系统提示词替换为 report_prompt.txt |
使用抽象工厂模式创建模型实例:
BaseModelFactory:抽象基类,定义create()接口ChatModelFactory:创建ChatTongyi(model="qwen3-max")实例EmbeddingsFactory:创建DashScopeEmbeddings(model="text-embedding-v4")实例- 模块级单例
chat_model和embed_model,在导入时即创建,全局复用
构建并管理 RAG 检索链:
- 初始化
VectorStoreService获取向量存储实例 - 将向量存储包装为 LangChain Retriever(top-k 检索)
- 构建 LCEL 链:
PromptTemplate ← chat_model ← StrOutputParser rag_summarize(query)方法:检索 → 格式化上下文 → 调用 LLM → 返回结构化回答
管理 ChromaDB 向量数据库的完整生命周期:
- 初始化:连接 Chroma(持久化模式),创建
RecursiveCharacterTextSplitter(200 字符块、20 字符重叠、中文友好分隔符) - 文档加载 (
load_document()):- 扫描
data/目录下的.txt和.pdf文件 - 计算文件 MD5,与
md5.txt比对,跳过已入库文件 - 使用
PyPDFLoader/TextLoader加载新文件 - 切分为文本块,通过
DashScopeEmbeddings向量化 - 存入 ChromaDB,更新
md5.txt
- 扫描
- 检索 (
get_retriever()):返回配置了 top-k 的检索器
| 文件 | 关键配置项 | 说明 |
|---|---|---|
rag.yml |
chat_model_name: qwen3-maxembedding_model_name: text-embedding-v4 |
LLM 和嵌入模型名称 |
chroma.yml |
collection_name: agentpersist_directory: chroma_dbk: 3chunk_size: 200chunk_overlap: 20allowed_types: [txt, pdf]md5_hex_store: md5.txtdata_path: data中文分隔符列表 |
向量数据库及文档处理配置 |
prompts.yml |
main_prompt_pathrag_summarize_prompt_pathreport_prompt_path |
三个提示词文件的相对路径 |
agent.yml |
external_data_path: data/external/records.csv |
外部用户数据 CSV 路径 |
.env |
DASHSCOPE_API_KEYDASHSCOPE_BASE_URL |
阿里云 DashScope API 密钥和端点 |
定义智能体为"智扫通产品专家",遵循 ReAct 循环。详细描述:
- 7 个工具的用途、适用场景和调用规则
- 严格的 5 次工具调用上限(超限后必须给出最终回答或承认能力不足)
- 报告生成的固定流程:
get_user_id → get_current_month → fill_context_for_report → fetch_external_data - 回答规范:中文回答、结构化输出、禁止虚构信息
包含 {input} 和 {context} 占位符。要求模型:
- 严格依据参考资料回答
- 仅使用中文
- 不添加额外结构或推测
角色切换后的系统提示词,将智能体重新定位为"专业报告撰写师"。特点:
- 仅列出 4 个报告相关工具(
get_user_id、get_current_month、fetch_external_data、rag_summarize) - 要求输出 Markdown 格式,标题为"智扫通扫地机器人使用情况报告与保养建议"
| 文件 | 功能 |
|---|---|
config_handler.py |
加载 4 个 YAML 配置文件为模块级字典:rag_conf、chroma_conf、prompts_conf、agent_conf |
file_handler.py |
get_file_md5_hex() — 流式 MD5 哈希;listdir_with_allowed_type() — 按扩展名过滤文件;pdf_load() / txt_load() — 文档加载器 |
logger_handler.py |
创建名为 "agent" 的 Logger,同时输出到控制台 (INFO) 和带时间戳的文件 (DEBUG) |
path_tool.py |
get_project_root() — 根据当前文件位置推导项目根目录;get_abs_path() — 将相对路径转为绝对路径 |
prompt_loader.py |
load_system_prompts() / load_rag_prompts() / load_report_prompts() — 从 prompts.yml 配置的路径读取对应的 .txt 提示词文件 |
| 技术 | 版本 | 用途 |
|---|---|---|
| Python | 3.13+ | 编程语言 |
| LangChain | >=1.3.2 | 智能体框架(ReAct Agent、Tools、Middleware、Chains) |
| LangGraph | (依赖) | 运行时上下文与指令类型支持 |
| Streamlit | >=1.58.0 | Web 聊天界面 |
| ChromaDB | >=1.5.9 | 向量数据库,RAG 知识存储与检索 |
| DashScope | >=1.25.19 | 阿里云灵积模型服务 SDK(通义千问 LLM + 文本嵌入) |
| langchain-chroma | >=1.1.0 | LangChain 与 ChromaDB 的集成 |
| langchain-community | >=0.4.2 | 社区集成(ChatTongyi、文档加载器等) |
| pypdf | >=6.12.2 | PDF 文档解析 |
| PyYAML | >=6.0.3 | YAML 配置文件解析 |
| python-dotenv | (间接) | .env 环境变量加载 |
| uv | — | Python 包管理器 |
# 1. 克隆项目
git clone <repo-url>
cd projectAgent
# 2. 配置环境变量
# 编辑 .env 文件,填入你的 API Key
# DASHSCOPE_API_KEY=your-api-key
# DASHSCOPE_BASE_URL=https://dashscope.aliyuncs.com/api/v1
# 3. 安装依赖
uv sync
# 4. 初始化知识库(首次运行或知识库文件有更新时执行)
uv run python -m rag.vector_store
# 5. 启动 Web 应用
uv run streamlit run app.py启动后,浏览器访问 http://localhost:8501 即可使用。
用户提问
→ ReAct Agent 接收问题(main_prompt.txt 模式)
→ Agent 推理:是否需要工具?
├── 需要 → 调用工具(如 rag_summarize)
│ → RAG Service 检索 ChromaDB
│ → LLM 基于检索结果生成回答
│ → 返回工具结果给 Agent
│ → Agent 继续推理...
└── 不需要 → 直接生成最终回答
→ 流式输出到前端
用户请求报告(如"生成我的使用报告")
→ Agent 识别为报告请求
→ 执行固定流程:
1. get_user_id() → 获取用户 ID
2. get_current_month() → 获取当前月份
3. fill_context_for_report() → 触发 report_prompt_switch 中间件
→ 系统提示词自动切换为 report_prompt.txt
→ 4. fetch_external_data(user_id, month) → 查询用户使用数据
→ 5. rag_summarize(query) → 检索相关保养知识
→ Agent 整合数据,生成 Markdown 格式报告
→ 流式输出到前端
执行 python -m rag.vector_store
→ 扫描 data/ 目录所有 .txt / .pdf 文件
→ 对每个文件计算 MD5
→ 与 md5.txt 比对
├── 已存在 → 跳过
└── 新文件 → 加载文档 → 文本切分 → 向量化嵌入 → 存入 ChromaDB
→ 更新 md5.txt
| 模式 | 应用位置 | 说明 |
|---|---|---|
| ReAct Agent | agent/react_agent.py |
推理-行动循环,智能体自主决定调用工具获取信息 |
| 抽象工厂 | model/factory.py |
ChatModelFactory / EmbeddingsFactory 统一创建模型实例 |
| 中间件链 | agent/tools/middleware.py |
工具调用拦截、日志记录、动态提示词切换 |
| RAG | rag/ |
检索增强生成:文档检索 → 上下文注入 → 精准回答 |
| 单例模式 | app.py / model/factory.py |
ReactAgent 通过 st.session_state 单例化;模型实例为模块级单例 |
| 配置-代码分离 | config/ + prompts/ |
所有配置和提示词独立于代码,便于维护和调整 |
- 模拟数据:
get_weather、get_user_id、get_user_location、get_current_month目前返回的是硬编码的模拟数据,生产部署时需替换为真实 API 调用 - API Key:确保
.env中的DASHSCOPE_API_KEY有效且有足够配额 - 知识库更新:添加新知识文档到
data/目录后,需重新运行python -m rag.vector_store以入库 - Python 版本:项目要求 Python 3.13+,建议使用
uv管理环境 - ChromaDB 目录:
rag/chroma_db/是主向量数据库,agent/chroma_db/和agent/tools/chroma_db/为测试残留,可忽略