1. 项目概述
在当今自动化办公场景中,处理重复性文档工作是一个常见痛点。以销售报价单为例,每次需要根据客户信息手动修改Word文档中的多个字段,既耗时又容易出错。本期项目将构建一个能够自动填写Word报价单的智能代理(Agent)系统,通过Python实现文档的自动读取、内容填充和保存功能。
这个项目的核心价值在于:
- 解放人力:将重复性文档处理工作自动化
- 减少错误:避免人工填写可能导致的错漏
- 提高效率:批量处理文档时优势尤为明显
- 可扩展性:基础框架可应用于其他文档自动化场景
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心原理与技术选型
2.1 Agent工具调用机制
Agent系统的核心在于"思考"与"执行"的分离。AI负责决策流程,而具体操作由专用工具完成。在本项目中:
- 决策层:分析用户需求,确定操作步骤(先读后写再保存)
- 执行层:通过三个独立工具实现具体功能:
- read_file:读取文档并识别占位符
- write_content:替换文档内容
- save_file:保存结果文档
这种分离设计使得系统更易于维护和扩展,每个工具只需关注单一功能。
2.2 技术栈选择
选择python-docx库处理Word文档主要基于以下考虑:
| 技术选项 | 优势 | 适用场景 |
|---|---|---|
| python-docx | 原生支持.docx格式,API简洁 | 需要精确控制Word文档内容 |
| OpenPyXL | 支持更复杂的文档结构 | 处理包含表格、样式的文档 |
| PyPDF2 | 轻量级PDF处理 | 仅需提取文本内容时 |
python-docx特别适合本项目是因为:
- 直接操作.docx文件结构
- 支持段落和文本run级别的精确修改
- 活跃的社区支持和良好的文档
3. 详细实现步骤
3.1 环境准备与项目初始化
首先需要搭建开发环境:
bash复制# 创建项目目录结构
mkdir -p sales-agent/{templates,output}
# 安装依赖库
pip install python-docx
项目目录结构说明:
- templates/:存放报价单模板文件
- output/:保存生成的报价单
- agent.py:主程序文件
3.2 报价单模板设计
模板设计是项目成功的关键。推荐使用标准占位符格式:
markdown复制销售报价单
客户名称:【客户名称】
联系人:【联系人】
联系电话:【联系电话】
报价日期:【报价日期】
产品明细:
产品名称:【产品名称】
规格型号:【产品型号】
报价金额:【报价金额】
备注:【备注】
注意:占位符使用中文方括号【】包裹,确保程序能准确识别。避免使用特殊字符或空格。
3.3 核心工具实现
3.3.1 文件读取工具
python复制from docx import Document
import re
def read_file(file_path):
"""
读取Word文档并识别占位符
:param file_path: Word文档路径
:return: (文档内容文本, 占位符列表)
"""
try:
doc = Document(file_path)
full_text = []
placeholders = set()
# 识别段落中的占位符
placeholder_pattern = re.compile(r'【(.*?)】')
for para in doc.paragraphs:
text = para.text
full_text.append(text)
matches = placeholder_pattern.findall(text)
if matches:
placeholders.update(matches)
return '\n'.join(full_text), list(placeholders)
except Exception as e:
print(f"❌ 文件读取失败: {str(e)}")
return None, None
3.3.2 内容写入工具
python复制def write_content(template_path, field_data):
"""
将数据写入文档模板
:param template_path: 模板文件路径
:param field_data: 字段数据字典
:return: 修改后的文档对象
"""
try:
doc = Document(template_path)
for para in doc.paragraphs:
for run in para.runs:
text = run.text
# 替换所有占位符
for field, value in field_data.items():
placeholder = f'【{field}】'
if placeholder in text:
run.text = text.replace(placeholder, str(value))
print(f"✓ 已填写字段: {field} → {value}")
return doc
except Exception as e:
print(f"❌ 内容写入失败: {str(e)}")
return None
3.3.3 文件保存工具
python复制import os
def save_file(doc, output_path):
"""
保存文档到指定路径
:param doc: 文档对象
:param output_path: 输出路径
:return: 是否保存成功
"""
try:
# 确保输出目录存在
os.makedirs(os.path.dirname(output_path), exist_ok=True)
doc.save(output_path)
print(f"✅ 文件已保存至: {output_path}")
return True
except Exception as e:
print(f"❌ 文件保存失败: {str(e)}")
return False
3.4 主程序集成
python复制def main():
# 1. 读取模板
template_path = "templates/报价单模板.docx"
text, placeholders = read_file(template_path)
if not placeholders:
print("未识别到有效占位符,请检查模板格式")
return
print("📋 识别到占位符:", placeholders)
# 2. 准备测试数据
test_data = {
"客户名称": "深圳科技有限公司",
"联系人": "李总",
"联系电话": "138-0000-0000",
"报价日期": "2025年6月15日",
"产品名称": "工业传感器套件",
"产品型号": "XS-2000B",
"报价金额": "58,000元",
"备注": "含安装调试服务"
}
# 3. 写入内容
filled_doc = write_content(template_path, test_data)
if not filled_doc:
return
# 4. 保存结果
output_path = "output/报价单_测试.docx"
if save_file(filled_doc, output_path):
print("🎉 报价单生成完成!")
if __name__ == "__main__":
main()
4. 常见问题与解决方案
4.1 占位符识别问题
问题现象:程序运行后占位符未被正确替换
排查步骤:
- 检查模板文件是否使用【】包裹占位符
- 确认python-docx版本不低于0.8.11
- 检查字段数据字典的键是否与占位符内容完全匹配
解决方案:
python复制# 在write_content函数中添加调试输出
print("待替换字段:", field_data.keys())
print("找到占位符:", re.findall(r'【(.*?)】', para.text))
4.2 格式丢失问题
问题现象:生成文档后原有格式(如字体、颜色)丢失
原因分析:直接替换run.text会清除原有格式属性
改进方案:
python复制# 修改write_content函数中的替换逻辑
original_runs = para.runs
para.clear()
for run in original_runs:
new_run = para.add_run(run.text.replace(placeholder, str(value)))
# 保留原格式
new_run.bold = run.bold
new_run.italic = run.italic
new_run.font.name = run.font.name
4.3 性能优化建议
当处理大量文档时,可采用以下优化措施:
- 批量处理:一次性读取多个模板文件
- 多线程:使用concurrent.futures处理独立文档
- 缓存机制:对不变模板缓存Document对象
python复制from concurrent.futures import ThreadPoolExecutor
def batch_process(templates, data_list):
with ThreadPoolExecutor() as executor:
results = list(executor.map(
lambda x: process_single(x[0], x[1]),
zip(templates, data_list)
))
return results
5. 进阶扩展方向
5.1 支持更多文档类型
现有框架可轻松扩展支持其他Office文档:
-
Excel处理:使用openpyxl库
python复制from openpyxl import load_workbook wb = load_workbook('template.xlsx') ws = wb.active ws['A1'] = new_value -
PDF处理:结合pdfkit和jinja2模板
python复制from jinja2 import Template template = Template(source) html = template.render(data) pdfkit.from_string(html, 'output.pdf')
5.2 集成到工作流系统
将Agent集成到现有业务系统中:
-
API接口:使用Flask暴露服务
python复制@app.route('/generate_quote', methods=['POST']) def generate_quote(): data = request.json doc = write_content(data['template'], data['fields']) save_file(doc, data['output_path']) return jsonify({'status': 'success'}) -
定时任务:通过APScheduler实现自动生成
python复制from apscheduler.schedulers.background import BackgroundScheduler scheduler = BackgroundScheduler() scheduler.add_job(generate_reports, 'cron', hour=2) scheduler.start()
5.3 加入AI决策能力
使用大语言模型增强决策能力:
-
自然语言接口:
python复制def parse_natural_language(command): # 调用LLM解析用户指令 response = llm_client.chat( messages=[{"role": "user", "content": command}] ) return extract_parameters(response) -
智能字段映射:
python复制def smart_field_mapping(user_input, placeholders): # 自动匹配用户提供的信息与模板占位符 prompt = f"将用户输入映射到占位符:\n输入:{user_input}\n占位符:{placeholders}" return llm_client.complete(prompt)
6. 实际应用建议
- 版本控制:将模板文件纳入Git管理,确保变更可追溯
- 日志记录:记录每次生成的文档参数和结果
python复制import logging logging.basicConfig(filename='agent.log', level=logging.INFO) logging.info(f"Generated {output_path} for {field_data}") - 异常处理:添加重试机制应对临时故障
python复制from tenacity import retry, stop_after_attempt @retry(stop=stop_after_attempt(3)) def read_file_with_retry(file_path): return read_file(file_path)
在真实业务场景中部署时,建议先在测试环境充分验证,特别是处理重要业务文档时。可以逐步替换人工流程,先让系统生成初稿,人工复核后再正式使用。
