Skip to content

数据库设计:从业务数据到 Agent 运行记录 ​

1. 这一篇解决什么问题 ​

数据库不只是保存用户和业务表,还要保存 Agent 任务、文档、Chunk、工具调用、Trace、评测结果、用户反馈和审计记录。

在 AI Agent 项目中,数据库是“状态与证据中心”。状态指当前任务执行到哪里、文档是否入库、评测是否完成;证据指模型输入输出、工具调用参数、引用来源、错误事件和用户反馈。

如果只保存最终答案,不保存执行过程,系统上线后会很难排查问题。一次回答错误,可能来自文档解析、检索召回、上下文构建、模型生成、工具调用或权限过滤。数据库设计的目标就是让这些过程可以被记录、查询和分析。

2. 学习目标 ​

  • 理解 Agent 项目需要保存哪些数据。
  • 学会区分业务数据、运行数据、评测数据和审计数据。
  • 掌握任务表、文档表、工具调用表、Trace 表的基础设计。
  • 理解索引、迁移、软删除和审计字段。

3. 数据类型分层 ​

数据类型示例作用
业务数据用户、工单、项目、知识库表达真实业务对象,是系统服务的核心对象
文档数据document、chunk、embedding_version支撑 RAG 检索、引用溯源和知识库更新
任务数据task、run、step描述 Agent 任务从创建到完成的状态变化
工具调用数据tool_call、tool_result保存 Agent 调用工具的参数、结果、状态和耗时
Trace 数据run_trace、step_trace、error_event还原执行轨迹,支持调试和问题定位
评测数据eval_case、eval_run、eval_result支持版本对比、指标计算和失败样本沉淀
审计数据audit_log、permission_event记录敏感操作、权限变更和高风险工具调用

分层的好处是边界清晰。业务数据回答“系统服务谁、处理什么业务”,运行数据回答“Agent 做了什么”,评测数据回答“效果好不好”,审计数据回答“谁在什么时候做了什么高风险操作”。

4. 核心表设计 ​

下面是示例设计,不要求在每个项目中完全照搬,但字段含义和建模思路可以迁移。

users ​

字段含义
id用户唯一 ID
username用户名
role用户角色,例如 admin、operator、viewer
created_at创建时间
updated_at更新时间

用户表不仅用于登录,还会关联任务、文档权限、工具权限和审计记录。

documents ​

字段含义
id文档唯一 ID
title文档标题
source_type来源类型,例如 upload、url、manual、ticket
source_uri来源地址或来源标识
content_hash文档内容哈希,用于去重和增量更新
status解析状态,例如 pending、parsed、failed
created_at创建时间

文档表记录的是原始文档级别的信息。RAG 中的 Chunk、向量和引用都应该能回到文档。

document_chunks ​

字段含义
idChunk 唯一 ID
document_id所属文档 ID
chunk_index文档内 Chunk 顺序
textChunk 文本
token_countToken 数量估计
embedding_versionEmbedding 版本
vector_id向量库中的向量 ID
metadata页码、章节、标题层级等元数据
created_at创建时间

Chunk 表是 RAG 的关键表。它连接文档、向量库和引用溯源。如果不保存 embedding_version,模型升级后很难判断哪些向量需要重建。

agent_tasks ​

字段含义
id任务唯一 ID
user_id创建任务的用户 ID
task_type任务类型,例如 rag_query、document_ingest、agent_run、eval_run
status任务状态,例如 pending、running、succeeded、failed
input任务输入 JSON
output任务输出 JSON
error_message失败信息
created_at创建时间
updated_at更新时间
finished_at完成时间

任务表面向外部调用方,表示一个用户请求或系统任务的生命周期。

agent_runs ​

字段含义
idRun 唯一 ID
task_id所属任务 ID
run_type运行类型,例如 rag、agent、evaluation
model_name使用的模型名称
status运行状态
started_at开始时间
ended_at结束时间
total_tokens总 Token 消耗
total_cost成本估算

