Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CHECKLIST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Demo4 CHECKLIST

## 过关前自检

- [ ] `GET /health` 返回 200
- [ ] `/health` 响应里有 `status: ok`
- [ ] `POST /chat` 返回 `reply`
- [ ] `POST /chat` 返回 `tools_used`
- [ ] `GET /tasks` 返回列表结构
- [ ] `POST /tasks` 能创建任务并返回 `id`
- [ ] `DELETE /tasks/{id}` 能删除已创建任务
- [ ] 删除不存在任务时返回 404
- [ ] 缺少 `message` 字段时 `/chat` 返回 422
- [ ] `pytest "demo4_综合项目/tests/test_app.py" -q` 通过
- [ ] push 到 `demo4-starter` 后,Actions 通过
30 changes: 30 additions & 0 deletions FAQ.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Demo4 FAQ

## 1. 为什么这一关只有一个 `main.py`?

因为这里先强调接口整合。
把最小 Web 服务跑起来,比先做复杂目录拆分更重要。

## 2. `/chat` 的核心是什么?

不是“回答得多智能”,而是能稳定返回约定结构:

- `reply`
- `tools_used`

## 3. 404 和 422 分别怎么理解?

- 404:资源不存在,比如删除一个不存在的任务
- 422:请求体不符合定义,比如缺少必填字段

## 4. 为什么 FastAPI 很适合这一关?

因为它天然适合做:

- 请求校验
- JSON 接口
- Swagger 文档展示

## 5. 怎么排查接口测试失败?

先对照测试文件看响应结构,再检查状态码,最后看字段名是不是完全一致。
22 changes: 22 additions & 0 deletions HINTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Demo4 HINTS

只给思路,不给答案。

## 你可以先想清楚的点

- 这一关重点是 API 结构,不是页面美观
- 测试已经把接口契约写出来了,按测试返回结构实现最稳
- 先做“最小服务”,再考虑集成更多前面关卡能力

## 容易卡住的地方

- `/chat` 需要返回 `reply` 和 `tools_used`
- 删除不存在任务时要返回 404
- 请求体验证失败时,FastAPI + Pydantic 会自动给 422,不需要你手写太多

## 实现顺序建议

1. 建 `app = FastAPI()`
2. 完成 `/health`
3. 完成 `/chat`
4. 完成任务接口
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,31 @@
<strong>Agent</strong> · <strong>ML</strong> · <strong>FastAPI</strong> · <strong>RAG</strong> · <strong>SSE</strong> · <strong>CI Unlock Flow</strong>
</p>

## 当前分支

你现在位于 `demo4-starter`。

- Demo1 到 Demo3 的实现已保留,可直接复用
- 当前需要自己完成 `demo4_综合项目/` 的 FastAPI 应用集成
- 通过本关后,push 到 `demo4-starter` 会自动解锁 Demo5
- 完整答案仍然保留在 `main` 分支

## 学习入口

- 先读 [TODO.md](TODO.md)
- 卡住时看 [HINTS.md](HINTS.md)
- 易错点和排查看 [FAQ.md](FAQ.md)
- 提交前对照 [CHECKLIST.md](CHECKLIST.md)
- 完成后回看 [REFLECTION.md](REFLECTION.md)
- 再看 `demo4_综合项目/tests/test_app.py`
- 当前只需要关注 `demo4_综合项目/main.py`

## 建议实现步骤

1. 先补 `/health`。
2. 再补 `/chat`。
3. 最后补任务相关接口和 404 处理。

## 快速导航

