You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

16 KiB

实施计划:Agent 文档工具

设计文档:docs/superpowers/specs/2026-08-09-agent-document-tool-design.md 创建时间:2026-08-09

概述

为 agent 系统新增"文档"工具,让 agent 能够创建、读取、更新、删除、列出文档(memory / document 两种类型),用于上下文优化与交付物产出。采用方案 A(纯 SQL 模糊搜索 + 摘要截断 + token 预算),零额外依赖。

实施原则

  • TDD:每个可测单元先写测试再写实现
  • DRY:复用现有 defineWrappedResponseHandler、R.success/R.error、validate、requireUser 等工具
  • YAGNI:不实现向量检索、不打通 articles、不做全文索引
  • 频繁提交:每个阶段完成后提交一次
  • 小步前进:每个 task 独立可验证

Phase 1:数据层(Schema + Migration + Seed)

Task 1.1 — 创建 agent_documents 表 schema

文件:packages/drizzle-pkg/lib/schema/agent-document.ts(新建)

内容:

  • 定义 agentDocuments 表,字段:
    • id integer primary key autoincrement
    • userId integer not null(用户隔离)
    • sessionId text nullable(关联会话,可选)
    • type text not null default 'document'('memory' | 'document')
    • title text not null
    • content text not null
    • summary text nullable
    • tags text nullable(JSON 数组字符串)
    • createdAt integer not null(unix timestamp ms)
    • updatedAt integer not null(unix timestamp ms)
  • 定义 3 个索引:
    • idx_agent_documents_user_id on (userId)
    • idx_agent_documents_user_type on (userId, type)
    • idx_agent_documents_user_session on (userId, sessionId)
  • 导出 AgentDocumentType 常量数组 ['memory', 'document']

验证:文件存在、导出 agentDocuments 表对象、字段类型正确

提交:feat(drizzle): add agent_documents table schema

Task 1.2 — 在 AgentToolTypes 枚举中新增 document

文件:packages/drizzle-pkg/lib/schema/agent-tool.ts(修改)

内容:

  • 在 AgentToolTypes 枚举中新增 'document' 值

验证:AgentToolTypes 包含 'document'

提交:与 1.1 合并提交

Task 1.3 — 生成数据库迁移

命令:

cd packages/drizzle-pkg && bun drizzle-kit generate

验证:packages/drizzle-pkg/migrations/ 下新增 0002_*.sql 文件,包含 CREATE TABLE agent_documents 和 3 个 CREATE INDEX

提交:feat(drizzle): generate migration for agent_documents

Task 1.4 — 执行迁移

命令:

cd packages/drizzle-pkg && bun drizzle-kit migrate

验证:开发数据库中 agent_documents 表存在

提交:与 1.3 合并(迁移文件即提交物)

Task 1.5 — 在 seed.ts 中新增 document 工具种子

文件:packages/drizzle-pkg/seed.ts(修改)

内容:

  • 在 TOOLS_SEED 数组末尾新增:
    {
      id: <下一个可用 id>,
      name: '文档工具',
      slug: 'document',
      description: '创建、读取、更新、删除和列出文档,用于记忆管理和交付物产出',
      type: 'document',
      config: {
        approvalRequiredActions: ['delete'],
        maxContentLength: 10000,
        defaultTokenBudget: 2000,
        maxListSize: 20,
      },
      enabled: true,
      needsApproval: true,
      adminOnly: false,
      sortOrder: 150,
    }
    
  • sortOrder 为 150(当前最大 140 + 10)

验证:seed.ts 中 TOOLS_SEED 包含 slug='document' 的条目

提交:feat(seed): add document tool seed data


Phase 2:Service 层(CRUD + 权限隔离)

Task 2.1 — 编写 Service 层单元测试

文件:server/service/agent-document/index.test.ts(新建)

