{"id":51298102,"url":"https://github.com/mi4646/xiaochu","last_synced_at":"2026-06-30T16:32:18.489Z","repository":{"id":365444170,"uuid":"1272093057","full_name":"mi4646/xiaochu","owner":"mi4646","description":"做菜领域多意图 AI 助手 · FastAPI + OpenAI 协议兼容","archived":false,"fork":false,"pushed_at":"2026-06-17T10:02:05.000Z","size":36,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-17T12:04:22.878Z","etag":null,"topics":["ai-assistant","chatbot","cooking","fastapi","openai-api","openai-sdk","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mi4646.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-17T09:29:26.000Z","updated_at":"2026-06-17T10:02:20.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mi4646/xiaochu","commit_stats":null,"previous_names":["mi4646/xiaochu"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/mi4646/xiaochu","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mi4646%2Fxiaochu","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mi4646%2Fxiaochu/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mi4646%2Fxiaochu/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mi4646%2Fxiaochu/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mi4646","download_url":"https://codeload.github.com/mi4646/xiaochu/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mi4646%2Fxiaochu/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34975669,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-30T02:00:05.919Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-assistant","chatbot","cooking","fastapi","openai-api","openai-sdk","python"],"created_at":"2026-06-30T16:32:17.483Z","updated_at":"2026-06-30T16:32:18.465Z","avatar_url":"https://github.com/mi4646.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 小厨 Xiaochu 🍳\n\n做菜领域的 AI 助手 —— 用户自由输入，自动识别意图并给出对应回答。\n\n## 支持的意图\n\n| 意图 | 示例输入 | 输出 |\n|------|---------|------|\n| **recipe** | \"宫保鸡丁\"、\"宫保鸡丁+木须肉\" | 完整食谱（单/多菜） |\n| **recommend** | \"今晚两人吃什么\"、\"三菜一汤\"、\"推荐 5 道家常菜\" | 推荐菜品列表 + 并发生成前 N 道详细菜谱（默认 3 道，最多 7 道） |\n| **ingredient** | \"冰箱里有鸡蛋番茄米饭\" | 可做的菜列表 |\n| **cooking_qa** | \"怎么炒菜不糊锅\"、\"鸡肉怎么腌嫩\" | 技巧问答（支持多轮追问） |\n| **chitchat** | 其他闲聊 | 友好兜底 |\n\n## 项目结构\n\n```\nxiaochu/\n├── apps/\n│   ├── chat/                # 统一聊天入口\n│   │   ├── routes.py        # POST /chat\n│   │   ├── intents.py       # Intent 枚举\n│   │   ├── router.py        # 意图识别（一次 LLM 调用）\n│   │   ├── dispatcher.py    # 按 intent 分发 + 历史摘要 + 流式分支\n│   │   ├── session.py       # SessionStore（SQLite 持久化）\n│   │   └── schemas.py       # ChatRequest / ChatResponse\n│   ├── recipe/\n│   │   ├── handler.py       # 单/多菜食谱生成 + 多菜歧义检测\n│   │   └── schemas.py       # Pydantic 数据模型\n│   ├── recommend/handler.py # 推荐 + 并发生成菜谱\n│   ├── ingredient/handler.py# 食材反查\n│   └── qa/handler.py        # 烹饪问答（含流式版 handle_stream）\n├── core/\n│   ├── config.py            # .env 配置（pydantic-settings）\n│   ├── llm.py               # 统一 OpenAI 兼容客户端（同步 / 流式）\n│   ├── logging.py           # 文件日志 + 轮转\n│   └── storage.py           # SQLite 初始化与连接\n├── main.py                  # FastAPI 入口\n├── cli.py                   # 命令行交互入口（默认逐字流式 + 表格）\n├── tests/\n├── pytest.ini\n├── requirements.txt\n├── requirements-dev.txt\n├── .env.example\n└── .python-version          # pyenv 虚拟环境绑定\n```\n\n## 技术栈\n\n- **AI 接口**：OpenAI 兼容协议（OpenAI / DeepSeek / 通义千问 / Moonshot 等任意服务）\n- **Web**：FastAPI + Uvicorn\n- **数据**：Pydantic v2\n- **CLI**：Rich\n- **配置**：pydantic-settings + python-dotenv\n\n## 依赖\n\n\u003c!-- AUTO-GENERATED: derived from requirements.txt --\u003e\n\n| 包 | 版本约束 | 用途 |\n|----|---------|------|\n| `openai` | `\u003e=1.50.0` | OpenAI 兼容协议 SDK |\n| `fastapi` | `\u003e=0.115.0` | Web 框架 |\n| `uvicorn[standard]` | `\u003e=0.32.0` | ASGI 服务器 |\n| `pydantic` | `\u003e=2.0.0` | 数据校验 |\n| `pydantic-settings` | `\u003e=2.0.0` | .env 配置加载 |\n| `python-dotenv` | `\u003e=1.0.0` | dotenv 解析 |\n| `rich` | `\u003e=13.0.0` | CLI 终端美化 |\n\n\u003c!-- /AUTO-GENERATED --\u003e\n\n## 环境变量\n\n\u003c!-- AUTO-GENERATED: derived from .env.example --\u003e\n\n| 变量 | 必填 | 默认值 | 说明 |\n|------|------|--------|------|\n| `OPENAI_API_KEY` | ✅ | — | LLM 服务 API 密钥 |\n| `OPENAI_BASE_URL` | ❌ | `https://api.openai.com/v1` | OpenAI 兼容协议 base_url |\n| `XIAOCHU_MODEL` | ❌ | `gpt-4o-mini` | 使用的模型名 |\n| `XIAOCHU_HOST` | ❌ | `127.0.0.1` | FastAPI 监听地址 |\n| `XIAOCHU_PORT` | ❌ | `8000` | FastAPI 监听端口 |\n| `XIAOCHU_LOG_LEVEL` | ❌ | `INFO` | 日志级别（DEBUG/INFO/WARNING/ERROR）|\n| `XIAOCHU_LOG_DIR` | ❌ | `logs` | 日志目录 |\n| `XIAOCHU_LOG_TO_CONSOLE` | ❌ | `1` | 是否同时输出到控制台 |\n| `XIAOCHU_LOG_MAX_BYTES` | ❌ | `5242880` | 单文件轮转大小（字节）|\n| `XIAOCHU_LOG_BACKUP_COUNT` | ❌ | `5` | 轮转保留份数 |\n| `XIAOCHU_DB_PATH` | ❌ | `xiaochu.db` | SQLite 会话存储文件路径 |\n| `XIAOCHU_CLI_STREAM` | ❌ | `1` | CLI 是否启用逐字流式（设 `0` 等同 `--no-stream`）|\n| `XIAOCHU_CLI_CHAR_DELAY_MS` | ❌ | `20` | CLI 流式逐字节奏（毫秒/字）|\n\n\u003c!-- /AUTO-GENERATED --\u003e\n\n常见 OpenAI 兼容服务参考：\n\n| 服务      | OPENAI_BASE_URL                                          | 示例模型           |\n|-----------|----------------------------------------------------------|--------------------|\n| OpenAI    | https://api.openai.com/v1                                | gpt-4o-mini        |\n| DeepSeek  | https://api.deepseek.com/v1                              | deepseek-chat      |\n| 通义千问  | https://dashscope.aliyuncs.com/compatible-mode/v1        | qwen-plus          |\n| Moonshot  | https://api.moonshot.cn/v1                               | moonshot-v1-8k     |\n\n## 环境准备\n\n项目已绑定 pyenv 虚拟环境（Python 3.12.13），进入项目目录自动激活。\n\n```bash\ncd /var/www/xiaochu\npip install -r requirements.txt\ncp .env.example .env\n# 编辑 .env，填入真实 OPENAI_API_KEY / OPENAI_BASE_URL / XIAOCHU_MODEL\n```\n\n## 使用方式\n\n### CLI 交互（推荐）\n\n```bash\npython cli.py                    # 进入多轮交互（默认逐字流式 + 表格）\npython cli.py 宫保鸡丁           # 单次请求\npython cli.py 今晚吃什么         # 自动识别为推荐 + 详细菜谱\npython cli.py 冰箱里有鸡蛋番茄   # 自动识别为食材反查\npython cli.py --no-stream 宫保鸡丁   # 关闭流式，立即一次性渲染\n```\n\n输入 `:q` 或 Ctrl+C 退出。\n\n#### 流式输出\n\nCLI 默认开启逐字流式：\n- **JSON 类**（recipe / recommend / ingredient）：先生成完整结果，再把 rich 渲染（含 Panel/表格）的 ANSI 字符串逐字打印——视觉与 `--no-stream` 一致，只是字符依次出现。\n- **自然语言类**（cooking_qa / chitchat）：直接走真 token 流式，LLM 边出边打。\n\n控制方式（优先级从高到低）：\n1. CLI 参数 `--no-stream` / `--stream`\n2. 环境变量 `XIAOCHU_CLI_STREAM=0` 全局关闭\n3. `XIAOCHU_CLI_CHAR_DELAY_MS` 调整逐字节奏（默认 20，单位毫秒）\n\n#### 多菜歧义二次确认\n\n输入未含分隔符（如 `+`、`和`、`、`、`跟`、`与`）但 LLM 输出 ≥2 道菜时，CLI 会先打印理解结果并请求确认：\n\n```\n意图: recipe\n\n⚠ 我把「鸡丁肉丝」理解为这 2 道菜： 宫保鸡丁、鱼香肉丝\n继续渲染？ [y/n] (y):\n```\n\n- 回车 / `y`：继续渲染\n- `n`：取消，**不写入 history**，便于换种说法重输（如「鸡丁炒肉丝」）\n\n明确分隔（`鸡丁+肉丝`、`鸡丁和肉丝`）视为陛下主动多菜，**不触发确认**。HTTP 接口忽略此特性，仅 CLI 层启用。\n\n### Web API\n\n```bash\npython main.py\n```\n\n启动后访问：\n- 文档：http://127.0.0.1:8000/docs\n- 接口：`POST /chat`，body：`{\"message\": \"...\", \"session_id\": \"可选\"}`\n\n## API 参考\n\n\u003c!-- AUTO-GENERATED: derived from FastAPI routes --\u003e\n\n| 方法 | 路径 | 说明 |\n|------|------|------|\n| `POST` | `/chat` | 统一聊天入口（自动识别意图分发） |\n| `GET` | `/` | 健康检查 |\n\n\u003c!-- /AUTO-GENERATED --\u003e\n\n### `POST /chat` 请求\n\n```json\n{\n  \"message\": \"宫保鸡丁+木须肉\",\n  \"session_id\": \"可选，不传则自动创建\"\n}\n```\n\n### `POST /chat` 响应\n\n统一壳：\n\n```json\n{\n  \"session_id\": \"abc123...\",\n  \"intent\": \"recipe | recommend | ingredient | cooking_qa | chitchat\",\n  \"data\": { ... 按 intent 不同结构不同 ... }\n}\n```\n\n按 `intent` 不同的 `data` 结构：\n\n#### `intent = recipe`\n\n```json\n{\n  \"recipes\": [\n    {\n      \"dish_name\": \"宫保鸡丁\",\n      \"ingredients\": [{\"name\": \"鸡腿肉\", \"amount\": \"300g\"}],\n      \"steps\": [{\"order\": 1, \"description\": \"...\"}],\n      \"tips\": [\"...\"],\n      \"nutrition\": {\n        \"calories\": \"约 450 kcal/份\",\n        \"difficulty\": \"中等\",\n        \"cook_time\": \"约 30 分钟\",\n        \"servings\": \"2 人份\"\n      }\n    }\n  ]\n}\n```\n\n#### `intent = recommend`\n\n```json\n{\n  \"dishes\": [\"番茄炒蛋\", \"凉拌黄瓜\", \"紫菜蛋花汤\"],\n  \"recipes\": [ /* 与 recipe 同结构，前 N 道详细菜谱 */ ],\n  \"note\": \"已为你详细生成前 3 道菜的菜谱。\"\n}\n```\n\n#### `intent = ingredient`\n\n```json\n{\n  \"dishes\": [\"番茄炒蛋\", \"蒸蛋羹\", \"...\"],\n  \"note\": \"调用 /chat 并发送菜名可继续生成完整菜谱\"\n}\n```\n\n#### `intent = cooking_qa` / `chitchat`\n\n```json\n{ \"answer\": \"...\" }\n```\n\n## 多轮对话\n\nCLI 交互模式与 Web 接口都支持会话上下文：传 `session_id` 即可携带历史，让\"调淡一点\"、\"再来一道\"、\"第二点能详细说说吗\"这类引用生效。\n\n每轮 assistant 回复以**自然语言摘要**写入 history（而非 JSON），保证 LLM 在后续轮次能正确指代上文。\n\n## 推荐 + 菜谱并发策略\n\n| 用户场景 | 详细生成几道菜谱 |\n|---------|----------------|\n| 没明说数量（\"今晚吃什么\"） | 默认 3 道 |\n| 明说 N（N ≤ 7） | 生成 N 道 |\n| 明说 N（N \u003e 7） | 仍只生成前 7 道，note 提示分批 |\n\n实现：LLM 推荐时同时返回 `desired_count`，handler 用 `ThreadPoolExecutor` 并发调 recipe handler 生成前 N 道完整菜谱；单道失败不影响其他。\n\n## 架构原则\n\n- **不引入 agent 框架**（LangChain / LangGraph 暂不需要）：纯 Python + OpenAI SDK 已足够\n- **意图分发**：先识别再处理，handler 职责单一\n- **会话存储**：SQLite 持久化（`XIAOCHU_DB_PATH`），重启后历史不丢\n- **历史摘要**：assistant 回复以自然语言进 history，让多轮上下文对 LLM 可读\n- **流式分层**：自然语言走真 token 流；JSON 类先生成后整体渲染再 ANSI 逐字打印，保住表格美观又不破坏 Pydantic 校验\n\n## 测试\n\n83 个 pytest 用例，全 mock LLM，约 1 秒跑完。\n\n```bash\npip install -r requirements-dev.txt\npython -m pytest\n```\n\n详见 [docs/TESTING.md](docs/TESTING.md)。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmi4646%2Fxiaochu","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmi4646%2Fxiaochu","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmi4646%2Fxiaochu/lists"}