- [在线演示](#在线演示)
Expand Down
13 changes: 13 additions & 0 deletions REFLECTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Demo4 REFLECTION

学完这一关,你应该能说清楚这些事:

- 如何把前面的 Agent/任务/ML 能力服务化
- 为什么接口契约和返回结构在服务层很重要
- FastAPI、Pydantic、测试三者如何配合
- 为什么先做最小 API,再扩展前端和日志,是更稳的工程路径

如果你已经完成本关,说明你已经具备:

- 搭建一个最小 AI 服务接口层的能力
- 为完整项目版打基础的能力
29 changes: 29 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Demo4 TODO

当前目标:把前面的能力整合成一个 FastAPI 服务。

## 你需要实现的文件

- `demo4_综合项目/main.py`

## 建议实现步骤

1. 先创建 `FastAPI()` 应用实例。
2. 完成 `/health`,返回一个最小健康检查结果。
3. 完成 `/chat`,至少返回 `reply` 和 `tools_used`。
4. 完成 `/tasks` 的 GET 和 POST。
5. 完成 `/tasks/{task_id}` 的 DELETE,并处理不存在任务时的 404。
6. 跑测试确认响应结构符合预期。

## 完成标准

- `pytest "demo4_综合项目/tests/test_app.py" -q` 通过
- 推送到 `demo4-starter` 后,GitHub Actions 成功运行
- Issues 页面出现 “Demo5 已解锁”

## 卡住时看哪里

- 已完成的 Demo1 到 Demo3
- 当前分支的 `README.md`
- `docs/demo_specs.md`
- 完整答案在 `main` 分支
71 changes: 15 additions & 56 deletions demo4_综合项目/main.py
Original file line number Diff line number Diff line change
@@ -1,75 +1,34 @@
from __future__ import annotations
from fastapi import FastAPI

import json
from typing import Iterator, List
from uuid import uuid4

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from fastapi.responses import StreamingResponse


app = FastAPI(title="Demo4 Integrated App")
TASKS = {}


class ChatRequest(BaseModel):
message: str


class TaskCreateRequest(BaseModel):
title: str
priority: str
due_date: str
app = FastAPI(title="Demo4 Starter")


@app.get("/health")
def health():
return {"status": "ok", "version": "0.4.0"}
"""TODO: return a basic health response."""
raise NotImplementedError("Implement /health for Demo4")


@app.post("/chat")
def chat(payload: ChatRequest):
message = payload.message.strip()
tools_used: List[str] = []
if "任务" in message and any(token in message for token in ["列", "list", "所有"]):
tools_used.append("list_tasks")
reply = f"当前共有 {len(TASKS)} 个任务。"
elif any(token in message for token in ["你好", "hi", "hello"]):
reply = "你好,这里是 Demo4 综合项目接口。"
else:
reply = "消息已收到,我可以处理聊天和任务相关请求。"
return {"reply": reply, "tools_used": tools_used}
def chat():
"""TODO: accept a message payload and return reply + tools_used."""
raise NotImplementedError("Implement /chat for Demo4")


@app.get("/tasks")
def get_tasks():
return {"tasks": list(TASKS.values())}
"""TODO: return a task list."""
raise NotImplementedError("Implement GET /tasks for Demo4")


@app.post("/tasks", status_code=201)
def create_task(payload: TaskCreateRequest):
task = payload.model_dump()
task["id"] = str(uuid4())
TASKS[task["id"]] = task
return task
@app.post("/tasks")
def create_task():
"""TODO: create a task object and return it."""
raise NotImplementedError("Implement POST /tasks for Demo4")


@app.delete("/tasks/{task_id}")
def delete_task(task_id: str):
if task_id not in TASKS:
raise HTTPException(status_code=404, detail="Task not found")
del TASKS[task_id]
return {"deleted": True}


def _sse_event_stream(message: str) -> Iterator[str]:
for chunk in ["demo4", "stream", message]:
payload = json.dumps({"type": "token", "content": chunk}, ensure_ascii=False)
yield f"data: {payload}\n\n"
yield "data: {\"type\": \"done\"}\n\n"


@app.get("/stream")
def stream(message: str):
return StreamingResponse(_sse_event_stream(message), media_type="text/event-stream")
"""TODO: delete an existing task or return 404."""
raise NotImplementedError("Implement DELETE /tasks/{task_id} for Demo4")
1 change: 0 additions & 1 deletion demo5_full_project/app/__init__.py

This file was deleted.

1 change: 0 additions & 1 deletion demo5_full_project/app/core/__init__.py

This file was deleted.

1 change: 0 additions & 1 deletion demo5_full_project/app/core/rag/__init__.py

This file was deleted.

21 changes: 0 additions & 21 deletions demo5_full_project/app/core/rag/embedder.py

This file was deleted.

35 changes: 0 additions & 35 deletions demo5_full_project/app/core/rag/retriever.py

This file was deleted.

1 change: 0 additions & 1 deletion demo5_full_project/app/ml/__init__.py

This file was deleted.

24 changes: 0 additions & 24 deletions demo5_full_project/app/ml/model_io.py

This file was deleted.

26 changes: 0 additions & 26 deletions demo5_full_project/app/ml/tuner.py

This file was deleted.

Loading