CrewAI DOCXSearchTool 实战:为 DOCX 文档构建语义搜索工具的完整解析
本文围绕 CrewAI 工具包中的 DOCXSearchTool 展开:它是一个面向 Word 文档的 RAG(检索增强生成)工具,让 Agent 能够以自然语言查询的形式在 .docx 文件中做语义搜索与内容提取,适用于数据分析、信息管理和研究类任务。读完本文,你将掌握该工具的两种初始化方式、参数含义、底层加载/分块/检索的调用链路,以及自定义向量库与 Embedding 的配置方法。
工具定位:面向 DOCX 文档的语义检索
DOCXSearchTool 的官方定位是一个"用于在 DOCX 文档内进行语义搜索的 RAG 工具",它支持基于查询词(query-based search)从 Word 文档中检索并抽取相关信息,从而简化在大型文档集合中定位特定信息的流程。
从源码结构看,该工具位于 docx_search_tool.py,是一个 RagTool 的子类,注册名为 "Search a DOCX's content":
class DOCXSearchTool(RagTool):
name: str = "Search a DOCX's content"
description: str = (
"A tool that can be used to semantic search a query from a DOCX's content."
)
args_schema: type[BaseModel] = DOCXSearchToolSchema
它通过包级导出对外暴露,crewai_tools/init.py 与 tools/init.py 均在 __all__ 中导出了 DOCXSearchTool,因此可以直接 from crewai_tools import DOCXSearchTool。
安装方式
在终端执行以下命令安装 crewai_tools 包:
pip install 'crewai[tools]'
需要注意的一点:文档内容解析依赖 python-docx 库。从 DOCXLoader 的实现看,它采用延迟导入,缺失时会抛出明确的安装指引:
python-docx is required for DOCX loading. Install with: 'uv pip install python-docx' or pip install crewai-tools[rag]
两种初始化模式:通用模式与锁定单一文档
通用模式:允许运行时指定任意 DOCX 文件
from crewai_tools import DOCXSearchTool
# 初始化为"可搜索任意 DOCX 文件内容"的工具
tool = DOCXSearchTool()
锁定模式:只搜索指定文档
# 初始化时绑定特定 DOCX 文件,Agent 只能搜索该文件的指定内容
tool = DOCXSearchTool(docx='path/to/your/document.docx')
两种模式的差异在源码中体现得非常清晰。构造函数 DOCXSearchTool.init 会根据是否传入 docx 参数切换行为:
def __init__(self, docx: str | None = None, **kwargs: Any) -> None:
super().__init__(**kwargs)
if docx is not None:
self.add(docx)
self.description = f"A tool that can be used to semantic search a query the {docx} DOCX's content."
self.args_schema = FixedDOCXSearchToolSchema
self._generate_description()
关键行为有三点:
- 立即加载文档:
self.add(docx)会在初始化时就把文档载入知识库,add方法固定以DataType.DOCX类型登记; - 描述被改写:description 中被拼入具体文件路径,让 LLM 在工具选择时明确知道检索范围;
- 参数模式被收紧:
args_schema从DOCXSearchToolSchema切换为FixedDOCXSearchToolSchema。
这两个 Schema 的定义也值得对照阅读(docx_search_tool.py):
class FixedDOCXSearchToolSchema(BaseModel):
"""Input for DOCXSearchTool."""
docx: str | None = Field(
..., description="File path or URL of a DOCX file to be searched"
)
search_query: str = Field(
...,
description="Mandatory search query you want to use to search the DOCX's content",
)
class DOCXSearchToolSchema(FixedDOCXSearchToolSchema):
"""Input for DOCXSearchTool."""
search_query: str = Field(
...,
description="Mandatory search query you want to use to search the DOCX's content",
)
从源码结构看,锁定模式下 docx 字段仍为 Optional(默认允许为 None),即 Agent 在锁定模式下调用工具时仍可以传入额外的 docx 参数,触发"运行中追加文档"的逻辑——这由 _run 方法兜底处理:
def _run(
self,
search_query: str,
docx: str | None = None,
similarity_threshold: float | None = None,
limit: int | None = None,
) -> Any:
if docx is not None:
self.add(docx)
return super()._run(
query=search_query, similarity_threshold=similarity_threshold, limit=limit
)
也就是说,即使初始化为通用模式,Agent 每次调用时携带的 docx 路径也会被先增量入库、再执行检索,天然支持"多轮对话中逐个引入新文档"的场景。
参数说明
| 参数 | 位置 | 说明 |
|---|---|---|
docx |
构造函数 / 运行参数 | 可选。指定要搜索的 DOCX 文档的文件路径或 URL。初始化时不提供,则允许在后续调用时再指定任意 DOCX 文件路径 |
search_query |
运行参数 | 必填。用于语义检索 DOCX 内容的查询语句 |
similarity_threshold |
运行参数 / 工具字段 | 相似度阈值,None 时回落到工具默认值 0.6(见 RagTool 的字段定义) |
limit |
运行参数 / 工具字段 | 返回的检索结果条数上限,None 时回落到默认值 5 |
config |
构造函数(透传给 RagTool) |
配置字典,可自定义向量库、Embedding 等,见下一节 |
补充说明 RagTool 上与检索行为直接相关的默认字段(rag_tool.py):
summarize: bool = False——是否对检索结果做摘要;similarity_threshold: float = 0.6——默认相似度阈值;limit: int = 5——默认返回条数;collection_name: str = "rag_tool_collection"——向量集合名称。
_run 的返回结果会被格式化为 Relevant Content:\n{...} 的字符串,即把命中的相关片段直接喂给 Agent。
底层链路:加载、分块与检索
DOCXSearchTool 本身并不直接解析 Word 文件,它的 add 方法将路径标记为 DataType.DOCX 后交给上层 RAG 管道处理。整条链路在 data_types.py 的工厂映射中定义:
chunkers = {
...
DataType.DOCX: ("text_chunker", "DocxChunker"),
...
}
loaders = {
...
DataType.DOCX: ("docx_loader", "DOCXLoader"),
...
}
DOCXLoader:如何提取文档文本
DOCXLoader 的 load 方法支持两类输入源:
- 本地文件路径:文件存在时直接交给
python-docx的Document解析; - HTTP(S) URL:先通过安全请求封装
safe_get下载到临时文件(请求头携带Accept: application/vnd.openxmlformats-officedocument.wordprocessingml.document),解析完成后删除临时文件; - 两者都不是时抛出
ValueError: Source must be a valid file path or URL。
文本提取策略是遍历 doc.paragraphs,过滤空白段落后用换行符拼接全部正文,并附带元数据:
metadata = {
"format": "docx",
"paragraphs": len(doc.paragraphs),
"tables": len(doc.tables),
}
注意从源码看,当前版本提取的是段落文本,元数据中虽记录了表格数量,但表格内容并未并入正文内容。对应的行为在测试文件 test_docx_loader.py 中有完整覆盖,包括本地加载、URL 下载(含自定义 headers)、网络错误、无效来源、空文档以及 doc_id 稳定性等用例,例如 test_load_docx_from_file 验证了内容拼接结果与 {"format": "docx", "paragraphs": 3, "tables": 0} 的元数据断言。
DocxChunker:分块参数
提取出的全文交给 DocxChunker 分块,默认参数为:
class DocxChunker(BaseChunker):
def __init__(
self,
chunk_size: int = 2500,
chunk_overlap: int = 250,
...
):
即默认 2500 字符/块、250 字符重叠,分隔符优先级依次为多级换行、句号、感叹号、问号、分号、逗号、空格,尽量把切分点落在语义边界上。
检索端:RagTool 与默认 ChromaDB
父类 RagTool 在模型校验阶段(_ensure_adapter)会为工具构建 CrewAIRagAdapter,并从 RagToolConfig 解析向量库配置;_parse_config 的默认行为是:未提供 vectordb 时使用本地 chromadb,显式配置时仅支持 chromadb 与 qdrant 两种 provider。add 方法在进入适配器前还会经过路径与 URL 的安全校验(validate_file_path / validate_url),用于拦截不安全的文件读取与 SSRF 风险,这也是文档路径"必须是合法路径或 URL"这一约束的底层来源。
自定义模型与 Embedding
README 给出的默认行为说明是:工具默认使用 OpenAI 完成 Embedding 与摘要。README 中的自定义示例如下:
tool = DOCXSearchTool(
config=dict(
llm=dict(
provider="ollama", # 或 google、openai、anthropic、llama2 等
config=dict(
model="llama2",
# temperature=0.5,
# top_p=1,
# stream=true,
),
),
embedder=dict(
provider="google",
config=dict(
model="models/embedding-001",
task_type="retrieval_document",
# title="Embeddings",
),
),
)
)
补充一点源码侧的现状:当前 types.py 中定义的 RagToolConfig 以 embedding_model(Embedding 模型配置,接受 ProviderSpec)与 vectordb(provider 为 chromadb 或 qdrant,附 provider 级 config)为主要字段,_parse_config 会通过 build_embedder 把 Embedding 规格实例化后注入 ChromaDB/Qdrant 配置。如果你在使用中发现 README 示例的键名与实际校验报错不一致,建议以当前版本 RagToolConfig 的实际字段和报错信息为准——RagTool 内还专门实现了 _validate_embedding_config,会把 18 个 provider 联合类型的校验错误收窄为只报当前 provider 的问题,便于定位配置错误。
典型使用场景小结
结合源码行为,可以归纳出三类典型用法:
- 单一文档问答:
DOCXSearchTool(docx='report.docx')后交给 Agent,Agent 只能通过search_query检索该报告内容,工具描述中已包含文件路径,便于 LLM 理解工具边界; - 多文档按需检索:使用通用模式
DOCXSearchTool(),由 Agent 在每轮调用中携带docx路径,文档会随调用增量入库并参与后续检索; - URL 文档接入:由于
DOCXLoader支持 URL 输入,docx参数传入https://.../file.docx时会自动下载、解析并清理临时文件,可用于挂载在线文档。
参考文件索引
- 工具说明文档:README.md
- 工具实现:docx_search_tool.py
- RAG 基类:rag_tool.py
- 配置类型定义:types.py
- 数据类型与加载器/分块器工厂:data_types.py
- DOCX 加载器:docx_loader.py
- DOCX 分块器:text_chunker.py
- 加载器测试:test_docx_loader.py
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00