docs(course): 建立Agent学习路线并新增第一课
This commit is contained in:
@@ -1,18 +1,20 @@
|
||||
# JobRadar · 职途雷达
|
||||
|
||||
JobRadar 是一套面向个人使用的智能岗位发现与决策系统。项目计划从指定招聘网站采集岗位信息,依据可配置规则完成筛选,并结合联网检索与大模型分析补充企业性质、岗位匹配度、判断依据和置信度,最终形成可追溯的个人岗位库。
|
||||
JobRadar 是一套面向个人使用、以 Agent 为核心的智能岗位发现与决策系统。Agent 根据用户目标制定执行步骤,调用岗位采集、企业调查、规则筛选、评分和数据持久化工具,完成多步骤任务,并将过程、证据和结果展示在 WebUI 中。
|
||||
|
||||
> 当前项目处于基础骨架阶段,已完成 Django 工程初始化;岗位采集、企业研判、评分、异步任务和业务页面均在后续迭代范围内。
|
||||
|
||||
## 项目目标
|
||||
|
||||
- 通过独立站点适配器采集授权范围内的岗位信息。
|
||||
- 使用正规的 Agent 运行循环完成任务规划、工具调用、状态延续和结果输出。
|
||||
- 通过独立站点工具采集授权范围内的岗位信息。
|
||||
- 统一不同来源的岗位字段,并识别重复岗位和内容变化。
|
||||
- 使用硬性规则优先排除明显不符合要求的岗位。
|
||||
- 联网补充企业主体、企业性质和行业等信息,保留来源与查询时间。
|
||||
- 按可配置权重计算岗位匹配分,输出推荐理由和风险提示。
|
||||
- 提供适合个人使用的岗位管理、收藏、忽略和投递跟踪页面。
|
||||
- 保留原始数据、规则版本和分析证据,使每项结论可以复核。
|
||||
- 展示 Agent 的执行轨迹、工具调用、耗时、失败原因和人工确认节点,用于学习与项目展示。
|
||||
|
||||
## 技术栈
|
||||
|
||||
@@ -26,7 +28,11 @@ JobRadar 是一套面向个人使用的智能岗位发现与决策系统。项
|
||||
| 普通请求 | HTTPX | 规划中 |
|
||||
| 异步任务 | Celery + Redis | 规划中 |
|
||||
| 定时调度 | Celery Beat | 规划中 |
|
||||
| AI 分析 | 兼容 OpenAI 接口的模型服务 | 规划中 |
|
||||
| Agent 运行时 | OpenAI Agents SDK(Python) | 规划中 |
|
||||
| 模型接口 | OpenAI Responses API | 规划中 |
|
||||
| Agent 输出 | Pydantic 结构化模型 | 规划中 |
|
||||
| 可观测性 | Agents SDK Tracing + 业务运行记录 | 规划中 |
|
||||
| Agent 评测 | 官方 Agent Evals 思路 + 本地评测集 | 规划中 |
|
||||
| 用户系统 | Django 内置认证 + 简化用户资料 | 规划中 |
|
||||
| WebUI | Django Admin + 自定义 Django 页面 | 规划中 |
|
||||
| 部署 | Docker Compose + Nginx/Caddy + HTTPS | 规划中 |
|
||||
@@ -58,12 +64,27 @@ companies/ 企业主体及企业证据
|
||||
crawlers/ 招聘网站采集适配器
|
||||
screening/ 硬性筛选规则
|
||||
ranking/ 权重评分
|
||||
ai_analysis/ 语义分析及结构化输出
|
||||
agent_runtime/ Agent 定义、运行上下文与编排入口
|
||||
agent_tools/ 采集、检索、筛选、评分和持久化工具
|
||||
evals/ Agent 行为评测用例与结果
|
||||
task_center/ 采集、分析与调度任务
|
||||
audit/ 日志、证据与版本追踪
|
||||
```
|
||||
|
||||
详细边界参见 [架构设计](docs/architecture.md),开发顺序参见 [实施路线图](docs/roadmap.md)。
|
||||
详细边界参见 [架构设计](docs/architecture.md),Agent 的目标、工具和状态约定参见 [Agent 契约](docs/agent-contract.md),开发顺序参见 [实施路线图](docs/roadmap.md)。
|
||||
|
||||
## 学习与开发方式
|
||||
|
||||
JobRadar 同时是实际项目和 Agent 学习项目,采用“逐阶段、逐课、手动实现”的协作方式:
|
||||
|
||||
1. 我先说明本课目标、核心原理以及它在完整系统中的位置。
|
||||
2. 对照 Java、Spring 和常见后端设计解释 Python、Django 与 Agents SDK 的差异。
|
||||
3. 我提供本课所需的完整参考代码、运行命令、预期结果和常见错误。
|
||||
4. 你根据讲解亲手把代码写入 JobRadar,不直接复制一个已完成项目。
|
||||
5. 你完成后,我检查正确性、可读性、Agent 设计和安全边界,并解释问题。
|
||||
6. 当前一课达到验收标准后,再开始下一课。
|
||||
|
||||
默认情况下,我不会直接替你写入业务代码,也不会一次性生成后续全部模块。只有你明确要求我落地某段代码时,我才修改项目文件。详细约定参见 [学习协作说明](docs/learning-guide.md)。
|
||||
|
||||
## 本地运行
|
||||
|
||||
@@ -153,12 +174,23 @@ python -m playwright install chromium
|
||||
## 开发约定
|
||||
|
||||
- 一个招聘网站对应一个采集适配器,禁止把站点特有解析逻辑写入公共业务模块。
|
||||
- Agent 通过类型明确的工具完成外部操作,不在提示词中伪造浏览、查询或入库结果。
|
||||
- 第一版只实现一个岗位研究 Agent;只有单 Agent 难以维持明确职责时才增加专家 Agent 和 handoff。
|
||||
- 涉及持久化或外部副作用的工具必须边界窄、可审计、可重试。
|
||||
- 高风险或不可逆操作必须设置人工确认;第一版不允许 Agent 自动投递简历或联系招聘人员。
|
||||
- 原始数据与标准化数据分开保存,避免解析规则变化后无法追溯。
|
||||
- 先执行确定性筛选,再调用联网服务和大模型,降低成本与误判范围。
|
||||
- 评分权重由配置决定,大模型只输出结构化维度分析,不直接修改最终分数。
|
||||
- 规则筛选和权重计算作为确定性工具提供给 Agent,最终分数不能由模型随意生成。
|
||||
- 采集、企业补全和 AI 分析任务必须可重复执行,并通过唯一约束或幂等键防止重复入库。
|
||||
- 重要判断应包含证据,不以模型生成的自然语言作为唯一依据。
|
||||
|
||||
## 明确不采用的捷径
|
||||
|
||||
- 不把一次普通的大模型请求包装成 Agent;必须存在真实的运行循环、工具调用、状态和轨迹。
|
||||
- 不用 LangChain 等兼容层统一不同模型或 Agent 框架,优先直接学习 OpenAI Agents SDK 的原生能力。
|
||||
- 当前业务没有知识库检索需求,不引入 pgvector、独立向量数据库或为了展示而搭建 RAG。
|
||||
- 不一开始拆成多 Agent;先用一个 Agent 验证完整闭环,再依据评测结果演进。
|
||||
|
||||
## 验证命令
|
||||
|
||||
```powershell
|
||||
@@ -180,4 +212,4 @@ JobRadar 仅用于个人岗位信息整理与求职辅助。使用前应确认
|
||||
|
||||
当前版本:`0.1.0-dev`
|
||||
|
||||
当前阶段:Django 基础工程和项目文档初始化。
|
||||
当前阶段:[阶段一第1课“Django项目配置与开发/生产环境拆分”](docs/lessons/01_Agent工程基线/1_1_Django项目配置与开发生产环境拆分/README.md)已经创建,等待学习者手动完成项目配置拆分。
|
||||
|
||||
@@ -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
@@ -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、可信来源、登录限流、密钥注入、日志审计和数据库备份。
|
||||
|
||||
@@ -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
@@ -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 证书管理方式。
|
||||
|
||||
Reference in New Issue
Block a user