Run 表比 Task 更接近执行层。一个 Task 可能对应一个或多个 Run,例如一次评测任务可能包含多次模型运行。

agent_steps ​

字段含义
idStep 唯一 ID
run_id所属 Run ID
step_index步骤顺序
step_type步骤类型,例如 retrieve、rerank、generate、tool_call
input步骤输入 JSON
output步骤输出 JSON
status步骤状态
error_message错误信息
created_at创建时间

Step 表用于记录执行轨迹。RAG 中可以记录 Query Rewrite、检索、重排、生成;Agent 中可以记录任务拆解、工具调用、模型输出摘要等。

tool_calls ​

字段含义
id工具调用唯一 ID
run_id所属 Run ID
step_id所属 Step ID
tool_name工具名称
arguments工具参数 JSON
result工具结果 JSON
status调用状态
latency_ms调用耗时
created_at创建时间

工具调用表是 Agent 系统排查问题的关键。如果工具参数和结果都不记录,就无法判断错误来自模型决策还是工具执行。

eval_results ​

字段含义
id评测结果唯一 ID
eval_run_id所属评测运行 ID
case_id测试用例 ID
run_id被评测的运行 ID
score分数
reason评分原因
created_at创建时间

评测结果表用于版本对比。每次模型、Prompt、检索参数或 Chunk 策略变更后,都可以通过评测结果判断效果是否提升。

5. 索引设计 ​

常见索引如下:

索引字段作用
task.status快速查询待执行或运行中的任务
task.user_id快速查询某个用户的任务列表
run.task_id根据任务查找运行记录
step.run_id聚合某次运行的执行步骤
tool_call.run_id查询某次运行中的全部工具调用
document.content_hash文档去重和增量更新
chunk.document_id查询文档下的全部 Chunk
eval_result.eval_run_id聚合某次评测的结果

查询任务状态要快,因为前端可能频繁轮询。查询执行轨迹要按 run_id 聚合,因为排查问题时通常先定位一次运行,再展开步骤和工具调用。

文档去重要依赖 hash。只靠标题或文件名不可靠,同一份文档可能被重复上传,也可能文件名不同但内容相同。

评测结果要按版本比较。评测表通常还会关联模型版本、Prompt 版本、检索参数版本和数据集版本。

6. 迁移管理 ​

数据库 schema 会持续变化。项目早期可能只保存任务和文档,后续会增加工具调用、Trace、评测结果、审计字段。如果没有迁移管理,线上数据库很容易和代码不一致。

建议使用 Alembic 或类似工具管理 schema 变更。每次表结构变化都生成迁移文件,并随代码一起提交。

不要手动改线上数据库。手工修改不可复现,也很难在测试环境、预发环境和生产环境保持一致。

迁移要可回滚。新增字段、创建索引、拆表和数据回填都要考虑失败后如何恢复。

字段新增要考虑默认值和历史数据。例如给任务表新增 status 字段时,要明确历史数据的默认状态;给文档表新增 embedding_version 时,要考虑旧 Chunk 是否需要补齐版本。

7. 软删除与审计字段 ​

文档、任务、用户数据通常不建议直接物理删除。特别是 Agent 执行记录和工具调用记录,可能用于问题排查、评测回放和审计。

常见做法是使用 deleted_at 或 status 标记删除状态。业务查询默认过滤已删除数据,但管理员或审计任务仍可追溯历史记录。

created_at、updated_at、created_by 有利于问题追踪。比如某个文档什么时候上传、由谁上传、什么时候被更新,都会影响 RAG 检索结果。

审计字段不只服务合规,也服务工程排查。很多线上问题的关键不在代码,而在“谁在什么时候改了配置或上传了文档”。

8. 和 RAG / Agent 的关系 ​

RAG 需要 documents、document_chunks、embedding_version。这些表支撑文档管理、向量重建、引用溯源和失败样本分析。

