AI 模块 API 文档

基础信息

  • Base URL: /api/ai
  • 认证方式: JWT Bearer Token

数据库表

api_usage_logs 表(AI 调用记录)

字段类型必填说明
idtext (PK)必填10位随机ID
user_idtext (FK→users)选填请求用户
bill_idtext (FK→bills)选填账单
providertext选填AI 提供商
modeltext选填模型名
prompt_tokensinteger选填输入 token 数
completion_tokensinteger选填输出 token 数
total_tokensinteger选填总 token 数
duration_msinteger选填请求耗时(毫秒)
created_atdatetime必填请求时间

接口列表


POST/api/ai/chat

1. 文本对话(流式 SSE)

触发时机: 在 首页(HomePage) 的 ChatBar 输入文字消息后点击发送(或按回车)
HomePage ChatBar → 输入文字 → 点击发送/按回车 → POST /api/ai/chat
请求参数:
参数类型必填说明
billIdstring必填账单ID
providerstring必填AI 提供商(openai/anthropic/gemini
modelstring选填模型名(不传自动选择)
messagesarray必填对话历史 [{role, content}]
owner_typestring选填指定 Key 类型
owner_idnumber选填指定 Key 所有者
messages 格式:
[
  { "role": "system", "content": "你是一个助手..." },
  { "role": "user", "content": "这个月花了多少钱?" }
]
后端处理: 1. 校验用户是账单成员 2. Key 解析(优先级:owner_type+owner_id > provider+model > provider 自动) 3. 调用 proxyChatByProvider,流式输出 SSE 响应格式(SSE):
data: [DONE]

或错误时:
data: [ERROR] AI 请求失败,请稍后重试
data: [DONE]
重要问题:见 P07

POST/api/ai/chat-simple

2. 文本对话(非流式)

触发时机: HomePage 加载时(可能用于获取欢迎语),或 ChatBaruseEffect 初始化
HomePage 加载 → 可能在首次打开 ChatBar 时调用 → POST /api/ai/chat-simple
请求参数:
参数类型必填说明
billIdstring必填账单ID
providerstring选填AI 提供商(不传自动选择)
modelstring选填模型名
messagesarray必填对话历史
后端处理: 1. 与 /chat 类似,但不输出 SSE 2. 无可用 Key 时返回 { content: "", info: "no_ai_configured" }
POST/api/ai/transcribe

3. 语音转写(Whisper)

触发时机: 在 HomePage ChatBar 按住麦克风按钮说话,松开后自动触发
HomePage ChatBar → 按住麦克风按钮 → 说话 → 松开 → POST /api/ai/transcribe
Content-Type: multipart/form-data 请求参数:
参数类型必填说明
billIdstring必填账单ID(FormData)
providerstring选填提供商(默认 openai,Whisper)
audioFile必填音频文件,最大 12MB
后端处理: 1. 校验用户是账单成员 2. 获取 OpenAI Whisper Key 3. POST 到 OpenAI Transcription API(语言 zh,格式 json,温度 0
依赖:见 P15
响应:
{ "text": "转写后的文字内容" }
错误:
  • 无 OpenAI Key → 400: 语音转写需要可用的 OpenAI Key
  • 音频无法识别 → 422: 未识别到有效语音内容

POST/api/ai/voice-chat

4. 语音直发对话

触发时机: 在 HomePage ChatBar 点击「语音直发」模式,说完一段话后自动发送
HomePage ChatBar → 切换到语音直发模式 → 按住说话 → 松开发送 → POST /api/ai/voice-chat
Content-Type: multipart/form-data 请求参数:
参数类型必填说明
billIdstring必填账单ID
providerstring选填偏好提供商(默认 openai)
systemPromptstring选填系统提示词
historystring选填历史消息(JSON 字符串)
audioFile必填音频文件,最大 12MB
后端处理: 1. 获取语音候选 Key(resolveNativeVoiceCandidates) 2. 遍历候选,用每个 Provider 的 TTS/语音模型处理 3. 第一个成功立即返回
问题:见 P16

Key 解析机制(后端内部逻辑)

优先级

owner_type + owner_id 精确匹配
    ↓ 不存在
provider + model 精确匹配(当前用户 Key)
    ↓ 不存在
provider 自动选择(priority 最小的)
    ↓ 都不存在
返回 400 错误

可用 Provider(chatProviders.js)

Provider模型示例API Endpoint流式
openaigpt-4o, gpt-4o-miniapi.openai.com/v1/chat/completions必填
anthropicclaude-3-5-sonnetapi.anthropic.com/v1/messages选填(实际不走流)
geminigemini-1.5-progenerativelanguage.googleapis.com必填
ollamallama3, qwenlocalhost:11434/api/chat必填
azuregpt-4o(Azure URL)必填
deepseekdeepseek-chatapi.deepseek.com/v1/chat/completions必填

API 调用日志记录

成功调用后由 proxyChatByProvider 记录到 api_usage_logs 表:
// 在 chatProviders.js 中,成功响应后:
db.knex('api_usage_logs').insert({
  user_id, bill_id, provider, model,
  prompt_tokens, completion_tokens, total_tokens,
  duration_ms: elapsed
})
注意:见 P17

已知问题汇总

1. Anthropic 流式假象: /chat 对 Anthropic 实际不走 SSE(非流式 /v1/messages) 2. Key 明文存储: api_key_encrypted = Base64 编码,可直接解码 3. Ollama 无认证: 通常本地运行,无 API Key 验证 4. 语音直发全部失败: 返回 502,无友好提示 5. API 日志记录不完整: Provider 请求失败时不记录 api_usage_logs