fischer-agentkit/docs/plans/2026-06-23-002-feat-documen...

18 KiB
Raw Blame History

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-templateAgent 工具和前端 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-docxWord、openpyxlExcel、reportlabPDF、python-docx-templateWord 模板填充。MCP Document Tools 降级为可选增强,不在 v1 范围。
  • DocumentService 统一封装 — DocumentService 作为唯一业务逻辑层Agent 工具和前端 REST API 都是薄封装。内部按格式分派到对应的 renderer 模块。
  • Agent 生成 MarkdownService 负责格式映射 — 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?) -> DocumentMetaget_conversation_documents(conversation_id) -> list[DocumentMeta]get_download_path(doc_id) -> Path
  • DocumentMeta dataclassid, filename, stored_name, format, size, conversation_id, created_at, download_url
  • 数据库表 documentsid (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.pyaiosqlite 模式)、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 with bold=True
    • *斜体* → run with italic=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) 继承 Tool
  • name = "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.pyTool 基类模式、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.pyAPIRouter 模式、文件上传/下载)、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.tsdocumentsByConversation: ref<Map<string, DocumentMeta[]>>fetchDocuments(convId)addDocument(convId, doc)
  • api/documents.tscreateDocument()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.tsPinia store 模式)、src/agentkit/server/frontend/src/api/client.tsAPI 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 已支持 PDFPyMuPDF/pdfplumber和 DOCXpython-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
  • 文档过期清理的定时任务实现 — v2v1 手动清理或懒清理)

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.pysrc/agentkit/tools/memory_tool.py
  • ToolRegistrysrc/agentkit/tools/registry.pysrc/agentkit/server/app.py(第 239-269 行)
  • 路由模式:src/agentkit/server/routes/chat.pysrc/agentkit/server/routes/kb_management.py
  • 数据库模式:src/agentkit/server/auth/models.pyaiosqlite 裸连接模式)
  • 前端组件:src/agentkit/server/frontend/src/components/chat/messages/FileAttachment.vue
  • 前端 storesrc/agentkit/server/frontend/src/stores/chat.ts
  • 文档解析:src/agentkit/memory/document_loader.py
  • MCP Document Tools 验证报告:版本 0.1.0,未验证,不建议生产使用,不支持模板填充