agent+fastapi+docker完整部署项目实战教学
扫地机器人智能客服 Agent
- 项目地址:gitee
基于 LangChain ReAct 框架构建的扫地机器人 / 扫拖一体机器人智能客服系统,支持知识库问答、天气适配查询、用户个性化使用报告生成等功能,并通过中间件机制实现动态提示词切换与全链路日志监控。
目录
项目概览
本项目是一个面向扫地机器人领域的垂直智能客服 Agent,核心能力包括:
- 知识库问答(RAG):将产品手册、选购指南、维护保养、故障排除等文档向量化存储,支持语义检索后结合 LLM 生成精准回答。
- 环境适配查询:自动获取用户所在城市与实时天气,判断环境对机器人使用的影响。
- 个性化使用报告:根据用户 ID 和月份,从外部 CSV 数据中检索使用记录,生成结构化 Markdown 报告并给出保养建议。
- 动态提示词切换:通过中间件机制,在报告生成场景下自动将主提示词切换为专业报告提示词。
- 全链路日志:记录每次工具调用、模型调用的详细信息,同时输出到控制台和日志文件。
项目结构
Agent-test/
├── config.py # 全局配置入口(路径、日志格式)
├── .env # API Key 配置(不提交到版本库)
│
├── config/ # YAML 配置文件
│ ├── rag.yml # 模型名称与接口地址
│ ├── chroma.yml # 向量库参数(分块、存储路径等)
│ ├── prompts.yml # 提示词文件路径映射
│ └── agent.yml # 外部数据文件路径
│
├── model/
│ └── factory.py # 模型工厂(ChatModel / EmbeddingModel)
│
├── rag/
│ ├── vector_store.py # 向量库管理(加载文档、增量存储、检索)
│ └── rag_summarize.py # RAG 链(检索 + LLM 总结)
│
├── agent/
│ ├── react_agent.py # ReAct Agent 入口
│ └── tools/
│ ├── agent_tools.py # 7 个 Agent 工具定义
│ └── middleware.py # 3 个中间件(工具监控、模型前置日志、动态提示词)
│
├── prompts/
│ ├── main_prompt.txt # 主客服提示词
│ ├── rag_summarize_prompt.txt # RAG 总结提示词
│ └── report_prompt.txt # 报告生成提示词
│
├── utils/
│ ├── config_handler.py # YAML 配置加载
│ ├── logger_handler.py # 日志初始化
│ ├── prompt_handler.py # 提示词文件读取
│ ├── file_handler.py # 文档加载(txt/pdf)与 MD5 工具
│ └── path_tool.py # 绝对路径工具
│
├── data/
│ ├── *.txt / *.pdf # 知识库文档(产品问答、维护、选购等)
│ └── external/
│ └── records.csv # 用户使用记录(用户ID、月份、清洁数据)
│
├── chroma_db/ # ChromaDB 持久化目录(运行后自动生成)
│ └── md5.txt # 已处理文档的 MD5 记录(增量更新用)
│
└── logs/ # 运行日志(运行后自动生成)
核心模块说明
1. 模型工厂 model/factory.py
使用抽象基类 BaseModel 统一管理模型初始化,子类分别实现 Chat 模型和 Embedding 模型。
class ChatModel(BaseModel): # 对话模型,默认 DeepSeek
class EmbeddingModel(BaseModel): # 向量模型,默认 text-embedding-3-large
API Key 从 .env 文件读取,模型名称和接口地址从 config/rag.yml 读取,两端解耦,切换模型只需改 YAML,切换 Key 只需改 .env。
模块末尾直接导出单例:
chat_model = ChatModel().generator()
embedding_model = EmbeddingModel().generator()
其他模块直接 from model.factory import chat_model 使用即可。
2. 向量数据库与 RAG
rag/vector_store.py — VectorStore
管理 ChromaDB 向量库的全生命周期:
- 初始化:读取
config/chroma.yml中的 collection 名、存储路径、分块参数,创建Chroma实例和RecursiveCharacterTextSplitter。 - 增量加载
load_document():- 创建
loading.lock锁文件防止并发重复加载。 - 遍历
data/目录下所有允许类型(txt/pdf)的文件。 - 计算文件 MD5,与
chroma_db/md5.txt中已记录的哈希比对,已处理的文件跳过,实现增量更新。 - 读取文档 → 分块 → 写入向量库 → 记录 MD5。
- 创建
- 获取检索器
get_retriever():返回 top-k 语义检索器(k 由配置决定,默认 3)。
rag/rag_summarize.py — RagSummarize
将检索与生成串联为一条 LangChain 链:
PromptTemplate → (可选打印调试) → chat_model → StrOutputParser
rag_summarize(input) 方法:
- 调用检索器获取 top-k 相关文档片段。
- 将文档片段拼接为带编号的
context字符串。 - 注入
{input}和{context}到提示词模板,调用链返回总结文本。
提示词约束模型严格基于参考资料回答,不编造内容,仅输出纯文本。
3. Agent 工具 agent/tools/agent_tools.py
共 7 个工具,通过 @tool 装饰器注册:
| 工具名 | 入参 | 功能 |
|---|---|---|
rag_summarize |
input(检索词) |
从向量库检索相关资料并总结 |
get_weather |
city(城市名) |
返回指定城市天气(当前为 Mock) |
get_user_location |
user |
随机返回用户所在城市(当前为 Mock) |
get_uesr_id |
user |
随机返回用户 ID(当前为 Mock) |
get_uesr_time |
user |
随机返回当前月份,格式 YYYY-MM(当前为 Mock) |
get_external_data |
user_id, user_time |
从 CSV 中检索用户指定月份的使用记录 |
fill_context_for_report |
无 | 触发中间件,将运行时上下文 report 标记为 True |
get_external_data 使用懒加载模式,首次调用时将整个 CSV 解析为嵌套字典 { user_id: { month: {...} } } 并缓存到模块级变量,后续调用直接读内存。
fill_context_for_report 本身不做任何业务逻辑,仅作为信号工具——当 Agent 调用它时,monitor_tool 中间件捕获到此调用并将 request.runtime.context['report'] 置为 True,触发后续的提示词切换。
4. 中间件 agent/tools/middleware.py
三个中间件挂载到 Agent 的执行链路上:
monitor_tool(@wrap_tool_call)
每次工具调用前后均会执行:
- 调用前记录工具名和入参(INFO 级别)。
- 执行工具,捕获异常并记录错误日志。
- 调用成功后,若工具名为
fill_context_for_report,将request.runtime.context['report']置为True。
log_before_model(@before_model)
在每次模型调用前执行:
- INFO 级别记录当前消息条数。
- DEBUG 级别记录最后一条消息的类型和内容(截断显示)。
report_prompt_switch(@dynamic_prompt)
每次生成提示词之前自动触发:
- 读取
request.runtime.context['report']标志。 - 若为
True,加载prompts/report_prompt.txt作为系统提示词。 - 否则加载
prompts/main_prompt.txt(默认客服提示词)。
这三个中间件共同实现了工具监控 → 状态感知 → 动态提示词注入的完整链路。
5. ReAct Agent agent/react_agent.py
class ReactAgent():
def __init__(self):
self.agent = create_agent(
model=chat_model,
system_prompt=None, # 提示词由 dynamic_prompt 中间件动态注入
tools=[...], # 7 个工具
middleware=[...], # 3 个中间件
)
def execute_stream(self, input):
# 以流式方式执行,逐块 yield 输出
system_prompt=None:主提示词完全由report_prompt_switch中间件在运行时动态决定,无需硬编码。stream_mode='values':每次 yield Agent 状态中最新一条消息的内容。context={'report': False}:初始化运行时上下文,确保首次提示词切换逻辑正常工作。
入口示例:
agent = ReactAgent()
for chunk in agent.execute_stream("扫地机器人在我所在的地区的气温下如何保养"):
print(chunk, end="", flush=True)
6. 配置系统
所有配置分离到 config/ 目录下的 YAML 文件,通过 utils/config_handler.py 统一加载:
| 文件 | 内容 |
|---|---|
config/rag.yml |
对话模型名/URL、Embedding 模型名/URL |
config/chroma.yml |
ChromaDB collection 名、持久化路径、top-k、数据目录、分块参数 |
config/prompts.yml |
三个提示词文件的相对路径 |
config/agent.yml |
外部 CSV 数据文件路径 |
config.py 是全局入口,定义各 YAML 的绝对路径及日志格式,其他模块通过 config_handler 获取已解析的字典对象。
7. 提示词
| 文件 | 用途 | 关键约束 |
|---|---|---|
prompts/main_prompt.txt |
主客服系统提示词 | ReAct 思考框架、7 个工具使用规范、报告生成固定调用链约束 |
prompts/rag_summarize_prompt.txt |
RAG 总结提示词 | 严格基于参考资料、纯文本输出、不编造 |
prompts/report_prompt.txt |
报告生成提示词 | Markdown 格式输出、给出保养建议、不直接输出原始查询数据 |
提示词中对报告生成有强约束:Agent 必须按照 get_uesr_id → get_uesr_time → fill_context_for_report → get_external_data 的固定顺序调用工具,不得跳步。
8. 日志系统
utils/logger_handler.py 封装了标准 logging:
- 控制台:INFO 级别及以上实时输出。
- 文件:DEBUG 级别及以上写入
logs/agent_<时间戳>.log,每次启动创建新文件。 - 日志格式:
时间 - logger名 - 级别 - 文件名:行号 - 消息。
模块末尾导出单例 logger,各模块 from utils.logger_handler import logger 直接使用。
数据说明
知识库文档 data/
存放扫地机器人相关的 txt/pdf 文档,当前包含:
扫地机器人100问.pdf/扫地机器人100问2.txt扫拖一体机器人100问.txt故障排除.txt维护保养.txt选购指南.txt
首次运行时 VectorStore.load_document() 会自动将上述文档分块并写入 ChromaDB。后续再次运行时通过 MD5 比对跳过已处理文件,向 data/ 目录新增文档后无需额外操作,下次启动自动增量入库。
外部使用记录 data/external/records.csv
字段:用户ID, 特征, 清洁效率, 耗材, 对比, 时间
包含用户 ID 1001~1010 在 2025-01 至 2025-12 的逐月使用记录,覆盖面积特征、清洁覆盖率、耗材寿命、横向对比等维度。
快速开始
1. 安装依赖
pip install langchain langchain-openai langchain-chroma langchain-text-splitters python-dotenv pyyaml
2. 配置 API Key
在项目根目录创建 .env 文件:
CHAT_API_KEY=your_chat_api_key
EMBEDDING_API_KEY=your_embedding_api_key
3. 配置模型(可选)
编辑 config/rag.yml,修改模型名称和接口地址:
chat_model_name: "deepseek-chat"
chat_model_url: "https://api.deepseek.com/v1"
embedding_model_name: "text-embedding-3-large"
embedding_model_url: "https://your-embedding-api/v1"
4. 初始化向量库
首次使用前需要将知识库文档写入向量数据库:
python rag/vector_store.py
执行后 chroma_db/ 目录下会生成持久化数据,md5.txt 记录已处理文件的哈希。
5. 运行 Agent
python agent/react_agent.py
默认执行一条测试问题:扫地机器人在我所在的地区的气温下如何保养。
如需集成到其他入口,直接实例化并调用:
from agent.react_agent import ReactAgent
agent = ReactAgent()
for chunk in agent.execute_stream("帮我生成本月的使用报告"):
print(chunk, end="", flush=True)
配置说明
| 配置项 | 文件 | 说明 |
|---|---|---|
chat_model_name |
config/rag.yml |
对话模型 ID |
chat_model_url |
config/rag.yml |
对话模型 API 地址(OpenAI 兼容格式) |
embedding_model_name |
config/rag.yml |
向量模型 ID |
embedding_model_url |
config/rag.yml |
向量模型 API 地址 |
collection_name |
config/chroma.yml |
ChromaDB 集合名 |
persist_directory |
config/chroma.yml |
ChromaDB 持久化目录 |
k |
config/chroma.yml |
RAG 检索返回文档数(top-k) |
chunk_size |
config/chroma.yml |
文本分块大小(字符数) |
chunk_overlap |
config/chroma.yml |
分块重叠大小 |
data_path |
config/chroma.yml |
知识库文档目录 |
allow_type |
config/chroma.yml |
允许的文档类型列表 |
external_data_path |
config/agent.yml |
用户使用记录 CSV 路径 |
主要流程图
普通问答流程
用户提问
└─> ReactAgent.execute_stream()
└─> report_prompt_switch 加载 main_prompt
└─> LLM 思考 → 决定调用工具
├─> rag_summarize(知识库检索+总结)
├─> get_user_location → get_weather(天气查询)
└─> LLM 整合信息 → 生成回答 → 流式输出
报告生成流程
用户请求报告
└─> ReactAgent
└─> LLM 识别报告意图
├─> get_uesr_id(获取用户ID)
├─> get_uesr_time(获取当前月份)
├─> fill_context_for_report
│ └─> monitor_tool 中间件: context['report'] = True
├─> get_external_data(读取CSV使用记录)
└─> report_prompt_switch 检测到 report=True
└─> 切换为 report_prompt → LLM 生成 Markdown 报告
更多推荐

所有评论(0)