17 KiB
reviewStatus, reviewedAt, replacedBy
| reviewStatus | reviewedAt | replacedBy |
|---|---|---|
| pending | null | null |
第一阶段工程基线后端落地设计
1. 文档定位
本文档定义 JobRadar 第一阶段工程基线的后端落地边界,供后续模型设计、编码、迁移、测试和验收使用。本文档是待审核设计,不代表相关代码已经实现。
设计依据:
当前技术基线为 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. 总体调用关系
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 依赖方向
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:与 DjangoUser一对一关联。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 状态机
运行状态:
pendingrunningwaiting_approvalsucceededfailedcancelled
允许的转换:
| 当前状态 | 可转换状态 |
|---|---|
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。
- 追加运行事件。
- 开始、完成或失败一次工具调用。
- 创建人工确认请求。
- 处理人工确认结果。
每个状态写方法按统一顺序执行:
- 校验操作者身份、用户归属或管理权限。
- 锁定需要修改的记录。
- 校验当前状态和目标状态。
- 修改主记录。
- 追加不可变事件或更新关联记录。
- 提交事务后再触发任何外部操作。
8. 事务、并发与一致性
关键状态变更使用 transaction.atomic()。
同一 Run 的并发控制策略:
- PostgreSQL 使用行锁串行化状态更新。
lock_version提供乐观并发检测。- 事件序号在锁定 Run 后生成。
(run, sequence)唯一约束作为最后一致性防线。- SQLite 测试只验证业务约束,不能代替 PostgreSQL 行锁与并发行为验证。
外部 SDK 或工具调用不得包裹在数据库长事务中:
事务 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. 页面、权限与错误语义
建议路由:
/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. 实施顺序
实现采用“功能闭环驱动、模块边界承载”的纵向切片,而不是依次把各技术模块全部做完:
- 用户登录、资料维护和跨用户隔离闭环。
- 管理员创建测试 Run 并审计状态、事件、工具调用和人工确认的闭环。
- 普通用户查看自己的运行列表和运行轨迹闭环。
- SDK 测试替身驱动一次成功或失败持久化运行的闭环。
- SQLite、页面、日志、质量工具和 PostgreSQL 的完整验收闭环。
每个切片内部仍按模型与迁移、服务与事务、入口与页面、测试与文档的依赖顺序实施。切片必须形成可运行结果并完成相应验证,才进入下一片;没有当前需求的未来模块和抽象不提前创建。
13. 测试与验收策略
自动化测试至少覆盖:
- 用户资料首次创建和重复获取。
- 未登录访问拒绝。
- 用户只能读取和修改自己的资料。
- 用户 A 查询用户 B 的 Run 返回 404。
- 管理员能够查看全部运行记录。
- 所有合法和非法状态转换。
- 终态不可修改。
- 并发状态更新只能有一个成功。
- 事件序号连续且唯一。
- 工具调用幂等键冲突。
- 工具调用成功、失败和超时记录。
- 人工确认只能处理一次且处理人权限正确。
- 敏感字段不会写入日志和摘要。
- 未配置 API Key 时非 Agent 页面正常。
- SDK 测试替身的成功、失败和异常链路。
- SQLite 迁移和全量测试。
- PostgreSQL 空库迁移、唯一约束和并发验证。
阶段验收命令沿用开发计划:
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 或真实模型调用。