--- 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// ``` 访问规则: - 未登录用户访问资料和 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 或真实模型调用。