Agent 需要 tasks、runs、steps。这些表支撑任务生命周期、执行状态和步骤级排查。

Trace 需要 tool_calls 和 error_events。这些数据帮助还原工具调用记录、状态变化和错误位置。

Evaluation 需要 eval_cases 和 eval_results。这些数据让系统能比较不同模型、不同 Prompt、不同检索参数的效果。

Safety 需要 audit_logs。权限变更、高风险工具调用、敏感文档访问都应该有审计记录。

9. 最小实现示例 ​

下面是一个任务与运行记录的示例设计:

sql
CREATE TABLE agent_tasks (
  id TEXT PRIMARY KEY,
  user_id TEXT NOT NULL,
  task_type TEXT NOT NULL,
  status TEXT NOT NULL,
  input JSONB NOT NULL,
  output JSONB,
  error_message TEXT,
  created_at TIMESTAMP NOT NULL,
  updated_at TIMESTAMP NOT NULL,
  finished_at TIMESTAMP
);

CREATE TABLE agent_runs (
  id TEXT PRIMARY KEY,
  task_id TEXT NOT NULL REFERENCES agent_tasks(id),
  run_type TEXT NOT NULL,
  model_name TEXT,
  status TEXT NOT NULL,
  started_at TIMESTAMP NOT NULL,
  ended_at TIMESTAMP,
  total_tokens INTEGER,
  total_cost NUMERIC
);

这个示例只表达核心关系:Task 面向用户请求,Run 面向实际执行。真实项目中还会补充用户表、步骤表、工具调用表、错误事件表和评测表。

10. 生产环境注意点 ​

  • JSON 字段适合保存灵活结构,但核心查询字段要单独列出来。
  • 大文本不要无限制塞进单表,要考虑归档、压缩或对象存储。
  • 高频轮询字段要建立索引,例如任务状态和用户任务列表。
  • Trace 数据增长很快,要考虑保留周期和归档策略。
  • 成本、Token、延迟等指标要可聚合,方便后续分析。
  • 权限相关数据要避免只存在缓存中,关键决策需要可审计。

11. 常见误区 ​

误区一:只存最终答案,不存执行过程 ​

最终答案无法解释系统为什么这么回答。没有执行过程,就无法定位错误来源。

误区二:文档 Chunk 不记录 embedding 版本 ​

Embedding 模型升级后,如果不知道每个 Chunk 使用哪个版本,就无法稳定重建和比较召回效果。

误区三:工具调用不记录参数和结果 ​

只记录工具名没有意义。排查问题时必须知道工具参数、返回结果、状态和耗时。

误区四:没有 run_id,导致无法排查问题 ​

没有 run_id 就无法把模型调用、检索、工具调用、错误事件串起来。

误区五:不区分任务状态和运行状态 ​

任务状态面向用户,运行状态面向执行引擎。两者混在一起会让长任务、重试和评测变复杂。

误区六:不做迁移管理 ​

手工改库会导致环境不一致。表结构变化必须通过迁移文件管理。

12. 面试表达 ​

我会把 Agent 数据分成业务数据、运行数据、评测数据和审计数据。业务数据描述用户和业务对象,运行数据记录任务、Run、Step 和工具调用,评测数据用于效果对比,审计数据用于权限和风险追踪。

对 RAG,我会保存 document、chunk、embedding_version、citation 等信息。这样不仅能完成检索,还能做引用溯源、向量重建、失败样本分析和质量评测。

对 Agent,我会保存 task、run、step、tool_call,用于 Trace、评测和问题排查。上线后如果用户反馈结果错误,可以通过 run_id 还原检索、生成、工具调用和错误事件。

13. 后续学习 TODO ​

  • 补充项目 A 的工单、文档、引用和反馈表设计。
  • 补充 Agent Trace 的错误事件表设计。
  • 补充 Evaluation 数据集版本表设计。
  • 补充数据库归档和 Trace 保留策略。

14. 相关链接 ​