682 lines
19 KiB
Markdown
682 lines
19 KiB
Markdown
# 编码规范
|
||
|
||
> 一个人的项目更需要规范。没有规范,三个月后你自己都看不懂自己写的代码。
|
||
> 规范的核心目的:**让代码可读、可维护、不纠结**。
|
||
|
||
---
|
||
|
||
## 一、技术栈与语言
|
||
|
||
| 层级 | 语言 | 框架 | 代码放在哪 |
|
||
|------|------|------|-----------|
|
||
| 后端 | Python 3.12 + Flask | BaoDan 原生框架 | `api/insurance/` |
|
||
| 前端 | TypeScript | Vue 3 + Vite | `frontend/src/` |
|
||
| 脚本 | Python 3.12 | 脚本工具 | `scripts/` |
|
||
| 配置 | YAML / JSON | — | 项目根目录 |
|
||
|
||
---
|
||
|
||
## 二、命名规范
|
||
|
||
### 2.1 Python 后端
|
||
|
||
| 类型 | 规则 | 示例 | 反例 |
|
||
|------|------|------|------|
|
||
| 变量 | 小写 + 下划线 | `user_id`, `chat_history` | `userId`, `ChatHistory` |
|
||
| 常量 | 全大写 + 下划线 | `MAX_RETRY_COUNT`, `BAODAN_BASE_URL` | `maxRetry`, `baodanBaseUrl` |
|
||
| 函数 | 小写 + 下划线 | `get_user_info()`, `send_message()` | `GetUserInfo()`, `sendMessage()` |
|
||
| 类 | 大驼峰 | `WecomClient`, `BaoDanService` | `wecom_client`, `baodan_service` |
|
||
| 文件名 | 小写 + 下划线 | `wecom_client.py`, `config.py` | `WecomClient.py`, `Config.py` |
|
||
| 模块/包 | 小写 + 下划线 | `routers/wecom_oauth.py` | `routers/WecomOauth.py` |
|
||
|
||
### 2.2 TypeScript / Vue 前端
|
||
|
||
| 类型 | 规则 | 示例 | 反例 |
|
||
|------|------|------|------|
|
||
| 变量 | 小驼峰 | `userName`, `chatHistory` | `user_name`, `UserName` |
|
||
| 常量 | 全大写 + 下划线 | `MAX_RETRY_COUNT` | `maxRetryCount` |
|
||
| 函数 | 小驼峰 | `getUserInfo()`, `sendMessage()` | `get_user_info()` |
|
||
| 接口/类型 | 大驼峰,I 开头或直接命名 | `UserInfo`, `ApiResponse` | `userInfo`, `i_user_info` |
|
||
| Vue 组件 | 大驼峰 | `RecommendForm.vue`, `ChatPanel.vue` | `recommend-form.vue` |
|
||
| Vue 组件引用 | 大驼峰 | `<RecommendForm />` | `<recommend-form />` |
|
||
| CSS 类名 | 短横线 | `.chat-panel`, `.btn-primary` | `.chatPanel`, `.chat_panel` |
|
||
|
||
### 2.3 数据库
|
||
|
||
| 类型 | 规则 | 示例 |
|
||
|------|------|------|
|
||
| 表名 | 小写 + 下划线,复数 | `wecom_users`, `recommend_logs` |
|
||
| 字段名 | 小写 + 下划线 | `created_at`, `user_id` |
|
||
| 主键 | `id` | `id SERIAL PRIMARY KEY` |
|
||
| 外键 | `{关联表单数}_id` | `user_id`, `document_id` |
|
||
|
||
### 2.4 API 路径
|
||
|
||
| 规则 | 示例 | 反例 |
|
||
|------|------|------|
|
||
| 小写 + 短横线 | `/api/wecom/oauth` | `/api/wecomOAuth` |
|
||
| 名词复数 | `/api/recommend/logs` | `/api/recommend/log` |
|
||
| 版本号可选 | `/api/v1/wecom/callback` | — |
|
||
|
||
---
|
||
|
||
## 三、注释规范
|
||
|
||
### 3.1 什么需要注释
|
||
|
||
**必须注释**:
|
||
- 所有公开函数/方法的 docstring
|
||
- 所有类的 docstring
|
||
- 复杂业务逻辑(不是显而易见的代码)
|
||
- 为什么要这样做的原因("为什么"比"做了什么"重要)
|
||
|
||
**不需要注释**:
|
||
- 简单的 getter/setter
|
||
- 一看就懂的代码(`user_id = data["user_id"]`)
|
||
- TODO / FIXME(用 issue 追踪,不要堆在代码里)
|
||
|
||
### 3.2 Python 注释格式
|
||
|
||
```python
|
||
class BaoDanClient:
|
||
"""BaoDan API 客户端,负责与 BaoDan 后端通信。"""
|
||
|
||
def chat(self, user_id: str, query: str, conversation_id: str = "") -> dict:
|
||
"""发送对话消息并获取 AI 回复。
|
||
|
||
Args:
|
||
user_id: 企微用户 ID,作为 BaoDan 的用户标识
|
||
query: 用户的问题文本
|
||
conversation_id: 对话 ID,空字符串表示新对话
|
||
|
||
Returns:
|
||
dict: 包含 answer(回答文本)和 conversation_id(对话 ID)
|
||
|
||
Raises:
|
||
BaoDanAPIError: BaoDan 返回非 200 状态码时抛出
|
||
"""
|
||
# 企微要求 5 秒内响应,所以用 blocking 模式
|
||
# 如果 BaoDan 响应超过 5 秒,调用方需要用异步队列处理
|
||
resp = requests.post(...)
|
||
...
|
||
```
|
||
|
||
### 3.3 TypeScript / Vue 注释格式
|
||
|
||
```typescript
|
||
/**
|
||
* 调用 BaoDan Workflow 生成产品推荐方案
|
||
*
|
||
* @param formData - 用户填写的需求信息(年龄、职业、预算等)
|
||
* @returns 推荐方案的 Markdown 文本
|
||
* @throws 当 BaoDan API 调用失败时抛出错误
|
||
*/
|
||
async function generateRecommendation(formData: RecommendFormData): Promise<string> {
|
||
// ...
|
||
}
|
||
```
|
||
|
||
```vue
|
||
<template>
|
||
<!-- 产品推荐表单 — 用户填写需求信息 -->
|
||
<div class="recommend-form">
|
||
...
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
/**
|
||
* RecommendForm 组件
|
||
*
|
||
* 用户填写保险需求信息(年龄、职业、预算等),
|
||
* 提交后调用后端接口生成 AI 推荐方案。
|
||
*/
|
||
|
||
// 表单数据 — 所有字段都有默认值,避免提交时空值
|
||
const formData = ref<RecommendFormData>({
|
||
age: 30,
|
||
gender: 'male',
|
||
occupation: '',
|
||
annualIncome: 0,
|
||
budget: 0,
|
||
insuranceTypes: [],
|
||
})
|
||
</script>
|
||
```
|
||
|
||
### 3.4 中文还是英文
|
||
|
||
| 场景 | 语言 | 原因 |
|
||
|------|------|------|
|
||
| 变量名/函数名 | 英文 | 代码必须英文 |
|
||
| 注释 | 中文 | 你是中文团队,读中文比英文快 |
|
||
| Docstring | 中文 | 同上 |
|
||
| Git commit | 英文 | 国际惯例,工具兼容性好 |
|
||
| 日志 | 中文 | 方便排查问题 |
|
||
| 错误信息 | 中文 | 用户/管理员看得懂 |
|
||
|
||
---
|
||
|
||
## 四、代码格式
|
||
|
||
### 4.1 Python
|
||
|
||
使用自动格式化工具,不要手动对齐:
|
||
|
||
```bash
|
||
# 安装
|
||
pip install ruff
|
||
|
||
# 格式化
|
||
ruff format backend/
|
||
|
||
# 检查代码风格
|
||
ruff check backend/
|
||
```
|
||
|
||
**`pyproject.toml` 配置**(放在 `backend/` 目录下):
|
||
|
||
```toml
|
||
[tool.ruff]
|
||
line-length = 120 # 行宽 120(比默认 88 宽一些,适合现代屏幕)
|
||
target-version = "py312" # Python 版本
|
||
|
||
[tool.ruff.format]
|
||
quote-style = "double" # 字符串用双引号
|
||
|
||
[tool.ruff.lint]
|
||
select = ["E", "F", "I"] # 基础检查 + import 排序
|
||
```
|
||
|
||
### 4.2 TypeScript / Vue
|
||
|
||
使用 ESLint + Prettier:
|
||
|
||
```bash
|
||
# 安装(在 frontend/ 目录下)
|
||
pnpm add -D eslint @vue/eslint-config-typescript prettier
|
||
|
||
# 格式化
|
||
pnpm prettier --write src/
|
||
|
||
# 检查
|
||
pnpm eslint src/
|
||
```
|
||
|
||
### 4.3 通用规则
|
||
|
||
| 规则 | Python | TypeScript |
|
||
|------|--------|------------|
|
||
| 行宽 | 120 字符 | 120 字符 |
|
||
| 缩进 | 4 空格 | 2 空格 |
|
||
| 引号 | 双引号 `"` | 单引号 `'` |
|
||
| 分号 | 不需要 | 不需要(Prettier 自动处理) |
|
||
| 尾逗号 | 是 | 是 |
|
||
|
||
---
|
||
|
||
## 五、项目结构规范
|
||
|
||
### 5.1 Python 后端
|
||
|
||
```
|
||
backend/
|
||
├── main.py # 入口文件,只做 app 初始化和路由注册
|
||
├── config.py # 配置读取,所有配置从这里获取
|
||
├── routers/ # 路由层:只做参数校验和响应,不写业务逻辑
|
||
│ ├── __init__.py
|
||
│ ├── wecom.py # 企微消息回调
|
||
│ ├── wecom_oauth.py # 企微 OAuth
|
||
│ └── recommend.py # 产品推荐
|
||
├── services/ # 业务层:核心逻辑在这里
|
||
│ ├── __init__.py
|
||
│ ├── baodan_client.py # 封装 BaoDan API 调用
|
||
│ └── wecom_client.py # 封装企微 API 调用
|
||
├── models/ # 数据模型
|
||
│ ├── __init__.py
|
||
│ └── schemas.py # Pydantic 请求/响应模型
|
||
├── utils/ # 工具函数
|
||
│ ├── __init__.py
|
||
│ └── crypto.py # 加解密工具
|
||
└── requirements.txt # 依赖列表(锁定版本号)
|
||
```
|
||
|
||
**分层原则**:
|
||
- `routers/` 只做 HTTP 相关的事(解析请求、校验参数、返回响应)
|
||
- `services/` 做业务逻辑(调 API、处理数据)
|
||
- `models/` 定义数据结构(Pydantic 模型)
|
||
- `utils/` 放通用工具函数
|
||
|
||
### 5.2 Vue 前端
|
||
|
||
```
|
||
frontend/src/
|
||
├── main.ts # 入口
|
||
├── App.vue # 根组件
|
||
├── router/
|
||
│ └── index.ts # 路由定义
|
||
├── pages/ # 页面组件(一个路由对应一个页面)
|
||
│ ├── ChatPage.vue # 对话页面
|
||
│ ├── RecommendPage.vue # 产品推荐页面
|
||
│ └── LoginPage.vue # 登录页面
|
||
├── components/ # 通用组件(可复用的 UI 组件)
|
||
│ ├── RecommendForm.vue
|
||
│ └── RecommendResult.vue
|
||
├── composables/ # 组合函数(复用逻辑)
|
||
│ ├── useApi.ts # API 请求封装
|
||
│ └── useAuth.ts # 登录状态管理
|
||
├── utils/
|
||
│ └── request.ts # HTTP 请求工具(axios 封装)
|
||
└── types/
|
||
└── index.ts # TypeScript 类型定义
|
||
```
|
||
|
||
**组件拆分原则**:
|
||
- 一个 `.vue` 文件不超过 300 行,超过就拆分
|
||
- `pages/` 放页面级组件(有路由)
|
||
- `components/` 放可复用组件(无路由)
|
||
- `composables/` 放可复用逻辑(类似 React hooks)
|
||
|
||
---
|
||
|
||
## 六、错误处理规范
|
||
|
||
### 6.1 Python 后端
|
||
|
||
```python
|
||
# 定义统一的错误响应格式
|
||
class AppError(Exception):
|
||
"""业务异常基类"""
|
||
def __init__(self, code: int, message: str):
|
||
self.code = code
|
||
self.message = message
|
||
|
||
class BaoDanAPIError(AppError):
|
||
"""BaoDan API 调用失败"""
|
||
def __init__(self, detail: str = ""):
|
||
super().__init__(code=2001, message=f"BaoDan API 调用失败: {detail}")
|
||
|
||
class WecomAPIError(AppError):
|
||
"""企微 API 调用失败"""
|
||
def __init__(self, detail: str = ""):
|
||
super().__init__(code=2002, message=f"企微 API 调用失败: {detail}")
|
||
|
||
|
||
# 全局异常处理器
|
||
@app.exception_handler(AppError)
|
||
async def app_error_handler(request, exc: AppError):
|
||
return JSONResponse(
|
||
status_code=200, # 业务异常用 200,靠 code 区分
|
||
content={"code": exc.code, "message": exc.message, "data": None},
|
||
)
|
||
|
||
@app.exception_handler(Exception)
|
||
async def general_error_handler(request, exc: Exception):
|
||
# 记录日志
|
||
logger.error(f"未处理的异常: {exc}", exc_info=True)
|
||
return JSONResponse(
|
||
status_code=500,
|
||
content={"code": 9999, "message": "服务器内部错误", "data": None},
|
||
)
|
||
```
|
||
|
||
### 6.2 Vue 前端
|
||
|
||
```typescript
|
||
// utils/request.ts
|
||
import axios from 'axios'
|
||
|
||
const request = axios.create({
|
||
baseURL: '/api',
|
||
timeout: 60000, // 产品推荐可能要等 30-60 秒
|
||
})
|
||
|
||
// 响应拦截器:统一处理错误
|
||
request.interceptors.response.use(
|
||
(response) => {
|
||
const { code, message, data } = response.data
|
||
if (code !== 0) {
|
||
// 业务错误,用组件库的 message 提示
|
||
ElMessage.error(message)
|
||
return Promise.reject(new Error(message))
|
||
}
|
||
return data
|
||
},
|
||
(error) => {
|
||
// 网络错误
|
||
ElMessage.error('网络异常,请稍后重试')
|
||
return Promise.reject(error)
|
||
},
|
||
)
|
||
```
|
||
|
||
---
|
||
|
||
## 七、日志规范
|
||
|
||
### 7.1 日志级别
|
||
|
||
| 级别 | 什么时候用 | 示例 |
|
||
|------|-----------|------|
|
||
| `DEBUG` | 开发调试信息 | 请求参数、API 响应详情 |
|
||
| `INFO` | 正常业务流程 | 收到企微消息、AI 回复成功 |
|
||
| `WARNING` | 不影响功能但需要注意 | API 响应慢、重试成功 |
|
||
| `ERROR` | 功能失败 | BaoDan 调用失败、企微回复失败 |
|
||
|
||
### 7.2 日志格式
|
||
|
||
```python
|
||
import logging
|
||
|
||
logging.basicConfig(
|
||
level=logging.INFO,
|
||
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
||
datefmt="%Y-%m-%d %H:%M:%S",
|
||
)
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# 使用
|
||
logger.info("收到企微消息: user=%s, content=%s", user_id, content[:50])
|
||
logger.error("BaoDan API 调用失败: status=%d, body=%s", resp.status_code, resp.text[:200])
|
||
```
|
||
|
||
### 7.3 日志原则
|
||
|
||
1. **记录关键节点**:收到请求、调用外部 API、返回结果
|
||
2. **不要记录敏感信息**:API Key、用户密码、完整的消息内容(记录前 50 字符即可)
|
||
3. **包含上下文**:user_id、request_id 等,方便排查
|
||
4. **日志不是调试工具**:不要到处 `print()`,用 `logger.debug()` 代替
|
||
|
||
---
|
||
|
||
## 八、Git 分支管理
|
||
|
||
### 8.1 分支策略
|
||
|
||
一个人的项目不需要复杂的 Git Flow,用简单的 **主分支 + 功能分支** 即可:
|
||
|
||
```
|
||
main ← 稳定版本,可随时部署
|
||
│
|
||
├── dev ← 日常开发分支,功能合入这里
|
||
│ │
|
||
│ ├── feature/wecom-bot ← 功能分支
|
||
│ ├── feature/recommend ← 功能分支
|
||
│ └── fix/timeout-bug ← 修复分支
|
||
│
|
||
└── hotfix/xxx ← 紧急修复(从 main 拉出)
|
||
```
|
||
|
||
### 8.2 分支命名
|
||
|
||
| 类型 | 命名规则 | 示例 |
|
||
|------|---------|------|
|
||
| 主分支 | `main` | — |
|
||
| 开发分支 | `dev` | — |
|
||
| 功能分支 | `feature/功能名` | `feature/wecom-bot` |
|
||
| 修复分支 | `fix/问题描述` | `fix/message-timeout` |
|
||
| 紧急修复 | `hotfix/问题描述` | `hotfix/crash-on-startup` |
|
||
|
||
### 8.3 Commit Message 规范
|
||
|
||
格式:`类型(范围): 描述`
|
||
|
||
```
|
||
类型:
|
||
feat 新功能
|
||
fix 修复 bug
|
||
docs 文档更新
|
||
style 代码格式调整(不影响功能)
|
||
refactor 重构
|
||
test 测试
|
||
chore 构建/依赖/配置变更
|
||
```
|
||
|
||
示例:
|
||
```
|
||
feat(wecom): 添加企微群聊消息接收功能
|
||
fix(recommend): 修复推荐结果为空时页面报错
|
||
docs(api): 更新产品推荐接口文档
|
||
refactor(baodan): 将 BaoDan 调用抽成独立 service
|
||
chore(deps): 升级 fastapi 到 0.115
|
||
```
|
||
|
||
### 8.4 工作流程
|
||
|
||
```
|
||
1. 从 dev 创建功能分支:git checkout -b feature/xxx dev
|
||
2. 开发 + 提交(一个小功能一个 commit)
|
||
3. 功能完成后合并回 dev:git checkout dev && git merge feature/xxx
|
||
4. 删除功能分支:git branch -d feature/xxx
|
||
5. dev 稳定后合并到 main:git checkout main && git merge dev
|
||
6. 打 tag:git tag v0.1.0
|
||
```
|
||
|
||
### 8.5 Git 忽略文件
|
||
|
||
```gitignore
|
||
# .gitignore(项目根目录)
|
||
|
||
# Python
|
||
__pycache__/
|
||
*.pyc
|
||
.venv/
|
||
*.egg-info/
|
||
|
||
# Node
|
||
node_modules/
|
||
dist/
|
||
|
||
# 环境变量(包含密钥,绝对不能提交)
|
||
.env
|
||
.env.local
|
||
.env.production
|
||
|
||
# IDE
|
||
.vscode/
|
||
.idea/
|
||
*.swp
|
||
|
||
# 系统文件
|
||
.DS_Store
|
||
Thumbs.db
|
||
|
||
# BaoDan 源码(如果作为子模块引入)
|
||
baodan/
|
||
|
||
# 上传的文件
|
||
uploads/
|
||
```
|
||
|
||
---
|
||
|
||
## 九、依赖管理
|
||
|
||
### 9.1 Python 依赖
|
||
|
||
```bash
|
||
# 安装时锁定版本
|
||
pip install fastapi==0.115.0
|
||
pip install uvicorn==0.32.0
|
||
|
||
# 生成 requirements.txt
|
||
pip freeze > requirements.txt
|
||
|
||
# requirements.txt 里必须锁定版本号,不要用 >= 或 ~=
|
||
# 正确:fastapi==0.115.0
|
||
# 错误:fastapi>=0.100
|
||
```
|
||
|
||
### 9.2 Node 依赖
|
||
|
||
```bash
|
||
# 安装时精确版本
|
||
pnpm add axios@1.7.0
|
||
|
||
# 不要混用 npm 和 pnpm,统一用 pnpm
|
||
```
|
||
|
||
### 9.3 核心依赖清单
|
||
|
||
**Python 后端(`backend/requirements.txt`)**:
|
||
```
|
||
fastapi==0.115.0
|
||
uvicorn==0.32.0
|
||
requests==2.32.0
|
||
python-dotenv==1.0.0
|
||
pydantic==2.10.0
|
||
wechatpy==1.8.18
|
||
cryptography==44.0.0
|
||
```
|
||
|
||
**Vue 前端(`frontend/package.json` 核心依赖)**:
|
||
```
|
||
vue: ^3.5.0
|
||
vue-router: ^4.4.0
|
||
axios: ^1.7.0
|
||
marked: ^15.0.0
|
||
element-plus: ^2.9.0
|
||
```
|
||
|
||
---
|
||
|
||
## 十、代码审查清单(自检用)
|
||
|
||
一个人没有 code review 搭档,但提交前自己过一遍这个清单:
|
||
|
||
### Python
|
||
- [ ] 函数有 docstring 吗?
|
||
- [ ] 参数有类型注解吗?
|
||
- [ ] 异常有处理吗?(不要裸 `except:`)
|
||
- [ ] 日志有记录关键节点吗?
|
||
- [ ] API Key / 密码等敏感信息没写死在代码里吧?
|
||
- [ ] `ruff format` 和 `ruff check` 通过了吗?
|
||
|
||
### Vue / TypeScript
|
||
- [ ] 组件不超过 300 行?
|
||
- [ ] 变量有类型定义吗?(不要到处 `any`)
|
||
- [ ] 有 loading / error 状态处理吗?
|
||
- [ ] API 请求超时设置合理吗?
|
||
- [ ] ESLint 和 Prettier 通过了吗?
|
||
|
||
### Git
|
||
- [ ] commit message 符合规范?
|
||
- [ ] `.env` 文件没有提交吧?
|
||
- [ ] 不相关的改动没有混在这个 commit 里吧?
|
||
|
||
---
|
||
|
||
## 十一、BaoDan 集成规范
|
||
|
||
### 11.1 架构说明
|
||
|
||
自研代码放在 `baodan-main/api/insurance/` 目录下,作为 Flask Blueprint 注册到 BaoDan 的 Flask 应用中。**不使用 FastAPI**,直接复用 BaoDan 的 Flask 框架。
|
||
|
||
### 11.2 代码组织
|
||
|
||
```
|
||
baodan-main/api/insurance/
|
||
├── __init__.py
|
||
├── wecom/
|
||
│ ├── wecom_bot.py # 企微消息回调处理
|
||
│ ├── wecom_oauth.py # 企微 OAuth 登录
|
||
│ └── routes.py # Blueprint 路由
|
||
├── recommend/
|
||
│ ├── recommend_api.py # 产品推荐接口
|
||
│ └── workflow_helper.py # Workflow 调用封装
|
||
├── permissions/
|
||
│ ├── models.py # 角色/权限数据模型
|
||
│ ├── middleware.py # 权限校验
|
||
│ └── routes.py # 角色管理接口
|
||
├── stats/
|
||
│ └── stats_api.py # 数据统计接口
|
||
└── db/
|
||
├── wecom_user.py # 企微用户映射表
|
||
└── recommend_record.py # 推荐记录表
|
||
```
|
||
|
||
### 11.3 调用 BaoDan 核心功能(直接 import)
|
||
|
||
```python
|
||
# 调用 BaoDan RAG 检索(直接 import,不走 HTTP)
|
||
from core.rag.retrieval.dataset_retrieval import DatasetRetrieval
|
||
|
||
# 调用 BaoDan LLM
|
||
from core.model_runtime.model_manager import ModelManager
|
||
|
||
# 调用 BaoDan Workflow
|
||
from core.workflow.workflow_engine import WorkflowEngine
|
||
|
||
# 复用 BaoDan 数据库连接
|
||
from extensions.ext_database import db
|
||
from models.dataset import Dataset, Document
|
||
|
||
# 复用 BaoDan 认证
|
||
from extensions.ext_login import login_required
|
||
```
|
||
|
||
### 11.4 禁止事项
|
||
|
||
- **不要在 `api/insurance/` 以外的目录写代码**
|
||
- **不要修改 `api/core/`、`api/controllers/`、`api/models/` 等 BaoDan 目录**
|
||
- **不要自己装 FastAPI**——直接用 BaoDan 的 Flask
|
||
- **不要自己建数据库连接**——用 `extensions.ext_database.db`
|
||
- **不要自己装 Redis 客户端**——用 BaoDan 的 Redis 连接
|
||
- **不要自己写日志系统**——用 BaoDan 的 logging
|
||
|
||
def chat(self, user_id: str, query: str, conversation_id: str = "") -> dict:
|
||
"""发送对话消息。返回 {answer, conversation_id, sources}"""
|
||
resp = requests.post(
|
||
f"{self.base_url}/chat-messages",
|
||
headers={"Authorization": f"Bearer {self.app_key}"},
|
||
json={
|
||
"query": query,
|
||
"response_mode": "blocking",
|
||
"user": user_id,
|
||
"conversation_id": conversation_id,
|
||
},
|
||
timeout=60,
|
||
)
|
||
resp.raise_for_status()
|
||
data = resp.json()
|
||
return {
|
||
"answer": data.get("answer", ""),
|
||
"conversation_id": data.get("conversation_id", ""),
|
||
"sources": data.get("metadata", {}).get("retriever_resources", []),
|
||
}
|
||
|
||
def run_workflow(self, inputs: dict, user_id: str) -> dict:
|
||
"""执行 Workflow。返回 {status, outputs}"""
|
||
resp = requests.post(
|
||
f"{self.base_url}/workflows/run",
|
||
headers={"Authorization": f"Bearer {self.workflow_key}"},
|
||
json={"inputs": inputs, "response_mode": "blocking", "user": user_id},
|
||
timeout=120,
|
||
)
|
||
resp.raise_for_status()
|
||
data = resp.json()
|
||
return {
|
||
"status": data.get("data", {}).get("status", "unknown"),
|
||
"outputs": data.get("data", {}).get("outputs", {}),
|
||
}
|
||
```
|
||
|
||
### 11.2 调用规则
|
||
|
||
| 规则 | 说明 |
|
||
|------|------|
|
||
| 统一入口 | 所有 BaoDan 调用走 `BaoDanClient`,不直接 `requests.post` |
|
||
| 超时设置 | Chat API 60 秒,Workflow API 120 秒 |
|
||
| 错误处理 | 捕获异常后抛出 `BaoDanAPIError`,不要吞掉异常 |
|
||
| 日志记录 | 每次调用记录 request_id、user_id、耗时 |
|
||
| 重试策略 | BaoDan 5xx 错误重试 1 次,4xx 不重试 |
|
||
| 用户标识 | 企微用户用 `wecom_{userid}`,网页用户用 `web_{baodan_user_id}` |
|
||
|
||
### 11.3 禁止事项
|
||
|
||
- 禁止在前端直接调用 BaoDan API(API Key 会暴露)
|
||
- 禁止在多个文件中重复写 BaoDan 调用逻辑
|
||
- 禁止不设超时调用 BaoDan(可能导致请求永久挂起)
|
||
- 禁止将 BaoDan 返回的原始 JSON 直接传给前端(需先清洗字段)
|