内容:使用 bun:test,测试以下场景:

  • createDocument:创建成功,返回完整记录
  • createDocument:content 超过 maxContentLength 抛错
  • getDocumentById:存在且属于该用户 → 返回记录
  • getDocumentById:存在但不属于该用户 → 返回 null(权限隔离)
  • getDocumentById:不存在 → 返回 null
  • listDocuments:按 userId 过滤
  • listDocuments:按 type 过滤
  • listDocuments:按关键词模糊搜索 title/content
  • listDocuments:分页正确
  • listDocuments:list 结果不含 content 字段(上下文优化)
  • updateDocument:更新成功
  • updateDocument:不属于该用户 → 返回 null
  • deleteDocument:删除成功
  • deleteDocument:不属于该用户 → 返回 null
  • generateSummary:content 超过阈值时生成摘要
  • generateSummary:content 短于阈值时返回 null

注意:测试需要 mock 或使用测试数据库。参考 server/service/captcha/store.test.ts 的 bun:test 模式。由于 Service 直接操作 dbGlobal,测试使用独立测试数据库或 mock dbGlobal。

验证:测试文件存在,覆盖所有 CRUD + 权限 + 摘要场景

提交:test(agent-document): add service layer unit tests

Task 2.2 — 实现 Service 层

文件:server/service/agent-document/index.ts(新建)

内容:

  • 导入 dbGlobal from #drizzle/db、agentDocuments from #drizzle/schema/agent-document、eq, and, like, desc, sql from drizzle-orm
  • 导出类型 AgentDocument(inferSelect)
  • 导出函数:
    • createDocument(params: { userId: number; sessionId?: string; type: 'memory' | 'document'; title: string; content: string; tags?: string[] }): Promise<AgentDocument>
      • 自动生成 summary(调用 generateSummary)
      • 自动设置 createdAt/updatedAt
    • getDocumentById(id: number, userId: number): Promise<AgentDocument | null>
      • WHERE id = ? AND userId = ?(权限隔离)
    • listDocuments(params: { userId: number; type?: string; keyword?: string; page: number; pageSize: number }): Promise<{ items: Omit<AgentDocument, 'content'>[]; total: number }>
      • list 结果排除 content 字段(上下文优化)
      • keyword 模糊匹配 title 和 content
      • 按 updatedAt desc 排序
    • updateDocument(id: number, userId: number, params: { title?: string; content?: string; tags?: string[] }): Promise<AgentDocument | null>
      • WHERE id = ? AND userId = ?
      • content 更新时重新生成 summary
      • 更新 updatedAt
    • deleteDocument(id: number, userId: number): Promise<boolean>
      • WHERE id = ? AND userId = ?
    • generateSummary(content: string, maxChars = 200): string | null
      • content.length > maxChars 时截取前 maxChars 字符 + '...'
      • 否则返回 null(表示无需摘要)

验证:Task 2.1 的测试全部通过

bun test server/service/agent-document/index.test.ts

提交:feat(agent-document): implement service layer with CRUD and permission isolation


Phase 3:Executor 层(工具执行器)

Task 3.1 — 创建 document executor config

文件:server/service/agent-tool/executors/document/config.ts(新建)

内容:参考 executors/fetch/config.ts 模式

  • 定义 DocumentToolConfigSchema:
    z.object({
      approvalRequiredActions: z.array(z.string()).default(['delete']),
      maxContentLength: z.number().int().positive().default(10000),
      defaultTokenBudget: z.number().int().positive().default(2000),
      maxListSize: z.number().int().positive().default(20),
    })
    
  • 导出 DEFAULT_DOCUMENT_CONFIG
  • 导出 parseDocumentConfig(config: unknown): DocumentToolConfig

验证:文件存在,schema 正确,默认值合理

提交:feat(agent-tool/document): add config schema

Task 3.2 — 创建 document executor

文件:server/service/agent-tool/executors/document/document.ts(新建)

内容:实现 ToolExecutor<DocumentToolConfig> 接口

  • buildInputSchema():返回 z.object,含 action 字段(enum: create/read/update/delete/list)+ 各 action 对应字段
    • create: title, content, type?, tags?, sessionId?
    • read: id, fullContent?
    • update: id, title?, content?, tags?
    • delete: id
    • list: type?, keyword?, page?, pageSize?
  • buildDescription():返回中文描述,说明各 action 用法
  • execute(input, ctx):
    • 根据 input.action 分发到对应处理函数
    • 检查 ctx.userId,为 null 时返回 error
    • delete/update 操作检查 ctx.approved,若 config.approvalRequiredActions 包含该 action 且 ctx.approved !== true,返回 { success: false, error: '此操作需要审批' }
    • create/update 时检查 content 长度不超过 config.maxContentLength
    • list 时 pageSize 不超过 config.maxListSize
    • read 时默认返回 summary,fullContent=true 才返回完整 content
    • 返回 ToolResult 格式

