# Agent 文档功能 — 交接文档 > 日期:2026-08-09 > 分支:feat/agent-independence > Commits:b62a417 → 10688be(共 16 个 commit) ## 一、功能概述 Agent 文档功能让 agent 能够跨会话持久化信息,分为两种类型: | 类型 | 用途 | 上下文行为 | |------|------|------------| | `memory` | 内部记忆(用户偏好、身份信息等) | 每次对话自动注入 system prompt | | `document` | 交付物(周报、需求文档、会议纪要等) | 不自动注入,需主动调用工具读取 | ### 核心能力 1. **Agent 工具**:LLM 可通过 `document` 工具 create/read/update/delete/list 文档 2. **记忆注入**:`type=memory` 的文档在每次对话时自动拼接到 system prompt,LLM 无需调工具即可使用 3. **用户界面**:右侧抽屉组件,在会话页面内查看/搜索/删除文档,不跳转页面 4. **REST API**:5 个端点供前端直接操作文档 5. **权限隔离**:所有操作强制 `WHERE userId = ?`,用户级隔离 6. **审批机制**:delete 操作默认需用户审批 ## 二、架构设计 ``` ┌─────────────────────────────────────────────────────────┐ │ 前端 │ │ ┌──────────────┐ ┌──────────────────────────────────┐ │ │ │ AgentToolbar │ │ AgentSidebar (desktop + mobile) │ │ │ │ 文档按钮 │ │ 文档按钮 │ │ │ └──────┬───────┘ └────────────┬─────────────────────┘ │ │ │ emit('showDocuments')│ │ │ └──────────┬────────────┘ │ │ ▼ │ │ ┌──────────────────────────────────────────────────┐ │ │ │ AgentChatArea → emit('showDocuments') │ │ │ └──────────────────────┬───────────────────────────┘ │ │ ▼ │ │ ┌──────────────────────────────────────────────────┐ │ │ │ chat/[agentSlug]/index.vue │ │ │ │ showDocumentDrawer = true │ │ │ │ ┌────────────────────────────────────────────┐ │ │ │ │ │ AgentDocumentDrawer.vue │ │ │ │ │ │ ┌──────────┐ ┌────────────────────────┐ │ │ │ │ │ │ │ 列表视图 │ │ 详情视图 (markdown) │ │ │ │ │ │ │ │ 筛选+搜索 │ │ 删除按钮 │ │ │ │ │ │ │ │ 删除按钮 │ │ AgentMarkdown 渲染 │ │ │ │ │ │ │ └──────────┘ └────────────────────────┘ │ │ │ │ │ └────────────────────────────────────────────┘ │ │ │ └──────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────┐ │ 后端 │ │ │ │ ┌─────────────┐ ┌──────────────┐ ┌───────────────┐ │ │ │ REST API │ │ chat-engine │ │ agent-tool │ │ │ │ 5 endpoints │ │ │ │ registry │ │ │ │ │ │ getMemory │ │ │ │ │ │ GET /api │ │ Context() │ │ document │ │ │ │ POST /api │ │ → systemPrompt│ │ executor │ │ │ │ GET /:id │ │ │ │ │ │ │ │ PATCH /:id │ └──────────────┘ └───────┬───────┘ │ │ │ DELETE /:id │ │ │ │ └──────┬──────┘ │ │ │ │ │ │ │ ▼ ▼ │ │ ┌──────────────────────────────────────────────────┐ │ │ │ agent-document service (CRUD + 权限隔离) │ │ │ │ createDocument / getDocumentById / listDocuments│ │ │ │ updateDocument / deleteDocument │ │ │ │ getMemoryContext (查 memory 类型,拼接上下文) │ │ │ └──────────────────────┬───────────────────────────┘ │ │ ▼ │ │ ┌──────────────────────────────────────────────────┐ │ │ │ SQLite: agent_documents 表 │ │ │ │ id / userId / sessionId / type / title / │ │ │ │ content / summary / tags / createdAt / updatedAt │ │ │ └──────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────┘ ``` ## 三、文件清单 ### 数据层 | 文件 | 说明 | |------|------| | `packages/drizzle-pkg/lib/schema/agent-document.ts` | `agentDocuments` 表 schema,`createdAt`/`updatedAt` 用 `mode: "timestamp_ms"` | | `packages/drizzle-pkg/lib/schema/agent-tool.ts` | `AgentToolTypes` 新增 `"document"` | | `packages/drizzle-pkg/migrations/0002_noisy_ronan.sql` | 建表迁移 | | `packages/drizzle-pkg/seed.ts` | document 工具种子数据(id=`tool_document`) | ### Service 层 | 文件 | 说明 | |------|------| | `server/service/agent-document/index.ts` | CRUD + `getMemoryContext()` + `deserializeTags` | | `server/service/agent-document/index.test.ts` | 32 个单元测试 | ### Executor 层 | 文件 | 说明 | |------|------| | `server/service/agent-tool/executors/document/config.ts` | document executor config(审批白名单、maxContentLength 等) | | `server/service/agent-tool/executors/document/document.ts` | executor 实现,5 种 action 分发,`toISO()` 序列化 Date | | `server/service/agent-tool/registry.ts` | `ToolContext` 扩展 `approved?: boolean` | | `server/service/agent-tool/index.ts` | document executor 注册(line 128-133) | ### API 层 | 文件 | 说明 | |------|------| | `server/api/agent-documents/index.get.ts` | GET 列表(分页 + 类型筛选 + 关键词搜索) | | `server/api/agent-documents/index.post.ts` | POST 创建 | | `server/api/agent-documents/[id].get.ts` | GET 详情 | | `server/api/agent-documents/[id].patch.ts` | PATCH 更新 | | `server/api/agent-documents/[id].delete.ts` | DELETE 删除 | ### 前端 | 文件 | 说明 | |------|------| | `app/components/agent/AgentDocumentDrawer.vue` | 右侧抽屉(列表 + 详情 + 删除 + 筛选 + 搜索) | | `app/components/agent/AgentToolbar.vue` | 工具栏文档按钮 → emit `showDocuments` | | `app/components/agent/AgentSidebar.vue` | 侧边栏文档按钮(desktop + mobile)→ emit `showDocuments` | | `app/components/agent/AgentChatArea.vue` | 透传 `showDocuments` 事件 | | `app/pages/chat/[agentSlug]/index.vue` | 接入 `showDocumentDrawer` 状态 + 渲染抽屉 | | `app/pages/agent-documents/index.vue` | 独立列表页(保留,导航入口在 TopNav) | | `app/pages/agent-documents/[id].vue` | 独立详情页(保留) | ### Chat Engine | 文件 | 说明 | |------|------| | `server/service/agent/chat-engine.ts` | 记忆注入 system prompt(line 250-260)+ `buildModelMessages` 修复 | ### 测试 | 文件 | 说明 | |------|------| | `e2e/specs/agent-documents.spec.ts` | 10 个 E2E 测试用例 | ### 文档 | 文件 | 说明 | |------|------| | `docs/superpowers/specs/2026-08-09-agent-document-tool-design.md` | 设计文档 | | `docs/superpowers/plans/2026-08-09-agent-document-tool.md` | 实施计划 | | `docs/handover/2026-08-09-agent-document-tool.md` | 本交接文档 | ## 四、关键实现细节 ### 4.1 记忆注入上下文 ```typescript // chat-engine.ts line 250-260 let systemPrompt = baseSystemPrompt; if (user) { const memoryContext = await getMemoryContext(user.id); if (memoryContext) { const memoryInstruction = "\n\n# 持久记忆(已注入上下文)\n" + "以下 标签内的信息是你的持久记忆,每次对话开始时自动加载,始终是最新的。\n" + "**禁止调用 document 工具的 read 或 list 操作来读取这些记忆** — 它们已经在你的上下文中了。\n" + "直接使用 中的内容回答用户问题即可。\n" + "只有需要创建新记忆或修改已有记忆时,才使用 document 工具。"; systemPrompt = systemPrompt ? `${systemPrompt}${memoryInstruction}\n${memoryContext}` : `${memoryInstruction}\n${memoryContext}`; } } ``` - **实时查询**:每次 `executeChat` 调用时从数据库查 `type=memory` 的文档,文档更新后下一轮对话自动生效 - **格式**:每条记忆用 `## {title}\n{content}` 格式,整体包裹在 `` 标签内 - **防冗余调用**:system prompt 中明确禁止 LLM 调用 document 工具读取已有记忆 ### 4.2 getMemoryContext 实现 ```typescript // agent-document/index.ts export async function getMemoryContext(userId: number): Promise { const rows = await dbGlobal .select({ title: agentDocuments.title, content: agentDocuments.content }) .from(agentDocuments) .where(and(eq(agentDocuments.userId, userId), eq(agentDocuments.type, "memory"))) .orderBy(desc(agentDocuments.updatedAt)); if (rows.length === 0) return null; const parts = rows.map((r) => `## ${r.title}\n${r.content}`); return `\n${parts.join("\n\n")}\n`; } ``` ### 4.3 Date 序列化问题 drizzle `timestamp_ms` mode 返回 `Date` 对象,AI SDK Zod 校验拒绝 Date(期望 string)。executor 中用 `toISO()` helper 序列化: ```typescript function toISO(date: Date | null | undefined): string | null { return date ? date.toISOString() : null; } ``` ### 4.4 buildModelMessages 审批修复 `approval-responded` + `hasResult=false` 时只 push `tool-approval-request`,不 push 无 output 的 `tool-call`(AI SDK 校验失败)。 ### 4.5 抽屉组件 - `Teleport to="body"` + `Transition` 实现滑入动画 - 列表视图:类型筛选 tabs(全部/文档/记忆)+ 搜索框(400ms 防抖)+ 卡片列表 - 详情视图:返回按钮 + 标题 + `AgentMarkdown` 渲染 + 删除按钮 - 每次 `show` 变 true 都重新 fetch(不缓存) - 删除带 `confirm()` 确认,删除后自动从列表移除 ### 4.6 agent-tool 关联 工具必须通过 `agent_tool_associations` 表关联到 agent 才能被 LLM 使用,仅 `enabled=1` 不够。已为 default agent(id=1)手动插入关联记录。 ## 五、数据库表结构 ```sql CREATE TABLE agent_documents ( id INTEGER PRIMARY KEY AUTOINCREMENT, userId INTEGER NOT NULL, sessionId TEXT, type TEXT NOT NULL DEFAULT 'document', -- 'memory' | 'document' title TEXT NOT NULL, content TEXT NOT NULL, summary TEXT, tags TEXT, -- JSON array string createdAt INTEGER NOT NULL, -- timestamp_ms updatedAt INTEGER NOT NULL -- timestamp_ms ); -- 索引 CREATE INDEX idx_agent_documents_userId ON agent_documents(userId); CREATE INDEX idx_agent_documents_userId_type ON agent_documents(userId, type); CREATE INDEX idx_agent_documents_userId_updatedAt ON agent_documents(userId, updatedAt); ``` ## 六、API 接口 | 方法 | 路径 | 说明 | |------|------|------| | GET | `/api/agent-documents?page=1&pageSize=20&type=memory&keyword=xxx` | 列表查询 | | POST | `/api/agent-documents` | 创建文档 | | GET | `/api/agent-documents/:id` | 获取详情 | | PATCH | `/api/agent-documents/:id` | 更新文档 | | DELETE | `/api/agent-documents/:id` | 删除文档 | 响应格式:`{ code: 0, message: 'success', data: ... }` ## 七、Agent 工具接口 LLM 可调用的 `document` 工具,通过 `action` 字段分发: | action | 必填参数 | 可选参数 | 说明 | |--------|----------|----------|------| | create | title, content | type, tags | 创建文档/记忆 | | read | id | fullContent | 读取详情(默认仅返回 summary) | | update | id | title, content, tags | 更新文档 | | delete | id | — | 删除文档(默认需审批) | | list | — | type, keyword, page, pageSize | 列表查询 | ## 八、已知问题与注意事项 1. **seed.ts 不是 upsert**:admin 用户已存在时重跑 seed 会 UNIQUE 冲突;开发库需手动插入新工具记录 2. **guess-who 残留**:`packages/drizzle-pkg/seed.ts` line 215-225 有 guess-who 种子数据,但 executor 不存在 3. **前端无 agent 编辑页**:无法通过 UI 管理 agent-tool 关联,需手动插库 4. **typecheck 预先存在错误**:search、chat、favorite、ideas、llm 模块有 TS 错误,与本次改动无关 5. **记忆无 token 上限**:当前 `getMemoryContext` 拼接所有 memory 文档,如果记忆数量多可能撑爆上下文。后续可加 token 预算截断 ## 九、Commit 历史 | Commit | 说明 | |--------|------| | `b62a417` | feat: add agent_documents table schema, migration, and seed data | | `2bd451a` | feat: add agent-document service layer with CRUD, permission isolation, and tests | | `b40bfa4` | feat: add document tool executor with config, registration, and approved flag | | `e5d7e68` | feat: add REST API endpoints for agent documents | | `d06a642` | feat: add agent-documents frontend pages (list + detail) and nav entry | | `0c8d51a` | feat: add POST create endpoint and E2E tests for agent-documents | | `6b381d6` | docs: add agent-documents E2E test entry to AGENTS.md | | `322accb` | fix: buildModelMessages approval-responded without result should not emit tool-call | | `0ae318a` | feat(agent): add document entry in sidebar footer and toolbar | | `50f0ca4` | fix(agent-document): serialize Date fields to ISO strings in executor output | | `c5e9ae9` | fix(agent-document): deserialize tags in listDocuments + null guard in template | | `72659af` | feat(agent-documents): replace page navigation with right-side drawer | | `bf88935` | feat(agent-documents): add delete in drawer + inject memory into system prompt | | `c2bf347` | fix(chat-engine): instruct LLM not to call document tool for existing memory | | `10688be` | fix(chat-engine): strengthen memory instruction to prevent redundant tool calls | ## 十、后续可优化方向 1. **记忆 token 预算**:`getMemoryContext` 加 token 上限,超限时按优先级截断 2. **向量检索**:文档量大时用向量检索替代 SQL 模糊搜索 3. **agent 编辑页**:前端 UI 管理 agent-tool 关联,避免手动插库 4. **记忆分类**:memory 细分为 preference/identity/fact 等,按类别注入 5. **文档分享**:document 类型支持生成分享链接 6. **版本历史**:文档更新时保留历史版本