475 lines
17 KiB
Markdown
475 lines
17 KiB
Markdown
---
|
||
reviewStatus: pending
|
||
reviewedAt: null
|
||
replacedBy: null
|
||
---
|
||
|
||
# 第一阶段工程基线后端落地设计
|
||
|
||
## 1. 文档定位
|
||
|
||
本文档定义 JobRadar 第一阶段工程基线的后端落地边界,供后续模型设计、编码、迁移、测试和验收使用。本文档是待审核设计,不代表相关代码已经实现。
|
||
|
||
设计依据:
|
||
|
||
- [第一阶段工程基线开发计划](../../../docs/engineering-baseline/dev-plan.md)
|
||
- [架构设计](../../../docs/architecture.md)
|
||
- [Agent 契约](../../../docs/agent-contract.md)
|
||
- [AI 开发约定](../../../docs/development-guide.md)
|
||
|
||
当前技术基线为 Python 3.13、Django 6.0.8 和 OpenAI Agents SDK 0.22.0。项目没有已登记的 Django 公共 profile,实现时以当前项目规则和对应版本的官方契约为准。
|
||
|
||
## 2. 目标与范围
|
||
|
||
第一阶段将现有 Django 配置骨架扩展为一个可登录、可隔离、可审计、可记录 Agent 运行,但暂不执行真实岗位研究的模块化单体。
|
||
|
||
阶段完成后应具备:
|
||
|
||
- Django 内置用户的登录、退出及基础资料维护能力。
|
||
- 普通用户之间的数据归属隔离。
|
||
- Agent Run、运行事件、工具调用和人工确认的持久化能力。
|
||
- 合法的运行状态转换、事件顺序和幂等控制。
|
||
- OpenAI Agents SDK 的稳定适配入口及无外部费用的测试替身。
|
||
- 包含请求和运行关联标识的脱敏日志。
|
||
- SQLite 完整测试及 PostgreSQL 核心兼容验证能力。
|
||
|
||
第一阶段明确不实现:
|
||
|
||
- 招聘网站采集、企业调查、岗位标准化、筛选、评分和推荐报告。
|
||
- 真实岗位研究 Agent 循环或付费模型调用。
|
||
- Celery、Redis、定时调度和失败任务自动重试。
|
||
- 公开注册、复杂 RBAC、前后端分离、REST API 和独立前端框架。
|
||
- 自动投递简历、自动联系招聘人员或其他不可逆外部操作。
|
||
|
||
## 3. 总体调用关系
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
USER["普通用户"] --> AUTH["Django 登录与 Session"]
|
||
AUTH --> PROFILE["用户资料"]
|
||
AUTH --> RUN_VIEW["运行列表与详情"]
|
||
ADMIN["管理员"] --> ADMIN_SITE["Django Admin"]
|
||
ADMIN_SITE --> RUN_SERVICE["Agent Run 服务层"]
|
||
RUN_SERVICE --> RUN["AgentRun"]
|
||
RUN_SERVICE --> EVENT["AgentRunEvent"]
|
||
RUN_SERVICE --> TOOL["ToolCall"]
|
||
RUN_SERVICE --> APPROVAL["HumanApproval"]
|
||
SDK_ADAPTER["SDK Gateway / 测试替身"] --> RUN_SERVICE
|
||
```
|
||
|
||
所有页面、Admin 扩展和 SDK 适配器通过服务层改变运行状态,不直接拼装跨模型写入。
|
||
|
||
## 4. 模块职责
|
||
|
||
### 4.1 `common`
|
||
|
||
`common` 只提供跨业务复用的基础能力:
|
||
|
||
- 创建时间和更新时间抽象模型。
|
||
- 用户归属抽象模型及用户范围查询入口。
|
||
- 领域异常基类。
|
||
- 请求关联标识中间件。
|
||
- 日志上下文和敏感信息脱敏工具。
|
||
|
||
`common` 不包含账户、Agent 或未来岗位业务逻辑,也不依赖业务应用。
|
||
|
||
### 4.2 `accounts`
|
||
|
||
`accounts` 负责:
|
||
|
||
- Django 内置 `User` 的一对一扩展资料。
|
||
- 当前用户资料的读取和修改。
|
||
- 登录、退出及资料页面组织。
|
||
- 用户停用约定和资料管理后台。
|
||
|
||
第一阶段不自定义用户模型,不开放公开注册。用户由管理员创建,日常账户退出使用停用而非物理删除。
|
||
|
||
### 4.3 `agent_runtime`
|
||
|
||
`agent_runtime` 负责:
|
||
|
||
- Agent Run 生命周期和状态转换。
|
||
- 运行事件的有序追加。
|
||
- 工具调用、幂等键及结果摘要记录。
|
||
- 人工确认请求和处理结果。
|
||
- SDK 运行结果到本地持久化模型的适配。
|
||
- 运行列表、运行详情和管理后台。
|
||
|
||
该模块不拥有岗位、企业、筛选和评分数据。
|
||
|
||
### 4.4 依赖方向
|
||
|
||
```text
|
||
JobRadar 配置与 URL
|
||
↓
|
||
accounts ──────→ common
|
||
↓
|
||
agent_runtime ─→ common
|
||
↓
|
||
OpenAI Agents SDK 适配层
|
||
```
|
||
|
||
禁止 `common` 反向依赖业务应用,禁止 `accounts` 依赖 `agent_runtime`。
|
||
|
||
## 5. 领域对象与数据所有权
|
||
|
||
### 5.1 用户归属抽象模型
|
||
|
||
建议提供抽象模型 `UserOwnedModel`:
|
||
|
||
- `owner`:关联 Django 用户。
|
||
- `created_at`:创建时间。
|
||
- `updated_at`:更新时间。
|
||
|
||
规则:
|
||
|
||
- 所有用户私有模型必须继承该模型或提供等价的 `owner` 字段。
|
||
- 普通查询必须从 `owner=request.user` 的数据范围开始,不能先全表查询再在 Python 中校验。
|
||
- 审计数据对用户删除采用保护策略;包含历史运行记录的用户不得直接物理删除。
|
||
- 管理员跨用户查询仅允许出现在 Django Admin 或明确的管理服务中。
|
||
|
||
### 5.2 `UserProfile`
|
||
|
||
建议字段:
|
||
|
||
- `user`:与 Django `User` 一对一关联。
|
||
- `display_name`:可选显示名。
|
||
- `timezone`:默认 `Asia/Shanghai`。
|
||
- `created_at`、`updated_at`。
|
||
|
||
第一阶段不加入技能、薪资、地区和企业偏好。这些内容属于后续个人画像领域,应在需求明确后独立建模并支持版本化。
|
||
|
||
资料由账户服务显式 `get_or_create`,不依赖隐藏的 Signal 自动创建。
|
||
|
||
### 5.3 `AgentRun`
|
||
|
||
建议字段分组:
|
||
|
||
- 归属:`owner`。
|
||
- 标识与状态:主键、`title`、`status`、`lock_version`。
|
||
- 输入输出:`input_summary`、`output_summary`。
|
||
- 版本:`agent_name`、`agent_version`、`instruction_version`、`toolset_version`、`model_name`。
|
||
- 追踪:`trace_id`、`started_at`、`finished_at`、`duration_ms`。
|
||
- 用量:`input_tokens`、`output_tokens`、`estimated_cost`。
|
||
- 失败:`error_code`、`error_summary`。
|
||
- 审计:`created_at`、`updated_at`。
|
||
|
||
输入、输出和错误只保存经过脱敏、限制大小的摘要,不保存 API Key、密码、Cookie 或完整敏感工具参数。第一阶段不执行真实模型调用,因此 `model_name`、token 和费用字段允许为空。
|
||
|
||
### 5.4 `AgentRunEvent`
|
||
|
||
建议字段:
|
||
|
||
- `run`、`sequence`、`event_type`。
|
||
- `summary`、`payload_summary`。
|
||
- `occurred_at`、`created_at`。
|
||
|
||
约束:
|
||
|
||
- `(run, sequence)` 唯一。
|
||
- 默认按 Run 和序号升序读取。
|
||
- 事件作为审计记录追加,普通业务不得修改或删除。
|
||
|
||
事件类型至少包括:
|
||
|
||
- `run_created`、`run_started`、`run_paused`、`run_resumed`。
|
||
- `run_succeeded`、`run_failed`、`run_cancelled`。
|
||
- `tool_started`、`tool_completed`、`tool_failed`。
|
||
- `approval_requested`、`approval_resolved`。
|
||
|
||
### 5.5 `ToolCall`
|
||
|
||
建议字段分组:
|
||
|
||
- 归属:`run`、`call_id`。
|
||
- 工具:`tool_name`、`tool_version`、`idempotency_key`。
|
||
- 状态:`status`、`started_at`、`finished_at`、`duration_ms`。
|
||
- 摘要:`arguments_summary`、`result_summary`。
|
||
- 失败:`error_code`、`error_summary`。
|
||
- 审计:`created_at`、`updated_at`。
|
||
|
||
约束:
|
||
|
||
- `(run, call_id)` 唯一。
|
||
- 非空幂等键在明确的业务作用域内唯一。
|
||
- 已成功的相同幂等调用不得重复产生副作用。
|
||
- 第一阶段只验证记录能力,不实现真实采集工具。
|
||
|
||
### 5.6 `HumanApproval`
|
||
|
||
建议字段:
|
||
|
||
- `run`、`request_key`、`approval_type`、`status`。
|
||
- `request_summary`、`decision_summary`。
|
||
- `requested_at`、`resolved_at`、`resolved_by`。
|
||
- `created_at`、`updated_at`。
|
||
|
||
状态包括 `pending`、`approved`、`rejected`、`expired` 和 `cancelled`。
|
||
|
||
约束:
|
||
|
||
- `(run, request_key)` 唯一。
|
||
- 只有 `pending` 状态可以处理。
|
||
- `resolved_by` 必须是 Run 所属用户或具备管理权限的管理员。
|
||
- 第一阶段只在 Admin 中处理人工确认,不开发独立用户审批页面。
|
||
|
||
数据库字段类型、索引名和完整删除策略应在编码前通过数据库专项设计进一步确认。
|
||
|
||
## 6. Agent Run 状态机
|
||
|
||
运行状态:
|
||
|
||
- `pending`
|
||
- `running`
|
||
- `waiting_approval`
|
||
- `succeeded`
|
||
- `failed`
|
||
- `cancelled`
|
||
|
||
允许的转换:
|
||
|
||
| 当前状态 | 可转换状态 |
|
||
| --- | --- |
|
||
| `pending` | `running`、`cancelled` |
|
||
| `running` | `waiting_approval`、`succeeded`、`failed`、`cancelled` |
|
||
| `waiting_approval` | `running`、`failed`、`cancelled` |
|
||
| `succeeded` | 无 |
|
||
| `failed` | 无 |
|
||
| `cancelled` | 无 |
|
||
|
||
状态规则:
|
||
|
||
- 终态不可再次转换。
|
||
- 成功和失败必须记录完成时间;失败还必须记录脱敏错误摘要。
|
||
- 进入 `waiting_approval` 时必须在同一事务中创建待处理的 `HumanApproval` 和对应事件。
|
||
- 确认通过后才能恢复运行;确认拒绝默认将 Run 置为 `cancelled`。
|
||
- 状态转换和对应事件必须在同一事务中提交。
|
||
- 第一阶段不支持失败 Run 原地重试;后续重试应创建新 Run 并关联原 Run。
|
||
|
||
## 7. 服务层设计
|
||
|
||
### 7.1 账户服务
|
||
|
||
建议职责:
|
||
|
||
- 获取或显式创建当前用户资料。
|
||
- 修改当前用户自己的资料。
|
||
- 由管理员停用指定用户。
|
||
|
||
普通用户请求其他用户资料时按资源不存在处理,避免泄露目标用户是否存在。
|
||
|
||
### 7.2 Agent Run 服务
|
||
|
||
服务层应提供以下原子能力:
|
||
|
||
- 创建 Run。
|
||
- 开始、完成、失败或取消 Run。
|
||
- 追加运行事件。
|
||
- 开始、完成或失败一次工具调用。
|
||
- 创建人工确认请求。
|
||
- 处理人工确认结果。
|
||
|
||
每个状态写方法按统一顺序执行:
|
||
|
||
1. 校验操作者身份、用户归属或管理权限。
|
||
2. 锁定需要修改的记录。
|
||
3. 校验当前状态和目标状态。
|
||
4. 修改主记录。
|
||
5. 追加不可变事件或更新关联记录。
|
||
6. 提交事务后再触发任何外部操作。
|
||
|
||
## 8. 事务、并发与一致性
|
||
|
||
关键状态变更使用 `transaction.atomic()`。
|
||
|
||
同一 Run 的并发控制策略:
|
||
|
||
- PostgreSQL 使用行锁串行化状态更新。
|
||
- `lock_version` 提供乐观并发检测。
|
||
- 事件序号在锁定 Run 后生成。
|
||
- `(run, sequence)` 唯一约束作为最后一致性防线。
|
||
- SQLite 测试只验证业务约束,不能代替 PostgreSQL 行锁与并发行为验证。
|
||
|
||
外部 SDK 或工具调用不得包裹在数据库长事务中:
|
||
|
||
```text
|
||
事务 1:登记调用开始 → 提交
|
||
外部调用:不持有数据库事务
|
||
事务 2:登记成功或失败 → 更新 Run → 追加事件 → 提交
|
||
```
|
||
|
||
该边界避免网络或模型调用长期占用数据库连接和行锁。
|
||
|
||
## 9. SDK 适配边界
|
||
|
||
第一阶段只建立 SDK 适配协议和测试替身:
|
||
|
||
- `AgentExecutionRequest`:本地运行编号、用户编号、版本和预算摘要。
|
||
- `AgentExecutionResult`:状态、输出摘要、trace 标识、用量和错误摘要。
|
||
- `AgentRunnerGateway`:运行入口协议。
|
||
- `StubAgentRunnerGateway`:自动化测试替身。
|
||
- `OpenAIAgentRunnerGateway`:真实 SDK 适配位置,岗位研究逻辑留到第二阶段。
|
||
|
||
约束:
|
||
|
||
- 未配置 OpenAI API Key 时,登录、资料、Admin 和运行查询仍应正常工作。
|
||
- 只有调用真实 SDK Gateway 时才检查 API Key。
|
||
- API Key 不进入运行上下文、数据库或日志。
|
||
- 第一阶段不接入 Celery,也不在 Web 请求内执行长时间 Agent Run。
|
||
- 使用测试替身验证运行开始、事件记录、成功和失败链路。
|
||
|
||
Celery 是目标架构中的后续运行载体;第一阶段仅稳定持久化和适配边界,两者不冲突。
|
||
|
||
## 10. 页面、权限与错误语义
|
||
|
||
建议路由:
|
||
|
||
```text
|
||
/accounts/login/
|
||
/accounts/logout/
|
||
/accounts/profile/
|
||
/runs/
|
||
/runs/<uuid>/
|
||
```
|
||
|
||
访问规则:
|
||
|
||
- 未登录用户访问资料和 Run 页面时跳转到登录页。
|
||
- 普通用户查询始终限定 `owner=request.user`。
|
||
- 跨用户访问 Run 统一返回 404,避免暴露资源存在性。
|
||
- 资料修改使用 POST 并启用 CSRF。
|
||
- Run 列表分页并按创建时间倒序。
|
||
- Run 详情按事件序号展示事件和工具调用摘要。
|
||
- 第一阶段不提供公开注册、账户自助删除或 HTTP API。
|
||
|
||
Admin 规则:
|
||
|
||
- 管理员可以维护用户资料并查看全部 Agent Run。
|
||
- 运行事件和完成后的工具调用原则上只读。
|
||
- Admin 中展示的参数和结果同样必须脱敏。
|
||
|
||
领域错误分类:
|
||
|
||
- `ResourceNotFound`:资源不存在或无权访问,页面响应为 404。
|
||
- `ValidationError`:输入不合法,显示表单错误或返回 400。
|
||
- `InvalidStateTransition`:运行状态冲突,语义为 409。
|
||
- `DuplicateOperation`:重复操作或幂等冲突,语义为 409。
|
||
- `AgentConfigurationError`:SDK 配置缺失,明确提示该功能不可用。
|
||
- `ExternalServiceError`:外部调用失败,保存脱敏错误并将 Run 置为失败。
|
||
|
||
用户响应不得包含堆栈、数据库信息、完整外部响应或敏感配置。
|
||
|
||
## 11. 日志与审计
|
||
|
||
增加请求关联标识中间件:
|
||
|
||
- 接受格式合法的入站关联标识,否则生成新标识。
|
||
- 将关联标识加入响应头和日志上下文。
|
||
- Agent 日志同时携带 `run_id`、`trace_id` 和 `tool_call_id`。
|
||
- 用户相关日志只记录内部用户编号。
|
||
|
||
必须记录:
|
||
|
||
- 登录失败和账户停用。
|
||
- Run 创建及每次状态转换。
|
||
- 工具调用开始、完成和失败。
|
||
- 人工确认请求及处理结果。
|
||
- 非法状态转换和跨用户访问尝试。
|
||
|
||
禁止记录:
|
||
|
||
- API Key、密码和 Session Cookie。
|
||
- 招聘网站完整 Cookie。
|
||
- 未脱敏的工具参数。
|
||
- 模型返回的完整敏感内容。
|
||
|
||
## 12. 实施顺序
|
||
|
||
实现采用“功能闭环驱动、模块边界承载”的纵向切片,而不是依次把各技术模块全部做完:
|
||
|
||
1. 用户登录、资料维护和跨用户隔离闭环。
|
||
2. 管理员创建测试 Run 并审计状态、事件、工具调用和人工确认的闭环。
|
||
3. 普通用户查看自己的运行列表和运行轨迹闭环。
|
||
4. SDK 测试替身驱动一次成功或失败持久化运行的闭环。
|
||
5. SQLite、页面、日志、质量工具和 PostgreSQL 的完整验收闭环。
|
||
|
||
每个切片内部仍按模型与迁移、服务与事务、入口与页面、测试与文档的依赖顺序实施。切片必须形成可运行结果并完成相应验证,才进入下一片;没有当前需求的未来模块和抽象不提前创建。
|
||
|
||
## 13. 测试与验收策略
|
||
|
||
自动化测试至少覆盖:
|
||
|
||
- 用户资料首次创建和重复获取。
|
||
- 未登录访问拒绝。
|
||
- 用户只能读取和修改自己的资料。
|
||
- 用户 A 查询用户 B 的 Run 返回 404。
|
||
- 管理员能够查看全部运行记录。
|
||
- 所有合法和非法状态转换。
|
||
- 终态不可修改。
|
||
- 并发状态更新只能有一个成功。
|
||
- 事件序号连续且唯一。
|
||
- 工具调用幂等键冲突。
|
||
- 工具调用成功、失败和超时记录。
|
||
- 人工确认只能处理一次且处理人权限正确。
|
||
- 敏感字段不会写入日志和摘要。
|
||
- 未配置 API Key 时非 Agent 页面正常。
|
||
- SDK 测试替身的成功、失败和异常链路。
|
||
- SQLite 迁移和全量测试。
|
||
- PostgreSQL 空库迁移、唯一约束和并发验证。
|
||
|
||
阶段验收命令沿用开发计划:
|
||
|
||
```powershell
|
||
python manage.py check
|
||
python manage.py check --deploy --settings=JobRadar.settings.production
|
||
python manage.py makemigrations --check --dry-run
|
||
python manage.py migrate
|
||
pytest
|
||
ruff check .
|
||
```
|
||
|
||
生产配置检查需要注入非秘密的验证用配置和隔离的数据库参数,不得连接生产库执行测试。
|
||
|
||
## 14. 兼容、迁移与回滚
|
||
|
||
- 新应用按 `common`、`accounts`、`agent_runtime` 的依赖顺序生成迁移。
|
||
- 模型和约束同时兼容 SQLite 与 PostgreSQL,不使用数据库方言专属 SQL。
|
||
- 每组迁移先在空 SQLite 数据库验证,再在空 PostgreSQL 数据库验证。
|
||
- 第一阶段没有既有业务数据迁移,不提供破坏性数据转换。
|
||
- 单步实现失败时回滚对应代码和未发布迁移;不得通过修改已发布迁移掩盖问题。
|
||
- 外部 SDK 未配置或不可用时关闭真实执行入口,不影响认证、资料和运行查询。
|
||
|
||
## 15. 风险与待确认项
|
||
|
||
以下事项不阻塞第一阶段编码,但必须在对应节点前确认:
|
||
|
||
| 事项 | 当前建议 | 最迟确认时间 |
|
||
| --- | --- | --- |
|
||
| PostgreSQL 实例位置 | 本地开发继续使用 SQLite | PostgreSQL 集成验证前 |
|
||
| OpenAI 模型与预算 | 第一阶段不固定付费模型 | 第二阶段启动前 |
|
||
| API Key 注入 | 仅使用环境变量或部署平台密钥 | 真实调用前 |
|
||
| 用户物理删除 | 默认禁止,使用停用代替 | 开放账户管理能力前 |
|
||
| 摘要最大长度 | 编码前给出统一上限 | 模型详细设计时 |
|
||
| 日志和审计保留周期 | 暂不自动清理 | 公网部署前 |
|
||
| 人工确认超时 | 第一阶段仅支持手动取消 | 第二阶段运行设计时 |
|
||
|
||
## 16. 实现交接清单
|
||
|
||
编码前应进一步固化:
|
||
|
||
- 使用数据库设计明确字段类型、索引、约束名和删除策略。
|
||
- 如需 HTTP API,再使用 API 设计明确端点和响应契约;第一阶段默认只提供 Django 页面。
|
||
- 确认 `common`、`accounts`、`agent_runtime` 的应用包名和 URL 命名空间。
|
||
- 确认摘要长度、JSON 大小限制和日志脱敏词表。
|
||
- 确认 PostgreSQL 集成验证环境。
|
||
|
||
推荐实现结论:
|
||
|
||
- 使用 Django 内置用户和一对一 `UserProfile`。
|
||
- 使用 `common`、`accounts`、`agent_runtime` 三个应用。
|
||
- 由服务层统一权限检查、状态转换和审计写入。
|
||
- 运行事件采用不可变追加模式。
|
||
- 数据库事务与外部调用分离。
|
||
- SDK 使用 Gateway 和测试替身建立稳定边界。
|
||
- 第一阶段不引入 Celery、DRF、Vue 或真实模型调用。
|