18 KiB
| date | status | origin |
|---|---|---|
| 2026-06-23 | active | docs/brainstorms/2026-06-23-document-processing-capability-requirements.md |
feat: Document Processing Capability
Summary
为 AgentKit 增加文档处理能力,v1 聚焦 Word/Excel/PDF 三种格式的创建和读取,以及 Word 模板填充。通过自研 DocumentService 统一封装所有文档操作(python-docx/openpyxl/reportlab/python-docx-template),Agent 工具和前端 REST API 共用同一套业务逻辑。生成的文档保存在服务器并持久化元数据,对话中返回文件卡片,同时在右侧面板展示当前对话的文档列表。
Problem Frame
当前 Agent 工具集没有格式化文档处理能力。用户需要生成报告、合同、数据表等文档时,Agent 只能通过 shell 创建纯文本文件。
原计划集成 MCP Document Tools,但功能验证发现:版本 0.1.0 未验证状态不建议生产使用、不支持模板填充(核心需求)、Office→PDF 仅限 docx。因此改为全部自研,使用成熟的 python-docx/openpyxl/reportlab/python-docx-template 库,完全可控且无外部依赖风险。
Requirements
Traceability to origin requirements doc (R-IDs preserved):
- R1-R4: 文档处理能力(Word/Excel/PDF 创建 + 读取)
- R5-R8: Agent 工具集成
- R9-R10: 前端界面
- R11-R16: 文件存储与生命周期
- R17-R18: 对话中文档展示
- R19-R22: 右侧文档/附件面板
- R23-R25: 模板填充(Word only)
- R26-R28: 安全
Key Technical Decisions
- 自研而非 MCP 集成 — MCP Document Tools 版本 0.1.0 未验证、不支持模板填充、不建议生产使用。改用成熟的生产级库:python-docx(Word)、openpyxl(Excel)、reportlab(PDF)、python-docx-template(Word 模板填充)。MCP Document Tools 降级为可选增强,不在 v1 范围。
- DocumentService 统一封装 — DocumentService 作为唯一业务逻辑层,Agent 工具和前端 REST API 都是薄封装。内部按格式分派到对应的 renderer 模块。
- Agent 生成 Markdown,Service 负责格式映射 — Agent 生成 Markdown 格式的结构化内容,DocumentService 内部有 Markdown→Word/Excel/PDF 的 renderer,将 Markdown 结构映射为目标格式。Agent 不直接操作 Office XML。
- 数据库用 aiosqlite 裸连接 — 遵循项目现有模式(auth.py 的
aiosqlite.connect),不引入 SQLAlchemy session 依赖注入。文档元数据表用原生 SQL 建表。 - Jinja2 沙箱化 — 模板填充使用
jinja2.sandbox.SandboxedEnvironment,防止 SSTI 攻击。 - 文件存储复用 data/uploads/ — 复用现有上传目录和
_sanitize_filename函数,但下载 API 新增认证。
Implementation Units
U1. DocumentService 核心架构 + 数据库模型
Goal: 建立 DocumentService 骨架和文档元数据持久化基础。
Requirements: R11, R13, R14, R15, R16
Dependencies: 无
Files:
src/agentkit/documents/__init__.py(新建)src/agentkit/documents/service.py(新建)src/agentkit/documents/models.py(新建)src/agentkit/documents/db.py(新建)pyproject.toml(修改:添加 python-docx, openpyxl, reportlab, docxtpl, jinja2 依赖)
Approach:
DocumentService类:create_document(format, content, conversation_id, template_path?) -> DocumentMeta、get_conversation_documents(conversation_id) -> list[DocumentMeta]、get_download_path(doc_id) -> PathDocumentMetadataclass:id, filename, stored_name, format, size, conversation_id, created_at, download_url- 数据库表
documents:id (UUID), filename, stored_name, format, size, conversation_id, created_at。用 aiosqlite 裸连接,init_documents_db()建表。 - 文件存储:UUID + 扩展名,存到
data/uploads/,复用_sanitize_filename。
Patterns to follow: src/agentkit/server/auth/models.py(aiosqlite 模式)、src/agentkit/server/routes/chat.py 的 _sanitize_filename 函数。
Test scenarios:
- Happy path: 创建文档元数据记录,查询返回正确数据
- Edge case: 不存在的 conversation_id 返回空列表
- Edge case: 文件名包含路径遍历字符(../)被清洗
- Integration: init_documents_db 幂等(重复调用不报错)
Verification: 运行 pytest tests/documents/test_db.py,确认元数据 CRUD 和文件存储正常。
U2. Word 文档创建(python-docx + Markdown→Word 映射)
Goal: 实现 Markdown→Word 的格式映射,Agent 生成 Markdown 内容,DocumentService 生成 .docx 文件。
Requirements: R1
Dependencies: U1
Files:
src/agentkit/documents/renderers/__init__.py(新建)src/agentkit/documents/renderers/word_renderer.py(新建)tests/documents/test_word_renderer.py(新建)
Approach:
WordRenderer.render(markdown_content: str, output_path: Path) -> Path- Markdown 解析:用
markdown库解析为 AST,遍历 AST 映射到 python-docx 对象:# 标题→doc.add_heading(text, level=1)## 二级标题→doc.add_heading(text, level=2)- 段落 →
doc.add_paragraph(text) - 列表项→doc.add_paragraph(text, style='List Bullet')1. 有序列表→doc.add_paragraph(text, style='List Number')- Markdown 表格 →
doc.add_table(rows, cols)+ 填充 **粗体**→ run withbold=True*斜体*→ run withitalic=True
Patterns to follow: python-docx 官方文档的基本用法。
Test scenarios:
- Happy path: 包含标题、段落、列表、表格的 Markdown 生成正确的 .docx
- Edge case: 空 Markdown 生成空文档(只有标题或完全空)
- Edge case: 嵌套格式(粗体+斜体混合)正确渲染
- Error path: 无效 Markdown 不崩溃,按纯文本处理
Verification: 运行 pytest tests/documents/test_word_renderer.py,打开生成的 .docx 确认格式正确。
U3. Excel 文档创建(openpyxl + Markdown 表格→Excel 映射)
Goal: 实现 Markdown 表格/JSON→Excel 的格式映射。
Requirements: R2
Dependencies: U1
Files:
src/agentkit/documents/renderers/excel_renderer.py(新建)tests/documents/test_excel_renderer.py(新建)
Approach:
ExcelRenderer.render(markdown_content: str, output_path: Path) -> Path- 解析 Markdown 中的表格(
| col1 | col2 |格式),每个表格映射到一个 worksheet - 非表格文本(标题、段落)作为注释行或单独的 "Summary" sheet
- 支持 JSON 格式输入:
{"Sheet1": [["A1","B1"],["A2","B2"]]}(当 content 是有效 JSON 时走 JSON 路径)
Patterns to follow: openpyxl 官方文档的基本用法。
Test scenarios:
- Happy path: Markdown 表格生成正确的 .xlsx,数据对齐
- Happy path: JSON 格式输入生成多 sheet Excel
- Edge case: 无表格的 Markdown 生成单 sheet 纯文本
- Edge case: 多个表格生成多个 sheet
Verification: 运行 pytest tests/documents/test_excel_renderer.py,打开生成的 .xlsx 确认数据正确。
U4. PDF 文档创建(reportlab + Markdown→PDF 映射)
Goal: 实现 Markdown→PDF 的格式映射,使用 reportlab 生成 PDF。
Requirements: R3
Dependencies: U1
Files:
src/agentkit/documents/renderers/pdf_renderer.py(新建)tests/documents/test_pdf_renderer.py(新建)
Approach:
PDFRenderer.render(markdown_content: str, output_path: Path) -> Path- 用 reportlab 的
SimpleDocTemplate+Paragraph+Table+ListFlowable - Markdown 解析同 U2,映射到 reportlab flowables:
# 标题→Paragraph(text, Heading1 style)- 段落 →
Paragraph(text, Normal style) - 列表 →
ListFlowable([ListItem(...)]) - 表格 →
Table(data)+ 基础样式 **粗体**→<b>text</b>(reportlab Paragraph 支持 HTML 标签)
Patterns to follow: reportlab 官方文档。
Test scenarios:
- Happy path: 包含标题、段落、列表、表格的 Markdown 生成正确的 PDF
- Edge case: 空 Markdown 生成空白 PDF
- Edge case: 中文字符正确渲染(需注册中文字体)
- Error path: 无效 Markdown 不崩溃
Verification: 运行 pytest tests/documents/test_pdf_renderer.py,打开生成的 PDF 确认格式和中文渲染。
U5. Word 模板填充(python-docx-template + Jinja2 沙箱)
Goal: 实现 Word 模板填充,用户上传 .docx 模板,Agent 提供数据,填充 Jinja2 占位符。
Requirements: R23, R24, R25, R26
Dependencies: U1, U2
Files:
src/agentkit/documents/renderers/template_renderer.py(新建)tests/documents/test_template_renderer.py(新建)
Approach:
TemplateRenderer.render(template_path: Path, data: dict, output_path: Path) -> Path- 用
docxtpl.DocxTemplate(template_path)加载模板 - 用
jinja2.sandbox.SandboxedEnvironment创建沙箱环境 template.render(data)填充数据- 支持
{{variable}}、{% if %}、{% for %}基本控制结构
Patterns to follow: python-docx-template 官方文档。
Test scenarios:
- Happy path: 模板包含
{{name}},data={"name":"张三"},输出文档中 "张三" 替换占位符 - Happy path:
{% for item in items %}循环正确展开 - Happy path:
{% if condition %}条件渲染正确 - Security: SSTI 攻击 payload(
{{config.__class__}})被沙箱拦截 - Edge case: 模板无占位符时原样输出
- Error path: data 缺少变量时,占位符保持原样或清空(不崩溃)
Verification: 运行 pytest tests/documents/test_template_renderer.py,确认填充和沙箱安全。
U6. Agent 工具封装(DocumentTool)
Goal: 创建 Agent 工具,LLM 通过 function calling 触发文档创建。
Requirements: R5, R6, R7, R8
Dependencies: U1, U2, U3, U4, U5
Files:
src/agentkit/tools/document_tool.py(新建)src/agentkit/server/app.py(修改:注册 DocumentTool)tests/tools/test_document_tool.py(新建)
Approach:
DocumentTool(service: DocumentService)继承Toolname = "document",description = "创建格式化文档(Word/Excel/PDF)或填充 Word 模板"input_schema:{ "type": "object", "properties": { "format": {"type": "string", "enum": ["word", "excel", "pdf"]}, "content": {"type": "string", "description": "Markdown 格式的文档内容"}, "template": {"type": "string", "description": "模板文件路径(可选,仅 word)"}, "template_data": {"type": "object", "description": "模板填充数据(可选)"} }, "required": ["format", "content"] }execute()调用service.create_document(),返回{"success": True, "filename": ..., "download_url": ..., "size": ...}- 在
app.py中注册:tool_registry.register(DocumentTool(service=document_service))
Patterns to follow: src/agentkit/tools/memory_tool.py(Tool 基类模式、input_schema、execute 返回格式)。
Test scenarios:
- Happy path: format=word, content="# 标题\n段落" → 返回 success + download_url
- Happy path: format=pdf, content="..." → 返回 success + download_url
- Happy path: format=word + template + template_data → 模板填充成功
- Error path: format 无效 → 返回 success=False + error message
- Error path: content 为空 → 返回 success=False + error message
- Integration: 工具注册后 agent._tool_registry.get("document") 能获取到
Verification: 运行 pytest tests/tools/test_document_tool.py,确认工具注册和调用正常。
U7. REST API 路由
Goal: 为前端提供文档处理的 REST API。
Requirements: R9, R10, R12, R27, R28
Dependencies: U1, U2, U3, U4, U5
Files:
src/agentkit/server/routes/documents.py(新建)src/agentkit/server/app.py(修改:注册 documents router)tests/routes/test_documents.py(新建)
Approach:
router = APIRouter(prefix="/documents", tags=["documents"])- 端点:
POST /api/v1/documents/create— 创建文档(body: format, content, conversation_id, template?)POST /api/v1/documents/upload-template— 上传模板文件(带认证)GET /api/v1/documents/conversation/{conversation_id}— 获取对话的文档列表GET /api/v1/documents/download/{doc_id}— 下载文档(带认证)
- 认证:复用
Depends(_verify_api_key)模式 - 文件大小限制:50MB
Patterns to follow: src/agentkit/server/routes/chat.py(APIRouter 模式、文件上传/下载)、src/agentkit/server/routes/kb_management.py(认证模式)。
Test scenarios:
- Happy path: POST /create format=word → 200 + 文件元信息
- Happy path: GET /conversation/{id} → 200 + 文档列表
- Happy path: GET /download/{doc_id} → 200 + 文件流
- Security: 未认证请求 → 401
- Edge case: 不存在的 doc_id → 404
- Edge case: 文件超过 50MB → 413
Verification: 运行 pytest tests/routes/test_documents.py,用 curl 验证端点。
U8. 前端文件卡片 + 右侧文档面板
Goal: 对话中渲染文件卡片,右侧面板展示当前对话的文档列表。
Requirements: R17, R18, R19, R20, R21, R22
Dependencies: U7
Files:
src/agentkit/server/frontend/src/components/chat/messages/DocumentCard.vue(新建)src/agentkit/server/frontend/src/components/chat/DocumentPanel.vue(新建,右侧面板)src/agentkit/server/frontend/src/stores/documents.ts(新建,Pinia store)src/agentkit/server/frontend/src/api/documents.ts(新建,API client)src/agentkit/server/frontend/src/views/ChatView.vue(修改:集成右侧面板)src/agentkit/server/frontend/src/stores/chat.ts(修改:token 事件中检测文件元信息并更新 documents store)
Approach:
DocumentCard.vue:复用FileAttachment.vue的设计,显示文件名、格式图标、大小、下载按钮。作为新的消息渲染类型。DocumentPanel.vue:右侧可折叠面板,展示当前对话的文档列表,每项显示文件名、格式图标、生成时间、下载链接。stores/documents.ts:documentsByConversation: ref<Map<string, DocumentMeta[]>>,fetchDocuments(convId),addDocument(convId, doc)。api/documents.ts:createDocument()、getConversationDocuments()、getDownloadUrl()。- ChatView 集成:在聊天区域右侧添加 DocumentPanel,根据当前 conversationId 加载文档列表。
- chat store 集成:当 Agent 工具返回文件元信息时,自动更新 documents store。
Patterns to follow: src/agentkit/server/frontend/src/components/chat/messages/FileAttachment.vue(组件模式)、src/agentkit/server/frontend/src/stores/chat.ts(Pinia store 模式)、src/agentkit/server/frontend/src/api/client.ts(API client 模式)。
Test scenarios:
- Happy path: Agent 生成文档后,对话中显示文件卡片
- Happy path: 右侧面板自动更新,显示新文档
- Happy path: 点击下载按钮,浏览器下载文件
- Happy path: 切换对话,面板显示对应对话的文档列表
- UI: 面板可折叠/展开
- Edge case: 对话无文档时,面板显示空状态
Verification: 启动前端开发服务器,手动测试文件卡片渲染和右侧面板交互。
U9. 文档读取能力(复用 DocumentLoader)
Goal: Agent 能读取用户上传的 Word/Excel/PDF 文档内容。
Requirements: R4
Dependencies: U1
Files:
src/agentkit/tools/document_tool.py(修改:添加 read 操作)src/agentkit/memory/document_loader.py(修改:确保 openpyxl 读取支持,或新增 Excel 读取)
Approach:
- DocumentTool 的 input_schema 新增
action参数:"create"|"read" action="read"时,调用DocumentLoader.load(path)读取文档内容- DocumentLoader 已支持 PDF(PyMuPDF/pdfplumber)和 DOCX(python-docx),需新增 Excel 读取(openpyxl)
- 返回
{"success": True, "content": "提取的文本内容"}
Patterns to follow: src/agentkit/memory/document_loader.py(现有解析模式)。
Test scenarios:
- Happy path: 读取 .docx 文件,返回文本内容
- Happy path: 读取 .xlsx 文件,返回表格内容
- Happy path: 读取 .pdf 文件,返回文本内容
- Edge case: 空文件返回空字符串
- Error path: 不存在的文件返回 success=False
Verification: 运行 pytest tests/tools/test_document_tool.py,确认读取功能正常。
Scope Boundaries
Deferred to Follow-Up Work
- PPT 创建(.pptx)— v2
- 格式转换(Office→PDF)— v2,可能需要 LibreOffice
- PDF 合并和拆分 — v2
- Excel/PPT 模板填充 — v2
- 文档编辑 — v2
- MCP Document Tools 集成(可选增强)— v2
- 文档过期清理的定时任务实现 — v2(v1 手动清理或懒清理)
Outside this product's identity
- OCR / 扫描文档识别
- 文档协作编辑
- 文档版本控制
- 云存储集成
- 文档水印 / 加密 / 数字签名
Risks & Dependencies
- Markdown→Office 格式映射的完整性 — Markdown 不能表达所有 Office 格式(如合并单元格、图片嵌入)。v1 只支持基本格式(标题、段落、列表、表格),复杂格式 defer。
- 中文字体在 PDF 中的渲染 — reportlab 默认不支持中文,需注册中文字体(如 SimSun 或 NotoSansCJK)。需确认服务器有中文字体文件。
- python-docx-template 的 Jinja2 语法限制 — Office XML 结构中 Jinja2 语法可能受限(如表格内的循环)。需测试复杂模板。
- 前端右侧面板的布局影响 — 现有 ChatView 布局可能需要调整以容纳右侧面板,需确认不破坏现有聊天 UI。
Sources & Research
- 需求文档:
docs/brainstorms/2026-06-23-document-processing-capability-requirements.md - Tool 基类:
src/agentkit/tools/base.py、src/agentkit/tools/memory_tool.py - ToolRegistry:
src/agentkit/tools/registry.py、src/agentkit/server/app.py(第 239-269 行) - 路由模式:
src/agentkit/server/routes/chat.py、src/agentkit/server/routes/kb_management.py - 数据库模式:
src/agentkit/server/auth/models.py(aiosqlite 裸连接模式) - 前端组件:
src/agentkit/server/frontend/src/components/chat/messages/FileAttachment.vue - 前端 store:
src/agentkit/server/frontend/src/stores/chat.ts - 文档解析:
src/agentkit/memory/document_loader.py - MCP Document Tools 验证报告:版本 0.1.0,未验证,不建议生产使用,不支持模板填充