验证:executor 文件存在,实现 ToolExecutor 接口

提交:feat(agent-tool/document): implement document executor

Task 3.3 — 创建 executor index 导出

文件:server/service/agent-tool/executors/document/index.ts(新建)

内容:

export { documentExecutor } from './document';
export { DEFAULT_DOCUMENT_CONFIG, parseDocumentConfig } from './config';
export type { DocumentToolConfig } from './config';

验证:导出正确

提交:与 3.2 合并

Task 3.4 — 扩展 ToolContext 类型

文件:server/service/agent-tool/registry.ts(修改)

内容:

  • 在 ToolContext 接口中新增 approved?: boolean 字段

验证:ToolContext 包含 approved 可选字段

提交:feat(agent-tool): add approved field to ToolContext

Task 3.5 — 注册 document executor

文件:server/service/agent-tool/index.ts(修改)

内容:

  • 导入 documentExecutor from ./executors/document
  • 在 TOOL_TYPE_REGISTRY 中新增 'document': documentExecutor

验证:TOOL_TYPE_REGISTRY 包含 'document' 键

提交:feat(agent-tool): register document executor

Task 3.6 — 修改 executeAgentTool 传递 approved 参数

文件:server/service/agent-tool/index.ts(修改)

内容:

  • executeAgentTool 函数签名新增 approved?: boolean 参数
  • 构造 ToolContext 时传入 approved
  • 在 server/service/agent/chat-engine.ts line 355 调用处传入 approved: true(审批通过后才执行)

验证:executeAgentTool 接受并传递 approved 参数

提交:feat(agent-tool): pass approved flag through executeAgentTool


Phase 4:API 层(REST 接口供前端查看页使用)

Task 4.1 — 创建 API:GET /api/agent-documents(列表)

文件:server/api/agent-documents/index.get.ts(新建)

内容:参考 server/api/articles/index.get.ts 模式

  • defineWrappedResponseHandler
  • requireUser(event) 获取 userId
  • 解析 query 参数:type, keyword, page (default 1), pageSize (default 20)
  • 调用 listDocuments({ userId, type, keyword, page, pageSize })
  • R.success(result)

验证:API 可访问,返回当前用户的文档列表

提交:feat(api): add GET /api/agent-documents list endpoint

Task 4.2 — 创建 API:GET /api/agent-documents/:id(详情)

文件:server/api/agent-documents/[id].get.ts(新建)

内容:参考 server/api/articles/[id].get.ts 模式

  • requireUser(event)
  • 解析 id 参数
  • 调用 getDocumentById(id, userId)
  • 不存在返回 R.throwError(404, ...)
  • R.success(document)

验证:API 可访问,返回文档详情(含 content)

提交:feat(api): add GET /api/agent-documents/:id detail endpoint

Task 4.3 — 创建 API:PATCH /api/agent-documents/:id(更新)

文件:server/api/agent-documents/[id].patch.ts(新建)

内容:

  • requireUser(event)
  • 解析 id 参数
  • validate body schema:title?, content?, tags?
  • 调用 updateDocument(id, userId, data)
  • 不存在返回 404
  • R.success(updated)

验证:API 可访问,更新成功

提交:feat(api): add PATCH /api/agent-documents/:id update endpoint

Task 4.4 — 创建 API:DELETE /api/agent-documents/:id(删除)

文件:server/api/agent-documents/[id].delete.ts(新建)

内容:

  • requireUser(event)
  • 解析 id 参数
  • 调用 deleteDocument(id, userId)
  • 不存在返回 404
  • R.success(null)

验证:API 可访问,删除成功

提交:feat(api): add DELETE /api/agent-documents/:id delete endpoint


Phase 5:前端查看页

Task 5.1 — 创建文档列表页

文件:app/pages/agent-documents/index.vue(新建)

