docs(course): 建立Agent学习路线并新增第一课

This commit is contained in:
bruce
2026-08-28 15:44:21 +08:00
parent ddab9242c7
commit 38f1b15d1c
14 changed files with 870 additions and 142 deletions
+69
View File
@@ -0,0 +1,69 @@
# JobRadar Agent 契约
## Agent 名称与目标
第一版只实现“岗位研究 Agent(Job Research Agent)”。它根据当前用户的岗位目标和个人画像,自主选择必要工具,查找候选岗位、补充企业证据、执行筛选与评分,最终输出可解释、可追溯的岗位研究结果。
## 输入
- 当前用户和个人画像编号
- 岗位关键词、地区、薪资、经验和学历边界
- 企业性质、行业偏好及排除条件
- 评分配置版本和目标招聘网站
- 最大岗位数量、运行时间和工具调用预算
## 结构化输出
- Agent Run 状态
- 候选、淘汰、推荐和待复核岗位数量
- 推荐岗位、维度分数和硬规则命中情况
- 企业性质、证据来源和置信度
- 风险提示、信息缺口和人工处理要求
- 工具调用及运行摘要
## 第一版工具
| 工具 | 单一责任 | 副作用 |
| --- | --- | --- |
| `search_jobs` | 获取候选岗位 | 保存原始记录 |
| `normalize_jobs` | 标准化并识别重复岗位 | 保存标准化结果 |
| `apply_hard_filters` | 执行确定性筛选规则 | 保存筛选结果 |
| `research_company` | 查询企业主体、性质和证据 | 保存企业证据 |
| `analyze_job_match` | 生成结构化匹配维度分析 | 保存分析结果 |
| `calculate_job_score` | 使用固定公式计算最终分数 | 保存评分结果 |
| `save_research_report` | 保存本次最终报告 | 完成运行状态 |
工具实现前可以调整命名,但必须保持类型化参数、结构化返回值、单一职责和清晰的副作用说明。
## 状态与上下文
运行上下文至少包含 `user_id`、`agent_run_id`、`search_task_id`、个人画像版本、规则版本、评分配置版本和当前运行预算。上下文不包含网站明文密码、Cookie、模型 API Key 等敏感值;工具在服务端根据授权范围读取凭据。
## 人工确认边界
以下情况必须暂停或转人工处理:
- 遇到验证码、登录失效或网站访问限制。
- 企业主体或企业性质证据冲突。
- 准备执行超出用户配置范围的大规模采集。
- 任何简历投递、消息发送或对外联系行为。
- 删除历史岗位、证据或运行记录。
第一版不向 Agent 提供自动投递、自动联系、任意 Shell 或任意 SQL 工具。
## 可观测性
每次运行记录 Agent、模型、指令和工具版本,以及开始、结束、暂停和失败时间。每次工具调用记录名称、参数摘要、结果摘要、耗时和错误,同时保存最终结构化输出、trace 标识、token 用量、成本和人工确认结果。
WebUI提供“最终结果”和“执行过程”两个视角,使项目既可用于求职,也可展示 Agent 如何完成真实任务。
## 非目标
- 不使用多 Agent 伪造复杂度。
- 不通过兼容层同时支持多个 Agent 框架。
- 不引入 pgvector、向量数据库或无明确需求的 RAG。
- 不允许模型直接访问数据库、执行任意代码或绕过工具权限。
## 多 Agent 演进条件
只有单一 instructions 无法表达职责、工具和上下文导致稳定性下降、评测证明不同领域互相干扰,或者某类任务需要独立模型和安全边界时,才拆分专家 Agent。拆分后使用 Agents SDK 正式 handoff 或 manager pattern,并通过评测证明收益。
+76 -75
View File
@@ -2,115 +2,116 @@
## 设计原则
JobRadar 第一阶段采用模块化单体架构。Django 负责业务模型、管理后台、自定义页面和任务入口;耗时的浏览器采集、企业信息补全和 AI 分析由异步 Worker 执行。各业务模块在代码层隔离,但共享 PostgreSQL 数据库,避免在个人项目初期引入微服务通信和部署成本。
JobRadar 采用 Agent 原生的模块化单体架构。Django 负责公网产品外壳、用户系统、业务数据和运行记录;OpenAI Agents SDK 负责 Agent 循环、工具调用、结构化输出、运行状态和追踪;Celery Worker 承载耗时运行。各模块共享 PostgreSQL,避免为了展示效果提前拆分微服务。
## 目标架构
```mermaid
flowchart LR
USER["公网用户"] --> PROXY["Nginx / Caddy 与 HTTPS"]
PROXY --> WEB["Django 登录、Admin 与岗位工作台"]
WEB --> APP["Django 业务应用"]
PROXY --> WEB["Django 登录与 Agent 工作台"]
WEB --> APP["Django 业务与运行管理"]
APP --> DB[("PostgreSQL")]
APP --> REDIS["Redis"]
APP --> REDIS["Redis 任务队列"]
BEAT["Celery Beat"] --> REDIS
REDIS --> WORKER["Celery Worker"]
WORKER --> COLLECT["Playwright / HTTPX 采集"]
WORKER --> ENRICH["企业信息联网补全"]
WORKER --> AI["LLM 结构化分析"]
COLLECT --> DB
ENRICH --> DB
AI --> DB
REDIS --> WORKER["Celery Agent Worker"]
WORKER --> RUNNER["Agents SDK Runner"]
RUNNER --> AGENT["岗位研究 Agent"]
AGENT --> TOOLS["类型化 Function Tools"]
TOOLS --> COLLECT["Playwright / HTTPX"]
TOOLS --> ENRICH["企业信息与联网调查"]
TOOLS --> RULES["规则筛选与权重计算"]
TOOLS --> DB
RUNNER --> TRACE["Tracing 与运行事件"]
```
## Agent 定位
Agent 不是对岗位描述进行一次总结的接口,而是任务执行主体。一次完整运行需要接收目标和边界、自主选择工具、根据工具结果继续调查,并在证据不足或涉及敏感操作时暂停,最终输出结构化结果和完整轨迹。
第一版只设置一个“岗位研究 Agent”。采集器、企业查询、评分器和数据库不是独立 Agent,而是 Agent 可以调用的确定性工具。只有评测证明单 Agent 在职责或指令复杂度上不可维护时,才拆分专家 Agent 并使用正式 handoff。
## Agent 运行边界
- 使用 OpenAI Agents SDK 的 `Agent` 和 `Runner` 实现运行循环,不自行模拟工具调用循环。
- 使用 `function_tool` 暴露业务能力,输入和输出采用明确的数据模型。
- Django 请求只创建任务;长时间 Agent Run 由 Celery Worker 执行。
- 运行上下文携带用户、任务、规则版本和数据库会话边界,不把敏感凭据写入提示词。
- Agent 最终输出使用结构化模型校验,数据库写入仍通过受控工具执行。
- 保存 run、turn、tool call、tool result、trace、耗时、token 用量、异常和人工确认状态。
- 第一版不使用 SandboxAgent;业务只需受控网络和数据库工具,不需要开放 Shell 或任意文件执行能力。
## 用户与权限边界
系统支持公网访问,第一阶段采用简化的用户体系:
- 使用 Django 内置认证、Session 和密码管理能力。
- 普通用户拥有个人画像、搜索任务、规则、Agent Run 和岗位操作记录。
- 用户私有数据通过所属用户字段隔离,工具执行前统一校验数据归属。
- 管理员通过 Django Admin 维护全局站点配置、工具开关和异常数据。
- 第一阶段仅区分普通用户与管理员,不建设复杂 RBAC。
- 默认由管理员创建账号并关闭公开注册。
- 直接使用 Django 内置用户、认证、Session 和密码管理能力。
- 普通用户拥有个人画像、搜索任务、筛选规则、评分配置和岗位操作记录。
- 所有用户私有业务表通过所属用户字段隔离,服务层和查询层统一限制当前用户的数据范围。
- 管理员通过 Django Admin 维护全局站点配置、采集状态和异常数据。
- 第一阶段仅区分普通用户与管理员,不建设 RBAC、组织机构、岗位角色和审批授权模型。
- 默认由管理员创建账号并关闭公开注册;开放注册属于后续独立功能。
## Agent 处理链路
即使初期实际只有一个账号,数据模型仍保留用户归属,避免未来增加账号时重新改造所有核心表。
## 处理链路
1. 用户配置目标网站、关键词、地区、硬性条件和评分权重。
2. 调度器创建采集批次,站点适配器获取并保存原始岗位记录。
3. 标准化模块统一岗位、公司、薪资、经验和地点字段。
4. 去重模块根据来源岗位编号、内容指纹和标准化字段建立岗位关系。
5. 筛选模块先执行可确定判断的硬规则,并记录通过、淘汰或待确认原因。
6. 企业补全模块查询可信来源,保存企业主体、性质、证据和置信度。
7. AI 模块对通过初筛的岗位进行技能、职责、风险和匹配度分析。
8. 评分模块根据固定公式生成最终分数,并保存评分配置版本。
9. 用户在岗位工作台中收藏、忽略或更新投递状态。
1. 用户提交岗位研究目标,Django 创建 Agent Run 并投递异步任务。
2. Agent 读取用户画像、目标网站、硬性条件、评分权重和运行预算。
3. Agent 调用搜索与采集工具获取候选岗位,工具保存原始证据。
4. Agent 调用标准化、去重和硬规则工具,剔除确定不符合的岗位。
5. Agent 按需调用企业查询和联网调查工具补足证据。
6. Agent 调用结构化分析及确定性评分工具,得到维度分数和风险项。
7. 证据冲突或置信度不足时标记待人工复核,不强行下结论。
8. Agent 输出研究报告,持久化工具保存结果、引用和运行状态。
9. WebUI 展示推荐结果、Agent 轨迹、工具调用和失败节点。
## 模块边界
### 采集模块
### Agent 运行模块
- 定义 instructions、运行上下文、工具集合和结构化输出契约。
- 控制最大轮次、时间预算、工具预算和失败恢复策略。
- 使用 SDK tracing 观察运行,并同步关键事件到本地数据库。
- 通过 guardrail 和人工确认限制高风险行为。
- 可以按内容和分析版本复用结果,但不引入向量检索兼容层。
### 工具层
- 一个工具只承担一种可描述、可测试的业务能力。
- 工具负责真实外部操作,Agent 不得声称执行未发生的查询或写入。
- 写工具必须幂等,记录调用来源、输入摘要、输出摘要和异常。
- 工具只返回下一步所需数据,避免把敏感信息放入模型上下文。
### 采集与企业研究工具
- 优先使用网站允许访问的接口或普通 HTTP 请求,动态页面才使用 Playwright。
- 每个网站独立实现适配器,并输出统一的原始岗位数据对象。
- 负责限流、超时、重试和采集状态,不负责岗位评分。
- 页面变更导致解析失败时,应保留错误证据并暂停对应适配器。
- 每个网站独立实现适配器,并输出统一的原始岗位对象。
- 企业结论保存来源、证据摘要、获取时间和置信度。
- 多来源冲突、主体不明确或置信度不足时请求人工复核。
### 标准化与去重模块
- 原始数据只追加或生成新版本,不被标准化结果覆盖。
- 同站唯一键建议为“站点编号 + 来源岗位编号”。
- 跨站重复判断结合公司标准名、岗位名、地点、薪资和内容指纹。
- 跨站重复岗位建立关联,不直接物理删除来源记录。
### 企业补全模块
- 按企业标准名称或统一社会信用代码识别主体。
- 结论至少保存来源地址、证据摘要、获取时间和置信度。
- 多来源冲突、主体不明确或置信度不足时进入人工复核。
- 人工确认结果优先级高于自动判断,但不得删除历史证据。
### AI 分析模块
- 输入为标准化岗位、个人画像和必要证据,不直接使用未经清洗的整页内容。
- 输出必须通过结构化模型校验,不接受无法解析的自由文本作为业务结果。
- 记录模型名称、提示词版本、输入摘要和分析时间。
- 相同内容与同一分析版本应优先复用缓存结果。
### 评分模块
### 规则与评分工具
- 硬性条件和最终加权公式由确定性代码执行。
- 每个评分维度保留原始分、权重、加权分和判断依据。
- 每个维度保存原始分、权重、加权分和判断依据。
- 权重或规则变化时创建新版本,历史结果仍可回溯。
- 风险扣分与正向评分分开保存,避免结果无法解释。
- Agent 不能绕过规则工具或自行生成最终分数。
## 数据边界
第一阶段核心实体包括:
- 招聘网站及站点配置
- 搜索任务与采集批次
- 原始岗位记录与标准化岗位
- 企业主体与企业证据
- 筛选规则、规则版本和筛选结果
- 评分配置、评分结果和 AI 分析记录
- 收藏、忽略、投递和面试状态
- 任务日志与人工复核记录
具体字段应在首个目标网站和筛选规则明确后再落地,避免提前设计大量无法验证的字段。
- 用户、个人画像及数据归属
- 招聘网站、搜索任务与采集批次
- 原始岗位、标准化岗位与企业证据
- 筛选规则、评分配置及其版本
- Agent Run、运行事件、工具调用和结构化输出
- Agent 指令版本、工具版本、模型配置和评测结果
- 收藏、忽略、投递、面试和人工复核记录
## 部署边界
系统目标部署到公网。目标运行单元为:
```text
web Django Web 服务
worker Celery Worker
worker Celery Agent Worker 与 Agents SDK Runtime
beat Celery 定时调度
postgres PostgreSQL
redis Redis
proxy Nginx 或 Caddy 反向代理及 HTTPS 终止
```
公网只开放反向代理的 HTTP/HTTPS 端口。Django、PostgreSQL 和 Redis 位于容器私有网络;Django 使用生产级 WSGI/ASGI 服务运行,不使用开发服务器对外提供服务。部署时必须配置 HTTPS、安全 Cookie、`ALLOWED_HOSTS`、可信 CSRF 来源、登录限流、密钥注入、日志审计和数据库备份。
公网只开放反向代理的 HTTP/HTTPS 端口。Django、PostgreSQL 和 Redis 位于容器私有网络,并配置 HTTPS、安全 Cookie、可信来源、登录限流、密钥注入、日志审计和数据库备份。
+82
View File
@@ -0,0 +1,82 @@
# JobRadar 学习协作说明
## 项目定位
JobRadar不只是需要交付的岗位系统,也是用于系统学习正规Agent开发的实战项目。学习目标包括Agents SDK运行循环、工具设计、状态管理、人工确认、可观测性、评测、公网Web应用和工程化部署。
## 默认协作方式
每次只推进一课,流程如下:
1. 回顾上一课成果和遗留问题。
2. 说明本课完成后能够做什么。
3. 解释新概念是什么、为什么需要以及在JobRadar中的位置。
4. 使用Java、Spring或常见后端设计进行必要对照。
5. 给出本课完整参考代码,并逐段解释执行顺序和设计原因。
6. 明确代码应该写入哪个文件以及如何运行。
7. 给出预期结果、常见错误、调试方法和验收标准。
8. 学习者手动完成代码。
9. 对学习者代码进行检查,不直接覆盖已有实现。
10. 当前课通过后再继续下一课。
## 每课内容结构
每课原则上包含:
- 本课目标
- 前置知识
- 架构位置
- 核心原理
- Java或Spring对照
- 完整参考代码
- 关键代码解析
- 手动实现步骤
- 运行命令与预期结果
- 常见错误与排查方法
- 课堂练习
- 自检清单
- 验收标准
- 本课小结
## 代码边界
- 默认由学习者手动编写业务代码。
- 我提供参考代码、解释、提示、测试思路和审查反馈。
- 未经明确要求,不直接向JobRadar写入本课业务实现。
- 不一次性生成未来阶段代码,不提前制造空模块。
- 不覆盖学习者已有实现;发现问题时先说明原因和修正方案。
- 用户明确要求代为修改时,先说明方案、涉及文件和预期结果,再执行改动。
## 练习约定
如果后续为课程创建练习文件,练习文件只包含注释形式的题目、操作提示、预期结果、自检项和验收标准,不包含导入语句、函数骨架、`pass`、测试数据、答案或运行记录。
参考答案与练习文件分离。学习者完成代码后,优先基于学习者实现进行讲解,而不是用参考答案覆盖。
## 验收方式
每课至少验证:
- 代码能够在`JobRadar` Conda环境中运行。
- Django系统检查或对应测试通过。
- 行为与本课预期结果一致。
- Agent工具真实执行,没有用自然语言伪造结果。
- 数据写入具备用户归属、幂等和审计边界。
- 密钥、Cookie和本地配置没有进入Git。
- 学习者能够说明关键代码为什么这样设计。
涉及Agent的课程还需要检查工具调用、结构化输出、trace、失败路径和人工确认边界,不能只检查最终文本是否看起来正确。
## 教学深度
学习者已有Java和数据库基础,因此不重复教授变量、类、SQL、事务等通用概念。课程重点解释:
- Python与Java在语言和工程组织上的差异。
- Django与Spring Boot在请求处理、ORM、配置和用户体系上的差异。
- Agents SDK与普通Service编排、工作流引擎及传统定时任务的差异。
- Agent何时自主决策,何时必须交给确定性工具或人工确认。
- 如何通过trace和eval判断一个Agent是否真正可靠。
## 当前起点
第一课[Django项目配置与开发/生产环境拆分](lessons/01_Agent工程基线/1_1_Django项目配置与开发生产环境拆分/README.md)已经创建。当前应阅读讲义、参考完整示例,然后手动拆分项目配置;完成后执行课程验收命令并提交代码供检查。
@@ -0,0 +1,267 @@
# 第1课:Django项目配置与开发/生产环境拆分
## 本课目标
完成本课后,你将能够:
1. 解释Django设置模块(settings module)如何被加载。
2. 将单一`settings.py`拆成公共、开发和生产三套配置。
3. 使用`DJANGO_SETTINGS_MODULE`选择运行环境。
4. 将生产密钥和域名移出Git仓库。
5. 使用Django部署检查发现危险配置。
本课只学习配置拆分,不接入PostgreSQL、Celery或Agents SDK。
## 前置知识
- 能运行`python manage.py check`和`python manage.py runserver`。
- 理解Python模块和`import`。
- 了解Spring Boot的Profile和`application-{profile}.yml`。
## 在JobRadar架构中的位置
JobRadar最终部署到公网,并需要数据库密码、模型API Key、招聘网站凭据等敏感配置。如果开发与生产共用一个`settings.py`,很容易出现以下问题:
- 将开发环境的`DEBUG = True`带到公网。
- 将生产密钥提交到Git。
- 本地SQLite和生产PostgreSQL配置互相覆盖。
- 本地地址、正式域名和HTTPS策略混在一起。
- Agent运行时读取了错误的模型或密钥。
因此,安全配置是用户系统和Agent功能之前的工程基线。
## 核心原理
### 1. settings本质上是Python模块
Django设置不是特殊配置格式,而是包含模块级变量的Python模块。例如:
```python
DEBUG = False
ALLOWED_HOSTS = ["jobradar.example.com"]
```
Django启动时读取`DJANGO_SETTINGS_MODULE`,然后导入该变量指定的Python模块:
```text
JobRadar.settings.development
JobRadar.settings.production
```
点号是Python包路径,不是文件系统斜杠。
### 2. 公共配置与环境差异分离
本课采用三层结构:
```text
JobRadar/
└── settings/
├── __init__.py
├── base.py
├── development.py
└── production.py
```
- `base.py`:应用、Middleware、模板、国际化等公共配置。
- `development.py`:开发密钥、`DEBUG = True`和本地地址。
- `production.py`:生产密钥、正式域名、HTTPS和安全Cookie。
`development.py`和`production.py`通过`from .base import *`继承公共配置。这种星号导入在普通业务代码中不推荐,但Django分层settings是一个边界明确的配置场景。
### 3. 与Spring Boot Profile对照
| Spring Boot | Django本课方案 |
| --- | --- |
| `application.yml` | `settings/base.py` |
| `application-dev.yml` | `settings/development.py` |
| `application-prod.yml` | `settings/production.py` |
| `spring.profiles.active=dev` | `DJANGO_SETTINGS_MODULE=JobRadar.settings.development` |
| `${DB_PASSWORD}` | `os.environ["DB_PASSWORD"]` |
| `@Profile`控制Bean | Django通常在settings中切换组件配置 |
最大的区别是:Spring配置主要是YAML/Properties,Django settings本身就是Python代码。它更灵活,也意味着不能在里面随意写复杂业务逻辑。
## 完整参考代码
参考目录位于本课的`reference/`中:
```text
reference/
├── settings/
│ ├── __init__.py
│ ├── base.py
│ ├── development.py
│ └── production.py
├── manage.py
├── asgi.py
└── wsgi.py
```
这些文件用于阅读和手动输入,不能直接覆盖当前项目。你需要理解每一处路径变化后,再把对应结构写入项目。
## 关键代码解析
### `BASE_DIR`为什么多一个`parent`
原始`settings.py`位于:
```text
JobRadar/settings.py
```
拆分后的`base.py`位于:
```text
JobRadar/settings/base.py
```
文件多进入了一层`settings`目录,因此项目根目录改为:
```python
BASE_DIR = Path(__file__).resolve().parent.parent.parent
```
如果仍使用两个`parent`,SQLite和模板目录都会指向错误位置。
### 为什么生产配置使用`os.environ[名称]`
```python
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
```
方括号读取在变量缺失时立即抛出`KeyError`,让生产进程启动失败。相比提供一个不安全默认值,这种“快速失败”(fail fast)更安全。
开发配置可以使用明确标记为仅限本地的默认密钥,但生产配置绝不能提供默认生产密钥。
### 为什么入口的默认环境不同
- `manage.py`默认开发配置,方便本地运行。
- `asgi.py`和`wsgi.py`默认生产配置,降低部署时误启用开发设置的风险。
- 命令行仍可通过`--settings`显式覆盖,便于检查两个环境。
## 手动实现步骤
1. 在`JobRadar`包内创建`settings`目录及`__init__.py`。
2. 参考`base.py`移动原`settings.py`的公共配置,并修正`BASE_DIR`。
3. 编写`development.py`和`production.py`。
4. 修改项目根目录的`manage.py`,默认指向开发配置。
5. 修改`JobRadar/asgi.py`和`JobRadar/wsgi.py`,默认指向生产配置。
6. 删除旧`JobRadar/settings.py`前,逐项确认内容已经迁移。
7. 分别运行开发和生产配置检查。
不要直接复制整个`reference`目录覆盖项目,因为参考文件的层级和真实文件位置并不完全相同。
## 运行方法
### 检查开发配置
```powershell
conda activate JobRadar
python manage.py check --settings=JobRadar.settings.development
```
预期结果:
```text
System check identified no issues (0 silenced).
```
### 检查生产配置的缺失密钥保护
先确保当前PowerShell没有设置课程变量:
```powershell
Remove-Item Env:DJANGO_SECRET_KEY -ErrorAction SilentlyContinue
python manage.py check --settings=JobRadar.settings.production
```
预期结果:命令失败,并明确提示缺少`DJANGO_SECRET_KEY`。这是安全保护生效,不是课程代码错误。
### 临时设置生产变量并检查
以下值只在当前PowerShell进程有效:
```powershell
$env:DJANGO_SECRET_KEY = "仅用于本地检查的长随机字符串-请勿用于生产"
$env:DJANGO_ALLOWED_HOSTS = "jobradar.example.com"
$env:DJANGO_CSRF_TRUSTED_ORIGINS = "https://jobradar.example.com"
python manage.py check --settings=JobRadar.settings.production
python manage.py check --deploy --settings=JobRadar.settings.production
```
普通`check`应通过。`check --deploy`可能继续提示HSTS时长等部署建议;本课重点是能区分错误和安全告警,不要求为了消除告警而盲目开启尚未理解的配置。
检查完成后清理临时变量:
```powershell
Remove-Item Env:DJANGO_SECRET_KEY
Remove-Item Env:DJANGO_ALLOWED_HOSTS
Remove-Item Env:DJANGO_CSRF_TRUSTED_ORIGINS
```
## 常见错误
### 错误1:`No module named 'JobRadar.settings.development'`
原因通常是:
- 没有创建`settings/__init__.py`。
- 包或模块名称拼错。
- 旧`settings.py`仍存在,导致目录结构未正确调整。
### 错误2:SQLite文件出现在`JobRadar/`包内
原因是拆分后没有为`BASE_DIR`增加一层`parent`。
### 错误3:生产检查仍然使用开发配置
先查看命令是否传入:
```text
--settings=JobRadar.settings.production
```
也可以运行:
```powershell
python manage.py diffsettings --settings=JobRadar.settings.production
```
### 错误4:把生产密钥写入`production.py`
生产密钥必须由服务器部署环境注入。Git中的示例只能写变量名和虚假示例,不能包含真实值。
### 错误5:`ALLOWED_HOSTS = ["*"]`
星号允许任意Host,失去了Django主机头校验的主要意义。公网项目应填写明确域名;本地开发单独允许`127.0.0.1`和`localhost`。
## 课堂练习
练习要求见[practice.py](practice.py)。该文件只有题目和验收标准,你的实际代码应写入JobRadar项目配置文件中。
## 自检清单
- [ ] 能解释`DJANGO_SETTINGS_MODULE`的作用。
- [ ] 能说明`base.py`中为什么使用三个`parent`。
- [ ] 开发环境默认开启`DEBUG`,生产环境固定关闭。
- [ ] 生产环境缺少密钥时会快速失败。
- [ ] 生产域名和CSRF可信来源来自外部配置。
- [ ] ASGI/WSGI默认指向生产配置。
- [ ] Git差异中没有真实密钥。
## 验收标准
1. `python manage.py check --settings=JobRadar.settings.development`通过。
2. 缺少`DJANGO_SECRET_KEY`时,生产配置检查按预期失败。
3. 临时提供生产变量后,普通生产配置检查通过。
4. `python manage.py makemigrations --check --dry-run --settings=JobRadar.settings.development`显示无遗漏迁移。
5. `git diff --check`通过。
6. `git diff`中没有真实密钥、Cookie或密码。
7. 学习者能解释开发和生产入口为何采用不同默认settings。
## 本课小结
本课没有增加业务功能,但建立了公网Agent项目的安全配置基础。Django通过`DJANGO_SETTINGS_MODULE`选择一个Python配置模块;公共设置放入`base.py`,开发和生产只覆盖差异。生产环境应关闭`DEBUG`、限制域名、启用HTTPS相关设置,并在缺少密钥时拒绝启动。
完成手动实现并通过验收后,下一课进入“Django用户系统与用户数据归属”。
@@ -0,0 +1,43 @@
# 第1课练习:Django项目配置与开发/生产环境拆分
#
# 练习目标:
# 1. 将当前单文件settings.py拆分为base、development和production三个配置模块。
# 2. 让本地管理命令默认使用开发配置,让ASGI和WSGI默认使用生产配置。
# 3. 验证生产密钥缺失时快速失败,提供临时变量后能够通过普通系统检查。
#
# 操作要求:
# 1. 在JobRadar包内创建settings目录和__init__.py。
# 2. 把公共配置迁移到base.py,并根据新目录层级修正BASE_DIR。
# 3. 在development.py中配置仅限本地使用的SECRET_KEY、DEBUG和ALLOWED_HOSTS。
# 4. 在production.py中从外部读取DJANGO_SECRET_KEY、DJANGO_ALLOWED_HOSTS和
# DJANGO_CSRF_TRUSTED_ORIGINS,固定关闭DEBUG并配置HTTPS安全项。
# 5. 调整manage.py、asgi.py和wsgi.py使用正确的默认配置模块。
# 6. 确认全部配置迁移后再删除旧settings.py。
#
# 预期结果:
# 1. 开发配置执行Django系统检查时通过。
# 2. 未设置DJANGO_SECRET_KEY时,生产配置检查明确失败。
# 3. 设置三个临时生产变量后,生产配置普通检查通过。
# 4. 项目根目录仍是BASE_DIR,SQLite和templates路径没有移动到JobRadar包中。
# 5. Git变更中不存在真实密钥、密码或Cookie。
#
# 自检问题:
# 1. DJANGO_SETTINGS_MODULE保存的是文件路径还是Python模块路径?
# 2. 为什么生产配置不应该为SECRET_KEY提供默认值?
# 3. 为什么DEBUG=False时必须正确设置ALLOWED_HOSTS?
# 4. 为什么manage.py和ASGI/WSGI选择不同的默认配置?
# 5. 为什么本课暂时不配置PostgreSQL和Agents SDK?
#
# 验收命令:
# python manage.py check --settings=JobRadar.settings.development
# python manage.py check --settings=JobRadar.settings.production
# python manage.py check --deploy --settings=JobRadar.settings.production
# python manage.py makemigrations --check --dry-run --settings=JobRadar.settings.development
# git diff --check
#
# 验收标准:
# 1. 能解释三个配置文件各自负责什么。
# 2. 能解释BASE_DIR层级变化。
# 3. 能复现生产密钥缺失时的失败,并确认这是预期安全行为。
# 4. 能区分普通系统检查错误与部署安全告警。
# 5. practice.py保持纯注释,不在此文件填写答案或运行记录。
@@ -0,0 +1,14 @@
"""JobRadar生产ASGI入口的参考实现。"""
import os
from django.core.asgi import get_asgi_application
# 部署入口默认选择生产配置,避免遗漏环境选择时启用DEBUG。
os.environ.setdefault(
"DJANGO_SETTINGS_MODULE",
"JobRadar.settings.production",
)
application = get_asgi_application()
@@ -0,0 +1,24 @@
#!/usr/bin/env python
"""JobRadar本地管理命令入口的参考实现。"""
import os
import sys
def main() -> None:
"""默认使用开发配置执行Django管理命令。"""
os.environ.setdefault(
"DJANGO_SETTINGS_MODULE",
"JobRadar.settings.development",
)
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"无法导入Django,请确认已经激活JobRadar Conda环境。"
) from exc
execute_from_command_line(sys.argv)
if __name__ == "__main__":
main()
@@ -0,0 +1 @@
"""JobRadar分环境配置包的参考入口。"""
@@ -0,0 +1,77 @@
"""JobRadar所有运行环境共享的Django配置参考。"""
from pathlib import Path
# base.py比原settings.py多位于一层settings目录中,因此需要向上三级到达项目根目录。
BASE_DIR = Path(__file__).resolve().parent.parent.parent
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
ROOT_URLCONF = "JobRadar.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR / "templates"],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
WSGI_APPLICATION = "JobRadar.wsgi.application"
ASGI_APPLICATION = "JobRadar.asgi.application"
# 第一课保留SQLite,下一阶段学习PostgreSQL时再调整数据库配置。
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
LANGUAGE_CODE = "zh-hans"
TIME_ZONE = "Asia/Shanghai"
USE_I18N = True
USE_TZ = True
STATIC_URL = "static/"
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
@@ -0,0 +1,16 @@
"""JobRadar本地开发环境的Django配置参考。"""
import os
from .base import * # noqa: F403
# 默认值只能用于本机开发,生产环境不会导入本模块。
SECRET_KEY = os.getenv(
"DJANGO_SECRET_KEY",
"django-insecure-jobradar-local-development-only",
)
DEBUG = True
ALLOWED_HOSTS = ["127.0.0.1", "localhost"]
@@ -0,0 +1,40 @@
"""JobRadar公网生产环境的Django配置参考。"""
import os
from django.core.exceptions import ImproperlyConfigured
from .base import * # noqa: F403
def get_required_environment_value(name: str) -> str:
"""读取必需的生产变量,缺失或空白时立即阻止应用启动。"""
value = os.getenv(name, "").strip()
if not value:
raise ImproperlyConfigured(f"缺少必需的环境变量:{name}")
return value
def get_required_environment_list(name: str) -> list[str]:
"""读取逗号分隔的必需列表,并去除每一项两侧的空白。"""
raw_value = get_required_environment_value(name)
values = [item.strip() for item in raw_value.split(",") if item.strip()]
if not values:
raise ImproperlyConfigured(f"环境变量没有有效配置项:{name}")
return values
SECRET_KEY = get_required_environment_value("DJANGO_SECRET_KEY")
DEBUG = False
ALLOWED_HOSTS = get_required_environment_list("DJANGO_ALLOWED_HOSTS")
CSRF_TRUSTED_ORIGINS = get_required_environment_list(
"DJANGO_CSRF_TRUSTED_ORIGINS"
)
# HTTPS在反向代理终止时,Django通过该请求头识别原始请求协议。
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = "DENY"
@@ -0,0 +1,14 @@
"""JobRadar生产WSGI入口的参考实现。"""
import os
from django.core.wsgi import get_wsgi_application
# 部署入口默认选择生产配置,避免遗漏环境选择时启用DEBUG。
os.environ.setdefault(
"DJANGO_SETTINGS_MODULE",
"JobRadar.settings.production",
)
application = get_wsgi_application()
+108 -60
View File
@@ -1,95 +1,143 @@
# JobRadar 实施路线图
## 阶段一:基础工程
## 推进规则
目标是建立可持续开发的 Django 项目基线。
本路线图用于确定学习顺序,不代表一次性生成所有代码。每个阶段继续拆成若干课,每次只开始当前一课:
- [x] 初始化 Django 项目。
- [x] 创建 Conda 环境。
- [x] 创建本地和远程 Git 仓库。
- [x] 补充项目、架构和路线图文档。
- [ ] 拆分开发、测试和生产配置。
- [ ] 将密钥及本地配置移出版本库。
- [ ] 接入 Django 用户认证并建立用户资料模型。
- [ ] 为搜索任务、筛选配置和岗位操作增加用户归属。
- [ ] 默认关闭公开注册,只保留管理员创建账号入口。
- 开课时先讲解原理、Agent职责和本课代码边界。
- 提供可运行的参考代码,但由学习者手动写入项目。
- 学习者提交实现后进行代码检查和运行验证。
- 当前课验收通过后,才准备下一课。
- 不提前创建后续课程的业务代码或空目录。
- 阶段结束时安排回顾、评测和一个可展示的阶段成果。
## 阶段一:Agent 工程基线
建议课程:
1. Django项目配置与开发/生产环境拆分。
2. Django用户系统与用户数据归属。
3. PostgreSQL迁移与Agent运行数据模型。
4. OpenAI Agents SDK最小Agent与Runner。
5. Agent Run、运行事件与工具调用持久化。
- [x] 初始化 Django 项目、Conda 环境和 Git 仓库。
- [x] 补充项目、架构、Agent 契约和路线图文档。
- [ ] 拆分开发、测试和生产配置,将密钥移出版本库。
- [ ] 接入 Django 用户认证和用户资料模型。
- [ ] 接入 PostgreSQL 并建立迁移基线。
- [ ] 接入 OpenAI Agents SDK。
- [ ] 建立 Agent Run、运行事件、工具调用和人工确认模型。
- [ ] 增加统一日志、测试和代码质量工具。
## 阶段二:单站采集闭环
## 阶段二:最小 Agent 闭环
目标是稳定采集一个明确授权范围内的招聘网站。
建议课程:
- [ ] 确认首个目标网站及其服务条款和访问边界。
- [ ] 定义站点适配器协议和原始岗位数据结构。
- [ ] 实现搜索任务、采集批次和原始岗位模型。
- [ ] 实现首个站点适配器。
1. Function Tool原理及第一个只读工具。
2. Pydantic结构化输入与输出。
3. 多工具选择和Agent运行循环。
4. 运行预算、超时、异常与人工确认。
5. Agent轨迹页面和最小评测集。
- [ ] 定义岗位研究 Agent 的 instructions、输入、输出、工具、状态和人工确认点。
- [ ] 使用 Agents SDK 实现一个 Agent 和 Runner 入口。
- [ ] 实现候选岗位查询、规则筛选和结果保存三个最小 Function Tool。
- [ ] 设置最大轮次、超时和工具调用预算。
- [ ] 保存工具调用、结果摘要、最终输出和 trace 标识。
- [ ] 在 WebUI 展示一次完整运行,而不只展示最终回答。
- [ ] 建立最小评测集,覆盖正常结果、证据不足、工具失败和禁止操作。
验收条件:Agent 能自主选择并调用真实工具,输出结构化结果;页面可以查看工具轨迹,评测能识别没有调用必要工具的伪结果。
## 阶段三:单站采集与筛选
建议课程:
1. 招聘网站适配器与工具边界。
2. HTTPX与Playwright采集。
3. 原始数据、标准化与幂等入库。
4. 硬性筛选工具和Agent调用。
5. 单站采集阶段项目。
- [ ] 确认首个招聘网站的服务条款和访问边界。
- [ ] 定义站点适配器协议和原始岗位结构。
- [ ] 实现首个站点适配器及采集工具。
- [ ] 增加限流、超时、重试和失败日志。
- [ ] 完成来源岗位编号去重和内容变更识别。
- [ ] 在 Django Admin 中提供任务和原始数据管理能力。
验收条件:可以手动运行一次采集,查看新增、更新、重复和失败数量,并从标准化岗位追溯到原始来源。
## 阶段三:岗位标准化与筛选
目标是用确定性规则减少无效岗位。
- [ ] 定义岗位、企业和岗位来源模型。
- [ ] 标准化岗位名、地点、薪资、经验和学历。
- [ ] 建立排除词、黑名单和硬性筛选规则。
- [ ] 输出通过、淘汰和待确认结果及具体原因。
- [ ] 实现岗位列表、详情、收藏、忽略和投递状态。
- [ ] 保存规则版本,支持重新筛选历史岗位。
- [ ] 完成来源岗位编号去重和内容变化识别。
- [ ] 建立排除词、黑名单和硬性筛选工具。
- [ ] 实现岗位列表、详情和个人处理状态。
验收条件:不调用大模型也能稳定过滤明确不符合要求的岗位,并解释每条淘汰原因。
验收条件:Agent 通过真实工具采集一个网站,所有岗位均可追溯到原始来源,确定性规则能解释淘汰原因。
## 阶段四:企业联网补全
## 阶段四:企业联网研究
目标是可靠判断企业主体和企业性质。
建议课程:
1. 企业研究任务拆解和证据模型。
2. 联网搜索工具与来源可信度。
3. 企业主体归一化和证据冲突。
4. 人工复核与可恢复运行。
- [ ] 确定合法、稳定的企业信息来源。
- [ ] 建立企业别名、标准主体和证据模型。
- [ ] 保存企业性质、行业、集团、来源和置信度。
- [ ] 实现多来源冲突检测和待复核队列。
- [ ] 支持人工确认,并保留自动判断历史。
- [ ] 实现企业研究工具,保存来源、时间和置信度。
- [ ] 实现多来源冲突检测和人工复核队列。
- [ ] 保留自动判断和人工确认历史。
验收条件:企业性质结论包含可访问的来源和查询时间,无法确认的企业不会被强行分类。
验收条件:Agent 不会在证据不足时强行分类,企业结论包含可访问来源和查询时间。
## 阶段五:AI 分析与权重评分
## 阶段五:评分、护栏与评测
目标是形成可解释的岗位推荐结果。
建议课程:
1. 个人画像和匹配分析契约。
2. 确定性评分工具。
3. Guardrail与越权防护。
4. Agent行为评测和回归用例。
5. Trace分析和提示词迭代。
- [ ] 定义个人岗位画像和技能偏好。
- [ ] 定义结构化 AI 输出模型。
- [ ] 提取技能、职责、隐含条件和风险点。
- [ ] 实现可配置评分维度及权重校验。
- [ ] 保存维度分数、风险扣分、理由和分析版本。
- [ ] 对相同内容建立缓存,控制调用成本。
- [ ] 完善 Agent 结构化输出模型和运行状态。
- [ ] 实现匹配分析工具与确定性评分工具。
- [ ] 保存维度分数、风险扣分、理由和版本。
- [ ] 增加输入/输出 guardrail 和人工确认节点。
- [ ] 建立工具选择、证据引用、拒绝越权和故障恢复评测。
- [ ] 使用 trace 分析失败运行并形成回归用例。
验收条件:每个总分都可以分解到评分维度、权重和证据,大模型不能越过硬性规则直接改变结果。
验收条件:每个总分都能分解到权重和证据;Agent 必须调用必要工具,不得编造外部事实或越过硬规则。
## 阶段六:调度、通知与扩站
## 阶段六:调度、公网部署与展示
目标是让系统长期稳定运行。
建议课程:
1. Celery异步Agent Run。
2. 定时任务、幂等和故障恢复。
3. Agent工作台与执行过程展示。
4. Docker Compose和生产配置。
5. HTTPS公网部署与最终演示。
- [ ] 接入 Celery、Redis 和 Celery Beat。
- [ ] 实现任务幂等、指数退避和失败恢复。
- [ ] 增加任务中心与运行状态展示。
- [ ] 实现运行幂等、指数退避、暂停和恢复。
- [ ] 完成 Agent 任务中心与实时运行状态展示。
- [ ] 增加高分新岗位通知。
- [ ] 增加第二个站点适配器,验证扩展边界。
- [ ] 增加备份、清理和恢复机制。
- [ ] 使用 Nginx 或 Caddy、生产级应用服务器和 HTTPS 完成公网部署。
- [ ] 增加安全 Cookie、CSRF、登录限流和安全响应头检查。
- [ ] 增加第二个站点工具,验证扩展边界。
- [ ] 使用 Nginx/Caddy、生产级应用服务器和 HTTPS 部署。
- [ ] 增加安全响应头、登录限流和数据库备份。
- [ ] 整理演示用 Agent 轨迹、评测结果和架构说明。
验收条件:定时任务连续运行时不会重复创建岗位,单个站点失败不会影响其他任务,异常可以从日志和任务记录中定位。
验收条件:系统可以在公网安全运行,用户能同时查看岗位结果与 Agent 执行过程,站点故障不会破坏其他运行。
## 第一轮开发前待确认
1. 首个目标招聘网站及允许使用的访问方式。
2. 搜索岗位、城市、薪资、经验和学历范围。
3. 必须包含与必须排除的关键词。
1. 首个招聘网站及允许使用的访问方式。
2. 岗位、城市、薪资、经验和学历范围。
3. 必须包含和必须排除的关键词。
4. 企业性质、行业和公司规模偏好。
5. 评分维度及初始权重。
6. 企业信息数据源和大模型服务方式。
7. 运行频率、单次采集页数和通知渠道。
8. 公网域名、部署服务器和 HTTPS 证书管理方式。
6. 企业信息数据源。
7. OpenAI 模型、调用预算及单次运行上限。
8. 运行频率、单次岗位数量和通知渠道。
9. 公网域名、服务器和 HTTPS 证书管理方式。