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表,字段:idinteger primary key autoincrementuserIdinteger not null(用户隔离)sessionIdtext nullable(关联会话,可选)typetext not null default 'document'('memory' | 'document')titletext not nullcontenttext not nullsummarytext nullabletagstext nullable(JSON 数组字符串)createdAtinteger not null(unix timestamp ms)updatedAtinteger not null(unix timestamp ms)
- 定义 3 个索引:
idx_agent_documents_user_idon (userId)idx_agent_documents_user_typeon (userId, type)idx_agent_documents_user_sessionon (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:不存在 → 返回 nulllistDocuments:按 userId 过滤listDocuments:按 type 过滤listDocuments:按关键词模糊搜索 title/contentlistDocuments:分页正确listDocuments:list 结果不含 content 字段(上下文优化)updateDocument:更新成功updateDocument:不属于该用户 → 返回 nulldeleteDocument:删除成功deleteDocument:不属于该用户 → 返回 nullgenerateSummary: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(新建)
内容:
- 导入
dbGlobalfrom#drizzle/db、agentDocumentsfrom#drizzle/schema/agent-document、eq, and, like, desc, sqlfromdrizzle-orm - 导出类型
AgentDocument(inferSelect) - 导出函数:
createDocument(params: { userId: number; sessionId?: string; type: 'memory' | 'document'; title: string; content: string; tags?: string[] }): Promise<AgentDocument>- 自动生成 summary(调用
generateSummary) - 自动设置 createdAt/updatedAt
- 自动生成 summary(调用
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(修改)
内容:
- 导入
documentExecutorfrom./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.tsline 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 模式
defineWrappedResponseHandlerrequireUser(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 参数
validatebody 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' })- 使用
requestfrom~/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 小时