内容:参考 app/pages/articles/index.vue 模式

  • definePageMeta({ layout: 'home' })
  • 使用 request from ~/utils/http/factory
  • 调用 GET /api/agent-documents 获取列表
  • 展示表格/卡片列表:标题、类型(memory/document 标签)、摘要、更新时间
  • 支持按类型筛选(全部 / memory / document)
  • 支持关键词搜索
  • 分页
  • 点击标题跳转 /agent-documents/[id]
  • 使用 $toast 提示错误

验证:页面可访问,显示文档列表,筛选/搜索/分页正常

提交:feat(frontend): add agent documents list page

Task 5.2 — 创建文档详情页

文件:app/pages/agent-documents/[id].vue(新建)

内容:参考 app/pages/articles/[id].vue 模式

  • definePageMeta({ layout: 'home' })
  • 调用 GET /api/agent-documents/:id 获取详情
  • 使用 AgentMarkdown 组件渲染 content
  • 显示标题、类型标签、创建时间、更新时间、tags
  • 编辑按钮 → 切换到编辑模式(textarea + 保存/取消)
  • 保存调用 PATCH /api/agent-documents/:id
  • 删除按钮 → 确认后调用 DELETE /api/agent-documents/:id,跳转回列表
  • 使用 $toast 提示操作结果

验证:页面可访问,markdown 正确渲染,编辑/删除功能正常

提交:feat(frontend): add agent document detail page with edit/delete

Task 5.3 — 在首页/导航中添加入口

文件:app/layouts/home.vue 或相关导航组件(修改)

内容:

  • 添加"Agent 文档"导航链接,指向 /agent-documents
  • 仅登录用户可见

验证:导航中出现入口,点击跳转正确

提交:feat(frontend): add agent documents nav entry


Phase 6:集成测试(E2E)

Task 6.1 — E2E 测试:文档列表页可访问

文件:e2e/specs/agent-documents.spec.ts(新建)

内容:

  • 登录 → 访问 /agent-documents
  • 验证页面标题、空状态提示
  • 验证类型筛选按钮存在

验证:测试通过

提交:test(e2e): add agent documents list page smoke test

Task 6.2 — E2E 测试:通过 agent 工具创建文档并验证

文件:e2e/specs/agent-documents.spec.ts(追加)

内容:

  • 登录 → 在 agent 对话中发送消息触发 document 工具 create action
  • (需要 Mock LLM 返回工具调用,或直接通过 API 创建测试数据)
  • 访问 /agent-documents 验证文档出现在列表
  • 点击进入详情页验证内容

注意:由于 Mock LLM 不支持工具调用,此测试通过 API 直接创建数据,验证前端展示

验证:测试通过

提交:test(e2e): add agent document create and view test


Phase 7:收尾

Task 7.1 — 更新 AGENTS.md

文件:AGENTS.md(修改)

内容:

  • 在 E2E 测试文件表格中新增 agent-documents.spec.ts 行
  • 如有其他需要记录的架构决策,补充说明

验证:AGENTS.md 更新

提交:docs: update AGENTS.md with agent documents info

Task 7.2 — 全量验证

命令:

# 类型检查
bun run typecheck
# Lint
bun run lint
# 单元测试
bun test server/service/agent-document/
# E2E 测试
bun run test:e2e -- --grep "agent documents"

验证:全部通过

提交:如有修复则提交,否则无提交


依赖关系

Phase 1 (数据层) → Phase 2 (Service) → Phase 3 (Executor) → Phase 4 (API)
                                                              ↓
                                                    Phase 5 (前端) → Phase 6 (E2E) → Phase 7 (收尾)

风险与缓解

风险 缓解
Service 测试需要数据库 使用 bun:test + 测试数据库,或 mock dbGlobal
Mock LLM 不支持工具调用 E2E 测试通过 API 直接创建数据验证前端
executeAgentTool 签名变更影响现有调用 新增可选参数,向后兼容
tags 字段 JSON 序列化 Service 层统一处理 string[] ↔ JSON string 转换

预计工作量

  • Phase 1:~30 分钟
  • Phase 2:~1 小时
  • Phase 3:~1.5 小时
  • Phase 4:~45 分钟
  • Phase 5:~1.5 小时
  • Phase 6:~45 分钟
  • Phase 7:~30 分钟
  • 总计:~6 小时