510 lines
20 KiB
Markdown
510 lines
20 KiB
Markdown
---
|
||
id: plan.week08
|
||
title: "第 8 周:阶段 0 盘点 + 阶段 1 读通与画图"
|
||
type: week
|
||
domain: plan
|
||
stack: [git]
|
||
week: [8]
|
||
est_hours: 12
|
||
order: 8
|
||
---
|
||
|
||
# 第 8 周:阶段 0 盘点 + 阶段 1 读通与画图
|
||
|
||
> 第三段“Agent 主线”的第 1 周。上周 `week07-frontend.md`,下周 `week09-case-cards.md`。
|
||
|
||
**为什么现在做**:你在公司做的多 Agent 产品是**最有说服力的经历**,但面试官要听的是“机制和取舍”,不是“我改过某个页面”。这周把公司里的真实链路读通、画清楚,下周提炼成案例卡,第 10~13 周的项目三就有了明确的设计输入。
|
||
|
||
**本周规则**
|
||
|
||
- 读公司代码在**公司电脑**上做;仓库里的笔记**只写机制,不写**真实路径、接口地址、表名、字段名、内网地址、密钥、客户数据。私有细节写在 `notes/private/`(已在 `.gitignore`)。
|
||
- 每天产出至少一个文件。
|
||
- 模块一律用**通用叫法**:比如“前端多 Agent 研讨模块”“Agent 网关的作业接口”“草稿存储”。
|
||
|
||
**本周产出**
|
||
|
||
```text
|
||
notes/
|
||
├── 00-inventory.md # 阶段 0:目标、模块清单、A~E 自评
|
||
├── 01-architecture.md # 阶段 1:架构图(mermaid)+ 时序图 + 断点标注
|
||
├── 01-api-table.md # 阶段 1:关键接口职责表(通用叫法)
|
||
├── 01-state-machine.md # 阶段 1:流程的状态机草图
|
||
├── 01-pitch-draft.md # 3 分钟口述初稿
|
||
└── private/ # 不进仓库
|
||
tools/
|
||
└── check_sensitive.py # 提交前的脱敏检查脚本
|
||
```
|
||
|
||
---
|
||
|
||
## 第 1 天:阶段 0——盘点
|
||
|
||
### 学习内容
|
||
|
||
**1. 一句话目标**
|
||
|
||
- 说明:写下来,后面每个取舍都拿它来对照。
|
||
- 案例:
|
||
|
||
```markdown
|
||
## 一句话目标
|
||
16 周后,以“有多 Agent 产品落地经验的 AI 应用工程师”身份,投递 AI Agent 开发 / 大模型应用开发 / AI 全栈岗位;
|
||
主语言 Python,加分项 Go 全栈 + 7 年前端。
|
||
```
|
||
|
||
**2. 模块清单**
|
||
|
||
- 说明:列出你在公司**亲自改过、排查过**的模块,只写通用名字和你做了什么。这份清单决定下周写哪些案例卡。
|
||
- 案例(示例格式,按你的实际情况填):
|
||
|
||
```markdown
|
||
| 模块(通用叫法) | 我做过什么 | 遇到过的问题 | 对应重点 |
|
||
| --- | --- | --- | --- |
|
||
| 前端多 Agent 研讨模块 | 实现多角色轮流发言的界面和流式渲染 | 关闭页面后研讨中断 | A、C |
|
||
| 前端会话与交付模块 | 交付结果展示、刷新后恢复 | 刷新后进度丢失 | B |
|
||
| Agent 网关的作业接口(联调) | 对接创建作业、查询进度 | 状态没有回写到任务 | B、E |
|
||
| 模型配置 / 切换 | 额度不足时的报错与切换 | 换模型后流程无法继续 | D |
|
||
```
|
||
|
||
**3. A~E 自评表**
|
||
|
||
- 说明:五个学习重点贯穿第 8~13 周。先诚实地评估自己现在的水平。
|
||
|
||
| # | 重点 | 一句话解释 | 学完应能回答 |
|
||
| --- | --- | --- | --- |
|
||
| A | 多 Agent 编排 | 多个角色(提议者、评审者、总结者……)由谁派活、按什么顺序、什么时候结束 | “角色怎么分工、什么时候结束、怎么避免同一个任务被跑两次” |
|
||
| B | 状态与权威源 | 同一份数据(进度、结果)如果存了多处,以哪一处为准 | “刷新或关页后为什么丢进度,权威数据写在哪” |
|
||
| C | 流式与长任务 | 结果边生成边推送;任务很长时,不能依赖一条连接一直不断 | “为什么编排不该只活在浏览器里” |
|
||
| D | 人机协作 | 流程中途需要用户确认、补充信息、换模型后继续 | “Agent 什么时候必须停下来问人” |
|
||
| E | 与业务系统集成 | 模型的输出最终要变成可审计的业务记录(任务、报告) | “LLM 的结果怎样落到可审计的业务对象上” |
|
||
|
||
```markdown
|
||
| 重点 | 已会 | 模糊 | 不会 |
|
||
| --- | --- | --- | --- |
|
||
| A 编排 | 前端按顺序调用各角色 | 结束条件由谁判断 | 服务端编排框架 |
|
||
| B 权威源 | | 草稿和任务表的关系 | |
|
||
| C 流式 | SSE 渲染 | 断线后服务端在做什么 | 后台作业、续跑 |
|
||
| D 人机协作 | | 澄清问题的触发条件 | interrupt / 恢复 |
|
||
| E 集成 | | 过程报告怎么生成 | 幂等写入 |
|
||
```
|
||
|
||
### 要建的文件
|
||
|
||
- `notes/00-inventory.md`:一句话目标、模块清单、A~E 自评、项目三技术栈确认(Python + LangGraph,先手写循环)
|
||
|
||
### 运行方式
|
||
|
||
- 无代码。写完后自己读一遍:每一行是否都**不含**公司的真实名称和路径。
|
||
|
||
### 完成标准
|
||
|
||
- [ ] 一句话目标已写下
|
||
- [ ] 模块清单至少 3 行
|
||
- [ ] A~E 三列填完;项目三技术栈已确认
|
||
|
||
---
|
||
|
||
## 第 2 天:脱敏机制——提交前自动检查
|
||
|
||
### 学习内容
|
||
|
||
- 说明:靠“记得小心”是不够的,要有机制。写一个脚本,在每次提交前扫描**即将提交的内容**,发现内网地址、密钥格式、你自定义的敏感词就拒绝提交。敏感词清单本身是敏感的,放在 `notes/private/sensitive-words.txt`(不进仓库)。再用 Git 的 `pre-commit` 钩子自动运行。
|
||
- 案例:
|
||
|
||
```python
|
||
# tools/check_sensitive.py —— 提交前扫描暂存区内容
|
||
# 不自动运行:作为 pre-commit 钩子在仓库根目录执行;加 --self-test 可以单独自检
|
||
import re
|
||
import subprocess
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
PATTERNS = {
|
||
"内网 IP": re.compile(r"\b10\.\d{1,3}\.\d{1,3}\.\d{1,3}\b|\b192\.168\.\d{1,3}\.\d{1,3}\b|\b172\.(?:1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}\b"),
|
||
"疑似密钥": re.compile(r"(?i)\b(?:sk|ak|api[_-]?key|secret|token)[\w-]*\s*[:=]\s*['\"]?[A-Za-z0-9_\-]{16,}"),
|
||
"私钥": re.compile(r"-----BEGIN [A-Z ]*PRIVATE KEY-----"),
|
||
"root 绝对路径": re.compile(r"(?<![\w/])/root/\S+"), # sensitive-ok
|
||
}
|
||
WORDS_FILE = Path("notes/private/sensitive-words.txt")
|
||
ALLOW_MARK = "sensitive-ok" # 行内写这个标记表示人工确认过,可以放行(比如文档里讲解正则的例子)
|
||
|
||
|
||
def load_words() -> list[str]:
|
||
if not WORDS_FILE.exists():
|
||
return []
|
||
return [w.strip() for w in WORDS_FILE.read_text(encoding="utf-8").splitlines() if w.strip() and not w.startswith("#")]
|
||
|
||
|
||
def scan_text(name: str, text: str, words: list[str]) -> list[str]:
|
||
problems = []
|
||
for no, line in enumerate(text.splitlines(), start=1):
|
||
if ALLOW_MARK in line:
|
||
continue
|
||
for label, pat in PATTERNS.items():
|
||
if pat.search(line):
|
||
problems.append(f"{name}:{no} {label}")
|
||
for w in words:
|
||
if w.lower() in line.lower():
|
||
problems.append(f"{name}:{no} 敏感词(见私有清单)") # 不打印敏感词本身
|
||
return problems
|
||
|
||
|
||
def staged_files() -> list[str]:
|
||
out = subprocess.run(["git", "diff", "--cached", "--name-only", "--diff-filter=ACM"],
|
||
capture_output=True, text=True, check=True).stdout
|
||
return [f for f in out.splitlines() if f]
|
||
|
||
|
||
def main() -> int:
|
||
words = load_words()
|
||
problems = []
|
||
for f in staged_files():
|
||
content = subprocess.run(["git", "show", f":{f}"], capture_output=True).stdout # 读暂存区版本
|
||
try:
|
||
problems += scan_text(f, content.decode("utf-8"), words)
|
||
except UnicodeDecodeError:
|
||
continue # 跳过二进制文件
|
||
for p in problems:
|
||
print("✗", p)
|
||
if problems:
|
||
print("发现疑似敏感内容,已阻止提交。确认无误的行可加注释 sensitive-ok。")
|
||
return 1 if problems else 0
|
||
|
||
|
||
if __name__ == "__main__" and "--self-test" in sys.argv:
|
||
sample = "url: http://192.168.1.20:8080\napi_key = 'abcdefghijklmnopqrstuvwxyz'\n正常的一行\n" # sensitive-ok
|
||
found = scan_text("demo.md", sample, ["某客户"])
|
||
assert len(found) == 2, found
|
||
assert scan_text("demo.md", "某客户的需求", ["某客户"]) == ["demo.md:1 敏感词(见私有清单)"]
|
||
print("自检通过:", found)
|
||
elif __name__ == "__main__":
|
||
sys.exit(main())
|
||
```
|
||
|
||
把它挂到 Git 钩子上(钩子文件不会被提交,换设备后要重新执行一次):
|
||
|
||
```bash
|
||
cat > .git/hooks/pre-commit <<'HOOK'
|
||
#!/bin/sh
|
||
exec python3 tools/check_sensitive.py
|
||
HOOK
|
||
chmod +x .git/hooks/pre-commit
|
||
python3 tools/check_sensitive.py --self-test
|
||
```
|
||
|
||
**易错点 / 面试常问(补充)**
|
||
|
||
- 易错点:钩子文件在 `.git/hooks/` 里,不会被提交,换电脑要重新安装(总纲第 12 节有步骤)。
|
||
- 易错点:敏感词清单本身也是敏感信息,只放 `notes/private/`;检查脚本误报时用允许标记,不要直接关掉钩子。
|
||
- 易错点:已经提交过的敏感内容,删掉文件后**仍然在 Git 历史里**;一旦推送过,要当作已经泄露处理(例如密钥立即作废),再考虑改写历史。
|
||
|
||
### 要建的文件
|
||
|
||
- `tools/check_sensitive.py`;`notes/private/sensitive-words.txt`(公司名、产品代号、内部项目名、同事名等,**不进仓库**);`.git/hooks/pre-commit`
|
||
|
||
### 运行方式
|
||
|
||
```bash
|
||
python3 tools/check_sensitive.py --self-test
|
||
echo "测试 192.168.0.1" > notes/x.md # sensitive-ok
|
||
git add notes/x.md && git commit -m test # 应被拒绝
|
||
git restore --staged notes/x.md && rm notes/x.md # 清理
|
||
```
|
||
|
||
### 完成标准
|
||
|
||
- 自检通过;故意提交含内网 IP 的文件会被拦下。
|
||
- 在 README 的“换设备”步骤里记一笔:克隆后要重新安装钩子。
|
||
|
||
---
|
||
|
||
## 第 3 天:阶段 1——怎么读一个大项目
|
||
|
||
### 学习内容
|
||
|
||
- 说明:不要从第一个文件读到最后一个。方法是**跟着一次请求走**:
|
||
1. 找入口:前端从“用户点击开始研讨”的按钮事件开始;后端从路由表开始。
|
||
2. 跟调用链:每一层只记“谁调谁、传了什么、状态存在哪”,不看细节实现。
|
||
3. 搜关键词:`grep -rn "stream\|sse\|EventSource"`、`"checkpoint\|job\|draft"`,快速定位流式和状态相关代码。
|
||
4. 打日志 / 打断点:在开发环境跑一遍,用浏览器 Network 面板看实际的请求顺序。
|
||
5. 边读边画时序图,读完一块画一块。
|
||
- 案例:用 mermaid 时序图记录调用链(下面是**通用示例**,按你读到的实际情况改,名字保持通用):
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant U as 用户
|
||
participant F as 前端
|
||
participant G as Agent 网关
|
||
participant L as 大模型
|
||
participant S as 草稿/作业存储
|
||
U->>F: 输入需求,点击开始
|
||
F->>G: 意图识别
|
||
G->>L: 调用模型
|
||
L-->>G: 意图结果
|
||
G-->>F: 返回意图
|
||
loop 每一轮研讨(由谁驱动?前端还是网关?)
|
||
F->>G: 让某个角色发言(SSE)
|
||
G->>L: 流式调用
|
||
L-->>G: token 流
|
||
G-->>F: SSE 推送
|
||
F->>S: 保存草稿(谁写?什么时候写?)
|
||
end
|
||
F->>G: 生成总结 / 交付
|
||
```
|
||
|
||
- 读的时候在图上标问号的地方,就是第 9 周案例卡的素材。
|
||
|
||
### 要建的文件
|
||
|
||
- `notes/01-architecture.md`:先放时序图;`notes/private/reading-log.md`:真实文件路径、函数名(不进仓库)
|
||
|
||
### 运行方式
|
||
|
||
- 在公司电脑上按上面的方法读 P0 模块:
|
||
|
||
| 优先级 | 要读的模块(通用叫法) | 要搞清的问题 |
|
||
| --- | --- | --- |
|
||
| P0 | 前端多 Agent 功能:意图、多席研讨、草稿、作业 | 几个步骤怎么串起来,谁发起 SSE,谁驱动下一轮 |
|
||
| P0 | 前端会话与交付模块 | 会话状态和交付状态分别存在哪 |
|
||
| P0 | Agent 网关里多 Agent、意图、作业、草稿相关的接口 | 服务端是“单轮调用”还是“后台作业”;用了什么框架、检查点存在哪 |
|
||
| P1 | 前端对 Agent 网关的接口契约 | 前后端约定了哪些接口 |
|
||
| P2 | 团队里关于“后台作业”的设计文档(如果有) | 理想架构和现状差在哪 |
|
||
|
||
### 完成标准
|
||
|
||
- 时序图覆盖一次完整流程;标出至少 3 个“问号”。
|
||
|
||
---
|
||
|
||
## 第 4 天:画架构图,标出断点
|
||
|
||
### 学习内容
|
||
|
||
- 说明:时序图讲“顺序”,架构图讲“组成和数据流向”。这张图要回答三个问题:
|
||
1. 编排(决定下一步做什么)跑在哪一层?
|
||
2. 每类状态(会话、草稿、作业进度、业务任务)的**权威数据**存在哪?
|
||
3. **关掉浏览器时,流程断在哪一层?**
|
||
- 案例(通用示例,用颜色标出风险点):
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
U[用户浏览器] -->|SSE| FE[前端<br/>编排循环在这里?]
|
||
FE -->|HTTP| GW[Agent 网关]
|
||
GW --> LLM[大模型服务]
|
||
GW --> D[(草稿存储)]
|
||
GW --> J[(作业表)]
|
||
GW --> B[(业务库:任务/报告)]
|
||
FE -.->|直接写草稿?| D
|
||
classDef risk fill:#ffe0e0,stroke:#d33
|
||
class FE risk
|
||
```
|
||
|
||
```markdown
|
||
### 断点分析
|
||
- 关闭浏览器 → 前端的编排循环停止 → 正在进行的这一轮在网关里可能继续跑完,但**不会有下一轮**。
|
||
- 刷新页面 → 前端内存里的进度丢失 → 只能从草稿恢复,而草稿可能不是最新的。
|
||
- 结论:编排如果只活在浏览器里,任务的生命周期就等于标签页的生命周期。
|
||
|
||
### 权威源
|
||
| 状态 | 存在哪 | 谁写 | 是否权威 | 问题 |
|
||
| --- | --- | --- | --- | --- |
|
||
| 研讨进度 | 前端内存 + 草稿 | 前端 | 否 | 刷新丢失 |
|
||
| 作业状态 | 作业表 | 网关 | 是 | 没回写业务任务 |
|
||
| 业务任务状态 | 业务库 | ? | 应该是 | 显示“未开始” |
|
||
```
|
||
|
||
### 要建的文件
|
||
|
||
- `notes/01-architecture.md`:补架构图 + 断点分析 + 权威源表
|
||
|
||
### 完成标准
|
||
|
||
- [ ] 图上标出“关掉浏览器时流程断在哪一层”
|
||
- [ ] 指出至少一处“状态双写或权威来源不清”的位置
|
||
|
||
---
|
||
|
||
## 第 5 天:接口职责表
|
||
|
||
### 学习内容
|
||
|
||
- 说明:把前后端约定的关键接口按**职责**整理(不写真实路径)。这张表是你第 10~12 周设计项目三接口的参照,面试时也能用来讲“我是怎么划分接口的”。
|
||
- 案例:
|
||
|
||
```markdown
|
||
| 职责(通用叫法) | 方法 | 输入 | 输出 | 同步/流式/异步 | 幂等吗 | 失败时前端怎么办 |
|
||
| --- | --- | --- | --- | --- | --- | --- |
|
||
| 初始化研讨 | POST | 需求、角色配置 | 会话 ID | 同步 | 否 | 提示重试 |
|
||
| 运行一轮并流式返回 | POST | 会话 ID、角色 | token 流 | 流式(SSE) | 否 | 断线后无法续上 |
|
||
| 保存 / 读取草稿 | PUT/GET | 会话 ID、内容 | 草稿 | 同步 | 是(覆盖写) | — |
|
||
| 创建后台作业 | POST | 会话 ID | 作业 ID | 异步 | ? | 轮询状态 |
|
||
| 查询作业进度 | GET | 作业 ID | 状态、进度 | 同步 | 是 | — |
|
||
```
|
||
|
||
- 面试常问:“这个接口为什么设计成流式 / 异步?”“重复提交会怎样?”——在表里先想清楚。
|
||
|
||
### 要建的文件
|
||
|
||
- `notes/01-api-table.md`
|
||
|
||
### 完成标准
|
||
|
||
- 至少 5 个接口;每个都填了“幂等吗”和“失败时怎么办”。
|
||
|
||
---
|
||
|
||
## 第 6 天:把流程画成状态机,并用代码验证思路
|
||
|
||
### 学习内容
|
||
|
||
- 说明:多 Agent 流程本质上是一个**状态机**:有限个状态(排队、研讨中、等待用户、总结中、完成、失败)+ 明确的转移规则。把它显式写出来有三个好处:
|
||
1. 任何时刻都知道“现在在哪一步”;
|
||
2. 状态可以存进数据库,进程重启后从同一步继续(第 12 周的检查点续跑);
|
||
3. 非法转移(比如“已完成”又回到“研讨中”)能被拦下。
|
||
- 案例:一个最小的状态机,每次转移都写进 SQLite,模拟“进程崩溃后从数据库恢复”。
|
||
|
||
```python
|
||
import sqlite3
|
||
from enum import Enum
|
||
|
||
|
||
class S(str, Enum):
|
||
QUEUED = "queued"
|
||
DISCUSSING = "discussing"
|
||
WAITING_HUMAN = "waiting_human"
|
||
SUMMARIZING = "summarizing"
|
||
DONE = "done"
|
||
FAILED = "failed"
|
||
|
||
|
||
ALLOWED = {
|
||
S.QUEUED: {S.DISCUSSING, S.FAILED},
|
||
S.DISCUSSING: {S.DISCUSSING, S.WAITING_HUMAN, S.SUMMARIZING, S.FAILED},
|
||
S.WAITING_HUMAN: {S.DISCUSSING, S.FAILED},
|
||
S.SUMMARIZING: {S.DONE, S.FAILED},
|
||
S.DONE: set(),
|
||
S.FAILED: set(),
|
||
}
|
||
|
||
|
||
class Run:
|
||
def __init__(self, db: sqlite3.Connection, run_id: str):
|
||
self.db, self.run_id = db, run_id
|
||
db.execute("CREATE TABLE IF NOT EXISTS runs (id TEXT PRIMARY KEY, state TEXT, round INT)")
|
||
db.execute("INSERT OR IGNORE INTO runs VALUES (?, ?, 0)", (run_id, S.QUEUED.value))
|
||
|
||
def load(self) -> tuple[S, int]:
|
||
state, rnd = self.db.execute("SELECT state, round FROM runs WHERE id = ?", (self.run_id,)).fetchone()
|
||
return S(state), rnd
|
||
|
||
def to(self, new: S, round_: int | None = None) -> None:
|
||
cur, rnd = self.load()
|
||
if new not in ALLOWED[cur]:
|
||
raise ValueError(f"非法转移:{cur.value} → {new.value}")
|
||
with self.db: # 事务:要么都写进去,要么都不写
|
||
self.db.execute("UPDATE runs SET state = ?, round = ? WHERE id = ?",
|
||
(new.value, rnd if round_ is None else round_, self.run_id))
|
||
|
||
|
||
db = sqlite3.connect(":memory:")
|
||
run = Run(db, "r1")
|
||
run.to(S.DISCUSSING, round_=1)
|
||
run.to(S.DISCUSSING, round_=2)
|
||
print("崩溃前:", run.load())
|
||
|
||
# 模拟进程崩溃:丢掉 Python 对象,只留下数据库
|
||
del run
|
||
recovered = Run(db, "r1")
|
||
state, rnd = recovered.load()
|
||
print(f"重启后:从 {state.value} 第 {rnd} 轮继续")
|
||
recovered.to(S.SUMMARIZING)
|
||
recovered.to(S.DONE)
|
||
try:
|
||
recovered.to(S.DISCUSSING)
|
||
except ValueError as e:
|
||
print("被拦下:", e)
|
||
```
|
||
|
||
- 对照第 4 天的断点分析:如果公司现在的进度只在前端内存里,就相当于没有这张 `runs` 表。
|
||
|
||
**易错点 / 面试常问(补充)**
|
||
|
||
- 易错点:状态机的每一步要先写“权威状态”(数据库),再做后续动作;顺序反了,崩溃时就会出现“动作做了但状态没记”的情况。
|
||
- 面试常问:为什么要把流程画成状态机(每个状态能做什么、出错后停在哪、怎么恢复,一目了然);“状态在内存”和“状态在数据库”对崩溃恢复意味着什么。
|
||
|
||
### 要建的文件
|
||
|
||
- `notes/01-state-machine.md`:mermaid 状态图 + 上面的代码 + “公司现状 vs 状态机化之后”的对比
|
||
|
||
```mermaid
|
||
stateDiagram-v2
|
||
[*] --> queued
|
||
queued --> discussing
|
||
discussing --> discussing: 下一轮
|
||
discussing --> waiting_human: 需要澄清
|
||
waiting_human --> discussing: 用户回复
|
||
discussing --> summarizing: 达成共识 / 到达轮数上限
|
||
summarizing --> done
|
||
queued --> failed
|
||
discussing --> failed
|
||
summarizing --> failed
|
||
```
|
||
|
||
### 运行方式
|
||
|
||
```bash
|
||
python3 notes/state_machine_demo.py # 把上面的代码存成这个文件运行
|
||
```
|
||
|
||
### 完成标准
|
||
|
||
- 能用状态图讲清“关页面、刷新、进程崩溃”三种情况下,状态机化前后分别会怎样。
|
||
|
||
---
|
||
|
||
## 第 7 天:阶段 1 验收 + 口述初稿
|
||
|
||
### 学习内容
|
||
|
||
- 说明:用“架构图 + 3 分钟”讲一遍,录音回听。结构建议:
|
||
1. 背景一句话(产品做什么,不说公司名);
|
||
2. 架构:几层、各自职责;
|
||
3. 关键问题:编排在哪、状态在哪、断了怎么办;
|
||
4. 如果重做:状态机化、后台作业、单一权威源。
|
||
- 案例:
|
||
|
||
```markdown
|
||
## 3 分钟口述初稿
|
||
我参与的是一个多 Agent 研讨产品:用户提出需求,系统先识别意图,再让提议、评审等多个角色轮流讨论,最后生成总结和交付物。
|
||
架构分三层:前端负责交互和流式展示,Agent 网关负责调用模型和保存草稿、作业,业务库保存任务和报告。
|
||
我遇到的核心问题是:研讨的“下一轮”由前端驱动,所以关掉页面流程就停了;进度一部分在前端内存、一部分在草稿里,刷新后会丢。
|
||
如果重做,我会把流程建成服务端状态机,每一步持久化,前端只订阅进度;业务任务的状态以作业表为唯一权威,由作业完成事件回写。
|
||
我在个人项目里用 LangGraph 的检查点实现了这套设计,可以演示中途杀掉进程后从断点继续。(第 12 周完成后补上)
|
||
```
|
||
|
||
### 要建的文件
|
||
|
||
- `notes/01-pitch-draft.md`;在 README 进度表勾掉第 8 周
|
||
|
||
### 运行方式
|
||
|
||
- 手机录音,计时;超过 3 分钟就删内容。
|
||
|
||
### 完成标准
|
||
|
||
- [ ] 不看代码能口述多 Agent 功能的主流程
|
||
- [ ] 架构图标出了断点和权威源问题
|
||
- [ ] 口述初稿 ≤ 3 分钟
|
||
- [ ] 提交前脱敏检查脚本通过
|
||
|
||
---
|
||
|
||
## 本周自查清单
|
||
|
||
- [ ] `notes/00-inventory.md`:目标、模块清单、A~E 自评
|
||
- [ ] `tools/check_sensitive.py` + pre-commit 钩子
|
||
- [ ] `notes/01-architecture.md`:时序图、架构图、断点、权威源
|
||
- [ ] `notes/01-api-table.md`:接口职责表
|
||
- [ ] `notes/01-state-machine.md`:状态图 + 可运行的状态机示例
|
||
- [ ] `notes/01-pitch-draft.md`:3 分钟口述初稿
|