Compare commits

...
Author SHA1 Message Date
dependabot[bot]andGitHub 4f7665d92d build(deps): bump opuslib-next in /main/xiaozhi-server
Bumps [opuslib-next](https://github.com/kalicyh/opuslib-next) from 1.1.5 to 1.1.6.
- [Commits](https://github.com/kalicyh/opuslib-next/commits/v1.1.6)

---
updated-dependencies:
- dependency-name: opuslib-next
  dependency-version: 1.1.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-11 11:43:00 +00:00
wengzhandGitHub 1b5c00231a Merge pull request #3165 from xinnan-tech/fix-chat-total
fix: 修复聊天记录左侧会话数量统计
2026-05-09 15:56:05 +08:00
zhuoqinglian 95acba1126 fix: 修复聊天记录左侧会话数量统计 2026-05-09 15:54:53 +08:00
wengzhandGitHub 1e6f0fd1be Merge pull request #3162 from xinnan-tech/perf-tool-call-optimization
fix:修复音乐播放工具TTS上报记录问题
2026-05-08 16:40:37 +08:00
DaGou12138 eb571fae57 fix:修复音乐播放工具TTS上报记录问题 2026-05-08 16:38:23 +08:00
wengzhandGitHub 2946be7284 Merge pull request #3161 from xinnan-tech/perf-tool-call-optimization
fix:优化天气工具调用提示词
2026-05-08 14:23:11 +08:00
DaGou12138 425cb1d6bd fix:优化天气工具调用提示词,本地上下文存在当前所在地的天气信息无需再度查询工具 2026-05-08 14:20:52 +08:00
wengzhandGitHub cf9b0ec712 Merge pull request #3160 from xinnan-tech/fix-chat-title
fix: 修复聊天记录标题过长展示异常的问题
2026-05-08 11:08:23 +08:00
zhuoqinglian 1b053dc9c8 fix: 修复聊天记录标题过长展示异常的问题 2026-05-08 11:05:20 +08:00
wengzhandGitHub d7ad656bd0 Merge pull request #3159 from xinnan-tech/perf-tool-call-optimization
fix:优化对话过程中JSON闭合符号清理的问题
2026-05-08 10:48:58 +08:00
DaGou12138 7dae67a57b fix:优化对话过程中JSON闭合符号清理的问题 2026-05-08 10:46:37 +08:00
wengzhandGitHub 7b0f59a747 Merge pull request #3158 from xinnan-tech/chore/cleanup
chore: 暂时移除近期不可用或不稳定的TTS供应器及相关配置
2026-05-08 10:38:53 +08:00
DaGou12138 2eaa5bbb11 fix:优化对话过程中异常符号记录的问题 2026-05-08 10:29:50 +08:00
FAN-yeB c693ff1564 update:linkerai 近期多次测试,linkerai接口无法提供稳定服务,先暂时下架这个供应器 2026-05-08 10:18:26 +08:00
DaGou12138andGitHub e9bf006d9d Merge pull request #3154 from haixinke/Bug-get-user-prifile-null
fix:当打开用户画像开关时,修复获取用户画像为空的问题
2026-05-07 16:01:57 +08:00
wengzhandGitHub bef96f36e2 Merge pull request #3157 from xinnan-tech/fix-streaming-tts-original-text
Fix streaming tts original text
2026-05-07 15:36:56 +08:00
wengzhandGitHub bebb7eac69 Merge pull request #3156 from xinnan-tech/perf-tool-call-optimization
perf:优化新闻工具新闻源配置获取的逻辑
2026-05-07 11:43:17 +08:00
DaGou12138 a787130b74 perf:
1、优化新闻工具新闻源获取的逻辑
2、调整新闻工具描述内容
2026-05-07 11:41:17 +08:00
FAN-yeB 8b7f16b9c2 update:TTS_GizwitsTTS 近期机智云语音合成不成功,先暂时下架这个供应器 2026-05-07 11:27:32 +08:00
FAN-yeB 9ed66ec013 update:TTS_ACGNTTS 海豚配音截止5月7日未找到token入口,先暂时下架这个供应器 2026-05-07 10:54:03 +08:00
FAN-yeB 05c22cdf2a update:更新 .gitignore忽略.claude目录 2026-05-07 10:23:02 +08:00
FAN-yeB 4bc0b67cd7 update:去除无用的.claude目录 2026-05-07 10:20:48 +08:00
Sakura-RanChen 0a6f811448 fix: 修复流式情况下替换词失败 2026-05-07 10:20:10 +08:00
wengzhandGitHub 5aeb55fffd Merge pull request #3155 from xinnan-tech/perf-tool-call-optimization
perf:优化工具调用问题、调整后端智能体搜索条件逻辑
2026-05-06 17:01:15 +08:00
DaGou12138 c33ee9442e perf:
1、优化小智后端智能体搜索条件逻辑
2、优化工具调用准确率问题,修改工具提示词,去除非必要的few-shot逻辑
3、优化工具描述内容
2026-05-06 16:53:06 +08:00
minwang 4670ad2925 delete log 2026-05-01 16:03:02 +08:00
minwangandClaude Sonnet 4.6 b542388053 🐛 修复PowerMem用户画像查询返回空的问题
修复 get_user_profile() 方法在缓存未命中时主动从 SDK 获取用户画
像,支持 profile_content 和 topics 两种格式,解决新对话中用户画像无法显示的问题

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 14:57:36 +08:00
rainv123 a71ce53a68 fix:修复使用替换词时,火山引擎双向流式tts的文本显示/上报问题 2026-04-30 17:33:40 +08:00
rainv123 dd88107131 Revert "fix: 修复流式TTS替换词显示/上报问题"
This reverts commit 8b847a3826.
2026-04-30 15:57:03 +08:00
rainv123 8b847a3826 fix: 修复流式TTS替换词显示/上报问题 2026-04-30 14:32:36 +08:00
wengzhandGitHub 036b89f24e Merge pull request #3151 from xinnan-tech/py-refactor-asr-tts
调整配置
2026-04-30 11:42:36 +08:00
Sakura-RanChen 354280d430 调整配置 2026-04-30 11:41:16 +08:00
hrzandGitHub e2d16ec8d4 Merge pull request #3150 from Mirrorium227/main
Closes #Issue3149 修复文档序号
2026-04-30 11:27:15 +08:00
Radar SongandGitHub 6a0192de27 Closes #Issue3149 修复文档序号 2026-04-29 20:18:17 +08:00
hrzandGitHub ac6eeb5c1f Merge pull request #3145 from haixinke/fix-bug_prompt_cache_key_wrong
Fix:【设备快速提示词缓存key错误】
2026-04-29 14:32:42 +08:00
minwang 23e213f6ea 修复设备快速提示词缓存key错误 2026-04-28 15:52:23 +08:00
wengzhandGitHub 382c948482 Merge pull request #3144 from xinnan-tech/fix-memory-config
fix:修复记忆模型配置保存字段未删除问题
2026-04-28 15:25:49 +08:00
DaGou12138 a67d938523 fix:修复记忆模型配置保存字段未删除问题 2026-04-28 15:23:57 +08:00
wengzhandGitHub 26aca54f4b Merge pull request #3141 from xinnan-tech/add-correct-word
fix修复 asyncio.gather 吞掉业务异常的问题
2026-04-27 17:31:47 +08:00
rainv123 4717b8beac fix修复 asyncio.gather 吞掉业务异常的问题 2026-04-27 17:29:41 +08:00
wengzhandGitHub cd580b6f81 Merge pull request #3139 from xinnan-tech/py-fix-mem0ai
fix: 修复Mem0AI记忆模式工具调用时保存记忆400错误
2026-04-27 16:57:26 +08:00
3030332422 70740af7a6 fix: 修复Mem0AI记忆模式工具调用时保存记忆400错误 2026-04-27 16:25:05 +08:00
wengzhandGitHub 7b30c25923 Merge pull request #3132 from xinnan-tech/replacement-word
feat: 新增替换词管理功能
2026-04-27 16:23:54 +08:00
wengzhandGitHub 7db13b8be2 Merge pull request #3130 from xinnan-tech/add-correct-word
add:替换词功能
2026-04-27 16:23:33 +08:00
wengzhandGitHub a0df41ac24 Merge pull request #3136 from xinnan-tech/performance_tester
update:解决base类修改后测速工具兼容性问题
2026-04-27 15:08:26 +08:00
FAN-yeB a432c295d3 update:解决base类修改后测速工具兼容性问题 2026-04-27 15:06:40 +08:00
wengzhandGitHub ab8a35e233 Merge pull request #3135 from xinnan-tech/perf-tool-call-optimization
Perf:优化工具调用准确性、禁用标题总结与记忆模型思考模式、拆分动态上下文
2026-04-27 14:54:16 +08:00
DaGou12138 160b67197f Merge branch 'refs/heads/main' into perf-tool-call-optimization 2026-04-27 14:23:28 +08:00
欣南科技andGitHub 3510282cd5 Merge pull request #3134 from xinnan-tech/0.9.3
Bump to 0.9.3
2026-04-27 12:46:10 +08:00
hrz bdf7c7facf Bump to 0.9.3 2026-04-27 12:44:02 +08:00
DaGou12138 151a0c1b99 perf:
1、优化工具调用相关提示词,拆分静态system-prompt和动态上下文。
2、新增few-shot示例,使用"调用→结果→回复"流程闭环,显著提高多次会话后工具调用准确性。
3、去除对话标题总结和记忆的模型思考模式。
2026-04-27 11:08:25 +08:00
zhuoqinglian 52af722463 update: 更新替换词数量限制 2026-04-24 14:54:37 +08:00
wengzhandGitHub fa91c97685 Merge pull request #3131 from xinnan-tech/fix-wifi-no-response
fix: 修复设备配网成功没有自动退出配网模式
2026-04-24 14:38:06 +08:00
zhuoqinglian a92d6ccdfb fix: 修复设备配网成功没有自动退出配网模式 2026-04-24 14:35:34 +08:00
zhuoqinglianandGitHub f5ce755fea Merge pull request #3116 from wujingquan/main
Add WiFi password translations for multiple languages
2026-04-24 14:15:27 +08:00
rainv123 606a42cfb2 update:替换词功能 2026-04-24 10:06:26 +08:00
zhuoqinglian f85e863144 feat: 新增替换词管理功能 2026-04-23 17:50:57 +08:00
wengzhandGitHub 550397de98 Merge pull request #3127 from xinnan-tech/py-fix-asr
Py fix asr
2026-04-23 10:46:09 +08:00
Sakura-RanChen ff66b815a5 fix: 在模型和工具调用期间进行打断后说完话错误退出问题 2026-04-23 10:17:43 +08:00
Sakura-RanChen 8c18e48b97 fix: 停止帧发送后与原先音频时序问题
回退退出工具打断处理(在大模型思考期间进行打断,然后大模型立马调用退出工具会造成死锁,因模型思考时间和是否调用工具不确定性,状态难管理)
2026-04-22 15:55:28 +08:00
wengzhandGitHub 4acd02e2b0 Merge pull request #3125 from xinnan-tech/py-fix-llm-chunk
fix: 修复频繁打断下生成器不会清理
2026-04-22 11:30:35 +08:00
wengzhandGitHub ddfbacc717 Merge pull request #3124 from xinnan-tech/py-add-ASRv2
使用双流优化版接口(速度提升),增加豆包流式语音识别模型2.0模型选择
2026-04-22 11:29:57 +08:00
Sakura-RanChen 2a4dbdb69b fix: 修复频繁打断下生成器不会清理 2026-04-22 11:24:43 +08:00
FAN-yeB e9a2489e48 update:测试工具更新豆包流式测试逻辑 2026-04-22 10:53:48 +08:00
Sakura-RanChen ce13a7e3e6 使用双流优化版接口(速度提升),增加豆包流式语音识别模型2.0模型选择 2026-04-22 10:28:47 +08:00
wengzhandGitHub 8a156e99f4 Merge pull request #3122 from xinnan-tech/py-add-TTS2.0
增加豆包语音合成模型2.0专属供应器
2026-04-21 15:44:04 +08:00
Sakura-RanChen 4313216816 增加豆包语音合成模型2.0专属供应器 2026-04-21 15:33:37 +08:00
DaGou12138 8c0899415a Merge branch 'refs/heads/main' into perf-tool-call-optimization 2026-04-21 15:02:31 +08:00
DaGou12138 3736a5845a perf:
1、优化提示词context动态变化导致的缓存失效问题,拆分静态system-prompt和动态上下文
2026-04-21 15:01:57 +08:00
wengzhandGitHub dc7615c008 Merge pull request #3120 from xinnan-tech/update-theme-generator
update: 更新设备主题生成功能,添加新表情
2026-04-21 14:50:53 +08:00
wengzhandGitHub b03f9374ea Merge pull request #3119 from xinnan-tech/fix-config-slm
feat: agent-models接口返回SLM模型,LLM类型同步返回所选SLM
2026-04-21 14:18:07 +08:00
欣南科技andGitHub cacfc9bfee Merge pull request #3121 from xinnan-tech/enhanced-prompts
update:优化提示词
2026-04-21 14:01:49 +08:00
hrz ce3c8a2bd8 update:优化提示词 2026-04-21 14:00:25 +08:00
zhuoqinglian e1a7e50e1f update: 更新设备主题生成功能,添加新表情 2026-04-21 11:44:03 +08:00
DaGou12138 90888de0ba perf:
1、新增工具调用Action枚举RECORD,通过完整的三段式 assistant(tool_calls) → tool → assistant(response)让模型准确学习到工具调用的正确行为,优化play_music的工具调用枚举NONE变更为RECORD,优化解决音乐播放工具调用缺陷问题。
2、处理悬空 tool_calls问题。
3、去除工具提示词强化注入逻辑。
2026-04-21 11:36:06 +08:00
rainv123 ad4d32a05d feat: agent-models接口返回SLM模型,LLM类型同步返回所选SLM 2026-04-21 11:12:18 +08:00
hrzandGitHub 08d6bb4056 Merge pull request #3118 from xinnan-tech/openai_nothinking
feat:默认禁用部分平台LLM思考模式
2026-04-20 17:39:20 +08:00
FAN-yeB 1f0db7dc47 feat:默认禁用部分平台LLM思考模式 2026-04-20 17:35:32 +08:00
wengzhandGitHub 6f52d004e8 Merge pull request #3115 from xinnan-tech/fix-knowledge-sync-ragflow-documents
fix: 实现知识库与RAGFlow双向同步(知识库名称/简介的修改、知识库删除、文档上传/删除)
2026-04-20 16:51:06 +08:00
rainv123 0681550bab fix: 实现知识库与RAGFlow双向同步(知识库名称/简介的修改、知识库删除、文档上传/删除),移除冷却机制确保实时生效,修复文档影子表UNIQUE约束冲突 2026-04-20 16:49:24 +08:00
wengzhandGitHub 97b9c95c2d Merge pull request #3117 from xinnan-tech/py-fix-ReportTime
fix: 修复聊天记录上报排序错乱的问题
2026-04-20 15:56:49 +08:00
wujingquan 6111c3b5a0 Add 'configuring' translation for multiple languages in device configuration 2026-04-20 15:24:53 +08:00
wujingquan 123c1e8880 Add WiFi password translations for multiple languages 2026-04-20 15:17:04 +08:00
rainv123 6ce4503b22 fix:实现知识库文档与RAGFlow双向同步,支持远端上传/删除自动对账 2026-04-20 14:49:01 +08:00
3030332422 b209de36f5 fix: 修复聊天记录上报排序错乱的问题 2026-04-20 11:00:04 +08:00
wengzhandGitHub fc3e0869fd Merge pull request #3112 from xinnan-tech/py-fix-repeated-text
Py fix repeated text
2026-04-20 10:43:09 +08:00
Sakura-RanChen 3436d50b43 只有文本内容一致的时候不进行合成 2026-04-20 10:40:08 +08:00
Sakura-RanChenandGitHub 7a58c04df3 Merge pull request #3077 from xuruiray/fix/duplicate-tts-on-tool-call
fix: LLM 流式输出文本后调用工具导致 TTS 重复播报
2026-04-20 10:08:06 +08:00
Sakura-RanChenandGitHub 150be65436 Merge branch 'py-fix-repeated-text' into fix/duplicate-tts-on-tool-call 2026-04-20 10:07:28 +08:00
wengzhandGitHub bacf4cd5e8 Merge pull request #3110 from xinnan-tech/fix_rag_pagerank
fix: 修复更新知识库时 pagerank 为 null 导致 RAGFlow 校验失败的问题
2026-04-17 16:52:02 +08:00
rainv123 7049e8cce7 fix: 修复更新知识库时 pagerank 为 null 导致 RAGFlow 校验失败的问题 2026-04-17 16:41:42 +08:00
wengzhandGitHub 61f137ce4b Merge pull request #3108 from xinnan-tech/fix-role-plugin
fix: 修复智能体tooltip高度超长的问题
2026-04-17 11:25:14 +08:00
zhuoqinglian 35ea14b95b fix: 修复智能体tooltip高度超长的问题 2026-04-17 11:10:33 +08:00
wengzhandGitHub b7d07f92a2 Merge pull request #3107 from xinnan-tech/py-fix-tts-markdown
fix: 调整处理顺序保留英文空格的同时修复markdown被识别为纯ASCII 字符的错误返回
2026-04-17 11:02:53 +08:00
Sakura-RanChen d2cbec0981 fix: 调整处理顺序保留英文空格的同时修复markdown被识别为纯ASCII 字符的错误返回 2026-04-17 10:59:29 +08:00
wengzhandGitHub 6348f12d60 Merge pull request #3106 from xinnan-tech/fix-role-plugin
fix: 修复角色配置-功能管理列表没有滚动条的问题
2026-04-17 10:51:45 +08:00
zhuoqinglian 11caf6bc8a fix: 修复角色配置-功能管理列表没有滚动条的问题 2026-04-17 10:42:47 +08:00
wengzhandGitHub 3cae9e3c32 Merge pull request #3102 from xinnan-tech/fix-title-summary
fix:将标题总结从记忆流程中拆分为独立接口,支持异步执行
2026-04-16 17:24:56 +08:00
欣南科技andGitHub 0161e642d0 Merge pull request #3104 from xinnan-tech/update-default-model
Update default model
2026-04-16 17:01:34 +08:00
hrz d41938fe07 update:这些文件已经完成使命,可移除 2026-04-16 16:54:33 +08:00
hrz e20c59e3df update:移除阿里云即将下架的模型 2026-04-16 16:42:34 +08:00
rainv123 fbaf28de5c fix:移除多余try和总结标题的异步 2026-04-16 14:43:16 +08:00
rainv123 2daf492e00 fix:将标题总结从记忆流程中拆分为独立接口,支持异步执行 2026-04-16 10:36:48 +08:00
wengzhandGitHub ac9083cfc7 Merge pull request #3101 from xinnan-tech/fix-title-summary
fix:修复PowerMem和Mem0AI记忆模型不会总结会话标题的问题
2026-04-16 09:04:58 +08:00
rainv123 f36e605aed fix:修复PowerMem和Mem0AI记忆模型不会总结会话标题的问题 2026-04-15 15:26:52 +08:00
wengzhandGitHub c1b836aaed Merge pull request #3100 from xinnan-tech/update-checkbox-style
update: 修改checkbox背景颜色
2026-04-15 14:32:46 +08:00
zhuoqinglian 07170f09eb update: 修改checkbox背景颜色 2026-04-15 14:28:20 +08:00
hrzandGitHub 8211de242f Merge pull request #3098 from xinnan-tech/fix-title-summary
fix:修复mem_report_only记忆模型没有总结会话标题
2026-04-15 14:20:34 +08:00
欣南科技andGitHub eea6807355 Merge pull request #3099 from xinnan-tech/agent-base-prompt
update:优化提示词
2026-04-15 13:59:04 +08:00
hrz 0ba7e81438 update:优化提示词 2026-04-15 12:59:15 +08:00
rainv123 703c707b4c fix:修复mem_report_only记忆模型没有总结会话标题 2026-04-15 11:54:22 +08:00
hrzandGitHub 3ab14578c2 Merge pull request #3097 from xinnan-tech/py-fix-stream-asr
fix: 修复豆包流式asr热词层级错误
2026-04-15 10:33:24 +08:00
Sakura-RanChen a15efa3090 fix: 修复豆包流式asr热词层级错误 2026-04-15 10:31:16 +08:00
wengzhandGitHub 8af8930997 Merge pull request #3094 from xinnan-tech/py-client-about
refactor:优化相关打断逻辑
2026-04-14 15:31:29 +08:00
Sakura-RanChen ad01b05940 打断判断优先级调整 2026-04-14 15:05:55 +08:00
Sakura-RanChen a0497fdc48 给复用型TTS增加相关过滤 2026-04-14 10:18:09 +08:00
Sakura-RanChen 9634f84a98 Merge remote-tracking branch 'origin/main' into py-client-about 2026-04-13 11:35:10 +08:00
Sakura-RanChen 27e5e1e9d7 refactor: 优化非流式音频过滤机制
fix: 修复等待中被打断队列被停止造成的死锁
2026-04-13 11:33:47 +08:00
wengzhandGitHub 8fd92729f2 Merge pull request #3090 from xinnan-tech/slm-and-title
update:添加SLM模型配置和会话标题功能
2026-04-13 09:08:11 +08:00
wengzhandGitHub 736ffd80bf Merge pull request #3086 from xinnan-tech/update-chat-history
角色配置表单标题添加Tooltip鼠标悬浮提示说明,调整小参数模型表单配置
2026-04-13 09:07:50 +08:00
wengzhandGitHub 3783351f5d Merge pull request #3085 from xinnan-tech/update-mobile-chat-history
mobile 角色配置添加小参数模型选项,历史记录标题优化,聊天详情参数记录折叠处理
2026-04-13 09:07:31 +08:00
zhuoqinglian 2cd9484c54 update: mobile 聊天记录获取最新数据 2026-04-10 16:11:04 +08:00
Sakura-RanChen 0f1e0d0d2e 音频会在队列积压等待发送,等待协程结果返回不设置超时时间 2026-04-10 14:22:36 +08:00
Sakura-RanChen 0e23672be7 fix: 修复直接回复是字幕未记录 2026-04-09 17:58:19 +08:00
Sakura-RanChen a6904f6ebd refactor: vad独立时间戳,相关残余状态处理 2026-04-09 17:15:35 +08:00
zhuoqinglian 784ba359f2 update: mobile 历史记录标题优化,聊天详情参数记录折叠处理 2026-04-09 14:32:32 +08:00
Sakura-RanChen e08eb48d9a refactor: 优化相关打断处理 2026-04-09 10:46:46 +08:00
rainv123 b9a1b154b7 update:添加SLM模型配置和会话标题功能 2026-04-09 09:23:54 +08:00
zhuoqinglian 64630e1b4e feat: mobile 角色配置添加小参数模型选项 2026-04-08 16:26:48 +08:00
zhuoqinglian 7dcc014ce4 update: 调整小参数模型表单配置 2026-04-08 11:41:12 +08:00
Rui Xu 389e8559aa fix: prevent duplicate TTS when LLM streams text before tool call 2026-04-06 21:49:28 +08:00
wengzhandGitHub c5f82369e5 Merge pull request #3060 from xinnan-tech/py_test_end
Py test end
2026-04-03 17:31:56 +08:00
wengzhandGitHub 52e63706a4 Merge pull request #3058 from xinnan-tech/py_fix_powermem
fix: 优化配置读取
2026-04-03 14:37:14 +08:00
Sakura-RanChenandGitHub a42a8b2b4b Merge pull request #3069 from xinnan-tech/py_fix_wakeup
fix:停止音频发送循环(仅在流控器已初始化时调用)
2026-04-03 09:48:16 +08:00
baigao 11142104af fix:停止音频发送循环(仅在流控器已初始化时调用) 2026-04-03 09:46:55 +08:00
wengzhandGitHub a9f5f1ab81 Merge pull request #3067 from xinnan-tech/memory-model-rename
fix:从有总结记忆的模型切换至仅上报聊天记录时,只清空记忆总结内容
2026-04-02 11:56:33 +08:00
rainv123 a8f68c42fc fix:从有总结记忆的模型切换至仅上报聊天记录时,只清空记忆 2026-04-02 11:27:11 +08:00
zhuoqinglian 1d3afb3c9a feat: 角色配置表单标题添加Tooltip鼠标悬浮提示说明 2026-04-02 09:45:33 +08:00
wengzhandGitHub 943622969a Merge pull request #3063 from xinnan-tech/py-bug-fix
fix:修复intent_llm模式下工具不存在时未正确播放提示信息的问题
2026-04-01 17:46:23 +08:00
wengzhandGitHub 9d39168a35 Merge pull request #3061 from xinnan-tech/memory-model-rename
选择仅上报聊天记录时,清除历史记忆;前端优化记忆模型切换体验
2026-04-01 15:57:44 +08:00
Sakura-RanChenandGitHub 61ab7f25d7 Merge branch 'main' into py_test_end 2026-04-01 14:45:05 +08:00
Sakura-RanChen 140fcd5887 补充sql,增加错误详情上报 2026-04-01 14:32:47 +08:00
rainv123 3c2024d00d fix: 前端优化记忆模型切换体验 - 切换无记忆模型时暂存记忆、切换恢复、保存配置后才真正清除 2026-04-01 14:32:30 +08:00
rainv123 2aee1bf567 fix:选择仅上报聊天记录时,清除历史记忆 2026-04-01 11:35:26 +08:00
wengzhandGitHub d89a26889c Merge pull request #3059 from xinnan-tech/memory-model-rename
fix:重命名记忆模型名称
2026-04-01 10:23:38 +08:00
rainv123 d611b2db73 update:重命名记忆模型名称 2026-04-01 09:54:46 +08:00
Sakura-RanChenandGitHub b0e911b248 Merge pull request #2996 from lgy1027/fix-end
fix: 优化退出流程并添加超时保护
2026-04-01 09:35:34 +08:00
Sakura-RanChenandGitHub fb3b7830ee Merge branch 'py_test_end' into fix-end 2026-04-01 09:35:23 +08:00
3030332422 10eeffdf31 fix:修复intent_llm模式下工具不存在时未正确播放提示信息的问题 2026-04-01 09:23:05 +08:00
rainv123 17847188dd Merge branch 'main' of https://github.com/xinnan-tech/xiaozhi-esp32-server into new 2026-04-01 09:12:39 +08:00
Sakura-RanChen 7b2bfc087d fix: 优化配置读取 2026-03-31 18:00:51 +08:00
wengzhandGitHub 24a8839753 Merge pull request #3044 from xinnan-tech/py-display
添加“工具调用日志记录”与“设备端工具调用信息显示”
2026-03-31 15:12:11 +08:00
CGDandGitHub 64d25533d9 Merge branch 'main' into py-display 2026-03-31 14:24:27 +08:00
3030332422 6f6c788cd5 update:修改ai_agent_chat_history表content字段类型为TEXT 2026-03-31 14:11:31 +08:00
欣南科技andGitHub 8aa12d0961 Merge pull request #3057 from xinnan-tech/weather-faq
update:补齐天气文档链接
2026-03-31 12:11:36 +08:00
hrz d770632152 udpate:补齐天气文档链接 2026-03-31 12:08:30 +08:00
wengzhandGitHub a30903b644 Merge pull request #3056 from xinnan-tech/update-manager-mobile
Update manager mobile
2026-03-31 11:02:40 +08:00
wengzhandGitHub 5469168d18 Merge pull request #3055 from xinnan-tech/fix-digital-human
fix: 修复数字人摄像头展示区域裁剪的问题
2026-03-31 11:02:21 +08:00
zhuoqinglian 3c31231781 fix: 修复数字人摄像头展示区域裁剪的问题 2026-03-31 10:34:38 +08:00
3030332422 cc2e30205f fix:修复play_music工具调用信息显示异常 2026-03-31 10:03:50 +08:00
3030332422 f01de027cd fix:修复 intent_llm 模式下工具调用不上报的问题 2026-03-30 16:36:30 +08:00
zhuoqinglian fb195fc57b update: mobile 切换记忆模式判断是否需要禁用历史记忆内容输入 2026-03-26 17:25:44 +08:00
zhuoqinglian cc504fe542 update: mobile 添加智能体限制调整 2026-03-26 14:21:03 +08:00
wengzhandGitHub fd93116d69 Merge pull request #3045 from xinnan-tech/fix_audioRateController
fix: 修复流式场景下的误判
2026-03-25 11:10:42 +08:00
Sakura-RanChen 5622a13735 fix: 修复流式场景下的误判 2026-03-25 11:07:43 +08:00
3030332422 079c8630d9 update:修改部分前端样式 2026-03-25 10:43:45 +08:00
wengzhandGitHub 11830b2f22 Merge pull request #3043 from xinnan-tech/update-manager-mobile
mobile 优化聊天记录音频播放功能
2026-03-25 09:34:29 +08:00
wengzhandGitHub 861b4dee4c Merge pull request #3042 from xinnan-tech/update-web-style
优化页面主体内容区域适配
2026-03-25 09:34:06 +08:00
zhuoqinglian 603fa67824 Merge branch 'main' into update-web-style 2026-03-25 09:27:30 +08:00
zhuoqinglian 4d8279d916 update: 优化页面主体内容区域适配 2026-03-25 09:26:33 +08:00
rainv123 f91c4acb8f Merge branch 'main' of https://github.com/xinnan-tech/xiaozhi-esp32-server into new 2026-03-24 11:12:32 +08:00
rainv123andGitHub 1342eccf86 Merge pull request #3039 from xinnan-tech/revert-3038-agent-chat-history-audio-retention
Revert "fix:修复智能体记忆模型配置变更时错误删除历史聊天音频的问题"
2026-03-24 11:08:44 +08:00
wengzhandGitHub 87f9ab7bb4 Revert "fix:修复智能体记忆模型配置变更时错误删除历史聊天音频的问题" 2026-03-24 11:05:35 +08:00
wengzhandGitHub 94cc8564aa Merge pull request #3038 from xinnan-tech/agent-chat-history-audio-retention
fix:修复智能体记忆模型配置变更时错误删除历史聊天音频的问题
2026-03-24 10:53:51 +08:00
rainv123 8fc2d05c99 Merge branch 'main' of https://github.com/xinnan-tech/xiaozhi-esp32-server into new 2026-03-24 09:51:14 +08:00
rainv123 1a6d9d7490 fix: 修复智能体配置变更时错误删除历史音频的问题 2026-03-24 09:49:57 +08:00
wengzhandGitHub 6c19f1bd26 Merge pull request #3035 from xinnan-tech/mem_report_only
fix:新增仅上报聊天记录的记忆模型供应器Mem report only
2026-03-24 09:01:31 +08:00
rainv123 6d89ae1c66 fix:增加仅上报模型说明和记忆模型排序 2026-03-23 16:58:51 +08:00
rainv123 2a49f0804c feat:新增仅上报聊天记录(mem_report_only)记忆供应器 2026-03-23 16:17:38 +08:00
zhuoqinglian d64bb36606 update: mobile 优化聊天记录音频播放功能 2026-03-23 14:18:53 +08:00
zhuoqinglian 852e097e0d fix: mobile 修复声纹管理-编辑模式下声纹向量展示异常的问题 2026-03-20 16:57:02 +08:00
zhuoqinglian 3aecb99f70 fix: 修复聊天记录播放音频重叠播放的问题 2026-03-20 16:21:58 +08:00
wengzhandGitHub 6eecf5d0cb Merge pull request #3026 from xinnan-tech/fix_audioRateController_state
fix: 修复队列恢复时音频播放时间错乱问题
2026-03-20 14:49:42 +08:00
wengzhandGitHub a4d02ca2ab Merge pull request #3029 from xinnan-tech/update-web-style
解决登录失败验证码刷新两次的问题
2026-03-20 14:48:45 +08:00
wengzhandGitHub 7ef5a67119 Merge pull request #3028 from xinnan-tech/update-manager-mobile
mobile 智能体管理多语言调整、缓存用户信息
2026-03-20 14:48:26 +08:00
zhuoqinglian 1c5dacd2d5 Merge branch 'main' into update-web-style 2026-03-20 14:45:50 +08:00
zhuoqinglian 7732b7b1b9 update: mobile 缓存用户信息 2026-03-20 14:42:47 +08:00
zhuoqinglian 6e7b938f51 fix: 解决登录失败验证码刷新两次的问题 2026-03-20 10:42:35 +08:00
zhuoqinglian 52f5859b1f update: mobile 智能体管理多语言调整 2026-03-19 17:56:27 +08:00
Sakura-RanChen 73d174bc4a fix: 修复队列恢复时音频播放时间错乱问题 2026-03-19 16:18:30 +08:00
Sakura-RanChen 9512fe642c 恢复多工具 2026-03-19 16:04:14 +08:00
Sakura-RanChen 0ef3a60a4c 调整缩进 2026-03-19 11:57:40 +08:00
Sakura-RanChen f40dc4a658 修复双重回复问题 2026-03-19 11:56:29 +08:00
wengzhandGitHub 4747e54f6c Merge pull request #3023 from xinnan-tech/update-manager-mobile
mobile 播放声纹弹窗样式调整,切换角色模板更新表单状态
2026-03-18 16:53:54 +08:00
wengzhandGitHub 9278fdb016 Merge pull request #3024 from xinnan-tech/update-web-style
解决MCP接入点输入框没有显示报错信息的问题
2026-03-18 16:53:14 +08:00
zhuoqinglian 5b23804954 Merge branch 'main' into update-manager-mobile 2026-03-18 16:50:29 +08:00
zhuoqinglian b2ed5eb2c0 fix: 解决MCP接入点输入框没有显示报错信息的问题 2026-03-18 16:47:48 +08:00
zhuoqinglian 07113dc4d2 update: mobile 播放声纹弹窗样式调整,切换角色模板更新表单状态 2026-03-18 16:44:27 +08:00
Sakura-RanChen 1a5dd2d9d9 fix: 修复日志音频上报 2026-03-18 16:40:32 +08:00
wengzhandGitHub 8a01432e9d Merge pull request #3022 from xinnan-tech/perf-userLanguage-complete
perf:补充MCP接入点国际化信息
2026-03-18 15:48:57 +08:00
DaGou12138 7a39fd0152 补充MCP接入点国际化信息 2026-03-18 09:48:11 +08:00
Sakura-RanChen 0d2cb7b3c9 增加工具调用日志记录,暂时单工具状态 2026-03-17 16:13:35 +08:00
wengzhandGitHub 9123a174da Merge pull request #3020 from xinnan-tech/update-web-style
优化表格底部按钮边距统一调整,修复刷新页面菜单、页脚闪动的问题
2026-03-17 16:11:54 +08:00
zhuoqinglian 2e165747d6 update: 参数弹窗去掉多余代码 2026-03-17 16:08:36 +08:00
zhuoqinglian 3bdf634e37 Merge branch 'main' into update-web-style 2026-03-17 16:04:22 +08:00
zhuoqinglian 20ac6f3f96 fix: 修复刷新页面菜单、页脚闪动的问题 2026-03-17 16:01:10 +08:00
zhuoqinglian 818ac1eefb style: 表格底部按钮边距统一调整 2026-03-17 10:30:43 +08:00
3030332422 980930cd42 update:在设备端显示工具调用信息 2026-03-16 13:11:45 +08:00
wengzhandGitHub 956ae37e56 Merge pull request #3015 from xinnan-tech/fix/update-sherpa-onnx
update:更新sherpa_onnx依赖至1.12.29版本
2026-03-16 11:51:01 +08:00
rainv123 15731892ad update:更新sherpa_onnx依赖至1.12.29版本 2026-03-16 11:43:07 +08:00
zhuoqinglian d6d50e0432 fix: 修复语言切换接口请求异常的问题 2026-03-16 10:37:47 +08:00
zhuoqinglian 286079c93a feat: 添加声纹管理-声纹向量音频播放功能 2026-03-16 09:50:24 +08:00
zhuoqinglian 860cbc36fd fix: 处理智能体管理全局触发下拉刷新问题 2026-03-16 09:46:43 +08:00
lgy1027 bcf03a0378 refactor: 优化超时配置,响应社区反馈
- 工具调用超时改为可配置项 tool_call_timeout,默认30秒
- OpenAI 超时恢复可配置机制,支持细粒度配置和单一值
- TTS 超时保护统一添加到所有流式TTS实现
- 将 ActionResponse 导入移到文件顶部
- 修复超时配置边界情况处理
2026-03-11 18:13:05 +08:00
lgy1027 4f615b3807 Merge remote-tracking branch 'origin/main' into fix-end 2026-03-11 17:15:59 +08:00
lgy1027 c2b95ca896 fix: 优化退出流程并添加超时保护
- 添加 is_exiting 标志防止退出流程被中断
- 工具调用添加 30 秒超时保护,避免流程卡死
- TTS 相关操作添加超时保护
- 优化 OpenAI 客户端超时配置
2026-03-10 11:45:38 +08:00
307 changed files with 7467 additions and 5680 deletions
+1
View File
@@ -149,6 +149,7 @@ tmp
.history
.DS_Store
main/xiaozhi-server/data
main/xiaozhi-server/.claude
main/manager-web/node_modules
.config.yaml
+2 -2
View File
@@ -214,8 +214,8 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/
|:---:|:---:|:---:|
| ASR(语音识别) | FunASR(本地) | 👍XunfeiStreamASR(讯飞流式) |
| LLM(大模型) | glm-4-flash(智谱) | 👍qwen-flash(阿里百炼) |
| VLLM(视觉大模型) | glm-4v-flash(智谱) | 👍qwen2.5-vl-3b-instructh(阿里百炼) |
| TTS(语音合成) | ✅LinkeraiTTS(灵犀流式) | 👍HuoshanDoubleStreamTTS(火山流式) |
| VLLM(视觉大模型) | glm-4v-flash(智谱) | 👍qwen3.5-flash(阿里百炼) |
| TTS(语音合成) | EdgeTTS(微软) | 👍HuoshanDoubleStreamTTS(火山流式) |
| Intent(意图识别) | function_call(函数调用) | function_call(函数调用) |
| Memory(记忆功能) | mem_local_short(本地短期记忆) | mem_local_short(本地短期记忆) |
+2 -2
View File
@@ -212,8 +212,8 @@ Websocket-Schnittstellenadresse: wss://2662r3426b.vicp.fun/xiaozhi/v1/
|:---:|:---:|:---:|
| ASR (Spracherkennung) | FunASR (Lokal) | 👍XunfeiStreamASR (Xunfei-Streaming) |
| LLM (Großes Modell) | glm-4-flash (Zhipu) | 👍qwen-flash (Alibaba Bailian) |
| VLLM (Vision Large Model) | glm-4v-flash (Zhipu) | 👍qwen2.5-vl-3b-instructh (Alibaba Bailian) |
| TTS (Sprachsynthese) | ✅LinkeraiTTS (Lingxi-Streaming) | 👍HuoshanDoubleStreamTTS (Volcano-Streaming) |
| VLLM (Vision Large Model) | glm-4v-flash (Zhipu) | 👍qwen3.5-flash (Alibaba Bailian) |
| TTS (Sprachsynthese) | EdgeTTS (Microsoft) | 👍HuoshanDoubleStreamTTS (Volcano-Streaming) |
| Intent (Absichtserkennung) | function_call (Funktionsaufruf) | function_call (Funktionsaufruf) |
| Memory (Gedächtnisfunktion) | mem_local_short (Lokales Kurzzeitgedächtnis) | mem_local_short (Lokales Kurzzeitgedächtnis) |
+2 -2
View File
@@ -212,8 +212,8 @@ Websocket Interface Address: wss://2662r3426b.vicp.fun/xiaozhi/v1/
|:---:|:---:|:---:|
| ASR(Speech Recognition) | FunASR(Local) | 👍XunfeiStreamASR(Xunfei Streaming) |
| LLM(Large Model) | glm-4-flash(Zhipu) | 👍qwen-flash(Alibaba Bailian) |
| VLLM(Vision Large Model) | glm-4v-flash(Zhipu) | 👍qwen2.5-vl-3b-instructh(Alibaba Bailian) |
| TTS(Speech Synthesis) | ✅LinkeraiTTS(Lingxi streaming) | 👍HuoshanDoubleStreamTTS(Volcano Streaming) |
| VLLM(Vision Large Model) | glm-4v-flash(Zhipu) | 👍qwen3.5-flash(Alibaba Bailian) |
| TTS(Speech Synthesis) | EdgeTTS(Microsoft) | 👍HuoshanDoubleStreamTTS(Volcano Streaming) |
| Intent(Intent Recognition) | function_call(Function calling) | function_call(Function calling) |
| Memory(Memory function) | mem_local_short(Local short-term memory) | mem_local_short(Local short-term memory) |
+2 -2
View File
@@ -212,8 +212,8 @@ Endereço da Interface WebSocket: wss://2662r3426b.vicp.fun/xiaozhi/v1/
|:---:|:---:|:---:|
| ASR(Reconhecimento de Fala) | FunASR(Local) | 👍XunfeiStreamASR(Xunfei Streaming) |
| LLM(Modelo de Linguagem) | glm-4-flash(Zhipu) | 👍qwen-flash(Alibaba Bailian) |
| VLLM(Modelo de Visão) | glm-4v-flash(Zhipu) | 👍qwen2.5-vl-3b-instructh(Alibaba Bailian) |
| TTS(Síntese de Voz) | ✅LinkeraiTTS(Lingxi streaming) | 👍HuoshanDoubleStreamTTS(Volcano Streaming) |
| VLLM(Modelo de Visão) | glm-4v-flash(Zhipu) | 👍qwen3.5-flash(Alibaba Bailian) |
| TTS(Síntese de Voz) | EdgeTTS(Microsoft) | 👍HuoshanDoubleStreamTTS(Volcano Streaming) |
| Intent(Reconhecimento de Intenção) | function_call(Chamada de função) | function_call(Chamada de função) |
| Memory(Função de Memória) | mem_local_short(Memória local de curto prazo) | mem_local_short(Memória local de curto prazo) |
+2 -2
View File
@@ -213,8 +213,8 @@ Công cụ kiểm tra dịch vụ: https://2662r3426b.vicp.fun/test/
|:---:|:---:|:---:|
| ASR(Nhận dạng giọng nói) | FunASR(Local) | 👍XunfeiStreamASR(Xunfei Streaming) |
| LLM(Mô hình lớn) | glm-4-flash(Zhipu) | 👍qwen-flash(Alibaba Bailian) |
| VLLM(Mô hình lớn thị giác) | glm-4v-flash(Zhipu) | 👍qwen2.5-vl-3b-instructh(Alibaba Bailian) |
| TTS(Tổng hợp giọng nói) | ✅LinkeraiTTS(Lingxi streaming) | 👍HuoshanDoubleStreamTTS(Volcano Streaming) |
| VLLM(Mô hình lớn thị giác) | glm-4v-flash(Zhipu) | 👍qwen3.5-flash(Alibaba Bailian) |
| TTS(Tổng hợp giọng nói) | EdgeTTS(Microsoft) | 👍HuoshanDoubleStreamTTS(Volcano Streaming) |
| Intent(Nhận dạng ý định) | function_call(Gọi hàm) | function_call(Gọi hàm) |
| Memory(Chức năng bộ nhớ) | mem_local_short(Bộ nhớ ngắn hạn cục bộ) | mem_local_short(Bộ nhớ ngắn hạn cục bộ) |
+1 -1
View File
@@ -418,7 +418,7 @@ manager-api:
secret: 12345678-xxxx-xxxx-xxxx-123456789000
```
## 5.运行项目
## 9.运行项目
```
# 确保在xiaozhi-server目录下执行
+3 -2
View File
@@ -40,8 +40,8 @@ conda install conda-forge::ffmpeg
|:---:|:---:|:---:|
| ASR(语音识别) | FunASR(本地) | 👍XunfeiStreamASR(讯飞流式) |
| LLM(大模型) | glm-4-flash(智谱) | 👍qwen-flash(阿里百炼) |
| VLLM(视觉大模型) | glm-4v-flash(智谱) | 👍qwen2.5-vl-3b-instructh(阿里百炼) |
| TTS(语音合成) | ✅LinkeraiTTS(灵犀流式) | 👍HuoshanDoubleStreamTTS(火山流式) |
| VLLM(视觉大模型) | glm-4v-flash(智谱) | 👍qwen3.5-flash(阿里百炼) |
| TTS(语音合成) | EdgeTTS(微软) | 👍HuoshanDoubleStreamTTS(火山流式) |
| Intent(意图识别) | function_call(函数调用) | function_call(函数调用) |
| Memory(记忆功能) | mem_local_short(本地短期记忆) | mem_local_short(本地短期记忆) |
@@ -83,6 +83,7 @@ VAD:
9、[知识库ragflow集成指南](./ragflow-integration.md)<br/>
10、[如何部署上下文源](./context-provider-integration.md)<br/>
11、[如何集成PowerMem智能记忆](./powermem-integration.md)<br/>
12、[如何配置天气插件查询天气](./weather-integration.md)<br/>
### 11、语音克隆、本地语音部署相关教程
1、[如何在智控台克隆音色](./huoshan-streamTTS-voice-cloning.md)<br/>
@@ -1,45 +0,0 @@
# 知识库模块全量集成测试报告
## 1. 测试背景
针对 `KnowledgeBaseController``KnowledgeFilesController` 共 14 个接口进行了深度集成测试。主要解决了本地影子库与 RAGFlow 远程服务之间的状态对齐、数据反序列化兼容性以及批量操作逻辑安全性问题。
## 2. 修复的核心 Bug 清单 (Hotfixes)
| 模块 | 问题类型 | 修复方案 | 验证结果 |
| :--- | :--- | :--- | :--- |
| **DTO** | `positions` 反序列化失败 | 类型从 `List<Integer>` 提升为 `Object`,支持嵌套数组 | ✅ 已验证 |
| **DTO** | 日期格式不兼容 | 针对 RAGFlow 的 RFC 1123 格式,将 `Date` 改为 `String` 透传 | ✅ 已验证 |
| **请求** | 检索参数 `null` 拒绝 | 增加 `@JsonInclude(NON_NULL)`,跳过可选字段的空值序列化 | ✅ 已验证 |
| **同步** | 状态自愈死锁 | 增加 `CANCEL/FAIL` 状态的 60s 低频同步机制,防止逻辑错误锁定 | ✅ 已验证 |
| **逻辑** | 删除守卫逻辑错误 | 将拦截条件从 `status="1"` 修正为 `run="RUNNING"` | ✅ 已验证 |
## 3. 全量接口测试统计
### KnowledgeBaseController (7/7)
- [x] 分页查询 (`GET /datasets`)
- [x] 详情获取 (`GET /datasets/{id}`)
- [x] 创建知识库 (`POST /datasets`)
- [x] 修改配置 (`PUT /datasets/{id}`)
- [x] 物理删除 (`DELETE /datasets/{id}`)
- [x] 批量删除 (`DELETE /datasets/batch`)
- [x] 模型列表获取 (`GET /datasets/rag-models`)
### KnowledgeFilesController (7/7)
- [x] 文档列表与同步 (`GET /datasets/{id}/documents`)
- [x] 状态过滤查询 (`GET /datasets/{id}/documents/status/{s}`)
- [x] 文档上传 (`POST /datasets/{id}/documents`)
- [x] 触发解析 (`POST /datasets/{id}/chunks`)
- [x] 切片详情 (`GET /datasets/{id}/documents/{docId}/chunks`)
- [x] 召回测试 (`POST /datasets/{id}/retrieval-test`)
- [x] 批量删除文档 (`DELETE /datasets/{id}/documents`)
## 4. 自动化审计结论
通过执行 `comprehensive_audit.ps1` 自动化脚本,模拟了“创建->上传->解析->同步->检索->删除”的完整生产链路。
- **解析成功率**100%
- **数据准确性**:DTO 转换无异常,坐标及得分提取正常
- **系统安全性**:解析中拦截机制生效
- **结论****准生产就绪 (Production Ready)**
---
*报告生成时间:2026-02-13*
*审核:dora--1206563805@qq.com*
@@ -95,6 +95,14 @@ public class SwaggerConfig {
.build();
}
@Bean
public GroupedOpenApi correctWordApi() {
return GroupedOpenApi.builder()
.group("correct-word")
.pathsToMatch("/correct-word/**")
.build();
}
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI().info(new Info()
@@ -151,6 +151,21 @@ public interface Constant {
*/
String MEMORY_NO_MEM = "Memory_nomem";
/**
* 仅上报聊天记录(不总结记忆)
*/
String MEMORY_MEM_REPORT_ONLY = "Memory_mem_report_only";
/**
* Mem0AI记忆
*/
String MEMORY_MEM0AI = "Memory_mem0ai";
/**
* PowerMem记忆
*/
String MEMORY_POWERMEM = "Memory_powermem";
/**
* 火山引擎双声道语音克隆
*/
@@ -304,7 +319,7 @@ public interface Constant {
/**
* 版本号
*/
public static final String VERSION = "0.9.2";
public static final String VERSION = "0.9.3";
/**
* 无效固件URL
@@ -251,4 +251,13 @@ public interface ErrorCode {
int AGENT_TAG_NOT_EXIST = 10198; // 标签不存在
int RAG_DOCUMENT_PARSING_DELETE_ERROR = 10199; // 文档解析中,禁止删除
// 智能体MCP相关错误码
int MCP_ACCESS_POINT_ADDRESS_NO_PERMISSION = 10200; // 没有权限查看该智能体的MCP接入点地址
int MCP_ACCESS_POINT_ADDRESS_NOT_CONFIGURED = 10201; // 请联系管理员进入参数管理配置mcp接入点地址
int MCP_ACCESS_POINT_TOOLS_LIST_NO_PERMISSION = 10202; // 没有权限查看该智能体的MCP工具列表
// 替换词相关错误码
int CORRECT_WORD_FILE_NAME_EXISTS = 10203; // 文件名已存在
int FILE_SIZE_OVER_LIMIT = 10204; // 文件大小超过限制
}
@@ -187,4 +187,5 @@ public class RedisKeys {
public static String getOtaUploadCountKey(Long username) {
return "ota:upload:count:" + username;
}
}
@@ -52,6 +52,7 @@ import xiaozhi.modules.agent.service.AgentContextProviderService;
import xiaozhi.modules.agent.service.AgentPluginMappingService;
import xiaozhi.modules.agent.service.AgentService;
import xiaozhi.modules.agent.service.AgentTemplateService;
import xiaozhi.modules.correctword.service.CorrectWordFileService;
import xiaozhi.modules.agent.vo.AgentChatHistoryUserVO;
import xiaozhi.modules.agent.vo.AgentInfoVO;
import xiaozhi.modules.device.entity.DeviceEntity;
@@ -73,6 +74,7 @@ public class AgentController {
private final AgentChatSummaryService agentChatSummaryService;
private final RedisUtils redisUtils;
private final AgentTagService agentTagService;
private final CorrectWordFileService correctWordFileService;
@GetMapping("/list")
@Operation(summary = "获取用户智能体列表")
@@ -150,6 +152,13 @@ public class AgentController {
}
}
@PostMapping("/chat-title/{sessionId}/generate")
@Operation(summary = "根据会话ID生成聊天标题")
public Result<Void> generateAndSaveChatTitle(@PathVariable String sessionId) {
agentChatSummaryService.generateAndSaveChatTitle(sessionId);
return new Result<Void>().ok(null);
}
@PutMapping("/{id}")
@Operation(summary = "更新智能体")
@RequiresPermissions("sys:role:normal")
@@ -170,6 +179,8 @@ public class AgentController {
agentPluginMappingService.deleteByAgentId(id);
// 删除关联的上下文源配置
agentContextProviderService.deleteByAgentId(id);
// 删除关联的替换词文件关联记录
correctWordFileService.deleteMappingsByAgentId(id);
// 再删除智能体
agentService.deleteById(id);
return new Result<>();
@@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import xiaozhi.common.exception.ErrorCode;
import xiaozhi.common.user.UserDetail;
import xiaozhi.common.utils.Result;
import xiaozhi.modules.agent.service.AgentMcpAccessPointService;
@@ -40,11 +41,11 @@ public class AgentMcpAccessPointController {
// 检查权限
if (!agentService.checkAgentPermission(agentId, user.getId())) {
return new Result<String>().error("没有权限查看该智能体的MCP接入点地址");
return new Result<String>().error(ErrorCode.MCP_ACCESS_POINT_ADDRESS_NO_PERMISSION);
}
String agentMcpAccessAddress = agentMcpAccessPointService.getAgentMcpAccessAddress(agentId);
if (agentMcpAccessAddress == null) {
return new Result<String>().ok("请联系管理员进入参数管理配置mcp接入点地址");
return new Result<String>().error(ErrorCode.MCP_ACCESS_POINT_ADDRESS_NOT_CONFIGURED);
}
return new Result<String>().ok(agentMcpAccessAddress);
}
@@ -58,7 +59,7 @@ public class AgentMcpAccessPointController {
// 检查权限
if (!agentService.checkAgentPermission(agentId, user.getId())) {
return new Result<List<String>>().error("没有权限查看该智能体的MCP工具列表");
return new Result<List<String>>().error(ErrorCode.MCP_ACCESS_POINT_TOOLS_LIST_NO_PERMISSION);
}
List<String> agentMcpToolsList = agentMcpAccessPointService.getAgentMcpToolsList(agentId);
return new Result<List<String>>().ok(agentMcpToolsList);
@@ -0,0 +1,12 @@
package xiaozhi.modules.agent.dao;
import org.apache.ibatis.annotations.Mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import xiaozhi.modules.agent.entity.AgentChatTitleEntity;
@Mapper
public interface AgentChatTitleDao extends BaseMapper<AgentChatTitleEntity> {
}
@@ -0,0 +1,21 @@
package xiaozhi.modules.agent.dao;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import xiaozhi.common.dao.BaseDao;
import xiaozhi.modules.agent.entity.AgentCorrectWordMappingEntity;
@Mapper
public interface AgentCorrectWordMappingDao extends BaseDao<AgentCorrectWordMappingEntity> {
int deleteByAgentId(@Param("agentId") String agentId);
int deleteByFileId(@Param("fileId") String fileId);
int batchInsertMapping(@Param("list") List<AgentCorrectWordMappingEntity> mappings);
List<AgentCorrectWordMappingEntity> selectByAgentId(@Param("agentId") String agentId);
}
@@ -23,4 +23,9 @@ public class AgentChatSessionDTO {
* 聊天条数
*/
private Integer chatCount;
/**
* 会话标题
*/
private String title;
}
@@ -33,6 +33,9 @@ public class AgentUpdateDTO implements Serializable {
@Schema(description = "大语言模型标识", example = "llm_model_02", nullable = true)
private String llmModelId;
@Schema(description = "小模型标识", example = "slm_model_02", nullable = true)
private String slmModelId;
@Schema(description = "VLLM模型标识", example = "vllm_model_02", required = false)
private String vllmModelId;
@@ -85,6 +88,9 @@ public class AgentUpdateDTO implements Serializable {
@Schema(description = "上下文源配置", nullable = true)
private List<ContextProviderDTO> contextProviders;
@Schema(description = "替换词文件ID列表", nullable = true)
private List<String> correctWordFileIds;
@Data
@Schema(description = "插件函数信息")
public static class FunctionInfo implements Serializable {
@@ -0,0 +1,36 @@
package xiaozhi.modules.agent.entity;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "ai_agent_chat_title")
public class AgentChatTitleEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
@TableField(value = "session_id")
private String sessionId;
@TableField(value = "title")
private String title;
@TableField(value = "created_at")
private Date createdAt;
@TableField(value = "updated_at")
private Date updatedAt;
}
@@ -0,0 +1,38 @@
package xiaozhi.modules.agent.entity;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@TableName("ai_agent_correct_word_mapping")
@Schema(description = "智能体替换词文件关联")
public class AgentCorrectWordMappingEntity {
@TableId(type = IdType.ASSIGN_UUID)
@Schema(description = "主键")
private String id;
@Schema(description = "智能体ID")
private String agentId;
@Schema(description = "替换词文件ID")
private String fileId;
@Schema(description = "创建者")
private Long creator;
@Schema(description = "创建时间")
private Date createdAt;
@Schema(description = "更新者")
private Long updater;
@Schema(description = "更新时间")
private Date updatedAt;
}
@@ -37,6 +37,9 @@ public class AgentEntity {
@Schema(description = "大语言模型标识")
private String llmModelId;
@Schema(description = "小模型标识")
private String slmModelId;
@Schema(description = "VLLM模型标识")
private String vllmModelId;
@@ -12,4 +12,12 @@ public interface AgentChatSummaryService {
* @return 保存结果
*/
boolean generateAndSaveChatSummary(String sessionId);
/**
* 根据会话ID生成聊天标题并保存
*
* @param sessionId 会话ID
* @return 是否成功
*/
boolean generateAndSaveChatTitle(String sessionId);
}
@@ -0,0 +1,10 @@
package xiaozhi.modules.agent.service;
import xiaozhi.modules.agent.entity.AgentChatTitleEntity;
public interface AgentChatTitleService {
void saveOrUpdateTitle(String sessionId, String title);
String getTitleBySessionId(String sessionId);
}
@@ -52,7 +52,7 @@ public class AgentChatHistoryBizServiceImpl implements AgentChatHistoryBizServic
public Boolean report(AgentChatHistoryReportDTO report) {
String macAddress = report.getMacAddress();
Byte chatType = report.getChatType();
Long reportTimeMillis = null != report.getReportTime() ? report.getReportTime() * 1000
Long reportTimeMillis = null != report.getReportTime() ? report.getReportTime()
: System.currentTimeMillis();
log.info("小智设备聊天上报请求: macAddress={}, type={} reportTime={}", macAddress, chatType, reportTimeMillis);
@@ -6,6 +6,7 @@ import java.util.Map;
import java.util.stream.Collectors;
import cn.hutool.core.collection.ListUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -26,6 +27,7 @@ import xiaozhi.modules.agent.dto.AgentChatHistoryDTO;
import xiaozhi.modules.agent.dto.AgentChatSessionDTO;
import xiaozhi.modules.agent.entity.AgentChatHistoryEntity;
import xiaozhi.modules.agent.service.AgentChatHistoryService;
import xiaozhi.modules.agent.service.AgentChatTitleService;
import xiaozhi.modules.agent.vo.AgentChatHistoryUserVO;
/**
@@ -36,9 +38,12 @@ import xiaozhi.modules.agent.vo.AgentChatHistoryUserVO;
* @since 1.0.0
*/
@Service
@RequiredArgsConstructor
public class AgentChatHistoryServiceImpl extends ServiceImpl<AiAgentChatHistoryDao, AgentChatHistoryEntity>
implements AgentChatHistoryService {
private final AgentChatTitleService agentChatTitleService;
@Override
public PageData<AgentChatSessionDTO> getSessionListByAgentId(Map<String, Object> params) {
String agentId = (String) params.get("agentId");
@@ -61,6 +66,7 @@ public class AgentChatHistoryServiceImpl extends ServiceImpl<AiAgentChatHistoryD
dto.setSessionId((String) map.get("session_id"));
dto.setCreatedAt((LocalDateTime) map.get("created_at"));
dto.setChatCount(((Number) map.get("chat_count")).intValue());
dto.setTitle(agentChatTitleService.getTitleBySessionId(dto.getSessionId()));
return dto;
}).collect(Collectors.toList());
@@ -91,7 +97,7 @@ public class AgentChatHistoryServiceImpl extends ServiceImpl<AiAgentChatHistoryD
if (ToolUtil.isNotEmpty(audioIds)) {
// 每批删除1000条
List<List<String>> batch = ListUtil.split(audioIds, 1000);
batch.forEach(dataList->{
batch.forEach(dataList -> {
baseMapper.deleteAudioByIds(dataList);
});
}
@@ -14,6 +14,7 @@ import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.RequiredArgsConstructor;
import xiaozhi.common.constant.Constant;
import xiaozhi.modules.agent.dto.AgentChatHistoryDTO;
import xiaozhi.modules.agent.dto.AgentChatSummaryDTO;
import xiaozhi.modules.agent.dto.AgentMemoryDTO;
@@ -21,6 +22,7 @@ import xiaozhi.modules.agent.dto.AgentUpdateDTO;
import xiaozhi.modules.agent.entity.AgentChatHistoryEntity;
import xiaozhi.modules.agent.service.AgentChatHistoryService;
import xiaozhi.modules.agent.service.AgentChatSummaryService;
import xiaozhi.modules.agent.service.AgentChatTitleService;
import xiaozhi.modules.agent.service.AgentService;
import xiaozhi.modules.agent.vo.AgentInfoVO;
import xiaozhi.modules.device.entity.DeviceEntity;
@@ -41,6 +43,7 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService {
private final AgentChatHistoryService agentChatHistoryService;
private final AgentService agentService;
private final AgentChatTitleService agentChatTitleService;
private final DeviceService deviceService;
private final LLMService llmService;
private final ModelConfigService modelConfigService;
@@ -90,33 +93,40 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService {
@Override
public boolean generateAndSaveChatSummary(String sessionId) {
try {
// 1. 生成总结
AgentChatSummaryDTO summaryDTO = generateChatSummary(sessionId);
if (!summaryDTO.isSuccess()) {
log.info("生成总结失败: {}", summaryDTO.getErrorMessage());
return false;
}
// 2. 获取设备信息(通过会话关联的设备)
DeviceEntity device = getDeviceBySessionId(sessionId);
if (device == null) {
log.info("未找到与会话 {} 关联的设备", sessionId);
return false;
}
// 3. 更新智能体记忆
AgentMemoryDTO memoryDTO = new AgentMemoryDTO();
memoryDTO.setSummaryMemory(summaryDTO.getSummary());
String agentId = device.getAgentId();
String memModelId = agentService.getAgentById(agentId).getMemModelId();
// 调用现有接口更新记忆
agentService.updateAgentById(device.getAgentId(),
new AgentUpdateDTO() {
if (memModelId == null || memModelId.equals(Constant.MEMORY_MEM_REPORT_ONLY)) {
log.info("会话 {} 使用仅上报聊天记录模式,跳过记忆总结", sessionId);
return true;
}
boolean shouldSummarizeMemory = !memModelId.equals(Constant.MEMORY_NO_MEM)
&& !memModelId.equals(Constant.MEMORY_MEM0AI)
&& !memModelId.equals(Constant.MEMORY_POWERMEM);
if (shouldSummarizeMemory) {
AgentChatSummaryDTO summaryDTO = generateChatSummary(sessionId);
if (summaryDTO.isSuccess()) {
agentService.updateAgentById(agentId, new AgentUpdateDTO() {
{
setSummaryMemory(summaryDTO.getSummary());
}
});
log.info("成功保存会话 {} 的聊天记录总结到智能体 {}", sessionId, agentId);
} else {
log.info("生成总结失败: {}", summaryDTO.getErrorMessage());
}
} else {
log.info("会话 {} 使用 {} 模式,跳过记忆总结", sessionId, memModelId);
}
log.info("成功保存会话 {} 的聊天记录总结到智能体 {}", sessionId, device.getAgentId());
return true;
} catch (Exception e) {
@@ -125,6 +135,98 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService {
}
}
@Override
public boolean generateAndSaveChatTitle(String sessionId) {
try {
// 自动获取agentId
String agentId = findAgentIdBySessionId(sessionId);
if (StringUtils.isBlank(agentId)) {
log.warn("会话 {} 无法获取智能体信息,跳过标题生成", sessionId);
return false;
}
List<AgentChatHistoryDTO> chatHistory = getChatHistoryBySessionId(sessionId);
if (chatHistory == null || chatHistory.isEmpty()) {
return false;
}
List<String> meaningfulMessages = extractMeaningfulMessages(chatHistory);
if (meaningfulMessages.isEmpty()) {
return false;
}
StringBuilder conversation = new StringBuilder();
for (int i = 0; i < meaningfulMessages.size(); i++) {
conversation.append("消息").append(i + 1).append(": ").append(meaningfulMessages.get(i)).append("\n");
}
String slmModelId = getSlmModelId(agentId);
String title = llmService.generateTitle(conversation.toString(), slmModelId);
if (StringUtils.isNotBlank(title)) {
agentChatTitleService.saveOrUpdateTitle(sessionId, title);
log.info("成功保存会话 {} 的标题: {}", sessionId, title);
return true;
}
return false;
} catch (Exception e) {
log.error("生成会话 {} 的标题时发生错误: {}", sessionId, e.getMessage());
return false;
}
}
private String getSlmModelId(String agentId) {
try {
if (StringUtils.isBlank(agentId)) {
return null;
}
AgentInfoVO agentInfo = agentService.getAgentById(agentId);
if (agentInfo == null) {
return null;
}
String slmModelId = agentInfo.getSlmModelId();
if (StringUtils.isNotBlank(slmModelId)) {
log.info("会话 {} 使用SLM模型: {}", agentId, slmModelId);
return slmModelId;
}
ModelConfigEntity defaultLlmConfig = getDefaultLLMConfig();
if (defaultLlmConfig != null) {
log.info("会话 {} 使用默认LLM模型: {}", agentId, defaultLlmConfig.getId());
return defaultLlmConfig.getId();
}
String llmModelId = agentInfo.getLlmModelId();
log.info("会话 {} 使用LLM模型(最终回退): {}", agentId, llmModelId);
return llmModelId;
} catch (Exception e) {
log.error("获取智能体slm模型ID失败,agentId: {}, 错误: {}", agentId, e.getMessage());
return null;
}
}
private ModelConfigEntity getDefaultLLMConfig() {
try {
List<ModelConfigEntity> llmConfigs = modelConfigService.getEnabledModelsByType("LLM");
if (llmConfigs == null || llmConfigs.isEmpty()) {
return null;
}
for (ModelConfigEntity config : llmConfigs) {
if (config.getIsDefault() != null && config.getIsDefault() == 1) {
return config;
}
}
return llmConfigs.get(0);
} catch (Exception e) {
log.error("获取默认LLM配置失败: {}", e.getMessage());
return null;
}
}
/**
* 根据会话ID获取聊天记录
*/
@@ -305,15 +407,13 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService {
*/
private String callJavaLLMForSummaryWithHistory(String conversation, String historyMemory, String agentId) {
try {
// 获取智能体配置,从中提取记忆总结的模型ID
String modelId = getMemorySummaryModelId(agentId);
String modelId = getSlmModelId(agentId);
if (StringUtils.isBlank(modelId)) {
log.info("未找到记忆总结的LLM模型配置,使用默认LLM服务");
log.info("未找到SLM模型,使用默认LLM服务");
return llmService.generateSummaryWithHistory(conversation, historyMemory, null, null);
}
// 使用指定的模型ID调用LLM服务(支持历史记忆合并)
String summary = llmService.generateSummaryWithHistory(conversation, historyMemory, null, modelId);
if (StringUtils.isNotBlank(summary) && !summary.equals("服务暂不可用") && !summary.equals("总结生成失败")) {
@@ -333,15 +433,13 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService {
*/
private String callJavaLLMForSummary(String conversation, String agentId) {
try {
// 获取智能体配置,从中提取记忆总结的模型ID
String modelId = getMemorySummaryModelId(agentId);
String modelId = getSlmModelId(agentId);
if (StringUtils.isBlank(modelId)) {
log.info("未找到记忆总结的LLM模型配置,使用默认LLM服务");
log.info("未找到SLM模型,使用默认LLM服务");
return llmService.generateSummary(conversation);
}
// 使用指定的模型ID调用LLM服务
String summary = llmService.generateSummaryWithModel(conversation, modelId);
if (StringUtils.isNotBlank(summary) && !summary.equals("服务暂不可用") && !summary.equals("总结生成失败")) {
@@ -0,0 +1,60 @@
package xiaozhi.modules.agent.service.impl;
import java.util.Date;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.RequiredArgsConstructor;
import xiaozhi.modules.agent.dao.AgentChatTitleDao;
import xiaozhi.modules.agent.entity.AgentChatTitleEntity;
import xiaozhi.modules.agent.service.AgentChatTitleService;
@Service
@RequiredArgsConstructor
public class AgentChatTitleServiceImpl implements AgentChatTitleService {
private final AgentChatTitleDao agentChatTitleDao;
@Override
public void saveOrUpdateTitle(String sessionId, String title) {
if (StringUtils.isBlank(sessionId) || StringUtils.isBlank(title)) {
return;
}
QueryWrapper<AgentChatTitleEntity> wrapper = new QueryWrapper<>();
wrapper.eq("session_id", sessionId);
AgentChatTitleEntity existing = agentChatTitleDao.selectOne(wrapper);
if (existing != null) {
existing.setTitle(title);
existing.setUpdatedAt(new Date());
agentChatTitleDao.updateById(existing);
} else {
AgentChatTitleEntity newEntity = AgentChatTitleEntity.builder()
.id(java.util.UUID.randomUUID().toString().replace("-", ""))
.sessionId(sessionId)
.title(title)
.createdAt(new Date())
.updatedAt(new Date())
.build();
agentChatTitleDao.insert(newEntity);
}
}
@Override
public String getTitleBySessionId(String sessionId) {
if (StringUtils.isBlank(sessionId)) {
return null;
}
QueryWrapper<AgentChatTitleEntity> wrapper = new QueryWrapper<>();
wrapper.eq("session_id", sessionId);
AgentChatTitleEntity entity = agentChatTitleDao.selectOne(wrapper);
return entity != null ? entity.getTitle() : null;
}
}
@@ -48,6 +48,7 @@ import xiaozhi.modules.agent.service.AgentService;
import xiaozhi.modules.agent.service.AgentTagService;
import xiaozhi.modules.agent.service.AgentTemplateService;
import xiaozhi.modules.agent.vo.AgentInfoVO;
import xiaozhi.modules.correctword.service.CorrectWordFileService;
import xiaozhi.modules.device.entity.DeviceEntity;
import xiaozhi.modules.device.service.DeviceService;
import xiaozhi.modules.model.dto.ModelProviderDTO;
@@ -74,6 +75,7 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
private final ModelProviderService modelProviderService;
private final AgentContextProviderService agentContextProviderService;
private final AgentTagService agentTagService;
private final CorrectWordFileService correctWordFileService;
@Override
public PageData<AgentEntity> adminAgentList(Map<String, Object> params) {
@@ -104,6 +106,10 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
agent.setContextProviders(contextProviderEntity.getContextProviders());
}
// 查询替换词文件ID列表
List<String> correctWordFileIds = correctWordFileService.getAgentCorrectWordFileIds(id);
agent.setCorrectWordFileIds(correctWordFileIds);
// 无需额外查询插件列表,已通过SQL查询出来
return agent;
}
@@ -140,40 +146,32 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
QueryWrapper<AgentEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_id", userId).orderByDesc("created_at");
// 如果有搜索关键词,根据搜索类型添加相应的查询条件
if (StringUtils.isNotBlank(keyword)) {
if ("mac".equals(searchType)) {
// 按MAC地址搜索:先搜索设备,再获取对应的智能体
queryWrapper.and(w -> {
// 按名称搜索
w.like("agent_name", keyword);
// 按MAC地址搜索:先查设备,再获取对应的智能体ID
List<DeviceEntity> devices = Optional
.ofNullable(deviceService.searchDevicesByMacAddress(keyword, userId)).orElseGet(ArrayList::new);
// 获取设备对应的智能体ID列表
.ofNullable(deviceService.searchDevicesByMacAddress(keyword, userId))
.orElseGet(ArrayList::new);
List<String> agentIds = devices.stream()
.map(DeviceEntity::getAgentId)
.distinct()
.collect(Collectors.toList());
if (ToolUtil.isNotEmpty(agentIds)) {
queryWrapper.in("id", agentIds);
} else {
return new ArrayList<>();
w.or().in("id", agentIds);
}
} else {
// 按名称搜索(默认):同时搜索智能体名称和标签名
// 按标签名搜索
List<String> tagAgentIds = agentTagService.getAgentIdsByTagName(keyword);
if (ToolUtil.isNotEmpty(tagAgentIds)) {
queryWrapper.and(wrapper -> wrapper
.like("agent_name", keyword)
.or()
.in("id", tagAgentIds));
} else {
queryWrapper.like("agent_name", keyword);
w.or().in("id", tagAgentIds);
}
}
});
}
// 执行查询
List<AgentEntity> agentEntities = baseDao.selectList(queryWrapper);
// 转换为DTO并设置所有必要字段
return agentEntities.stream().map(this::buildAgentDTO).collect(Collectors.toList());
}
@@ -300,6 +298,9 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
if (dto.getLlmModelId() != null) {
existingEntity.setLlmModelId(dto.getLlmModelId());
}
if (dto.getSlmModelId() != null) {
existingEntity.setSlmModelId(dto.getSlmModelId());
}
if (dto.getVllmModelId() != null) {
existingEntity.setVllmModelId(dto.getVllmModelId());
}
@@ -406,13 +407,14 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
existingEntity.setUpdatedAt(new Date());
// 更新记忆策略
if (existingEntity.getMemModelId() == null || existingEntity.getMemModelId().equals(Constant.MEMORY_NO_MEM)) {
// 删除所有记录
// 删除所有记录
if (existingEntity.getMemModelId() != null && existingEntity.getMemModelId().equals(Constant.MEMORY_NO_MEM)) {
agentChatHistoryService.deleteByAgentId(existingEntity.getId(), true, true);
existingEntity.setSummaryMemory("");
} else if (existingEntity.getChatHistoryConf() != null && existingEntity.getChatHistoryConf() == 1) {
// 删除音频数据
agentChatHistoryService.deleteByAgentId(existingEntity.getId(), true, false);
// 删除记忆
} else if (existingEntity.getMemModelId() != null
&& existingEntity.getMemModelId().equals(Constant.MEMORY_MEM_REPORT_ONLY)) {
existingEntity.setSummaryMemory("");
}
// 更新上下文源配置
@@ -423,6 +425,11 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
agentContextProviderService.saveOrUpdateByAgentId(contextEntity);
}
// 更新替换词文件关联
if (dto.getCorrectWordFileIds() != null) {
correctWordFileService.saveAgentCorrectWords(agentId, dto.getCorrectWordFileIds());
}
boolean b = validateLLMIntentParams(dto.getLlmModelId(), dto.getIntentModelId());
if (!b) {
throw new RenException(ErrorCode.LLM_INTENT_PARAMS_MISMATCH);
@@ -505,6 +512,13 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
entity.setLanguage(template.getLanguage());
}
if (entity.getSlmModelId() == null) {
String defaultSlmModelId = getDefaultLLMModelId();
if (defaultSlmModelId != null) {
entity.setSlmModelId(defaultSlmModelId);
}
}
// 设置用户ID和创建者信息
UserDetail user = SecurityUser.getUser();
entity.setUserId(user.getId());
@@ -543,4 +557,23 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
return entity.getId();
}
private String getDefaultLLMModelId() {
try {
List<ModelConfigEntity> llmConfigs = modelConfigService.getEnabledModelsByType("LLM");
if (llmConfigs == null || llmConfigs.isEmpty()) {
return null;
}
for (ModelConfigEntity config : llmConfigs) {
if (config.getIsDefault() != null && config.getIsDefault() == 1) {
return config.getId();
}
}
return llmConfigs.get(0).getId();
} catch (Exception e) {
return null;
}
}
}
@@ -25,4 +25,7 @@ public class AgentInfoVO extends AgentEntity
@Schema(description = "上下文源配置")
private List<ContextProviderDTO> contextProviders;
@Schema(description = "替换词文件ID列表")
private List<String> correctWordFileIds;
}
@@ -1,5 +1,7 @@
package xiaozhi.modules.config.controller;
import java.util.List;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -12,6 +14,7 @@ import lombok.AllArgsConstructor;
import xiaozhi.common.utils.Result;
import xiaozhi.common.validator.ValidatorUtils;
import xiaozhi.modules.config.dto.AgentModelsDTO;
import xiaozhi.modules.config.dto.CorrectWordsDTO;
import xiaozhi.modules.config.service.ConfigService;
/**
@@ -41,4 +44,12 @@ public class ConfigController {
Object models = configService.getAgentModels(dto.getMacAddress(), dto.getSelectedModule());
return new Result<Object>().ok(models);
}
@PostMapping("correct-words")
@Operation(summary = "获取智能体替换词")
public Result<Object> getCorrectWords(@Valid @RequestBody CorrectWordsDTO dto) {
ValidatorUtils.validateEntity(dto);
List<String> list = configService.getCorrectWords(dto.getMacAddress());
return new Result<Object>().ok(list);
}
}
@@ -0,0 +1,14 @@
package xiaozhi.modules.config.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
@Schema(description = "获取智能体替换词DTO")
public class CorrectWordsDTO {
@NotBlank(message = "设备MAC地址不能为空")
@Schema(description = "设备MAC地址")
private String macAddress;
}
@@ -1,11 +1,12 @@
package xiaozhi.modules.config.service;
import java.util.List;
import java.util.Map;
public interface ConfigService {
/**
* 获取服务器配置
*
*
* @param isCache 是否缓存
* @return 配置信息
*/
@@ -13,10 +14,18 @@ public interface ConfigService {
/**
* 获取智能体模型配置
*
*
* @param macAddress MAC地址
* @param selectedModule 客户端已实例化的模型
* @return 模型配置信息
*/
Map<String, Object> getAgentModels(String macAddress, Map<String, String> selectedModule);
/**
* 获取智能体替换词
*
* @param macAddress 设备MAC地址
* @return 替换词列表,格式如 ["模板1|模板01", "模板2|模板02"]
*/
List<String> getCorrectWords(String macAddress);
}
@@ -1,10 +1,12 @@
package xiaozhi.modules.config.service.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
@@ -30,7 +32,9 @@ import xiaozhi.modules.agent.service.AgentMcpAccessPointService;
import xiaozhi.modules.agent.service.AgentPluginMappingService;
import xiaozhi.modules.agent.service.AgentService;
import xiaozhi.modules.agent.service.AgentTemplateService;
import xiaozhi.modules.correctword.service.CorrectWordFileService;
import xiaozhi.modules.agent.vo.AgentVoicePrintVO;
import xiaozhi.modules.correctword.vo.CorrectWordSimpleVO;
import xiaozhi.modules.config.service.ConfigService;
import xiaozhi.modules.device.entity.DeviceEntity;
import xiaozhi.modules.device.service.DeviceService;
@@ -58,6 +62,7 @@ public class ConfigServiceImpl implements ConfigService {
private final AgentContextProviderService agentContextProviderService;
private final VoiceCloneService cloneVoiceService;
private final AgentVoicePrintDao agentVoicePrintDao;
private final CorrectWordFileService correctWordFileService;
@Override
public Object getConfig(Boolean isCache) {
@@ -99,6 +104,7 @@ public class ConfigServiceImpl implements ConfigService {
null,
null,
null,
null,
result,
isCache);
@@ -113,7 +119,7 @@ public class ConfigServiceImpl implements ConfigService {
// 检查是否为管理控制台请求
String redisKey = RedisKeys.getTmpRegisterMacKey(macAddress);
Object isAdminRequest = redisUtils.get(redisKey);
if (isAdminRequest != null && "true".equals(isAdminRequest)) {
// 管理控制台请求,返回getConfig的结果
redisUtils.delete(redisKey); // 使用后清理
@@ -203,10 +209,11 @@ public class ConfigServiceImpl implements ConfigService {
mcpEndpoint = mcpEndpoint.replace("/mcp/", "/call/");
result.put("mcp_endpoint", mcpEndpoint);
}
// 获取上下文源配置
AgentContextProviderEntity contextProviderEntity = agentContextProviderService.getByAgentId(agent.getId());
if (contextProviderEntity != null && contextProviderEntity.getContextProviders() != null && !contextProviderEntity.getContextProviders().isEmpty()) {
if (contextProviderEntity != null && contextProviderEntity.getContextProviders() != null
&& !contextProviderEntity.getContextProviders().isEmpty()) {
result.put("context_providers", contextProviderEntity.getContextProviders());
}
@@ -229,6 +236,7 @@ public class ConfigServiceImpl implements ConfigService {
agent.getAsrModelId(),
agent.getLlmModelId(),
agent.getVllmModelId(),
agent.getSlmModelId(),
agent.getTtsModelId(),
agent.getMemModelId(),
agent.getIntentModelId(),
@@ -239,6 +247,18 @@ public class ConfigServiceImpl implements ConfigService {
return result;
}
@Override
public List<String> getCorrectWords(String macAddress) {
DeviceEntity device = deviceService.getDeviceByMacAddress(macAddress);
if (device == null) {
return Collections.emptyList();
}
List<CorrectWordSimpleVO> items = correctWordFileService.getAllItemsByAgentId(device.getAgentId());
return items.stream()
.map(item -> item.getSourceWord() + "|" + item.getTargetWord())
.collect(Collectors.toList());
}
/**
* 构建配置信息
*
@@ -410,6 +430,7 @@ public class ConfigServiceImpl implements ConfigService {
String asrModelId,
String llmModelId,
String vllmModelId,
String slmModelId,
String ttsModelId,
String memModelId,
String intentModelId,
@@ -418,9 +439,9 @@ public class ConfigServiceImpl implements ConfigService {
boolean isCache) {
Map<String, String> selectedModule = new HashMap<>();
String[] modelTypes = { "VAD", "ASR", "TTS", "Memory", "Intent", "LLM", "VLLM", "RAG" };
String[] modelTypes = { "VAD", "ASR", "TTS", "Memory", "Intent", "LLM", "VLLM", "SLM", "RAG" };
String[] modelIds = { vadModelId, asrModelId, ttsModelId, memModelId, intentModelId, llmModelId, vllmModelId,
ragModelId };
slmModelId, ragModelId };
String intentLLMModelId = null;
String memLocalShortLLMModelId = null;
@@ -456,7 +477,7 @@ public class ConfigServiceImpl implements ConfigService {
// 火山引擎声音克隆需要替换resource_id
Map<String, Object> map = (Map<String, Object>) model.getConfigJson();
if (Constant.VOICE_CLONE_HUOSHAN_DOUBLE_STREAM.equals(map.get("type"))) {
// 如果voice是”S_开头的,使用seed-icl-1.0
// 如果voice是”S_开头的,使用seed-icl-1.0
if (voice != null && voice.startsWith("S_")) {
map.put("resource_id", "seed-icl-1.0");
}
@@ -474,7 +495,7 @@ public class ConfigServiceImpl implements ConfigService {
if (map.get("functions") != null) {
String functionStr = (String) map.get("functions");
if (StringUtils.isNotBlank(functionStr)) {
String[] functions = functionStr.split("\\;");
String[] functions = functionStr.split(";");
map.put("functions", functions);
}
}
@@ -507,6 +528,15 @@ public class ConfigServiceImpl implements ConfigService {
typeConfig.put(memLocalShortLLM.getId(), memLocalShortLLM.getConfigJson());
}
}
// LLM也返回所选的SLM,如果同名id则不重复显示
if (StringUtils.isNotBlank(slmModelId) && !slmModelId.equals(llmModelId)) {
if (!typeConfig.containsKey(slmModelId)) {
ModelConfigEntity slmModel = modelConfigService.getModelByIdFromCache(slmModelId);
if (slmModel != null && slmModel.getConfigJson() != null) {
typeConfig.put(slmModel.getId(), slmModel.getConfigJson());
}
}
}
}
}
result.put(modelTypes[i], typeConfig);
@@ -0,0 +1,116 @@
package xiaozhi.modules.correctword.controller;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import xiaozhi.common.constant.Constant;
import xiaozhi.common.page.PageData;
import xiaozhi.common.utils.Result;
import xiaozhi.modules.correctword.dto.CorrectWordFileCreateDTO;
import xiaozhi.modules.correctword.service.CorrectWordFileService;
import xiaozhi.modules.correctword.vo.CorrectWordFileVO;
@RestController
@RequestMapping("/correct-word")
@Tag(name = "替换词管理")
@AllArgsConstructor
public class CorrectWordController {
private final CorrectWordFileService correctWordFileService;
@PostMapping("/file")
@Operation(summary = "创建替换词文件")
@RequiresPermissions("sys:role:normal")
public Result<CorrectWordFileVO> createFile(@Valid @RequestBody CorrectWordFileCreateDTO dto) {
CorrectWordFileVO vo = correctWordFileService.createFile(dto);
return new Result<CorrectWordFileVO>().ok(vo);
}
@PutMapping("/file/{fileId}")
@Operation(summary = "修改替换词文件")
@RequiresPermissions("sys:role:normal")
public Result<Void> updateFile(@PathVariable String fileId, @Valid @RequestBody CorrectWordFileCreateDTO dto) {
correctWordFileService.updateFile(fileId, dto);
return new Result<>();
}
@GetMapping("/file/list")
@Operation(summary = "分页获取当前用户替换词文件列表")
@RequiresPermissions("sys:role:normal")
@Parameters({
@Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true),
@Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true),
})
public Result<PageData<CorrectWordFileVO>> listFiles(
@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
PageData<CorrectWordFileVO> page = correctWordFileService.listFiles(params);
return new Result<PageData<CorrectWordFileVO>>().ok(page);
}
@GetMapping("/file/select")
@Operation(summary = "智能体获取当前用户替换词文件列表")
@RequiresPermissions("sys:role:normal")
public Result<List<CorrectWordFileVO>> listAllFiles() {
List<CorrectWordFileVO> list = correctWordFileService.listAllFiles();
return new Result<List<CorrectWordFileVO>>().ok(list);
}
@GetMapping("/file/download/{fileId}")
@Operation(summary = "下载替换词文件")
@RequiresPermissions("sys:role:normal")
public ResponseEntity<byte[]> downloadFile(@PathVariable String fileId) {
CorrectWordFileVO vo = correctWordFileService.getFileContent(fileId);
if (vo == null || vo.getContent() == null || vo.getContent().isEmpty()) {
return ResponseEntity.notFound().build();
}
byte[] bytes = String.join("\n", vo.getContent()).getBytes(StandardCharsets.UTF_8);
String encodedFileName = URLEncoder.encode(vo.getFileName(), StandardCharsets.UTF_8).replace("+", "%20");
String asciiFileName = vo.getFileName().replaceAll("[^\\x00-\\x7F]", "_");
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + asciiFileName + "\"; filename*=UTF-8''" + encodedFileName)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(bytes);
}
@DeleteMapping("/file/{fileId}")
@Operation(summary = "删除替换词文件")
@RequiresPermissions("sys:role:normal")
public Result<Void> deleteFile(@PathVariable String fileId) {
correctWordFileService.deleteFile(fileId);
return new Result<>();
}
@PostMapping("/file/batch-delete")
@Operation(summary = "批量删除替换词文件")
@RequiresPermissions("sys:role:normal")
public Result<Void> batchDeleteFiles(@RequestBody List<String> fileIds) {
if (fileIds == null || fileIds.isEmpty()) {
return new Result<>();
}
correctWordFileService.batchDeleteFiles(fileIds);
return new Result<>();
}
}
@@ -0,0 +1,10 @@
package xiaozhi.modules.correctword.dao;
import org.apache.ibatis.annotations.Mapper;
import xiaozhi.common.dao.BaseDao;
import xiaozhi.modules.correctword.entity.CorrectWordFileEntity;
@Mapper
public interface CorrectWordFileDao extends BaseDao<CorrectWordFileEntity> {
}
@@ -0,0 +1,15 @@
package xiaozhi.modules.correctword.dao;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import xiaozhi.common.dao.BaseDao;
import xiaozhi.modules.correctword.entity.CorrectWordItemEntity;
@Mapper
public interface CorrectWordItemDao extends BaseDao<CorrectWordItemEntity> {
int batchInsert(@Param("list") List<CorrectWordItemEntity> items);
}
@@ -0,0 +1,24 @@
package xiaozhi.modules.correctword.dto;
import java.util.List;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
@Data
@Schema(description = "创建替换词文件DTO")
public class CorrectWordFileCreateDTO {
@NotBlank(message = "文件名不能为空")
@Schema(description = "文件名")
private String fileName;
@NotEmpty(message = "替换词内容不能为空")
@Schema(description = "替换词内容,每条格式:原词|替换词")
private List<String> content;
@Schema(description = "文件大小(字节),不能超过1MB")
private Long fileSize;
}
@@ -0,0 +1,41 @@
package xiaozhi.modules.correctword.entity;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@TableName("ai_agent_correct_word_file")
@Schema(description = "智能体替换词文件")
public class CorrectWordFileEntity {
@TableId(type = IdType.ASSIGN_UUID)
@Schema(description = "替换词文件ID")
private String id;
@Schema(description = "原始文件名")
private String fileName;
@Schema(description = "替换词数量")
private Integer wordCount;
@Schema(description = "文件原始内容(用于下载)")
private String content;
@Schema(description = "创建者")
private Long creator;
@Schema(description = "创建时间")
private Date createdAt;
@Schema(description = "更新者")
private Long updater;
@Schema(description = "更新时间")
private Date updatedAt;
}
@@ -0,0 +1,27 @@
package xiaozhi.modules.correctword.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@TableName("ai_agent_correct_word_item")
@Schema(description = "替换词词条")
public class CorrectWordItemEntity {
@TableId(type = IdType.ASSIGN_UUID)
@Schema(description = "词条ID")
private String id;
@Schema(description = "所属文件ID")
private String fileId;
@Schema(description = "原词")
private String sourceWord;
@Schema(description = "替换词")
private String targetWord;
}
@@ -0,0 +1,96 @@
package xiaozhi.modules.correctword.service;
import java.util.List;
import java.util.Map;
import xiaozhi.common.page.PageData;
import xiaozhi.modules.correctword.dto.CorrectWordFileCreateDTO;
import xiaozhi.modules.correctword.vo.CorrectWordFileVO;
import xiaozhi.modules.correctword.vo.CorrectWordSimpleVO;
public interface CorrectWordFileService {
/**
* 创建替换词文件
*
* @param dto 创建参数
* @return 文件VO
*/
CorrectWordFileVO createFile(CorrectWordFileCreateDTO dto);
/**
* 修改替换词文件(全量替换词条)
*
* @param fileId 文件ID
* @param dto 修改参数
*/
void updateFile(String fileId, CorrectWordFileCreateDTO dto);
/**
* 获取当前用户的替换词文件列表
*
* @param params 分页参数
* @return 分页数据
*/
PageData<CorrectWordFileVO> listFiles(Map<String, Object> params);
/**
* 获取当前用户的替换词文件列表(不分页,用于下拉选择)
*
* @return 文件列表
*/
List<CorrectWordFileVO> listAllFiles();
/**
* 获取文件原始内容(用于下载)
*
* @param fileId 文件ID
* @return 文件实体
*/
CorrectWordFileVO getFileContent(String fileId);
/**
* 删除替换词文件及其所有词条和关联记录
*
* @param fileId 文件ID
*/
void deleteFile(String fileId);
/**
* 删除智能体关联的替换词文件关联记录(不删文件本身)
*
* @param agentId 智能体ID
*/
void deleteMappingsByAgentId(String agentId);
/**
* 获取智能体的所有替换词条(精简版,供设备端使用)
*
* @param agentId 智能体ID
* @return 替换词列表
*/
List<CorrectWordSimpleVO> getAllItemsByAgentId(String agentId);
/**
* 获取智能体关联的替换词文件ID列表
*
* @param agentId 智能体ID
* @return 文件ID列表
*/
List<String> getAgentCorrectWordFileIds(String agentId);
/**
* 保存智能体关联的替换词文件(全量替换)
*
* @param agentId 智能体ID
* @param fileIds 文件ID列表
*/
void saveAgentCorrectWords(String agentId, List<String> fileIds);
/**
* 批量删除替换词文件
*
* @param fileIds 文件ID列表
*/
void batchDeleteFiles(List<String> fileIds);
}
@@ -0,0 +1,296 @@
package xiaozhi.modules.correctword.service.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.AllArgsConstructor;
import xiaozhi.common.exception.ErrorCode;
import xiaozhi.common.exception.RenException;
import xiaozhi.common.page.PageData;
import xiaozhi.common.service.impl.BaseServiceImpl;
import xiaozhi.common.utils.ConvertUtils;
import xiaozhi.modules.agent.dao.AgentCorrectWordMappingDao;
import xiaozhi.modules.correctword.dao.CorrectWordFileDao;
import xiaozhi.modules.correctword.dao.CorrectWordItemDao;
import xiaozhi.modules.correctword.dto.CorrectWordFileCreateDTO;
import xiaozhi.modules.agent.entity.AgentCorrectWordMappingEntity;
import xiaozhi.modules.correctword.entity.CorrectWordFileEntity;
import xiaozhi.modules.correctword.entity.CorrectWordItemEntity;
import xiaozhi.modules.correctword.service.CorrectWordFileService;
import xiaozhi.modules.correctword.vo.CorrectWordFileVO;
import xiaozhi.modules.correctword.vo.CorrectWordSimpleVO;
import xiaozhi.modules.security.user.SecurityUser;
@Service
@AllArgsConstructor
public class CorrectWordFileServiceImpl extends BaseServiceImpl<CorrectWordFileDao, CorrectWordFileEntity>
implements CorrectWordFileService {
private final CorrectWordFileDao correctWordFileDao;
private final CorrectWordItemDao correctWordItemDao;
private final AgentCorrectWordMappingDao agentCorrectWordMappingDao;
@Override
@Transactional(rollbackFor = Exception.class)
public CorrectWordFileVO createFile(CorrectWordFileCreateDTO dto) {
// 校验文件大小不能超过1MB
if (dto.getFileSize() != null && dto.getFileSize() > 1024 * 1024) {
throw new RenException(ErrorCode.FILE_SIZE_OVER_LIMIT);
}
// 校验文件名是否重复
Long userId = SecurityUser.getUserId();
LambdaQueryWrapper<CorrectWordFileEntity> nameWrapper = new LambdaQueryWrapper<>();
nameWrapper.eq(CorrectWordFileEntity::getCreator, userId)
.eq(CorrectWordFileEntity::getFileName, dto.getFileName());
if (correctWordFileDao.selectCount(nameWrapper) > 0) {
throw new RenException(ErrorCode.CORRECT_WORD_FILE_NAME_EXISTS);
}
List<CorrectWordItemEntity> items = parseContent(dto.getContent());
// 保存文件记录
CorrectWordFileEntity fileEntity = new CorrectWordFileEntity();
fileEntity.setFileName(dto.getFileName());
fileEntity.setWordCount(items.size());
fileEntity.setContent(String.join("\n", dto.getContent()));
fileEntity.setCreator(SecurityUser.getUserId());
fileEntity.setCreatedAt(new Date());
correctWordFileDao.insert(fileEntity);
// 设置fileId并批量保存词条
String fileId = fileEntity.getId();
for (CorrectWordItemEntity item : items) {
item.setFileId(fileId);
}
if (!items.isEmpty()) {
correctWordItemDao.batchInsert(items);
}
return toVO(fileEntity);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateFile(String fileId, CorrectWordFileCreateDTO dto) {
CorrectWordFileEntity fileEntity = correctWordFileDao.selectById(fileId);
if (fileEntity == null) {
return;
}
// 校验文件名是否重复(排除自身)
Long userId = SecurityUser.getUserId();
LambdaQueryWrapper<CorrectWordFileEntity> nameWrapper = new LambdaQueryWrapper<>();
nameWrapper.eq(CorrectWordFileEntity::getCreator, userId)
.eq(CorrectWordFileEntity::getFileName, dto.getFileName())
.ne(CorrectWordFileEntity::getId, fileId);
if (correctWordFileDao.selectCount(nameWrapper) > 0) {
throw new RenException("文件名已存在:" + dto.getFileName());
}
// 先删除旧词条
LambdaQueryWrapper<CorrectWordItemEntity> deleteWrapper = new LambdaQueryWrapper<>();
deleteWrapper.eq(CorrectWordItemEntity::getFileId, fileId);
correctWordItemDao.delete(deleteWrapper);
// 解析新词条并批量保存
List<CorrectWordItemEntity> items = parseContent(dto.getContent());
if (!items.isEmpty()) {
for (CorrectWordItemEntity item : items) {
item.setFileId(fileId);
}
correctWordItemDao.batchInsert(items);
}
// 更新文件记录
fileEntity.setFileName(dto.getFileName());
fileEntity.setWordCount(items.size());
fileEntity.setContent(String.join("\n", dto.getContent()));
fileEntity.setUpdater(SecurityUser.getUserId());
fileEntity.setUpdatedAt(new Date());
correctWordFileDao.updateById(fileEntity);
}
@Override
public PageData<CorrectWordFileVO> listFiles(Map<String, Object> params) {
Long userId = SecurityUser.getUserId();
IPage<CorrectWordFileEntity> page = getPage(params, "created_at", false);
LambdaQueryWrapper<CorrectWordFileEntity> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CorrectWordFileEntity::getCreator, userId)
.orderByDesc(CorrectWordFileEntity::getCreatedAt);
correctWordFileDao.selectPage(page, wrapper);
List<CorrectWordFileVO> voList = toVOList(page.getRecords());
return new PageData<>(voList, page.getTotal());
}
@Override
public List<CorrectWordFileVO> listAllFiles() {
Long userId = SecurityUser.getUserId();
LambdaQueryWrapper<CorrectWordFileEntity> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CorrectWordFileEntity::getCreator, userId)
.orderByDesc(CorrectWordFileEntity::getCreatedAt);
List<CorrectWordFileEntity> entities = correctWordFileDao.selectList(wrapper);
return toVOList(entities);
}
@Override
public CorrectWordFileVO getFileContent(String fileId) {
CorrectWordFileEntity entity = correctWordFileDao.selectById(fileId);
return toVO(entity);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteFile(String fileId) {
if (fileId == null || fileId.trim().isEmpty()) {
return;
}
// 先删除关联表记录
agentCorrectWordMappingDao.deleteByFileId(fileId);
// 删除词条
LambdaQueryWrapper<CorrectWordItemEntity> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(CorrectWordItemEntity::getFileId, fileId);
correctWordItemDao.delete(wrapper);
// 删除文件
correctWordFileDao.deleteById(fileId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteMappingsByAgentId(String agentId) {
agentCorrectWordMappingDao.deleteByAgentId(agentId);
}
@Override
public List<CorrectWordSimpleVO> getAllItemsByAgentId(String agentId) {
// 通过关联表获取文件ID列表
List<AgentCorrectWordMappingEntity> mappings = agentCorrectWordMappingDao.selectByAgentId(agentId);
if (mappings == null || mappings.isEmpty()) {
return new ArrayList<>();
}
List<String> fileIds = mappings.stream()
.map(AgentCorrectWordMappingEntity::getFileId)
.collect(Collectors.toList());
// 根据文件ID列表查询词条
LambdaQueryWrapper<CorrectWordItemEntity> wrapper = new LambdaQueryWrapper<>();
wrapper.in(CorrectWordItemEntity::getFileId, fileIds);
List<CorrectWordItemEntity> entities = correctWordItemDao.selectList(wrapper);
return ConvertUtils.sourceToTarget(entities, CorrectWordSimpleVO.class);
}
@Override
public List<String> getAgentCorrectWordFileIds(String agentId) {
List<AgentCorrectWordMappingEntity> mappings = agentCorrectWordMappingDao.selectByAgentId(agentId);
if (mappings == null || mappings.isEmpty()) {
return new ArrayList<>();
}
return mappings.stream()
.map(AgentCorrectWordMappingEntity::getFileId)
.collect(Collectors.toList());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void saveAgentCorrectWords(String agentId, List<String> fileIds) {
// 先删除旧的关联记录
agentCorrectWordMappingDao.deleteByAgentId(agentId);
if (fileIds == null || fileIds.isEmpty()) {
return;
}
// 批量插入新的关联记录
Long userId = SecurityUser.getUserId();
Date now = new Date();
List<AgentCorrectWordMappingEntity> mappings = new ArrayList<>();
for (String fileId : fileIds) {
AgentCorrectWordMappingEntity mapping = new AgentCorrectWordMappingEntity();
mapping.setAgentId(agentId);
mapping.setFileId(fileId);
mapping.setCreator(userId);
mapping.setCreatedAt(now);
mapping.setUpdater(userId);
mapping.setUpdatedAt(now);
mappings.add(mapping);
}
agentCorrectWordMappingDao.batchInsertMapping(mappings);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void batchDeleteFiles(List<String> fileIds) {
if (fileIds == null || fileIds.isEmpty()) {
return;
}
for (String fileId : fileIds) {
if (fileId == null || fileId.trim().isEmpty()) {
continue;
}
deleteFile(fileId.trim());
}
}
/**
* 解析替换词内容,每条格式:原词|替换词
*/
private List<CorrectWordItemEntity> parseContent(List<String> lines) {
List<CorrectWordItemEntity> items = new ArrayList<>();
if (lines == null || lines.isEmpty()) {
return items;
}
for (String line : lines) {
line = line.trim();
if (line.isEmpty()) {
continue;
}
int idx = line.indexOf('|');
if (idx <= 0 || idx >= line.length() - 1) {
continue;
}
String sourceWord = line.substring(0, idx).trim();
String targetWord = line.substring(idx + 1).trim();
if (sourceWord.isEmpty() || targetWord.isEmpty()) {
continue;
}
CorrectWordItemEntity item = new CorrectWordItemEntity();
item.setSourceWord(sourceWord);
item.setTargetWord(targetWord);
items.add(item);
}
return items;
}
private CorrectWordFileVO toVO(CorrectWordFileEntity entity) {
if (entity == null) {
return null;
}
CorrectWordFileVO vo = new CorrectWordFileVO();
vo.setId(entity.getId());
vo.setFileName(entity.getFileName());
vo.setWordCount(entity.getWordCount());
vo.setContent(entity.getContent() != null
? Arrays.asList(entity.getContent().split("\n"))
: new ArrayList<>());
vo.setCreatedAt(entity.getCreatedAt());
vo.setUpdatedAt(entity.getUpdatedAt());
return vo;
}
private List<CorrectWordFileVO> toVOList(List<CorrectWordFileEntity> entities) {
if (entities == null || entities.isEmpty()) {
return new ArrayList<>();
}
return entities.stream().map(this::toVO).collect(Collectors.toList());
}
}
@@ -0,0 +1,30 @@
package xiaozhi.modules.correctword.vo;
import java.util.Date;
import java.util.List;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "替换词文件列表VO")
public class CorrectWordFileVO {
@Schema(description = "替换词文件ID")
private String id;
@Schema(description = "原始文件名")
private String fileName;
@Schema(description = "替换词数量")
private Integer wordCount;
@Schema(description = "替换词内容,每行一条")
private List<String> content;
@Schema(description = "创建时间")
private Date createdAt;
@Schema(description = "更新时间")
private Date updatedAt;
}
@@ -0,0 +1,15 @@
package xiaozhi.modules.correctword.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "替换词精简VO(设备端使用)")
public class CorrectWordSimpleVO {
@Schema(description = "原词")
private String sourceWord;
@Schema(description = "替换词")
private String targetWord;
}
@@ -1,102 +0,0 @@
# RAGFlow API Interface Classification
## 1. External APIs (三方接入体系)
**Path Prefix:** `/api/v1`
**Authentication:** API Key (`@token_required`)
**Primary Use:** External system integration, SDK usage.
| Interface Type | Python File Path | Class/Function Name | URL Pattern | Notes |
|---|---|---|---|---|
| **External** | `api/apps/sdk/session.py` | `agent_bot_completions` | `/api/v1/agentbots/<agent_id>/completions` | Agent Bot completion |
| **External** | `api/apps/sdk/session.py` | `begin_inputs` | `/api/v1/agentbots/<agent_id>/inputs` | Get Agent Bot inputs |
| **External** | `api/apps/sdk/agents.py` | `list_agents` | `/api/v1/agents` | List Agents |
| **External** | `api/apps/sdk/agents.py` | `create_agent` | `/api/v1/agents` | Create Agent |
| **External** | `api/apps/sdk/agents.py` | `update_agent` | `/api/v1/agents/<agent_id>` | Update Agent |
| **External** | `api/apps/sdk/agents.py` | `delete_agent` | `/api/v1/agents/<agent_id>` | Delete Agent |
| **External** | `api/apps/sdk/session.py` | `agent_completions` | `/api/v1/agents/<agent_id>/completions` | Agent completion |
| **External** | `api/apps/sdk/session.py` | `create_agent_session` | `/api/v1/agents/<agent_id>/sessions` | Create Agent Session |
| **External** | `api/apps/sdk/session.py` | `list_agent_session` | `/api/v1/agents/<agent_id>/sessions` | List Agent Sessions |
| **External** | `api/apps/sdk/session.py` | `delete_agent_session` | `/api/v1/agents/<agent_id>/sessions` | Delete Agent Session |
| **External** | `api/apps/sdk/session.py` | `agents_completion_openai_compatibility` | `/api/v1/agents_openai/<agent_id>/chat/completions` | OpenAI compatible Agent completion |
| **External** | `api/apps/sdk/session.py` | `chatbot_completions` | `/api/v1/chatbots/<dialog_id>/completions` | Chatbot completion |
| **External** | `api/apps/sdk/session.py` | `chatbots_inputs` | `/api/v1/chatbots/<dialog_id>/info` | Chatbot info |
| **External** | `api/apps/sdk/chat.py` | `create` | `/api/v1/chats` | Create Chat |
| **External** | `api/apps/sdk/chat.py` | `delete_chats` | `/api/v1/chats` | Delete Chat |
| **External** | `api/apps/sdk/chat.py` | `list_chat` | `/api/v1/chats` | List Chats |
| **External** | `api/apps/sdk/chat.py` | `update` | `/api/v1/chats/<chat_id>` | Update Chat |
| **External** | `api/apps/sdk/session.py` | `chat_completion` | `/api/v1/chats/<chat_id>/completions` | Chat completion |
| **External** | `api/apps/sdk/session.py` | `create` | `/api/v1/chats/<chat_id>/sessions` | Create Chat Session |
| **External** | `api/apps/sdk/session.py` | `list_session` | `/api/v1/chats/<chat_id>/sessions` | List Chat Sessions |
| **External** | `api/apps/sdk/session.py` | `delete` | `/api/v1/chats/<chat_id>/sessions` | Delete Chat Session |
| **External** | `api/apps/sdk/session.py` | `update` | `/api/v1/chats/<chat_id>/sessions/<session_id>` | Update Chat Session |
| **External** | `api/apps/sdk/session.py` | `chat_completion_openai_like` | `/api/v1/chats_openai/<chat_id>/chat/completions` | OpenAI compatible Chat completion |
| **External** | `api/apps/sdk/dataset.py` | `create` | `/api/v1/datasets` | Create Dataset |
| **External** | `api/apps/sdk/dataset.py` | `delete` | `/api/v1/datasets` | Delete Dataset |
| **External** | `api/apps/sdk/dataset.py` | `list_datasets` | `/api/v1/datasets` | List Datasets |
| **External** | `api/apps/sdk/dataset.py` | `update` | `/api/v1/datasets/<dataset_id>` | Update Dataset |
| **External** | `api/apps/sdk/doc.py` | `parse` | `/api/v1/datasets/<dataset_id>/chunks` | Parse Document Chunks |
| **External** | `api/apps/sdk/doc.py` | `stop_parsing` | `/api/v1/datasets/<dataset_id>/chunks` | Stop Parsing |
| **External** | `api/apps/sdk/doc.py` | `upload` | `/api/v1/datasets/<dataset_id>/documents` | Upload Document |
| **External** | `api/apps/sdk/doc.py` | `list_docs` | `/api/v1/datasets/<dataset_id>/documents` | List Documents |
| **External** | `api/apps/sdk/doc.py` | `delete` | `/api/v1/datasets/<dataset_id>/documents` | Delete Document |
| **External** | `api/apps/sdk/doc.py` | `update_doc` | `/api/v1/datasets/<dataset_id>/documents/<document_id>` | Update Document |
| **External** | `api/apps/sdk/doc.py` | `download` | `/api/v1/datasets/<dataset_id>/documents/<document_id>` | Download Document |
| **External** | `api/apps/sdk/doc.py` | `list_chunks` | `/api/v1/datasets/<dataset_id>/documents/<document_id>/chunks` | List Chunks |
| **External** | `api/apps/sdk/doc.py` | `add_chunk` | `/api/v1/datasets/<dataset_id>/documents/<document_id>/chunks` | Add Chunk |
| **External** | `api/apps/sdk/doc.py` | `update_chunk` | `/api/v1/datasets/<dataset_id>/documents/<document_id>/chunks/<chunk_id>` | Update Chunk |
| **External** | `api/apps/sdk/dataset.py` | `knowledge_graph` | `/api/v1/datasets/<dataset_id>/knowledge_graph` | Knowledge Graph |
| **External** | `api/apps/sdk/dataset.py` | `delete_knowledge_graph` | `/api/v1/datasets/<dataset_id>/knowledge_graph` | Delete Knowledge Graph |
| **External** | `api/apps/sdk/doc.py` | `metadata_summary` | `/api/v1/datasets/<dataset_id>/metadata/summary` | Metadata Summary |
| **External** | `api/apps/sdk/doc.py` | `metadata_batch_update` | `/api/v1/datasets/<dataset_id>/metadata/update` | Batch Update Metadata |
| **External** | `api/apps/sdk/dataset.py` | `run_graphrag` | `/api/v1/datasets/<dataset_id>/run_graphrag` | Run GraphRAG |
| **External** | `api/apps/sdk/dataset.py` | `run_raptor` | `/api/v1/datasets/<dataset_id>/run_raptor` | Run Raptor |
| **External** | `api/apps/sdk/dataset.py` | `trace_graphrag` | `/api/v1/datasets/<dataset_id>/trace_graphrag` | Trace GraphRAG |
| **External** | `api/apps/sdk/dataset.py` | `trace_raptor` | `/api/v1/datasets/<dataset_id>/trace_raptor` | Trace Raptor |
| **External** | `api/apps/sdk/dify_retrieval.py` | `retrieval` | `/api/v1/dify/retrieval` | Dify Retrieval |
| **External** | `api/apps/sdk/files.py` | `get_all_parent_folders` | `/api/v1/file/all_parent_folder` | Get All Parent Folders |
| **External** | `api/apps/sdk/files.py` | `convert` | `/api/v1/file/convert` | File Convert |
| **External** | `api/apps/sdk/files.py` | `create` | `/api/v1/file/create` | File Create |
| **External** | `api/apps/sdk/files.py` | `download_attachment` | `/api/v1/file/download/<attachment_id>` | Download Attachment |
| **External** | `api/apps/sdk/files.py` | `get` | `/api/v1/file/get/<file_id>` | Get File |
| **External** | `api/apps/sdk/files.py` | `list_files` | `/api/v1/file/list` | List Files |
| **External** | `api/apps/sdk/files.py` | `move` | `/api/v1/file/mv` | Move File |
| **External** | `api/apps/sdk/files.py` | `get_parent_folder` | `/api/v1/file/parent_folder` | Get Parent Folder |
| **External** | `api/apps/sdk/files.py` | `rename` | `/api/v1/file/rename` | Rename File |
| **External** | `api/apps/sdk/files.py` | `rm` | `/api/v1/file/rm` | Remove File |
| **External** | `api/apps/sdk/files.py` | `get_root_folder` | `/api/v1/file/root_folder` | Get Root Folder |
| **External** | `api/apps/sdk/files.py` | `upload` | `/api/v1/file/upload` | Upload File |
| **External** | `api/apps/sdk/doc.py` | `retrieval_test` | `/api/v1/retrieval` | Retrieval Test |
| **External** | `api/apps/sdk/session.py` | `ask_about_embedded` | `/api/v1/searchbots/ask` | Searchbot Ask |
| **External** | `api/apps/sdk/session.py` | `detail_share_embedded` | `/api/v1/searchbots/detail` | Searchbot Detail |
| **External** | `api/apps/sdk/session.py` | `mindmap` | `/api/v1/searchbots/mindmap` | Searchbot Mindmap |
| **External** | `api/apps/sdk/session.py` | `related_questions_embedded` | `/api/v1/searchbots/related_questions` | Searchbot Related Questions |
| **External** | `api/apps/sdk/session.py` | `retrieval_test_embedded` | `/api/v1/searchbots/retrieval_test` | Searchbot Retrieval Test |
| **External** | `api/apps/sdk/session.py` | `ask_about` | `/api/v1/sessions/ask` | Session Ask |
| **External** | `api/apps/sdk/session.py` | `related_questions` | `/api/v1/sessions/related_questions` | Session Related Questions |
| **External** | `api/apps/sdk/agents.py` | `webhook` | `/api/v1/webhook_test/<agent_id>` | Webhook Test |
| **External** | `api/apps/sdk/agents.py` | `webhook_trace` | `/api/v1/webhook_trace/<agent_id>` | Webhook Trace |
| **External** | `api/apps/sdk/doc.py` | `rm_chunk` | `/api/v1datasets/<dataset_id>/documents/<document_id>/chunks` | Remove Chunk |
## 2. Internal APIs (内部前端体系)
**Path Prefix:** `/v1/<app_name>` matches file `api/apps/<app_name>_app.py`
**Authentication:** Session/Cookie (`@login_required`)
**Primary Use:** RAGFlow Web Frontend.
**Selected Core Interfaces:**
| Interface Type | Python File Path | Class/Function Name | URL Pattern | Notes |
|---|---|---|---|---|
| Internal | `api/apps/user_app.py` | `login` | `/v1/user/login` | User Login (Frontend) |
| Internal | `api/apps/user_app.py` | `log_out` | `/v1/user/logout` | User Logout |
| Internal | `api/apps/user_app.py` | `user_add` | `/v1/user/register` | User Registration |
| Internal | `api/apps/user_app.py` | `user_profile` | `/v1/user/info` | User Profile Info |
| Internal | `api/apps/api_app.py` | `new_token` | `/v1/api/new_token` | Generate new API Token |
| Internal | `api/apps/conversation_app.py` | `set_conversation` | `/v1/conversation/set` | Create/Update Conversation |
| Internal | `api/apps/conversation_app.py` | `completion` | `/v1/conversation/completion` | Chat Conversation Completion |
| Internal | `api/apps/kb_app.py` | `list_kbs` | `/v1/kb/list` | List Knowledge Bases |
| Internal | `api/apps/kb_app.py` | `create` | `/v1/kb/create` | Create Knowledge Base |
| Internal | `api/apps/document_app.py` | `upload` | `/v1/document/upload` | Upload Document to KB |
| Internal | `api/apps/document_app.py` | `parse` | `/v1/document/parse` | Parse Document |
*(For a complete list of all 200+ internal APIs, please refer to the `api_endpoints.txt` file or the full scan results)*
@@ -1,279 +0,0 @@
# RAGFlow Agent 与 Dify 兼容接口详解 (Agent & Dify Compatibility)
## 1. Dify 兼容检索 - `retrieval`
**接口描述**: 模拟 Dify API 格式的知识库检索接口。此接口主要用于让现有的 Dify 客户端或系统能够方便地接入 RAGFlow 的知识库检索能力。它支持文本检索、混合检索以及通过元数据过滤文档。
**请求方法**: `POST`
**接口地址**: `/api/v1/dify/retrieval`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| knowledge_id | string | 是 | - | **知识库 ID**。 |
| query | string | 是 | - | **查询文本**。用户输入的检索问题。 |
| use_kg | boolean | 否 | false | **使用知识图谱**。是否结合知识图谱进行检索。 |
| retrieval_setting | object | 否 | {} | **检索配置**。包含相似度阈值和 Top-K。 |
| metadata_condition | object | 否 | {} | **元数据过滤条件**。用于筛选特定文档。 |
#### 参数详情 (Detail Objects)
**retrieval_setting**:
```json
{
"score_threshold": 0.5, // 相似度阈值 (default: 0.0)
"top_k": 5 // 返回数量 (default: 1024)
}
```
**metadata_condition**:
```json
{
"logic": "and", // 逻辑关系 (and/or)
"conditions": [
{
"name": "author", // 字段名
"comparison_operator": "eq",// 运算符 (eq, ne, gt, lt 等)
"value": "Alice" // 字段值
}
]
}
```
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"records": [
{
"content": "RAGFlow 是一个基于深度文档理解的检索增强生成引擎...",
"score": 0.92,
"title": "RAGFlow_Introduction.pdf",
"metadata": {
"doc_id": "doc_uuid_123",
"author": "Alice",
"publish_year": "2024"
}
},
{
"content": "DeepDOC 模型能够精准识别复杂的表格结构...",
"score": 0.88,
"title": "DeepDOC_Tech_Report.pdf",
"metadata": {
"doc_id": "doc_uuid_456",
"author": "Bob"
}
}
]
}
}
```
---
## 2. 创建 Agent 会话 - `create_agent_session`
**接口描述**: 创建一个新的 Agent 会话 (Session)。会话是用户与 Agent 交互的上下文容器,保存了历史对话记录和 DSL(领域特定语言)状态。
**请求方法**: `POST`
**接口地址**: `/api/v1/agents/<agent_id>/sessions`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| agent_id | string | 是 | **Agent ID**。 |
#### Query Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| user_id | string | 否 | **用户标识**。用于区分不同终端用户的会话。若不传,默认为当前 Tenant ID。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"id": "session_uuid_new_123",
"agent_id": "agent_uuid_abc",
"user_id": "user_123",
"source": "agent",
"dsl": { ... }, // 完整的 Agent DSL 定义
"messages": [
{
"role": "assistant",
"content": "你好!我是你的智能助手,有什么可以帮你的吗?" // Prologue (开场白)
}
]
}
}
```
---
## 3. 获取 Agent 会话列表 - `list_agent_session`
**接口描述**: 分页获取指定 Agent 下的会话列表。支持按 ID 或 User ID 过滤。
**请求方法**: `GET`
**接口地址**: `/api/v1/agents/<agent_id>/sessions`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| agent_id | string | 是 | **Agent ID**。 |
#### Query Parameters
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| page | int | 否 | 1 | **页码**。 |
| page_size | int | 否 | 30 | **每页数量**。 |
| orderby | string | 否 | "update_time" | **排序字段**。 |
| desc | boolean | 否 | true | **是否降序**。 |
| id | string | 否 | - | **会话 ID**。精确筛选。 |
| user_id | string | 否 | - | **用户标识**。筛选特定用户的会话。 |
| dsl | boolean | 否 | true | **包含 DSL**。是否在返回结果中包含完整的 DSL 结构 (数据量较大)。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": [
{
"id": "session_uuid_123",
"agent_id": "agent_uuid_abc",
"user_id": "user_123",
"create_time": 1715000000000,
"update_time": 1715000050000,
"source": "agent",
"messages": [
{
"role": "assistant",
"content": "Hi there!"
},
{
"role": "user",
"content": "What is RAG?"
}
]
}
]
}
```
---
## 4. 删除 Agent 会话 - `delete_agent_session`
**接口描述**: 批量删除 Agent 会话。
**请求方法**: `DELETE`
**接口地址**: `/api/v1/agents/<agent_id>/sessions`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| agent_id | string | 是 | **Agent ID**。 |
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| ids | array<string> | 否 | **会话 ID 列表**。若不传该参数,将尝试删除(或清空)该 Agent 下的所有会话(需谨慎)。 |
**Request Example**:
```json
{
"ids": ["session_id_1", "session_id_2"]
}
```
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"success_count": 2,
"errors": []
}
}
```
---
## 5. Agent 对话 (流式) - `agent_completions`
**接口描述**: 向 Agent 发送用户问题并获取回复。这是 Agent 交互的核心接口,支持 **Server-Sent Events (SSE)** 流式响应。Agent 会根据编排好的 DSL 流程执行(可能涉及多个节点、知识库检索、LLM 推理等),并实时推送执行过程和最终结果。
**请求方法**: `POST`
**接口地址**: `/api/v1/agents/<agent_id>/completions`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| agent_id | string | 是 | **Agent ID**。 |
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| session_id | string | 是 | - | **会话 ID**。必须是 `create_agent_session` 返回的 ID。 |
| question | string | 是 | - | **用户问题**。 |
| stream | boolean | 否 | true | **是否流式响应**。强烈建议设为 `true` 以获得更好的用户体验。 |
| return_trace | boolean | 否 | false | **返回执行轨迹**。如果为 `true`,流式响应中将包含各个节点的执行过程数据 (Trace)。 |
### 响应参数 (Stream Response)
**Content-Type**: `text/event-stream`
响应是一个 SSE 流,每一行以 `data:` 开头,包含一个 JSON 对象。
**Event Types**:
- `message`: 普通文本消息片段。
- `node_finished`: (当 `return_trace=true` 时) 节点执行完成事件,包含节点输出数据。
- `message_end`: 消息结束。
- `[DONE]`: 流结束标志。
#### Stream Chunk Examples:
**1. 文本生成片段 (message)**:
```text
data:{"code": 0, "message": "success", "data": {"content": "Hello", "reference": {}, "id": "msg_uuid_1"}, "event": "message"}
data:{"code": 0, "message": "success", "data": {"content": " world", "reference": {}, "id": "msg_uuid_1"}, "event": "message"}
```
**2. 节点执行轨迹 (node_finished, return_trace=true)**:
```text
data:{"code": 0, "message": "success", "data": {"component_id": "retrieval_node_1", "content": "...", "trace": [...]}, "event": "node_finished"}
```
**3. 最终结束 (DONE)**:
```text
data:[DONE]
```
#### Non-Stream Response (stream=false)
如果不使用流式响应,将等待 Agent 全流程执行完毕后一次性返回 JSON。
```json
{
"code": 0,
"message": "success",
"data": {
"content": "Hello world! This is the final answer.",
"reference": {
"chunk_id_1": { ... } // 引用来源
},
"trace": [ ... ] // 如果 return_trace=true
}
}
```
@@ -1,233 +0,0 @@
## 1. 获取 Agent 列表 - `list_agents`
**接口描述**: 分页查询当前租户下的所有 Agent 列表,支持按 ID 或标题筛选。
**请求方法**: `GET`
**接口地址**: `/api/v1/agents`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
#### Query Parameters
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| page | int | 否 | 1 | 页码 |
| page_size | int | 否 | 30 | 每页条数 |
| orderby | string | 否 | update_time | 排序字段 (create_time, update_time, title) |
| desc | boolean | 否 | True | 是否降序排列 (True: 降序, False: 升序) |
| id | string | 否 | - | 按 Agent ID 精确筛选 |
| title | string | 否 | - | 按 Agent 标题精确筛选 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": [
{
"id": "e0d34e2c-...",
"title": "My Assistant",
"description": "A helpful AI assistant",
"dsl": { ... }, // Agent 的 DSL 流程定义
"user_id": "tenant_123",
"avatar": "", // 头像 Base64 或 URL
"canvas_category": "Agent",
"create_time": 1715623400000,
"update_time": 1715624500000
}
]
}
```
---
## 2. 创建 Agent - `create_agent`
**接口描述**: 创建一个新的 Agent,必须包含标题和 DSL 定义。
**请求方法**: `POST`
**接口地址**: `/api/v1/agents`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| title | string | 是 | - | Agent 的名称 (必须唯一) |
| dsl | object | 是 | - | Agent 的流程定义 (节点、连线配置) |
| description | string | 否 | - | Agent 的功能描述 |
| avatar | string | 否 | - | Agent 头像 (Base64 字符串或 URL) |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": true
}
```
---
## 3. 更新 Agent - `update_agent`
**接口描述**: 更新指定 Agent 的配置信息,支持增量更新(仅传递需要修改的字段)。
**请求方法**: `PUT`
**接口地址**: `/api/v1/agents/<agent_id>`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| agent_id | string | 是 | 要更新的 Agent ID |
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| title | string | 否 | - | 新的 Agent 名称 |
| dsl | object | 否 | - | 新的 DSL 流程定义 |
| description | string | 否 | - | 新的功能描述 |
| avatar | string | 否 | - | 新的头像 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": true
}
```
---
## 4. 删除 Agent - `delete_agent`
**接口描述**: 根据 ID 删除指定的 Agent。此操作不可恢复。
**请求方法**: `DELETE`
**接口地址**: `/api/v1/agents/<agent_id>`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| agent_id | string | 是 | 要删除的 Agent ID |
#### Body Parameters (JSON)
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": true
}
```
---
## 5. Webhook 测试触发 - `webhook`
**接口描述**: 用于测试 Agent 的 Webhook 触发功能。该接口模拟外部系统调用,触发 Agent 按照配置的 "Begin" 节点逻辑开始执行。支持同步等待结果或流式返回(取决于 Agent 配置)。
**请求方法**: `POST` (支持 GET/PUT/DELETE 等,取决于 Canvas 配置)
**接口地址**: `/api/v1/webhook_test/<agent_id>`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| agent_id | string | 是 | Agent 的唯一标识符 |
#### Query / Headers / Body Parameters
**说明**: 此接口的参数完全动态,取决于 Agent 画布中 **"Begin" (开始)** 节点的 **Webhook** 配置。
- 如果配置了 Query 参数验证,则需在 URL 中传递对应参数。
- 如果配置了 Header 验证,则需传递对应 Header。
- **Body**: 通常为 JSON 格式,包含 Agent 运行所需的变量(inputs)或上下文数据。
**Body Example (JSON)**:
```json
{
"inputs": {
"topic": "AI Trends",
"style": "professional"
},
"query": "Start generation"
}
```
### 响应参数 (Response)
**Content-Type**: `application/json` (或 `text/event-stream`)
**即时响应模式 (Immediately)**:
```json
{
"code": 0,
"data": {
"content": "生成的回答内容...",
"usage": { ... }
}
}
```
**流式响应模式 (SSE)**:
如果不使用 `webhook_test` 而是生产环境 `webhook` 且配置为 SSE,则返回流式数据。但在 `webhook_test` 接口中,通常配合 `webhook_trace` 进行异步调试。
---
## 6. Webhook 执行轨迹查询 - `webhook_trace`
**接口描述**: 轮询查询 Agent 在 Webhook 测试触发后的执行日志和中间状态。采用长轮询或游标机制,实时获取执行进度。
**请求方法**: `GET`
**接口地址**: `/api/v1/webhook_trace/<agent_id>`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| agent_id | string | 是 | Agent 的唯一标识符 |
#### Query Parameters
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| since_ts | float | 否 | 当前时间 | 起始时间戳。返回此时间之后的日志事件。首次调用可不传(获取当前时间作为游标)。 |
| webhook_id | string | 否 | - | Webhook 会话 ID。用于锁定特定的某次执行记录。首次轮询时不传,接口会返回新生成的 ID。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"webhook_id": "YWdlbnxxxx...", // 当前追踪的会话 ID (加密串)
"finished": false, // 执行是否已结束 (true/false)
"next_since_ts": 1715629999.5, // 下一次轮询应使用的 since_ts
"events": [ // 本次轮询获取到的新事件列表
{
"ts": 1715629998.1,
"event": "message", // 事件类型: message, start_to_think, finished, error 等
"data": {
"content": "思考中...",
"reference": []
}
}
]
}
}
```
### 💡 最佳实践 (调试流程)
1. **初始化**: 调用 `GET /webhook_trace/<id>` (不带参数),获取 `next_since_ts` (记为 `T0`)。
2. **触发**: 调用 `POST /webhook_test/<id>` 发送测试数据。
3. **首帧捕获**: 循环调用 `GET /webhook_trace/<id>?since_ts=T0`,直到返回 `webhook_id` (记为 `WID`) 和第一批 `events`
4. **持续追踪**: 使用 `WID` 和响应中的 `next_since_ts` 持续轮询,直到 `data.finished == true`
@@ -1,164 +0,0 @@
# RAGFlow 对话交互接口详解 (Chat Completion & OpenAI Compatibility)
## 5. 对话助手对话 (流式) - `chat_completion`
**接口描述**: 发送问题给对话助手 (Assistant/Chat) 并获取回复。这是 RAGFlow 最核心的原生对话接口,支持 **Server-Sent Events (SSE)** 流式响应。它会根据助手绑定的知识库进行 RAG 检索生成。
**请求方法**: `POST`
**接口地址**: `/api/v1/chats/<chat_id>/completions`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| chat_id | string | 是 | **助手 ID**。 |
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| session_id | string | 是 | - | **会话 ID**。从 `create_chat_session` 获取。 |
| question | string | 是 | - | **用户问题**。 |
| stream | boolean | 否 | true | **是否流式响应**。 |
| quote | boolean | 否 | false | **返回引用**。是否在响应中包含检索到的引用片段。 |
| doc_ids | string | 否 | - | **限定文档 ID**。多个 ID 用逗号分隔,仅检索指定文档。 |
| metadata_condition | object | 否 | {} | **元数据过滤**。用于限定检索范围。 |
### 响应参数 (Stream Response)
**Content-Type**: `text/event-stream`
每一行数据以 `data:` 开头,包含一个 JSON 对象。
**Event Example**:
```text
data:{"code": 0, "message": "success", "data": {"answer": "Hello", "reference": {}}}
data:{"code": 0, "message": "success", "data": {"answer": " world!", "reference": {}}}
data:{"code": 0, "message": "success", "data": {"answer": "", "reference": {"chunk_1": {...}}}} // 引用数据
```
### 响应参数 (Non-Stream Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"answer": "Hello world! This is the generated response.",
"reference": {
"chunk_id_1": {
"content_with_weight": "Original text...",
"doc_name": "manual.pdf"
}
}
}
}
```
---
## 6. OpenAI 兼容对话 - `chat_completion_openai_like`
**接口描述**: 提供与 **OpenAI API (`/v1/chat/completions`)** 完全兼容的接口。允许开发者使用 LangChain、OpenAI Python SDK 或其他支持 OpenAI 协议的工具直接调用 RAGFlow,实现无缝迁移。
**请求方法**: `POST`
**接口地址**: `/api/v1/chats_openai/<chat_id>/chat/completions`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| chat_id | string | 是 | **助手 ID**。在此上下文中充当 "Base URL" 的一部分。 |
#### Body Parameters (JSON - OpenAI Standard)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| messages | array | 是 | **消息列表**。包含 `role` (system/user/assistant) 和 `content`。 |
| model | string | 是 | **模型名称**。可以是任意非空字符串 (RAGFlow 会使用助手预设的模型)。 |
| stream | boolean | 否 | **是否流式**。默认为 `true`。 |
**Request Example**:
```json
{
"model": "ragflow_default",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Explain quantum physics."}
],
"stream": true
}
```
### 响应参数 (Stream Response - OpenAI Format)
**Content-Type**: `text/event-stream`
严格遵循 OpenAI Chunk 格式:
```text
data: {"id": "chatcmpl-123", "object": "chat.completion.chunk", "created": 1715000000, "model": "model", "choices": [{"index": 0, "delta": {"role": "assistant", "content": ""}, "finish_reason": null}]}
data: {"id": "chatcmpl-123", "object": "chat.completion.chunk", "created": 1715000001, "model": "model", "choices": [{"index": 0, "delta": {"content": "Quantum"}, "finish_reason": null}]}
data: {"id": "chatcmpl-123", "object": "chat.completion.chunk", "created": 1715000002, "model": "model", "choices": [{"index": 0, "delta": {"content": " physics"}, "finish_reason": null}]}
data: [DONE]
```
---
## 7. 嵌入式 Chatbot 对话 - `chatbot_completions`
**接口描述**: 专为 **嵌入式窗口 (Embed Window)** 设计的公开对话接口。它通常用于将 RAGFlow 助手作为客服窗口嵌入到第三方网站。与普通接口不同,它通过 `Authorization` Header 传递 **Beta Token** (即 API Key) 进行鉴权,且通常面向最终用户。
**请求方法**: `POST`
**接口地址**: `/api/v1/chatbots/<dialog_id>/completions`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dialog_id | string | 是 | **助手 ID** (Dialog ID)。 |
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| question | string | 是 | - | **用户问题**。 |
| stream | boolean | 否 | true | **是否流式**。 |
| session_id | string | 否 | - | **会话 ID**。用于维持上下文。 |
| quote | boolean | 否 | false | **返回引用**。 |
### 响应参数 (Stream Response)
**Content-Type**: `text/event-stream`
`chat_completion` 类似,返回 RAGFlow 原生 SSE 格式。
```text
data:{"code": 0, "message": "success", "data": {"answer": "Here is the answer...", "reference": {}}}
```
---
## 8. Chatbot 初始化信息 - `chatbots_inputs`
**接口描述**: 获取嵌入式 Chatbot 的初始化配置信息。通常在前端组件加载时调用,用于展示助手的头像、名称、开场白 (Prologue) 等信息。
**请求方法**: `GET`
**接口地址**: `/api/v1/chatbots/<dialog_id>/info`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dialog_id | string | 是 | **助手 ID**。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"title": "IT Support Bot", // 助手名称
"avatar": "http://...", // 头像 URL
"prologue": "Hi! How can I help?" // 开场白
}
}
```
@@ -1,208 +0,0 @@
# RAGFlow 聊天助手会话管理接口详解 (Chat Assistant Session Management)
## 1. 创建会话 - `create_chat_session`
**接口描述**: 为指定的聊天助手 (Chat/Assistant) 创建一个新的会话。系统会自动加载该助手的开场白 (Prologue) 作为第一条消息。
**请求方法**: `POST`
**接口地址**: `/api/v1/chats/<chat_id>/sessions`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| chat_id | string | 是 | **助手 ID** (Assistant/Dialog ID)。 |
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| name | string | 否 | "New session" | **会话名称**。 |
| user_id | string | 否 | - | **用户标识**。用于区分不同终端用户的会话。 |
**Request Example**:
```json
{
"name": "Consulting regarding RAG",
"user_id": "client_001"
}
```
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"id": "session_uuid_123",
"chat_id": "chat_uuid_abc",
"name": "Consulting regarding RAG",
"user_id": "client_001",
"create_time": 1715000000000,
"create_date": "2024-05-01 10:00:00",
"update_time": 1715000000000,
"update_date": "2024-05-01 10:00:00",
"messages": [
{
"role": "assistant",
"content": "Hi! I am your AI assistant. How can I help you today?" // 自动加载的开场白
}
]
}
}
```
---
## 2. 获取会话列表 - `list_chat_session`
**接口描述**: 分页获取指定助手下的会话列表。支持按名称或用户 ID 过滤。
**请求方法**: `GET`
**接口地址**: `/api/v1/chats/<chat_id>/sessions`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| chat_id | string | 是 | **助手 ID**。 |
#### Query Parameters
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| page | int | 否 | 1 | **页码**。 |
| page_size | int | 否 | 30 | **每页数量**。 |
| orderby | string | 否 | "create_time" | **排序字段**。 |
| desc | boolean | 否 | true | **是否降序**。 |
| name | string | 否 | - | **会话名称搜索**。 |
| id | string | 否 | - | **会话 ID 精确筛选**。 |
| user_id | string | 否 | - | **用户标识筛选**。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": [
{
"id": "session_uuid_123",
"chat_id": "chat_uuid_abc",
"name": "Consulting regarding RAG",
"user_id": "client_001",
"create_time": 1715000000000,
"create_date": "2024-05-01 10:00:00",
"update_time": 1715000050000,
"update_date": "2024-05-01 10:00:50",
"messages": [
{
"role": "assistant",
"content": "Hi! I am your AI assistant. How can I help you today?"
},
{
"role": "user",
"content": "What is RAGFlow?"
}
]
},
{
"id": "session_uuid_456",
"chat_id": "chat_uuid_abc",
"name": "New session",
"user_id": "client_002",
"create_time": 1714900000000,
"create_date": "2024-04-30 09:00:00",
"update_time": 1714900000000,
"update_date": "2024-04-30 09:00:00",
"messages": [ ... ]
}
]
}
```
---
## 3. 更新会话 - `update_chat_session`
**接口描述**: 更新会话信息。目前主要用于 **重命名** 会话。注意:不能通过此接口修改消息记录 (`messages`)。
**请求方法**: `PUT`
**接口地址**: `/api/v1/chats/<chat_id>/sessions/<session_id>`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| chat_id | string | 是 | **助手 ID**。 |
| session_id | string | 是 | **会话 ID**。 |
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| name | string | 否 | **新的会话名称**。不可为空字符串。 |
**Request Example**:
```json
{
"name": "RAG Technical Discussion"
}
```
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": null
}
```
---
## 4. 删除会话 - `delete_chat_session`
**接口描述**: 批量删除指定助手下的会话。
**请求方法**: `DELETE`
**接口地址**: `/api/v1/chats/<chat_id>/sessions`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| chat_id | string | 是 | **助手 ID**。 |
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| ids | array<string> | 否 | **待删除的会话 ID 列表**。若不传该参数,将尝试删除该助手下的**所有会话**(请极其谨慎使用)。 |
**Request Example**:
```json
{
"ids": ["session_uuid_123", "session_uuid_456"]
}
```
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": null // 若全部删除成功
}
```
**Response (部分成功时)**:
```json
{
"code": 0,
"message": "Partially deleted 1 sessions with 1 errors",
"data": {
"success_count": 1,
"errors": ["The chat doesn't own the session session_uuid_999"]
}
}
```
@@ -1,213 +0,0 @@
## 1. 创建助手应用 - `create`
**接口描述**: 创建一个新的对话助手(Chat Assistant)。支持配置关联知识库、LLM 模型参数、提示词(Prompt)以及开场白等高级设置。
**请求方法**: `POST`
**接口地址**: `/api/v1/chats`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| name | string | 是 | - | 助手应用名称 (租户内唯一) |
| avatar | string | 否 | - | 助手头像 (URL 或 Base64 字符串) |
| description | string | 否 | "A helpful Assistant" | 助手的功能描述 |
| dataset_ids | array | 否 | [] | 关联的知识库 ID 列表 (必须是当前租户有权限访问的知识库) |
| llm | object | 否 | - | LLM 模型生成配置 (如模型名称、温度等) |
| prompt | object | 否 | - | 提示词引擎与检索配置 (包含 System Prompt, Opener, Rerank 等) |
**`llm` 对象详细结构**:
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| model_name | string | 是 | - | 模型名称 (例如: `deepseek-chat`, `gpt-4`, `qwen-turbo`) |
| temperature | float | 否 | 0.1 | 温度系数 (0.0 ~ 1.0),越高越随机,越低越确定 |
| top_p | float | 否 | 0.3 | 核采样概率阈值 |
| max_tokens | int | 否 | 512 | 单次回答的最大 Token 数限制 |
| presence_penalty | float | 否 | 0.4 | 话题新鲜度惩罚 (-2.0 ~ 2.0),正值鼓励讨论新话题 |
| frequency_penalty | float | 否 | 0.7 | 频率惩罚 (-2.0 ~ 2.0),正值减少重复词汇 |
**`prompt` 对象详细结构**:
*注意:此对象包含“提示词配置”与“检索策略配置”两部分。*
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| prompt | string | 否 | (内置默认提示词) | **System Prompt (系统提示词)**。给大模型的角色指令,例如 "你是一个客服..."。可使用变量占位符 `{knowledge}`。 |
| opener | string | 否 | "Hi! I'm your assistant..." | **开场白**。用户进入对话窗口时,助手自动发送的第一条欢迎语。 |
| show_quote | boolean | 否 | true | **显示引用**。回答中是否标注来源文档 (e.g., [1])。 |
| variables | array | 否 | `[{"key": "knowledge", "optional": false}]` | **变量列表**。定义用于填充 System Prompt 的变量。`knowledge` 为保留变量,代表检索到的知识片段。 |
| rerank_model | string | 否 | - | **重排序模型 ID**。配置后会对检索结果进行二次精排 (如 `BAAI/bge-reranker-v2-m3`)。 |
| keywords_similarity_weight | float | 否 | 0.7 | **关键字权重** (0.0 ~ 1.0)。控制混合检索的比例。更接近 1.0 侧重关键字匹配,更接近 0.0 侧重向量语义匹配。 |
| similarity_threshold | float | 否 | 0.2 | **相似度阈值** (0.0 ~ 1.0)。低于此相似度的文档块将被过滤,不喂给大模型。 |
| top_n | int | 否 | 6 | **Top N**。最终截取并输入给大模型的文档块数量。 |
| empty_response | string | 否 | "Sorry! No relevant..." | **空结果回复**。当没有检索到相关知识库内容时的兜底回复。 |
| tts | boolean | 否 | false | **启用 TTS**。是否将助手的文本回答自动转为语音播放。 |
| refine_multiturn | boolean | 否 | true | **多轮对话优化**。是否根据历史上下文重写用户问题 (Query Rewrite) 以提高检索准确率。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"id": "e0d34e2c-1234-5678-9xxx-xxxxxxxxxxxx",
"name": "企业知识库助手",
"avatar": "http://example.com/avatar.png",
"description": "用于回答员工内部问题的 AI",
"dataset_ids": ["kb_123", "kb_456"],
"llm": {
"model_name": "deepseek-chat",
"temperature": 0.1,
"top_p": 0.3,
"max_tokens": 512,
"presence_penalty": 0.4,
"frequency_penalty": 0.7
},
"prompt": {
"prompt": "你是一个智能助手,请根据以下知识回答问题:\n{knowledge}",
"opener": "你好!有什么可以帮你的?",
"show_quote": true,
"variables": [
{ "key": "knowledge", "optional": false }
],
"rerank_model": "",
"keywords_similarity_weight": 0.7,
"similarity_threshold": 0.2,
"top_n": 8,
"empty_response": "抱歉,知识库中没有找到相关答案。",
"tts": false,
"refine_multiturn": true
},
"create_time": 1715623400000,
"update_time": 1715624500000
}
}
```
---
## 2. 获取助手列表 - `list_chat`
**接口描述**: 获取当前租户下的所有助手应用列表。支持分页、排序及按名称/ID筛选。
**请求方法**: `GET`
**接口地址**: `/api/v1/chats`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
#### Query Parameters
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| page | int | 否 | 1 | 页码 |
| page_size | int | 否 | 30 | 每页条数 |
| orderby | string | 否 | create_time | 排序字段 (`create_time`, `update_time`) |
| desc | boolean | 否 | true | 是否降序排列 (`true`: 降序, `false`: 升序) |
| name | string | 否 | - | 按名称模糊搜索 (支持 partial match) |
| id | string | 否 | - | 按 ID 精确筛选 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": [
{
"id": "e0d34e2c-...",
"name": "客服机器人",
"avatar": "http://...",
"datasets": [
{
"id": "kb_1",
"name": "产品手册",
"avatar": "",
"chunk_num": 100
}
],
"llm": { ... }, // (结构同 create 接口响应)
"prompt": { ... }, // (结构同 create 接口响应)
"create_time": 1715623400000
}
]
}
```
---
## 3. 更新助手配置 - `update`
**接口描述**: 更新指定助手应用的配置信息。支持全量或增量更新部分字段。
**请求方法**: `PUT`
**接口地址**: `/api/v1/chats/<chat_id>`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| chat_id | string | 是 | 助手应用 ID |
#### Body Parameters (JSON)
*(以下所有字段均为可选,仅传递需要修改的字段即可)*
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| name | string | - | 新的助手名称 |
| avatar | string | - | 新的头像 URL 或 Base64 |
| dataset_ids | array | - | **全量替换**关联的知识库 ID 列表 |
| llm | object | - | 更新 LLM 配置。需包含 `model_name`,其他字段覆盖更新。 |
| prompt | object | - | 更新提示词配置。支持增量更新 (e.g. 只改 `opener`)。 |
| show_quotation | boolean | - | 是否显示引用来源 (此字段直接位于根对象下,对应 prompt.show_quote) |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": null
}
```
---
## 4. 批量删除助手 - `delete_chats`
**接口描述**: 批量删除一个或多个助手应用。
**请求方法**: `DELETE`
**接口地址**: `/api/v1/chats`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| ids | array<string> | 是 | 要删除的助手应用 ID 列表。**⚠️ 注意:若列表为空或不传,虽然后端有全量删除逻辑,但在实际业务中应严谨传递 ID。** |
**Request Example**:
```json
{
"ids": ["chat_id_1001", "chat_id_1002"]
}
```
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"success_count": 2, // 成功删除的数量
"errors": [] // 失败原因列表 (如 ID 不存在)
}
}
```
@@ -1,420 +0,0 @@
## 1. 创建知识库 - `create`
**接口描述**: 创建一个新的知识库(Dataset),用于存储和检索文档数据。支持配置嵌入模型(Embedding Model)、解析方法、权限范围等。
**请求方法**: `POST`
**接口地址**: `/api/v1/datasets`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| name | string | 是 | - | **知识库名称**。在同一个租户(Tenant)内必须唯一。 |
| avatar | string | 否 | "" | **知识库头像**。Base64 编码的图片字符串。 |
| description | string | 否 | "" | **描述信息**。用于说明知识库的用途或内容概要。 |
| embedding_model | string | 否 | (系统默认) | **嵌入模型名称** (例如 `BAAI/bge-large-zh-v1.5`)。若不传,则自动使用系统设置的默认 Embedding 模型。 |
| permission | string | 否 | "me" | **可见权限**`me`: 仅自己可见;`team`: 团队内所有成员可见。 |
| chunk_method | string | 否 | "naive" | **默认分块解析方法**。当上传文件未指定解析方式时使用。可选值: `naive` (通用), `manual` (手动), `qa` (Q&A拆分), `table` (表格), `paper` (论文), `book` (书籍), `laws` (法律), `presentation` (PPT), `picture` (图片), `one` (单文档), `email` (邮件)。 |
| parser_config | object | 否 | (见下文) | **解析器详细配置**。根据 `chunk_method` 的不同而变化。 |
**`parser_config` 默认配置参数 (Naive 通用模式)**:
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| chunk_token_num | int | 512 | **切片最大 Token 数**。超过该长度会被截断到下一块。 |
| delimiter | string | "\\n" | **分段分隔符**。用于识别段落边界。 |
| layout_recognize | string | "DeepDOC" | **布局识别模型**。用于处理复杂文档结构 (如 `DeepDOC``Simple`)。 |
| html4excel | boolean | false | **Excel转HTML**。是否将 Excel 表格转为 HTML 格式进行解析。 |
| auto_keywords | int | 0 | **自动关键词抽取**。0 表示不抽取;N>0 表示为每个切片抽取 N 个关键词。 |
| auto_questions | int | 0 | **自动问题生成**。0 表示不生成;N>0 表示为每个切片生成 N 个相关问题。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"id": "kb_uuid_12345678",
"name": "企业产品手册",
"avatar": "",
"tenant_id": "tenant_001",
"description": "存放所有产品相关的说明文档",
"embedding_model": "BAAI/bge-large-zh-v1.5",
"permission": "me",
"chunk_method": "naive",
"parser_config": {
"chunk_token_num": 512,
"delimiter": "\n",
"layout_recognize": "DeepDOC",
"html4excel": false,
"auto_keywords": 0,
"auto_questions": 0
},
"chunk_count": 0,
"document_count": 0,
"create_time": 1715623400000,
"update_time": 1715624500000
}
}
```
---
## 2. 删除知识库 - `delete`
**接口描述**: 批量删除一个或多个知识库。删除知识库将连带删除其中的所有文档和索引数据,**不可恢复**。
**请求方法**: `DELETE`
**接口地址**: `/api/v1/datasets`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| ids | array<string> | 是 | **ID 列表**。指定要删除的知识库 ID。如果传递 `null`,则会**清空当前租户下所有**知识库(高危操作,请谨慎使用)。 |
**Request Example**:
```json
{
"ids": ["kb_id_101", "kb_id_102"]
}
```
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "Successfully deleted 2 datasets, 0 failed...",
"data": {
"success_count": 2, // 成功删除的数量
"errors": [] // 失败的 ID 及原因列表
}
}
```
---
## 3. 获取知识库列表 - `list_datasets`
**接口描述**: 获取当前用户(及团队)有权限访问的知识库列表。支持分页、排序和筛选。
**请求方法**: `GET`
**接口地址**: `/api/v1/datasets`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
#### Query Parameters
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| page | int | 否 | 1 | **页码**。从 1 开始。 |
| page_size | int | 否 | 30 | **每页条数**。 |
| orderby | string | 否 | "create_time" | **排序字段**。可选值: `create_time` (创建时间), `update_time` (更新时间), `document_count` (文档数)。 |
| desc | boolean | 否 | true | **是否降序**`true`: 降序 (最新的在前); `false`: 升序。 |
| name | string | 否 | - | **名称筛选**。支持模糊匹配。 |
| id | string | 否 | - | **ID 筛选**。精确匹配知识库 ID。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": [
{
"id": "kb_uuid_123",
"name": "HR 政策库",
"document_count": 12, // 包含的文档数量
"token_num": 10240, // 总 Token 数
"chunk_count": 150, // 总切片数
"create_time": 1715623400000,
"permission": "team",
"embedding_model": "BAAI/bge-large-zh-v1.5"
}
],
"total": 1 // 匹配查询条件的总记录数 (用户分页计算)
}
```
---
## 4. 更新知识库配置 - `update`
**接口描述**: 更新指定知识库的配置信息。注意:如果知识库内已有解析过的切片,通常不允许修改嵌入模型 (`embedding_model`)。
**请求方法**: `PUT`
**接口地址**: `/api/v1/datasets/<dataset_id>`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dataset_id | string | 是 | 知识库 ID |
#### Body Parameters (JSON)
*(以下所有字段均为可选,仅传递需要修改的字段即可)*
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| name | string | - | **新名称**。需保持租户内唯一。 |
| avatar | string | - | **新头像**。Base64 字符串。 |
| description | string | - | **新描述**。 |
| permission | string | - | **新权限**`me``team`。 |
| embedding_model | string | - | **嵌入模型**。**注意**: 仅当知识库为空(chunk_count=0)时才允许修改。 |
| chunk_method | string | - | **默认解析方法**。修改后将应用于后续新上传的文件 (旧文件解析方式不变)。 |
| parser_config | object | - | **解析器配置**。全量覆盖旧配置 (结构参考 create 接口)。 |
| pagerank | int | 0 | **PageRank 权重**。仅在使用 Elasticsearch 引擎且需调整图谱权重时设置。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"id": "kb_uuid_...",
"name": "新名称",
"update_time": 1715629999000,
...
}
}
```
---
## 5. 获取知识图谱数据 - `knowledge_graph`
**接口描述**: 获取知识库构建的知识图谱数据,包含节点(Nodes)和边(Edges),用于前端可视化展示(如 ECharts 力导向图)。
**请求方法**: `GET`
**接口地址**: `/api/v1/datasets/<dataset_id>/knowledge_graph`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dataset_id | string | 是 | 知识库 ID |
#### Query Parameters
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"graph": {
"nodes": [
{
"id": "node_1",
"label": "人工智能", // 节点显示的文本
"pagerank": 0.05, // PageRank 权重 (决定节点大小)
"color": "#fcb", // 节点颜色
"img": "" // 节点图标 (如有)
},
{
"id": "node_2",
"label": "机器学习",
"pagerank": 0.03,
"color": "#e2b"
}
],
"edges": [
{
"source": "node_1", // 起始节点 ID
"target": "node_2", // 目标节点 ID
"weight": 0.8, // 边权重 (决定连线粗细)
"label": "includes" // 关系名称 (显示在连线上)
}
]
},
"mind_map": { // 思维导图结构的保留字段 (通常用于脑图展示)
"root": {
"id": "root_node",
"children": [...]
}
}
}
}
```
---
## 6. 清空知识图谱数据 - `delete_knowledge_graph`
**接口描述**: 删除指定知识库中已生成的知识图谱索引数据(包括所有实体节点和关系边)。
**注意**: 此操作**不会**删除原始文档或普通的向量索引,仅仅是重置图谱结构。如果需要重新生成图谱,请再次调用 `chunk` 相关接口或使用 `run_graphrag`
**请求方法**: `DELETE`
**接口地址**: `/api/v1/datasets/<dataset_id>/knowledge_graph`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dataset_id | string | 是 | 知识库 ID |
#### Body Parameters
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": true
}
```
---
## 7. 运行/触发 GraphRAG 索引任务 - `run_graphrag`
**接口描述**: 触发后台异步任务,对知识库中的文档进行 GraphRAG 索引构建。此过程会使用 LLM 抽取实体(Entities)和关系(Relationships),并构建全局社区摘要。
**前提条件**: 知识库中必须包含已解析的文档。
**请求方法**: `POST`
**接口地址**: `/api/v1/datasets/<dataset_id>/run_graphrag`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dataset_id | string | 是 | 知识库 ID |
#### Body Parameters (JSON)
*(Body 可为空 `{}`, 后续版本将扩展以下配置参数)*
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| entity_types | array | ["organization", "person", "geo", "event"] | **(预留)** 指定要抽取的实体类型列表。 |
| method | string | "light" | **(预留)** 构建模式: `light` (轻量级), `general` (标准), `complex` (深度)。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"graphrag_task_id": "task_uuid_12345678" // 异步任务 ID,用于后续追踪进度
}
}
```
---
## 8. 运行/触发 RAPTOR 递归摘要任务 - `run_raptor`
**接口描述**: 触发后台异步任务,对知识库中的文档运行 RAPTOR (Recursive Abstractive Processing for Tree-Organized Retrieval) 算法。
**功能说明**: 该算法会递归地对文档块进行聚类和摘要,生成多层级的树状索引,显著提升对长文档和复杂问题的回答能力。
**请求方法**: `POST`
**接口地址**: `/api/v1/datasets/<dataset_id>/run_raptor`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dataset_id | string | 是 | 知识库 ID |
#### Body Parameters (JSON)
*(Body 可为空 `{}`, 后续版本将扩展以下配置参数)*
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| max_cluster | int | 64 | **(预留)** 最大聚类数。 |
| prompt | string | (内置摘要提示词) | **(预留)** 用于生成摘要的 Prompt。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"raptor_task_id": "task_uuid_87654321" // 异步任务 ID
}
}
```
---
## 9. 查询 GraphRAG 任务进度 - `trace_graphrag`
**接口描述**: 查询指定知识库当前 **GraphRAG** 索引构建任务的实时状态。支持长轮询机制监测进度。
**请求方法**: `GET`
**接口地址**: `/api/v1/datasets/<dataset_id>/trace_graphrag`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dataset_id | string | 是 | 知识库 ID |
#### Query Parameters
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"id": "task_uuid_12345678", // 任务 ID
"doc_id": "doc_uuid_...", // 当前正在处理的文档 ID (如果是多文档任务)
"from_page": 0, // 当前处理的起始页码
"to_page": 10, // 当前处理的结束页码
"progress": 0.45, // **总进度** (0.0 ~ 1.0)。0.0: 未开始/刚开始; 1.0: 完成; -1.0: 失败。
"progress_msg": "Extracting entities from chunk 25...", // **当前状态描述**。用于前端展示 Loading 提示。
"create_time": 1715623400000,
"update_time": 1715624500000
}
}
```
---
## 10. 查询 RAPTOR 任务进度 - `trace_raptor`
**接口描述**: 查询指定知识库当前 **RAPTOR** 递归摘要任务的实时状态。
**请求方法**: `GET`
**接口地址**: `/api/v1/datasets/<dataset_id>/trace_raptor`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dataset_id | string | 是 | 知识库 ID |
#### Query Parameters
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"id": "task_uuid_87654321",
"progress": 1.0, // 进度值。1.0 表示树构建完成。
"progress_msg": "Tree construction completed.", // 状态消息。
"create_time": 1715629000000
}
}
```
@@ -1,757 +0,0 @@
## 1. 上传文档 - `upload`
**接口描述**: 向指定的知识库上传一个或多个文档文件。上传后,文档将立即被存入文件系统/对象存储,并在数据库中创建记录。默认解析状态为 `UNSTART` (未开始),解析配置将继承自 KnowledgeBase 的默认设置。
**请求方法**: `POST`
**接口地址**: `/api/v1/datasets/<dataset_id>/documents`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
**Content-Type**: `multipart/form-data`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dataset_id | string | 是 | **知识库 ID**。指定文档归属的知识库。 |
#### Form Data Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| file | file | 是 | **文件二进制流**。支持多文件上传 (Multiple Files)。<br>支持格式: PDF, DOCX, TXT, MD, CS, HTML, CSV, XLSX, PPTX 等。<br>单文件大小限制请参考系统配置 (默认通常为 10MB/100MB)。 |
| parent_path | string | 否 | **父级目录路径**。类似于文件系统的文件夹结构,默认为 `/`。如果指定 (如 `/docs/v1/`),文档将在该虚拟路径下列出。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": [
{
"id": "e457f92e3c0411ef8d4c0242ac120003",
"thumbnail": null,
"dataset_id": "d1234567890abcdef1234567890abcde",
"chunk_method": "naive",
"pipeline_id": null,
"parser_config": {
"chunk_token_num": 512,
"delimiter": "\\n",
"layout_recognize": "DeepDOC",
"html4excel": false,
"auto_keywords": 0,
"auto_questions": 0,
"topn_tags": 3,
"raptor": {
"use_raptor": false
},
"graphrag": {
"use_graphrag": false
}
},
"source_type": "local",
"type": "pdf",
"created_by": "user_id_123",
"name": "UserGuide_v2.pdf",
"location": "UserGuide_v2.pdf",
"size": 102400,
"token_count": 0,
"chunk_count": 0,
"progress": 0.0,
"progress_msg": "",
"process_begin_at": null,
"process_duration": 0.0,
"meta_fields": {},
"suffix": "pdf",
"run": "UNSTART",
"status": "1",
"create_time": 1715623400123,
"create_date": "2024-05-13 10:03:20",
"update_time": 1715623400123,
"update_date": "2024-05-13 10:03:20"
}
]
}
```
---
## 2. 获取文档列表 - `list_docs`
**接口描述**: 查询知识库下的文档列表。支持分页检索、关键词搜索、状态筛选等功能。
**请求方法**: `GET`
**接口地址**: `/api/v1/datasets/<dataset_id>/documents`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dataset_id | string | 是 | **知识库 ID**。 |
#### Query Parameters
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| page | int | 否 | 1 | **页码**。从 1 开始计数。 |
| page_size | int | 否 | 30 | **每页数量**。 |
| orderby | string | 否 | "create_time" | **排序字段**。支持 `create_time` (创建时间), `name` (文件名), `size` (大小) 等。 |
| desc | boolean | 否 | true | **是否降序**`true` (最新/最大在前), `false` (最旧/最小在前)。 |
| id | string | 否 | - | **精确筛选 ID**。仅返回指定 ID 的文档。 |
| name | string | 否 | - | **精确筛选文件名**。仅返回指定名称的文档。 |
| keywords | string | 否 | - | **模糊搜索**。匹配文档名称包含该关键词的记录。 |
| suffix | array | 否 | - | **文件后缀筛选** (如 `pdf`, `docx`)。 |
| run | array | 否 | - | **运行状态筛选**。可选值: `UNSTART`, `RUNNING`, `CANCEL`, `DONE`, `FAIL`。 |
| create_time_from | int | 否 | 0 | **起始时间戳** (毫秒)。查询在此时间之后创建的文档。 |
| create_time_to | int | 否 | 0 | **结束时间戳** (毫秒)。查询在此时间之前创建的文档。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"total": 128,
"docs": [
{
"id": "e457f92e3c0411ef8d4c0242ac120003",
"thumbnail": null,
"dataset_id": "d1234567890abcdef1234567890abcde",
"chunk_method": "naive",
"pipeline_id": null,
"parser_config": {
"chunk_token_num": 512,
"delimiter": "\\n",
"layout_recognize": "DeepDOC",
"html4excel": false,
"auto_keywords": 0,
"auto_questions": 0,
"topn_tags": 3,
"raptor": {
"use_raptor": false
},
"graphrag": {
"use_graphrag": false
}
},
"source_type": "local",
"type": "pdf",
"created_by": "user_id_123",
"name": "UserGuide_v2.pdf",
"location": "UserGuide_v2.pdf",
"size": 102400,
"token_count": 45000,
"chunk_count": 120,
"progress": 1.0,
"progress_msg": "Parsing finished",
"process_begin_at": "2024-05-13 10:05:00",
"process_duration": 45.2,
"meta_fields": {
"author": "RAGFlow Team",
"version": "2.0"
},
"suffix": "pdf",
"run": "DONE",
"status": "1",
"create_time": 1715623400123,
"create_date": "2024-05-13 10:03:20",
"update_time": 1715623450000,
"update_date": "2024-05-13 10:05:45"
}
]
}
}
```
---
## 3. 更新文档信息 - `update_doc`
**接口描述**: 更新文档的名称、状态或解析配置。
**特别注意**: 如果修改了 `chunk_method``parser_config`,后端会自动将 `run` 状态重置为 `UNSTART`,并清除已有的 chunk 数据,等待重新解析。
**请求方法**: `PUT`
**接口地址**: `/api/v1/datasets/<dataset_id>/documents/<document_id>`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dataset_id | string | 是 | **知识库 ID**。 |
| document_id | string | 是 | **文档 ID**。 |
#### Body Parameters (JSON)
*(仅需传递要修改的字段)*
| 参数名 | 类型 | 说明 |
|---|---|---|
| name | string | **新文档名称**。需包含文件后缀且不能改变原始文件类型 (如从 `.pdf` 改为 `.txt` 会导致错误)。 |
| enabled | boolean | **启用/禁用**`true`: 启用 (DEFAULT, 对应 status="1"); `false`: 禁用 (对应 status="0")。禁用后该文档不参与检索。 |
| chunk_method | string | **解析方法**。可选值: `naive`, `manual`, `qa`, `table`, `paper`, `book`, `laws`, `presentation`, `picture`, `one`, `knowledge_graph`, `email`。 |
| parser_config | object | **解析器详细配置**。应与 `chunk_method` 匹配。以下列出 `naive` (通用) 方法的完整配置参数。 |
**parser_config (Naive 模式全量参数)**:
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| chunk_token_num | int | 512 | **切片最大 Token 数**。 |
| delimiter | string | "\\n" | **分段符**。支持转义字符。 |
| layout_recognize | string | "DeepDOC" | **布局识别模型**。可选 `DeepDOC``Simple`。 |
| html4excel | boolean | false | **Excel转HTML**。是否将 Excel 解析为 HTML 表格。 |
| auto_keywords | int | 0 | **自动关键词数量**。0 表示不抽取。 |
| auto_questions | int | 0 | **自动问题数量**。0 表示不生成。 |
| topn_tags | int | 3 | **自动标签数量**。 |
| raptor | object | `{ "use_raptor": false }` | **RAPTOR 配置**。设置 `use_raptor: true` 可开启递归摘要索引。 |
| graphrag | object | `{ "use_graphrag": false }` | **GraphRAG 配置**。设置 `use_graphrag: true` 可开启图谱增强。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"id": "e457f92e3c0411ef8d4c0242ac120003",
"thumbnail": null,
"dataset_id": "d1234567890abcdef1234567890abcde",
"chunk_method": "naive",
"pipeline_id": null,
"parser_config": {
"chunk_token_num": 1024,
"delimiter": "\\n",
"layout_recognize": "DeepDOC",
"html4excel": false,
"auto_keywords": 0,
"auto_questions": 0,
"topn_tags": 3,
"raptor": {
"use_raptor": false
},
"graphrag": {
"use_graphrag": false
}
},
"source_type": "local",
"type": "pdf",
"created_by": "user_id_123",
"name": "Renamed_Guide.pdf",
"location": "UserGuide_v2.pdf",
"size": 102400,
"token_count": 45000,
"chunk_count": 0,
"progress": 0.0,
"progress_msg": "",
"process_begin_at": null,
"process_duration": 0.0,
"meta_fields": {},
"suffix": "pdf",
"run": "UNSTART",
"status": "0",
"create_time": 1715623400123,
"create_date": "2024-05-13 10:03:20",
"update_time": 1715629999000,
"update_date": "2024-05-13 12:00:00"
}
}
```
---
## 4. 删除文档 - `delete`
**接口描述**: 物理删除一个或多个文档。此操作不可恢复,将同时删除数据库记录、MinIO 中的源文件以及 Elasticsearch 中的所有相关切片索引。
**请求方法**: `DELETE`
**接口地址**: `/api/v1/datasets/<dataset_id>/documents`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dataset_id | string | 是 | **知识库 ID**。 |
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| ids | array<string> | 是 | **文档 ID 列表**。必须指定要删除的文档 ID。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": null
}
```
---
## 5. 下载/预览原始文件 - `download`
**接口描述**: 获取文档的原始二进制文件流。响应头将会包含 `Content-Disposition` 字段,指示浏览器以附件形式下载。
**请求方法**: `GET`
**接口地址**: `/api/v1/datasets/<dataset_id>/documents/<document_id>`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dataset_id | string | 是 | **知识库 ID**。 |
| document_id | string | 是 | **文档 ID**。 |
### 响应参数 (Response)
**Content-Type**: `application/octet-stream`
**Content-Disposition**: `attachment; filename="UserGuide_v2.pdf"`
*(直接返回文件的二进制数据流)*
## 6. 触发/重试文档解析 - `parse`
**接口描述**: 手动触发文档的解析任务。通常在上传文件后、或修改了解析配置(如 `chunk_method`)后调用此接口。支持批量触发。
**请求方法**: `POST`
**接口地址**: `/api/v1/datasets/<dataset_id>/chunks`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dataset_id | string | 是 | **知识库 ID**。 |
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| document_ids | array<string> | 是 | **文档 ID 列表**。指定需要(重新)解析的文档 ID。 |
**Request Example**:
```json
{
"document_ids": ["doc_id_1", "doc_id_2"]
}
```
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": null
}
```
---
## 7. 停止文档解析 - `stop_parsing`
**接口描述**: 停止当前正在进行的文档解析任务。
**请求方法**: `DELETE`
**接口地址**: `/api/v1/datasets/<dataset_id>/chunks`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dataset_id | string | 是 | **知识库 ID**。 |
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| document_ids | array<string> | 是 | **文档 ID 列表**。指定要停止解析的任务。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": null
}
```
---
## 8. 获取切片列表 - `list_chunks`
**接口描述**: 获取指定文档已解析出的切片(Chunk)列表。支持分页和关键词搜索。返回结果包含文档的详细元数据和具体的切片内容。
**请求方法**: `GET`
**接口地址**: `/api/v1/datasets/<dataset_id>/documents/<document_id>/chunks`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dataset_id | string | 是 | **知识库 ID**。 |
| document_id | string | 是 | **文档 ID**。 |
#### Query Parameters
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| page | int | 否 | 1 | **页码**。 |
| page_size | int | 否 | 30 | **每页数量**。 |
| keywords | string | 否 | - | **搜索关键词**。在切片内容中进行全文检索。 |
| id | string | 否 | - | **精确切片 ID**。若指定,则只返回该 ID 对应的切片。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"total": 150,
"chunks": [
{
"id": "e457f92e3c0411ef8d4c0242ac120003_0",
"content": "RAGFlow 是一款基于深度文档理解的开源 RAG(检索增强生成)引擎。它旨在为各种规模的企业提供精简的 RAG 工作流。RAGFlow 结合了传统文档处理的稳健性与现代大语言模型(LLM)的生成能力,确保在处理复杂格式数据(如 PDF 表格、扫描件等)时依然能保持极高的召回率和准确性。",
"document_id": "doc_uuid_123",
"docnm_kwd": "RAGFlow_UserGuide_v2.pdf",
"important_keywords": ["RAGFlow", "开源", "深度文档理解", "LLM"],
"questions": ["什么是 RAGFlow?", "RAGFlow 的主要特点是什么?"],
"image_id": "",
"dataset_id": "kb_uuid_456",
"available": true,
"positions": [1]
},
{
"id": "e457f92e3c0411ef8d4c0242ac120003_1",
"content": "主要特性:\n1. **深度文档解析**:内置 DeepDOC 识别引擎,精准还原表格、段落结构。\n2. **多路召回**:支持关键词 + 向量的混合检索。\n3. **可视化编排**:提供基于 Graph 的工作流编排能力。",
"document_id": "doc_uuid_123",
"docnm_kwd": "RAGFlow_UserGuide_v2.pdf",
"important_keywords": ["DeepDOC", "混合检索", "可视化编排"],
"questions": [],
"image_id": "img_uuid_789",
"dataset_id": "kb_uuid_456",
"available": true,
"positions": [2]
}
],
"doc": {
"id": "doc_uuid_123",
"name": "RAGFlow_UserGuide_v2.pdf",
"chunk_count": 150,
"token_count": 45000,
"chunk_method": "naive",
"run": "DONE",
"status": "1",
"progress": 1.0,
"progress_msg": "Parsing finished",
"process_begin_at": "2024-05-13 10:05:00",
"process_duration": 45.2,
"meta_fields": {
"author": "RAGFlow Team",
"version": "2.0"
},
"create_time": 1715623400123,
"create_date": "2024-05-13 10:03:20",
"update_time": 1715623450000,
"update_date": "2024-05-13 10:05:45",
"dataset_id": "kb_uuid_456"
}
}
}
```
---
## 9. 手动新增切片 - `add_chunk`
**接口描述**: 向指定文档中手动添加一个新的切片。系统会自动计算该切片的向量嵌入 (Embedding)。
**请求方法**: `POST`
**接口地址**: `/api/v1/datasets/<dataset_id>/documents/<document_id>/chunks`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dataset_id | string | 是 | **知识库 ID**。 |
| document_id | string | 是 | **文档 ID**。 |
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| content | string | 是 | **切片内容**。手动输入的文本内容。 |
| important_keywords | array<string> | 否 | **重要关键词**。用于关键词检索增强。 |
| questions | array<string> | 否 | **预设问题**。用于 Q&A 检索模式增强。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"chunk": {
"id": "new_chunk_uuid_999",
"content": "这是管理员手动添加的一条补充切片,用于修正文档中缺失的关键信息。",
"document_id": "doc_uuid_123",
"docnm_kwd": "RAGFlow_UserGuide_v2.pdf",
"important_keywords": ["手动添加", "补充信息"],
"questions": ["如何手动添加切片?"],
"image_id": "",
"dataset_id": "kb_uuid_456",
"available": true,
"positions": []
}
}
}
```
---
## 10. 修改切片信息 - `update_chunk`
**接口描述**: 修改已存在的切片内容、关键词、可用状态等。修改内容后,系统会自动重新计算向量。
**请求方法**: `PUT`
**接口地址**: `/api/v1/datasets/<dataset_id>/documents/<document_id>/chunks/<chunk_id>`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dataset_id | string | 是 | **知识库 ID**。 |
| document_id | string | 是 | **文档 ID**。 |
| chunk_id | string | 是 | **切片 ID**。 |
#### Body Parameters (JSON)
*(以下字段均为可选,仅传递需修改的字段)*
| 参数名 | 类型 | 说明 |
|---|---|---|
| content | string | **新的切片内容**。 |
| important_keywords | array<string> | **更新关键词列表**。覆盖原有列表。 |
| available | boolean | **启用/禁用**`true`: 启用 (默认); `false`: 禁用 (检索时将忽略此切片)。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": null
}
```
---
## 11. 删除切片 - `rm_chunk`
**接口描述**: 批量删除文档中的指定切片。
**请求方法**: `DELETE`
**接口地址**: `/api/v1/datasets/<dataset_id>/documents/<document_id>/chunks`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dataset_id | string | 是 | **知识库 ID**。 |
| document_id | string | 是 | **文档 ID**。 |
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| chunk_ids | array<string> | 是 | **切片 ID 列表**。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "deleted 2 chunks",
"data": null
}
```
## 12. 获取元数据摘要 - `metadata_summary`
**接口描述**: 获取知识库中所有文档的元数据摘要信息。通常用于前端展示知识库的数据分布概况,例如不同文件类型的数量统计、文件状态分布等。
**请求方法**: `GET`
**接口地址**: `/api/v1/datasets/<dataset_id>/metadata/summary`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dataset_id | string | 是 | **知识库 ID**。 |
#### Query Parameters
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"summary": {
"total_doc_count": 120,
"total_token_count": 500000,
"file_type_distribution": {
"pdf": 80,
"docx": 30,
"txt": 10
},
"status_distribution": {
"1": 118, // 正常启用
"0": 2 // 禁用
},
"custom_metadata": {
"author": {
"Alice": 50,
"Bob": 30
},
"department": {
"HR": 20,
"Engineering": 100
}
}
}
}
}
```
---
## 13. 批量更新元数据 - `metadata_batch_update`
**接口描述**: 对知识库中的文档进行批量元数据修改。支持基于复杂的条件筛选文档,然后执行批量更新或删除元数据字段的操作。
**请求方法**: `POST`
**接口地址**: `/api/v1/datasets/<dataset_id>/metadata/update`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dataset_id | string | 是 | **知识库 ID**。 |
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| selector | object | 否 | **筛选器**。定义要更新哪些文档。如果不传,可能作用于全量文档(请谨慎)。 |
| updates | array | 否 | **更新操作列表**。包含 `key``value`。 |
| deletes | array | 否 | **删除操作列表**。包含 `key`。 |
**Request Example (复杂场景)**:
```json
{
"selector": {
"document_ids": ["doc_id_101", "doc_id_102"],
"metadata_condition": {
"logic": "and",
"conditions": [
{"key": "author", "value": "OldName", "operator": "eq"},
{"key": "status", "value": "draft", "operator": "eq"}
]
}
},
"updates": [
{"key": "author", "value": "Admin"},
{"key": "reviewed_by", "value": "ManagerA"}
],
"deletes": [
{"key": "temp_tag"},
{"key": "draft_flag"}
]
}
```
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"updated": 2, // 实际更新成功的文档数量
"matched_docs": 2 // 匹配到的文档数量
}
}
```
---
## 14. 检索测试 (Hit Test) - `retrieval_test`
**接口描述**: 在指定的知识库中进行模拟检索测试。此接口用于验证分段(Chunk)质量、检索参数(相似度阈值、Top K)的效果,是调试 RAG 效果的核心工具。
**请求方法**: `POST`
**接口地址**: `/api/v1/retrieval`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
**注意**: 即使是简单的查询,由于包含较多配置参数,本接口也设计为 `POST` 请求。
### 请求参数 (Request)
#### Path Parameters
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| dataset_ids | array<string> | 是 | - | **目标知识库 ID 列表**。支持跨多个知识库检索。 |
| question | string | 是 | - | **用户查询问题**。 |
| similarity_threshold | float | 否 | 0.2 | **相似度阈值**。低于此分数的 Chunk 将被过滤。 |
| vector_similarity_weight | float | 否 | 0.3 | **向量权重**。混合检索时,向量检索结果的权重 (0~1)。剩余权重归于关键词检索。 |
| top_k | int | 否 | 1024 | **初筛数量**。向量检索返回的候选切片数量。 |
| rerank_id | string | 否 | - | **重排模型 ID**。若指定,将对检索结果进行 Rerank 二次排序。 |
| highlight | boolean | 否 | true | **高亮匹配**。是否在返回内容中高亮关键词。 |
| keyword | boolean | 否 | false | **关键词增强**。是否使用 LLM 提取问题关键词以增强检索。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"total": 15,
"chunks": [
{
"id": "e457f92e3c0411ef8d4c0242ac120003_12",
"content": "RAGFlow 支持多种文档解析模式,其中 DeepDOC 模式特别适合处理包含大量表格和扫描件的 PDF 文档。它使用深度学习模型识别文档布局,精准提取表格内容。",
"document_id": "doc_uuid_123",
"dataset_id": "kb_uuid_456",
"document_name": "RAGFlow_UserGuide_v2.pdf",
"document_keyword": "RAGFlow_UserGuide_v2.pdf",
"similarity": 0.88,
"vector_similarity": 0.85,
"term_similarity": 0.92,
"index": 12,
"highlight": "RAGFlow 支持多种<em>文档解析模式</em>,其中 <em>DeepDOC</em> 模式特别适合处理包含大量表格和扫描件的 PDF 文档。",
"important_keywords": ["DeepDOC", "PDF"],
"questions": ["DeepDOC 模式有什么用?"],
"image_id": "",
"positions": [12]
},
{
"id": "e457f92e3c0411ef8d4c0242ac120003_15",
"content": "如果文档主要由纯文本构成,建议使用 Naive 模式。该模式解析速度快,适合通用场景。",
"document_id": "doc_uuid_123",
"dataset_id": "kb_uuid_456",
"document_name": "RAGFlow_UserGuide_v2.pdf",
"document_keyword": "RAGFlow_UserGuide_v2.pdf",
"similarity": 0.45,
"vector_similarity": 0.40,
"term_similarity": 0.50,
"index": 15,
"highlight": "如果文档主要由纯文本构成,建议使用 <em>Naive</em> 模式。",
"important_keywords": ["Naive", "纯文本"],
"questions": [],
"image_id": "",
"positions": [15]
}
],
"doc_aggs": [
{
"doc_name": "RAGFlow_UserGuide_v2.pdf",
"doc_id": "doc_uuid_123",
"count": 2
}
]
}
}
```
@@ -1,503 +0,0 @@
# RAGFlow 文件管理接口详解 (File Management API)
## 1. 上传文件 - `upload`
**接口描述**: 上传一个或多个文件到指定文件夹。支持多文件上传 (Multipart)。上传成功后,文件将存储在 MinIO/S3 中,并返回文件元数据列表。
**请求方法**: `POST`
**接口地址**: `/api/v1/file/upload`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
**Content-Type**: `multipart/form-data`
### 请求参数 (Request)
#### Form Data Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| file | file | 是 | **文件二进制流**。支持多文件上传。 |
| parent_id | string | 否 | **父级目录 ID**。如果省略,默认上传到根目录 (root)。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": [
{
"id": "e457f92e3c0411ef8d4c0242ac120003",
"parent_id": "root_folder_id_123",
"tenant_id": "tenant_uuid_456",
"created_by": "user_uuid_789",
"type": "pdf",
"name": "ProjectReport.pdf",
"location": "ProjectReport.pdf",
"size": 204800,
"source_type": "",
"create_time": 1715623400123,
"create_date": "2024-05-13 10:03:20",
"update_time": 1715623400123,
"update_date": "2024-05-13 10:03:20"
}
]
}
```
---
## 2. 新建文件夹 - `create`
**接口描述**: 在指定父目录下创建一个新的文件夹(逻辑目录)。
**请求方法**: `POST`
**接口地址**: `/api/v1/file/create`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| name | string | 是 | **文件夹名称**。同一目录下不可重名。 |
| parent_id | string | 否 | **父级目录 ID**。省略则默认为根目录。 |
| type | string | 是 | **类型**。固定值为 `FOLDER` 创建文件夹。 |
**Request Example**:
```json
{
"name": "Year2024_Reports",
"parent_id": "root_folder_id_123",
"type": "FOLDER"
}
```
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"id": "folder_uuid_abc",
"parent_id": "root_folder_id_123",
"tenant_id": "tenant_uuid_456",
"created_by": "user_uuid_789",
"name": "Year2024_Reports",
"location": "",
"size": 0,
"type": "folder",
"source_type": "",
"create_time": 1715623500000,
"create_date": "2024-05-13 10:05:00",
"update_time": 1715623500000,
"update_date": "2024-05-13 10:05:00"
}
}
```
---
## 3. 获取文件列表 - `list_files`
**接口描述**: 分页获取指定文件夹下的文件和子文件夹列表。支持按名称模糊搜索。
**请求方法**: `GET`
**接口地址**: `/api/v1/file/list`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Query Parameters
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| parent_id | string | 否 | (Root) | **父级目录 ID**。指定要查看的目录 ID。 |
| keywords | string | 否 | - | **搜索关键词**。按文件名模糊搜索。 |
| page | int | 否 | 1 | **页码**。 |
| page_size | int | 否 | 15 | **每页数量**。 |
| orderby | string | 否 | "create_time" | **排序字段**。 |
| desc | boolean | 否 | true | **是否降序**。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"total": 25,
"parent_folder": {
"id": "root_folder_id_123",
"parent_id": "",
"tenant_id": "tenant_uuid_456",
"created_by": "system",
"name": "ROOT",
"location": "",
"size": 0,
"type": "folder",
"source_type": "",
"create_time": 1710000000000,
"create_date": "2024-03-01 00:00:00",
"update_time": 1710000000000,
"update_date": "2024-03-01 00:00:00"
},
"files": [
{
"id": "folder_uuid_abc",
"parent_id": "root_folder_id_123",
"tenant_id": "tenant_uuid_456",
"created_by": "user_uuid_789",
"name": "Year2024_Reports",
"location": "",
"size": 0,
"type": "folder",
"source_type": "",
"create_time": 1715623500000,
"create_date": "2024-05-13 10:05:00",
"update_time": 1715623500000,
"update_date": "2024-05-13 10:05:00"
},
{
"id": "e457f92e3c0411ef8d4c0242ac120003",
"parent_id": "root_folder_id_123",
"tenant_id": "tenant_uuid_456",
"created_by": "user_uuid_789",
"name": "ProjectReport.pdf",
"location": "ProjectReport.pdf",
"size": 204800,
"type": "pdf",
"source_type": "",
"create_time": 1715623400123,
"create_date": "2024-05-13 10:03:20",
"update_time": 1715623400123,
"update_date": "2024-05-13 10:03:20"
}
]
}
}
```
---
## 4. 获取文件流 (下载) - `get`
**接口描述**: 通过文件 ID 下载文件内容。不同于获取元数据,该接口直接返回文件的二进制流(Octet-stream 或 Image 等)。
**请求方法**: `GET`
**接口地址**: `/api/v1/file/get/<file_id>`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| file_id | string | 是 | **文件 ID**。 |
### 响应参数 (Response)
**Content-Type**: `application/octet-stream` (或具体 MIME 类型如 `image/png`)
*(返回二进制文件流)*
---
## 5. 下载附件 - `download_attachment`
**接口描述**: 这是一个通用的附件下载接口,通常用于系统内部引用或特定路径的下载。它使用 `attachment_id`(通常对应 MinIO 中的存储路径/Key)来检索文件。
**请求方法**: `GET`
**接口地址**: `/api/v1/file/download/<attachment_id>`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| attachment_id | string | 是 | **附件 ID / 存储 Key**。通常对应底层存储的唯一标识符。 |
#### Query Parameters
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| ext | string | 否 | "markdown" | **文件扩展名**。用于设置响应头中的 Content-Type。 |
### 响应参数 (Response)
**Content-Type**: `application/octet-stream` (或根据 ext 参数推断)
*(返回二进制文件流)*
## 6. 重命名文件/文件夹 - `rename`
**接口描述**: 修改文件或文件夹的名称。对于文件,通常不允许修改扩展名(后缀)。
**请求方法**: `POST`
**接口地址**: `/api/v1/file/rename`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| file_id | string | 是 | **目标文件/文件夹 ID**。 |
| name | string | 是 | **新名称**。需符合文件命名规范,且同一目录下不可重名。 |
**Request Example**:
```json
{
"file_id": "file_uuid_123",
"name": "New_Report_Final.pdf"
}
```
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": true
}
```
---
## 7. 移动文件/文件夹 - `move`
**接口描述**: 批量移动文件或文件夹到指定的目录 (Move)。
**请求方法**: `POST`
**接口地址**: `/api/v1/file/mv`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| src_file_ids | array<string> | 是 | **源文件/文件夹 ID 列表**。支持批量移动。 |
| dest_file_id | string | 是 | **目标文件夹 ID**。必须是已存在的文件夹 ID。 |
**Request Example**:
```json
{
"src_file_ids": ["file_id_1", "file_id_2"],
"dest_file_id": "folder_id_target"
}
```
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": true
}
```
---
## 8. 删除文件/文件夹 - `rm`
**接口描述**: 批量删除文件或文件夹。如果是文件夹,将递归删除其下的所有内容。此操作不可恢复。
**请求方法**: `POST`
**接口地址**: `/api/v1/file/rm`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| file_ids | array<string> | 是 | **待删除的文件/文件夹 ID 列表**。 |
**Request Example**:
```json
{
"file_ids": ["file_uuid_to_delete_1", "folder_uuid_to_delete_2"]
}
```
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": true
}
```
---
## 9. 文件转知识库文档 - `convert`
**接口描述**: 将已上传的文件(File)导入到指定的知识库(Dataset)中,转换为文档(Document)并进行解析。这是一个“文件 -> 知识库”的桥接操作。
**请求方法**: `POST`
**接口地址**: `/api/v1/file/convert`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| file_ids | array<string> | 是 | **源文件 ID 列表**。必须是已存在于文件管理系统中的 ID。 |
| kb_ids | array<string> | 是 | **目标知识库 ID 列表**。文件将被同时导入到这些知识库中。 |
**Request Example**:
```json
{
"file_ids": ["file_uuid_pdf_1", "file_uuid_txt_2"],
"kb_ids": ["dataset_uuid_A"]
}
```
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": [
{
"id": "mapping_uuid_1",
"file_id": "file_uuid_pdf_1",
"document_id": "doc_uuid_created_in_kb_A",
"create_time": 1715623600123,
"create_date": "2024-05-13 10:06:40",
"update_time": 1715623600123,
"update_date": "2024-05-13 10:06:40"
},
{
"id": "mapping_uuid_2",
"file_id": "file_uuid_txt_2",
"document_id": "doc_uuid_created_in_kb_A",
"create_time": 1715623600124,
"create_date": "2024-05-13 10:06:40",
"update_time": 1715623600124,
"update_date": "2024-05-13 10:06:40"
}
]
}
```
## 10. 获取根目录信息 - `get_root_folder`
**接口描述**: 获取当前用户的根目录文件夹信息。每个用户(Tenant)都有且仅有一个系统自动创建的根目录。
**请求方法**: `GET`
**接口地址**: `/api/v1/file/root_folder`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Query Parameters
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"root_folder": {
"id": "root_folder_id_123",
"parent_id": "",
"tenant_id": "tenant_uuid_456",
"created_by": "system",
"name": "ROOT",
"location": "",
"size": 0,
"type": "folder",
"source_type": "",
"create_time": 1710000000000,
"create_date": "2024-03-01 00:00:00",
"update_time": 1710000000000,
"update_date": "2024-03-01 00:00:00"
}
}
}
```
---
## 11. 获取父目录信息 - `get_parent_folder`
**接口描述**: 获取指定文件或文件夹的直接父级目录信息。
**请求方法**: `GET`
**接口地址**: `/api/v1/file/parent_folder`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Query Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| file_id | string | 是 | **当前文件/文件夹 ID**。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"parent_folder": {
"id": "root_folder_id_123",
"parent_id": "",
"tenant_id": "tenant_uuid_456",
"created_by": "system",
"name": "ROOT",
"location": "",
"size": 0,
"type": "folder",
"source_type": "",
"create_time": 1710000000000,
"create_date": "2024-03-01 00:00:00",
"update_time": 1710000000000,
"update_date": "2024-03-01 00:00:00"
}
}
}
```
---
## 12. 获取完整路径 (面包屑) - `get_all_parent_folders`
**接口描述**: 获取指定文件或文件夹的所有上级目录列表,形成完整的路径链。返回的列表顺序通常是从根目录到直接父目录(有序)。此接口常用于前端展示“面包屑导航” (Breadcrumbs)。
**请求方法**: `GET`
**接口地址**: `/api/v1/file/all_parent_folder`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Query Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| file_id | string | 是 | **目标文件/文件夹 ID**。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"parent_folders": [
{
"id": "root_folder_id_123",
"parent_id": "",
"tenant_id": "tenant_uuid_456",
"created_by": "system",
"name": "ROOT",
"location": "",
"size": 0,
"type": "folder",
"source_type": "",
"create_time": 1710000000000,
"create_date": "2024-03-01 00:00:00",
"update_time": 1710000000000,
"update_date": "2024-03-01 00:00:00"
},
{
"id": "folder_project_a_id",
"parent_id": "root_folder_id_123",
"tenant_id": "tenant_uuid_456",
"created_by": "user_id_001",
"name": "Project A Docs",
"location": "",
"size": 0,
"type": "folder",
"source_type": "",
"create_time": 1715000000000,
"create_date": "2024-05-01 09:00:00",
"update_time": 1715000000000,
"update_date": "2024-05-01 09:00:00"
}
]
}
}
```
@@ -1,228 +0,0 @@
# RAGFlow 搜索机器人 & AgentBot 接口详解 (SearchBot & AgentBot)
## 1. 搜索机器人对话 - `ask_about_embedded`
**接口描述**: 面向 **SearchBot (搜索机器人)** 的核心对话接口,通常用于嵌入式知识库问答场景。与普通 Chat 不同,它更侧重于从指定的 `kb_ids` 中直接检索答案,且鉴权使用 `Authorization: Bearer <Beta_Token>` (即 API Key)。
**请求方法**: `POST`
**接口地址**: `/api/v1/searchbots/ask`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| question | string | 是 | - | **用户问题**。 |
| kb_ids | array<string> | 是 | - | **知识库 ID 列表**。限定从哪些知识库中检索。 |
| search_id | string | 否 | - | **搜索应用 ID**。如果指定,将使用该搜索应用的配置 (Search App Config)。 |
**Request Example**:
```json
{
"question": "What is the refund policy?",
"kb_ids": ["dataset_uuid_1", "dataset_uuid_2"],
"search_id": "search_app_uuid_abc"
}
```
### 响应参数 (Stream Response)
**Content-Type**: `text/event-stream`
```text
data:{"code": 0, "message": "", "data": {"answer": "According to the ", "reference": {}}}
data:{"code": 0, "message": "", "data": {"answer": "policy, refunds are processed within 7 days.", "reference": {"chunk_1": {"content_with_weight": "Refunds...", "doc_name": "policy.pdf"}}}}
data:{"code": 0, "message": "", "data": true} // 结束标志
```
---
## 2. 获取思维导图 - `mindmap`
**接口描述**: 根据用户的查询或对话上下文,生成用于前端展示的思维导图数据结构。这通常用于帮助用户梳理复杂的搜索结果或知识结构。
**请求方法**: `POST`
**接口地址**: `/api/v1/searchbots/mindmap`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| question | string | 是 | **用户问题/主题**。 |
| kb_ids | array<string> | 是 | **知识库 ID 列表**。 |
| search_id | string | 否 | **搜索应用 ID**。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"root": {
"text": "Refund Policy", // 根节点文本
"children": [
{
"text": "Conditions",
"children": [
{ "text": "Product defect" },
{ "text": "Shipping error" }
]
},
{
"text": "Timeline",
"children": [
{ "text": "7-14 business days" }
]
}
]
}
}
}
```
---
## 3. 获取相关推荐问题 - `related_questions_embedded`
**接口描述**: 根据用户当前的问题,生成一组相关的推荐问题 (Suggest Questions)。常用于搜索结果页底部的“猜你想问”。
**请求方法**: `POST`
**接口地址**: `/api/v1/searchbots/related_questions`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| question | string | 是 | **用户当前问题**。 |
| search_id | string | 否 | **搜索应用 ID**。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": [
"How to apply for a refund online?",
"What items are non-refundable?",
"Contact customer support"
]
}
```
---
## 4. 获取 AgentBot 输入项 - `begin_inputs`
**接口描述**: 获取 **AgentBot** (嵌入式 Agent) 的初始化信息,特别是前置输入项 (Prolog/Inputs)。这用于在用户开始对话前,展示一个表单让用户输入必要信息(如姓名、邮箱、API Key 等),这些信息会被传递给 Agent 的 `Begin` 节点。
**请求方法**: `GET`
**接口地址**: `/api/v1/agentbots/<agent_id>/inputs`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| agent_id | string | 是 | **Agent ID**。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"title": "Booking Assistant",
"avatar": "http://...",
"prologue": "Welcome! Please tell me your details.",
"inputs": { // `Begin` 节点定义的输入变量
"user_name": {
"type": "string",
"description": "Your Name",
"required": true
},
"email": {
"type": "string",
"description": "Contact Email",
"required": false
}
},
"mode": "chat"
}
}
```
---
## 5. AgentBot 对话交互 - `agent_bot_completions`
**接口描述**: 面向 **AgentBot** 的嵌入式对话接口。与 `agent_completions` 类似,但它专为无需登录的 C 端用户设计,通过 API Key 鉴权。它支持完整的 Agent 流程执行和流式响应。
**请求方法**: `POST`
**接口地址**: `/api/v1/agentbots/<agent_id>/completions`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| agent_id | string | 是 | **Agent ID**。 |
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| session_id | string | 是 | **会话 ID**。 |
| inputs | object | 否 | **前置输入值**。对应 `begin_inputs` 中定义的变量,如 `{"user_name": "Alice"}`。 |
| query | string | 否 | **用户输入**。 |
| stream | boolean | 否 | **是否流式**。默认 `true`。 |
**Request Example**:
```json
{
"session_id": "session_uuid_123",
"inputs": {
"user_name": "Bob"
},
"query": "I want to book a room.",
"stream": true
}
```
### 响应参数 (Stream Response)
**Content-Type**: `text/event-stream`
```text
data:{"event": "message", "data": {"content": "Hello Bob, ", "reference": {}}}
data:{"event": "message", "data": {"content": "when do you want to check in?", "reference": {}}}
```
---
## 6. Agent OpenAI 兼容接口 - `agents_completion_openai_compatibility`
**接口描述**: 专门针对 Agent 的 **OpenAI 兼容** 接口。这使得外部工具可以像调用 OpenAI Chat Completion 一样调用 RAGFlow 配置好的复杂 Agent。
**请求方法**: `POST`
**接口地址**: `/api/v1/agents_openai/<agent_id>/chat/completions`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Path Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| agent_id | string | 是 | **Agent ID**。 |
#### Body Parameters (OpenAI Standard)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| messages | array | 是 | 包含 `role`, `content` 的消息数组。 |
| model | string | 是 | 占位符,任意字符串。 |
| stream | boolean | 否 | 默认 `true`。 |
### 响应参数 (Stream Response - OpenAI Format)
**Content-Type**: `text/event-stream`
```text
data: {"id": "agent-chat-uuid", "object": "chat.completion.chunk", "created": 1715000000, "model": "ragflow_agent", "choices": [{"index": 0, "delta": {"role": "assistant", "content": ""}, "finish_reason": null}]}
data: {"id": "agent-chat-uuid", "object": "chat.completion.chunk", "created": 1715000001, "model": "ragflow_agent", "choices": [{"index": 0, "delta": {"content": "Processing your request..."}, "finish_reason": null}]}
data: [DONE]
```
@@ -1,168 +0,0 @@
# RAGFlow SearchBot 补充与通用会话接口详解 (Session Extras)
## 1. 获取引用详情 - `detail_share_embedded`
**接口描述**: 当用户点击 SearchBot 回复中的引用标号 (e.g., [1]) 时,调用此接口获取该引用的详细内容(包括原文片段、来源文档名等)。此接口通常用于前端展示“引用来源”侧边栏或弹窗。它使用 API Key (Beta Token) 进行鉴权。
**请求方法**: `GET`
**接口地址**: `/api/v1/searchbots/detail`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Query Parameters
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| search_id | string | 是 | **搜索应用/SearchBot ID**。此接口需要验证调用者是否有权访问该 SearchBot。 |
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"id": "search_app_uuid_123",
"title": "IT Knowledge Base",
"description": "Tech support search bot",
"kb_ids": ["kb_uuid_1", "kb_uuid_2"],
"search_config": {
"top_k": 5,
"similarity_threshold": 0.5
},
// 注意:此接口目前主要返回 Search App 的详情配置,
// 前端通常使用 search_config 或其他信息来辅助展示引用。
// 具体引用内容的文本通常已包含在 `ask` 接口的 `reference` 字段中。
}
}
```
---
## 2. SearchBot 检索测试 - `retrieval_test_embedded`
**接口描述**: 面向 SearchBot 的**检索效果测试**接口。它不通过 LLM 生成答案,而是直接返回 RAG 检索到的文档片段 (`chunks`)。这用于调试 SearchBot 的检索参数(如相似度阈值、Top-K)是否合理。
**请求方法**: `POST`
**接口地址**: `/api/v1/searchbots/retrieval_test`
**鉴权方式**: Header `Authorization: Bearer <API_KEY>`
### 请求参数 (Request)
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| kb_id | string/array | 是 | - | **知识库 ID** (或列表)。支持单个 ID 字符串或 ID 列表。 |
| question | string | 是 | - | **测试查询词**。 |
| page | int | 否 | 1 | **页码**。 |
| size | int | 否 | 30 | **每页数量**。 |
| doc_ids | array<string> | 否 | - | **限定文档 ID**。仅在指定文档中检索。 |
| similarity_threshold | float | 否 | 0.0 | **相似度阈值**。 |
| top_k | int | 否 | 1024 | **Top-K 数量**。 |
| highlight | boolean | 否 | false | **高亮匹配**。是否在返回内容中标记匹配关键词。 |
**Request Example**:
```json
{
"kb_id": ["dataset_uuid_1"],
"question": "refund policy",
"top_k": 5,
"highlight": true
}
```
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": {
"total": 12, // 命中总是
"chunks": [
{
"content_with_weight": "Refunds are processed within <em>7 days</em>...", // 支持高亮
"doc_name": "policy.pdf",
"doc_id": "doc_uuid_101",
"similarity": 0.92,
"img_id": ""
},
{
"content_with_weight": "Product return guidelines...",
"doc_name": "guidelines.docx",
"doc_id": "doc_uuid_102",
"similarity": 0.88
}
],
"labels": [] // 如果启用了查询标签功能
}
}
```
---
## 3. 通用会话问答 - `ask_about`
**接口描述**: **内部/测试用**的通用会话问答接口。与 `ask_embedded` 不同,此接口通常用于 RAGFlow 控制台内部的“调试”或“预览”功能,鉴权依赖用户的登录 Token (User Token),且必须显式指定 `dataset_ids`。它不绑定特定的 Chat/Agent/SearchBot 配置。
**请求方法**: `POST`
**接口地址**: `/api/v1/sessions/ask`
**鉴权方式**: Header `Authorization: Bearer <USER_TOKEN>`
### 请求参数 (Request)
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| question | string | 是 | **用户问题**。 |
| dataset_ids | array<string> | 是 | **知识库 ID 列表**。必须是当前用户有权访问的知识库。 |
**Request Example**:
```json
{
"question": "Summary of report",
"dataset_ids": ["dataset_uuid_internal_1"]
}
```
### 响应参数 (Stream Response)
**Content-Type**: `text/event-stream`
```text
data:{"code": 0, "message": "", "data": {"answer": "Here is the summary:", "reference": {}}}
data:{"code": 0, "message": "", "data": {"answer": " The report indicates...", "reference": {}}}
data:{"code": 0, "message": "", "data": true} // 结束
```
---
## 4. 通用相关问题 - `related_questions`
**接口描述**: **内部/测试用**的通用相关问题推荐接口。根据用户的问题和行业背景,利用 LLM 生成推荐问题。通常用于内部测试台。
**请求方法**: `POST`
**接口地址**: `/api/v1/sessions/related_questions`
**鉴权方式**: Header `Authorization: Bearer <USER_TOKEN>`
### 请求参数 (Request)
#### Body Parameters (JSON)
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| question | string | 是 | - | **原始问题/关键词**。 |
| industry | string | 否 | "" | **行业背景** (e.g., "Finance", "Healthcare")。帮助 LLM 生成更专业的推荐。 |
**Request Example**:
```json
{
"question": "Data privacy",
"industry": "IT"
}
```
### 响应参数 (Response)
**Content-Type**: `application/json`
```json
{
"code": 0,
"message": "success",
"data": [
"GDPR compliance checklist",
"Data encryption standards",
"User consent management"
]
}
```
@@ -1,98 +0,0 @@
# RAGFlow External API Reference (Grouped by File)
## File: `api/apps/sdk/agents.py`
| Function Name | URL Pattern | Notes |
|---|---|---|
| `list_agents` | `/api/v1/agents` | List Agents |
| `create_agent` | `/api/v1/agents` | Create Agent |
| `update_agent` | `/api/v1/agents/<agent_id>` | Update Agent |
| `delete_agent` | `/api/v1/agents/<agent_id>` | Delete Agent |
| `webhook` | `/api/v1/webhook_test/<agent_id>` | Webhook Test |
| `webhook_trace` | `/api/v1/webhook_trace/<agent_id>` | Webhook Trace |
## File: `api/apps/sdk/chat.py`
| Function Name | URL Pattern | Notes |
|---|---|---|
| `create` | `/api/v1/chats` | Create Chat |
| `delete_chats` | `/api/v1/chats` | Delete Chat |
| `list_chat` | `/api/v1/chats` | List Chats |
| `update` | `/api/v1/chats/<chat_id>` | Update Chat |
## File: `api/apps/sdk/dataset.py`
| Function Name | URL Pattern | Notes |
|---|---|---|
| `create` | `/api/v1/datasets` | Create Dataset |
| `delete` | `/api/v1/datasets` | Delete Dataset |
| `list_datasets` | `/api/v1/datasets` | List Datasets |
| `update` | `/api/v1/datasets/<dataset_id>` | Update Dataset |
| `knowledge_graph` | `/api/v1/datasets/<dataset_id>/knowledge_graph` | Knowledge Graph |
| `delete_knowledge_graph` | `/api/v1/datasets/<dataset_id>/knowledge_graph` | Delete Knowledge Graph |
| `run_graphrag` | `/api/v1/datasets/<dataset_id>/run_graphrag` | Run GraphRAG |
| `run_raptor` | `/api/v1/datasets/<dataset_id>/run_raptor` | Run Raptor |
| `trace_graphrag` | `/api/v1/datasets/<dataset_id>/trace_graphrag` | Trace GraphRAG |
| `trace_raptor` | `/api/v1/datasets/<dataset_id>/trace_raptor` | Trace Raptor |
## File: `api/apps/sdk/dify_retrieval.py`
| Function Name | URL Pattern | Notes |
|---|---|---|
| `retrieval` | `/api/v1/dify/retrieval` | Dify Retrieval |
## File: `api/apps/sdk/doc.py`
| Function Name | URL Pattern | Notes |
|---|---|---|
| `parse` | `/api/v1/datasets/<dataset_id>/chunks` | Parse Document Chunks |
| `stop_parsing` | `/api/v1/datasets/<dataset_id>/chunks` | Stop Parsing |
| `upload` | `/api/v1/datasets/<dataset_id>/documents` | Upload Document |
| `list_docs` | `/api/v1/datasets/<dataset_id>/documents` | List Documents |
| `delete` | `/api/v1/datasets/<dataset_id>/documents` | Delete Document |
| `update_doc` | `/api/v1/datasets/<dataset_id>/documents/<document_id>` | Update Document |
| `download` | `/api/v1/datasets/<dataset_id>/documents/<document_id>` | Download Document |
| `list_chunks` | `/api/v1/datasets/<dataset_id>/documents/<document_id>/chunks` | List Chunks |
| `add_chunk` | `/api/v1/datasets/<dataset_id>/documents/<document_id>/chunks` | Add Chunk |
| `update_chunk` | `/api/v1/datasets/<dataset_id>/documents/<document_id>/chunks/<chunk_id>` | Update Chunk |
| `rm_chunk` | `/api/v1/datasets/<dataset_id>/documents/<document_id>/chunks` | Remove Chunk |
| `metadata_summary` | `/api/v1/datasets/<dataset_id>/metadata/summary` | Metadata Summary |
| `metadata_batch_update` | `/api/v1/datasets/<dataset_id>/metadata/update` | Batch Update Metadata |
| `retrieval_test` | `/api/v1/retrieval` | Retrieval Test |
## File: `api/apps/sdk/files.py`
| Function Name | URL Pattern | Notes |
|---|---|---|
| `get_all_parent_folders` | `/api/v1/file/all_parent_folder` | Get All Parent Folders |
| `convert` | `/api/v1/file/convert` | File Convert |
| `create` | `/api/v1/file/create` | File Create |
| `download_attachment` | `/api/v1/file/download/<attachment_id>` | Download Attachment |
| `get` | `/api/v1/file/get/<file_id>` | Get File |
| `list_files` | `/api/v1/file/list` | List Files |
| `move` | `/api/v1/file/mv` | Move File |
| `get_parent_folder` | `/api/v1/file/parent_folder` | Get Parent Folder |
| `rename` | `/api/v1/file/rename` | Rename File |
| `rm` | `/api/v1/file/rm` | Remove File |
| `get_root_folder` | `/api/v1/file/root_folder` | Get Root Folder |
| `upload` | `/api/v1/file/upload` | Upload File |
## File: `api/apps/sdk/session.py`
| Function Name | URL Pattern | Notes |
|---|---|---|
| `agent_bot_completions` | `/api/v1/agentbots/<agent_id>/completions` | Agent Bot completion |
| `begin_inputs` | `/api/v1/agentbots/<agent_id>/inputs` | Get Agent Bot inputs |
| `agent_completions` | `/api/v1/agents/<agent_id>/completions` | Agent completion |
| `create_agent_session` | `/api/v1/agents/<agent_id>/sessions` | Create Agent Session |
| `list_agent_session` | `/api/v1/agents/<agent_id>/sessions` | List Agent Sessions |
| `delete_agent_session` | `/api/v1/agents/<agent_id>/sessions` | Delete Agent Session |
| `agents_completion_openai_compatibility` | `/api/v1/agents_openai/<agent_id>/chat/completions` | OpenAI compatible Agent completion |
| `chatbot_completions` | `/api/v1/chatbots/<dialog_id>/completions` | Chatbot completion |
| `chatbots_inputs` | `/api/v1/chatbots/<dialog_id>/info` | Chatbot info |
| `chat_completion` | `/api/v1/chats/<chat_id>/completions` | Chat completion |
| `create` | `/api/v1/chats/<chat_id>/sessions` | Create Chat Session |
| `list_session` | `/api/v1/chats/<chat_id>/sessions` | List Chat Sessions |
| `delete` | `/api/v1/chats/<chat_id>/sessions` | Delete Chat Session |
| `update` | `/api/v1/chats/<chat_id>/sessions/<session_id>` | Update Chat Session |
| `chat_completion_openai_like` | `/api/v1/chats_openai/<chat_id>/chat/completions` | OpenAI compatible Chat completion |
| `ask_about_embedded` | `/api/v1/searchbots/ask` | Searchbot Ask |
| `detail_share_embedded` | `/api/v1/searchbots/detail` | Searchbot Detail |
| `mindmap` | `/api/v1/searchbots/mindmap` | Searchbot Mindmap |
| `related_questions_embedded` | `/api/v1/searchbots/related_questions` | Searchbot Related Questions |
| `retrieval_test_embedded` | `/api/v1/searchbots/retrieval_test` | Searchbot Retrieval Test |
| `ask_about` | `/api/v1/sessions/ask` | Session Ask |
| `related_questions` | `/api/v1/sessions/related_questions` | Session Related Questions |
@@ -1,45 +0,0 @@
# RAGFlow API 接口文档索引 (Unofficial Detailed Guide)
本文档汇集了 RAGFlow 核心模块的 API 详解。所有文档均遵循 **Zero Omissions (无省略)** 原则,全字段展开并包含中文注释。
## 📚 1. 知识库与文档管理 (Knowledge & Documents)
核心的数据管理模块,负责上传文件、解析文档与建立索引。
- **[知识库管理 (Dataset)](./RAGFlow_Dataset接口详解.md)**
- 涵盖知识库的创建、列表查询、更新、删除等接口。
- **[文档处理 (Document)](./RAGFlow_Document接口详解.md)**
- 涵盖文档的上传 (Upload)、解析配置更新 (Update)、解析状态查询 (Run Status)。
- **切片管理**: 解析后的 Chunk 列表查询、增删改查。
- **检索测试**: 直接对知识库进行召回测试 (Retrieval Test)。
- **[文件管理 (File)](./RAGFlow_File接口详解.md)**
- 类似网盘的文件操作体系。
- **CRUD**: 上传、下载、列表。
- **目录**: 文件夹创建、面包屑导航 (`get_all_parent_folders`)。
- **操作**: 移动、重命名、删除、导入知识库 (`convert`).
## 💬 2. 聊天助手 (Chat Assistant)
RAGFlow 原生的对话助手体系,基于 Assistant (Dialog) 模型。
- **[会话管理 (Chat Session)](./RAGFlow_Chat_Session接口详解.md)**
- 管理 `/chats/` 下的会话生命周期。
- 创建会话、获取历史记录、重命名、批量删除。
- **[对话交互 (Chat Completion)](./RAGFlow_Chat_Completion接口详解.md)**
- **Core Chat**: 原生流式对话 (`/chats/<id>/completions`), 支持引用 (`quote`)。
- **OpenAI Compatible**: 完美兼容 OpenAI `/v1/chat/completions` 协议。
- **Embedded Bot**: 面向 C 端嵌入窗口的对话接口 (`/chatbots/`).
## 🤖 3. Agent 与 机器人 (Agent & Bots)
基于 Graph (DAG) 编排的复杂应用与各类机器人扩展。
- **[Agent 与 Dify 兼容 (Agent & Dify)](./RAGFlow_Agent_Dify接口详解.md)**
- **Agent Session**: Agent 的会话管理与流式对话 (`agent_completions`)。
- **Dify Adapter**: 兼容 Dify 协议的检索接口 (`retrieval`).
- **[SearchBot 与 AgentBot](./RAGFlow_SearchBot_AgentBot接口详解.md)**
- **SearchBot**: 纯搜索机器人,支持思维导图 (`mindmap`)、相关问题 (`related_questions`).
- **AgentBot**: 嵌入式 Agent,支持前置表单 (`begin_inputs`).
- **Agent OpenAI**: Agent 的 OpenAI 兼容接口。
## 🛠️ 4. 其他 (Extras)
- **[通用与补充接口 (Session Extras)](./RAGFlow_Session_Extra接口详解.md)**
- **引用详情**: 获取 SearchBot 引用来源 (`detail_share_embedded`).
- **通用问答**: 内部调试用的直接问答 (`ask_about`).
@@ -7,6 +7,7 @@ import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import jakarta.validation.constraints.*;
/**
@@ -129,6 +130,7 @@ public class DatasetDTO {
@JsonProperty("parser_config")
private ParserConfig parserConfig;
@JsonInclude(JsonInclude.Include.NON_NULL)
@Schema(description = "PageRank 权重 (0-100)", example = "50")
private Integer pagerank;
}
@@ -176,12 +176,21 @@ public abstract class KnowledgeBaseAdapter {
/**
* 获取数据集的文档数量
*
*
* @param datasetId 数据集ID
* @return 文档数量
*/
public abstract Integer getDocumentCount(String datasetId);
/**
* 获取数据集完整信息(名称、简介、文档数量等)
* 用于检测 RAGFlow 端是否已删除、同步名称/简介变更
*
* @param datasetId 数据集ID
* @return 数据集详情,若 RAGFlow 端不存在则返回 null
*/
public abstract DatasetDTO.InfoVO getDatasetInfo(String datasetId);
/**
* 发送流式请求 (SSE)
*
@@ -486,7 +486,20 @@ public class RAGFlowAdapter extends KnowledgeBaseAdapter {
@Override
public Integer getDocumentCount(String datasetId) {
try {
// [Fix] 使用列表过滤接口获取详情 (GET /datasets?id={id})
DatasetDTO.InfoVO info = getDatasetInfo(datasetId);
if (info != null && info.getDocumentCount() != null) {
return info.getDocumentCount().intValue();
}
return 0;
} catch (Exception e) {
log.warn("获取文档数量失败: {}", e.getMessage());
return 0;
}
}
@Override
public DatasetDTO.InfoVO getDatasetInfo(String datasetId) {
try {
Map<String, Object> params = new HashMap<>();
params.put("id", datasetId);
params.put("page", 1);
@@ -498,20 +511,14 @@ public class RAGFlowAdapter extends KnowledgeBaseAdapter {
if (dataObj instanceof List) {
List<?> list = (List<?>) dataObj;
if (!list.isEmpty()) {
Object firstItem = list.get(0);
if (firstItem instanceof Map) {
Object countObj = ((Map<?, ?>) firstItem).get("document_count");
if (countObj instanceof Number) {
return ((Number) countObj).intValue();
}
}
return objectMapper.convertValue(list.get(0), DatasetDTO.InfoVO.class);
}
}
// 降级:未找到或结构不匹配
return 0;
// RAGFlow 端不存在该数据集
return null;
} catch (Exception e) {
log.warn("获取文档数量失败: {}", e.getMessage());
return 0;
log.warn("获取数据集信息失败: datasetId={}, error={}", datasetId, e.getMessage());
return null;
}
}
@@ -96,7 +96,7 @@ public interface KnowledgeFilesService {
/**
* 保存文档影子记录
*/
void saveDocumentShadow(String datasetId, KnowledgeFilesDTO result, String originalName, String chunkMethod,
boolean saveDocumentShadow(String datasetId, KnowledgeFilesDTO result, String originalName, String chunkMethod,
Map<String, Object> parserConfig);
/**
@@ -120,4 +120,13 @@ public interface KnowledgeFilesService {
* 同步所有处于 RUNNING 状态的文档 (供定时任务调用)
*/
void syncRunningDocuments();
/**
* 从RAGFlow全量同步文档到本地影子表
* 拉取远端所有文档,与本地影子表对比,插入缺失的记录
*
* @param datasetId 数据集ID
* @return 新同步的文档数量
*/
int syncDocumentsFromRAG(String datasetId);
}
@@ -19,6 +19,8 @@ import xiaozhi.common.service.impl.BaseServiceImpl;
import xiaozhi.common.utils.ConvertUtils;
import xiaozhi.common.utils.JsonUtils;
import xiaozhi.modules.knowledge.dao.KnowledgeBaseDao;
import xiaozhi.modules.knowledge.dao.DocumentDao;
import xiaozhi.modules.knowledge.entity.DocumentEntity;
import xiaozhi.modules.knowledge.dto.KnowledgeBaseDTO;
import xiaozhi.modules.knowledge.dto.dataset.DatasetDTO;
import xiaozhi.modules.knowledge.entity.KnowledgeBaseEntity;
@@ -46,6 +48,7 @@ public class KnowledgeBaseServiceImpl extends BaseServiceImpl<KnowledgeBaseDao,
implements KnowledgeBaseService {
private final KnowledgeBaseDao knowledgeBaseDao;
private final DocumentDao documentDao;
private final ModelConfigService modelConfigService;
private final ModelConfigDao modelConfigDao;
private final RedisUtils redisUtils;
@@ -67,27 +70,107 @@ public class KnowledgeBaseServiceImpl extends BaseServiceImpl<KnowledgeBaseDao,
// Enrich with Document Count from RAG (Optional / Lazy)
if (pageData != null && pageData.getList() != null) {
for (KnowledgeBaseDTO dto : pageData.getList()) {
pageData.getList().removeIf(dto -> {
enrichDocumentCount(dto);
}
// syncDatasetFromRAG 检测到 RAGFlow 端已删除时,会将本地记录清理
// 此时 datasetId 被置空作为标记,需要在列表中移除该条目
return dto.getDatasetId() == null;
});
}
return pageData;
}
private void enrichDocumentCount(KnowledgeBaseDTO dto) {
syncDatasetFromRAG(dto);
}
/**
* 从 RAGFlow 同步数据集信息:检测删除、同步名称/简介、获取文档数量
* 每次列表刷新时实时查询 RAGFlow,确保立即感知远端变更
*/
private void syncDatasetFromRAG(KnowledgeBaseDTO dto) {
try {
if (StringUtils.isNotBlank(dto.getDatasetId()) && StringUtils.isNotBlank(dto.getRagModelId())) {
KnowledgeBaseAdapter adapter = getAdapterByModelId(dto.getRagModelId());
if (adapter != null) {
dto.setDocumentCount(adapter.getDocumentCount(dto.getDatasetId()));
if (StringUtils.isBlank(dto.getDatasetId()) || StringUtils.isBlank(dto.getRagModelId())) {
return;
}
KnowledgeBaseAdapter adapter = getAdapterByModelId(dto.getRagModelId());
if (adapter == null) {
return;
}
DatasetDTO.InfoVO datasetInfo = adapter.getDatasetInfo(dto.getDatasetId());
if (datasetInfo == null) {
// RAGFlow 端已删除 → 本地级联清理
log.info("数据集 {} 在 RAGFlow 端不存在,执行本地清理", dto.getDatasetId());
cleanupLocalDataset(dto.getDatasetId(), dto.getId());
// 标记为已删除,让上层从列表中移除
dto.setDatasetId(null);
return;
}
// 同步名称(去掉 username_ 前缀)
String ragflowName = datasetInfo.getName();
if (StringUtils.isNotBlank(ragflowName)) {
String localName = ragflowName.contains("_") ? ragflowName.substring(ragflowName.indexOf('_') + 1) : ragflowName;
if (!localName.equals(dto.getName())) {
log.info("同步知识库名称: {} -> {}", dto.getName(), localName);
KnowledgeBaseEntity entity = knowledgeBaseDao.selectById(dto.getId());
if (entity != null) {
entity.setName(localName);
knowledgeBaseDao.updateById(entity);
dto.setName(localName);
}
}
}
// 同步简介
String ragflowDesc = datasetInfo.getDescription();
String localDesc = dto.getDescription();
boolean descChanged = (ragflowDesc == null && localDesc != null) || (ragflowDesc != null && !ragflowDesc.equals(localDesc));
if (descChanged) {
log.info("同步知识库简介: datasetId={}", dto.getDatasetId());
KnowledgeBaseEntity entity = knowledgeBaseDao.selectById(dto.getId());
if (entity != null) {
entity.setDescription(ragflowDesc);
knowledgeBaseDao.updateById(entity);
dto.setDescription(ragflowDesc);
}
}
// 设置文档数量(保留原有功能)
if (datasetInfo.getDocumentCount() != null) {
dto.setDocumentCount(datasetInfo.getDocumentCount().intValue());
}
} catch (Exception e) {
log.warn("无法获取知识库 {} 的文档计数: {}", dto.getName(), e.getMessage());
log.warn("同步数据集信息失败 {}: {}", dto.getName(), e.getMessage());
dto.setDocumentCount(0);
}
}
/**
* 本地级联清理:RAGFlow 端已删除时,清理本地所有关联数据
* 不调用 RAGFlow 删除 API
*/
@Transactional(rollbackFor = Exception.class)
public void cleanupLocalDataset(String datasetId, String entityId) {
try {
// 1. 删除文档影子记录
documentDao.delete(new QueryWrapper<DocumentEntity>().eq("dataset_id", datasetId));
// 2. 删除插件映射
knowledgeBaseDao.deletePluginMappingByKnowledgeBaseId(entityId);
// 3. 删除知识库记录
knowledgeBaseDao.deleteById(entityId);
// 4. 清理缓存
redisUtils.delete(RedisKeys.getKnowledgeBaseCacheKey(entityId));
log.info("本地级联清理完成: datasetId={}, entityId={}", datasetId, entityId);
} catch (Exception e) {
log.error("本地级联清理失败: datasetId={}, entityId={}", datasetId, entityId, e);
}
}
@Override
public KnowledgeBaseDTO getById(String id) {
KnowledgeBaseEntity entity = knowledgeBaseDao.selectById(id);
@@ -4,6 +4,8 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
@@ -76,6 +78,13 @@ public class KnowledgeFilesServiceImpl extends BaseServiceImpl<DocumentDao, Docu
throw new RenException(ErrorCode.RAG_DATASET_ID_AND_MODEL_ID_NOT_NULL);
}
// 全量对账同步: 从RAGFlow拉取远端文档,实时同步确保立即感知远端变更
try {
self.syncDocumentsFromRAG(datasetId);
} catch (Exception e) {
log.warn("从RAGFlow全量同步文档失败(不影响本地查询): datasetId={}, error={}", datasetId, e.getMessage());
}
// 1. 获取本地影子表数据 (MyBatis-Plus 分页)
Page<DocumentEntity> pageParams = new Page<>(page, limit);
QueryWrapper<DocumentEntity> queryWrapper = new QueryWrapper<>();
@@ -408,10 +417,13 @@ public class KnowledgeFilesServiceImpl extends BaseServiceImpl<DocumentDao, Docu
}
/**
* 原子化保存影子记录,确保本地数据绝对一致
* 原子化保存影子记录Upsert 语义)
* 若 document_id 已存在则更新,不存在则插入,避免 UNIQUE 约束冲突
*
* @return true=新插入, false=更新已有记录
*/
@Transactional(rollbackFor = Exception.class)
public void saveDocumentShadow(String datasetId, KnowledgeFilesDTO result, String originalName, String chunkMethod,
public boolean saveDocumentShadow(String datasetId, KnowledgeFilesDTO result, String originalName, String chunkMethod,
Map<String, Object> parserConfig) {
DocumentEntity entity = new DocumentEntity();
entity.setDatasetId(datasetId);
@@ -453,12 +465,23 @@ public class KnowledgeFilesServiceImpl extends BaseServiceImpl<DocumentDao, Docu
entity.setCreatedAt(result.getCreatedAt() != null ? result.getCreatedAt() : new Date());
entity.setUpdatedAt(result.getUpdatedAt() != null ? result.getUpdatedAt() : new Date());
// 插入影子表 (若失败将抛出异常,触发调用方报错,确保 Local-First 列表一致性)
documentDao.insert(entity);
// Upsert: 检查 document_id 是否已存在,存在则更新,不存在则插入
DocumentEntity existing = documentDao.selectOne(
new QueryWrapper<DocumentEntity>().eq("document_id", entity.getDocumentId()));
// Issue 4: 同步递增数据集文档总数统计,保持父子表一致
knowledgeBaseService.updateStatistics(datasetId, 1, 0L, 0L);
log.info("已同步递增数据集统计: datasetId={}", datasetId);
if (existing != null) {
entity.setId(existing.getId());
entity.setCreatedAt(existing.getCreatedAt()); // 保留原始创建时间
documentDao.updateById(entity);
log.info("影子记录已更新: documentId={}", entity.getDocumentId());
return false;
} else {
documentDao.insert(entity);
// 新增记录时递增数据集文档总数统计
knowledgeBaseService.updateStatistics(datasetId, 1, 0L, 0L);
log.info("影子记录已插入: documentId={}, datasetId={}", entity.getDocumentId(), datasetId);
return true;
}
}
@Override
@@ -738,6 +761,167 @@ public class KnowledgeFilesServiceImpl extends BaseServiceImpl<DocumentDao, Docu
this.deleteDocuments(datasetId, req);
}
@Override
public int syncDocumentsFromRAG(String datasetId) {
log.info("=== 开始从RAGFlow全量同步文档到本地影子表: datasetId={} ===", datasetId);
// 1. 获取适配器
Map<String, Object> ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(datasetId);
KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(extractAdapterType(ragConfig), ragConfig);
// 2. 分页拉取远端所有文档
List<KnowledgeFilesDTO> allRemoteDocs = new ArrayList<>();
int pageNum = 1;
int pageSize = 100;
long totalRemote = Long.MAX_VALUE;
while ((long) (pageNum - 1) * pageSize < totalRemote) {
DocumentDTO.ListReq req = DocumentDTO.ListReq.builder()
.page(pageNum)
.pageSize(pageSize)
.build();
PageData<KnowledgeFilesDTO> remotePage = adapter.getDocumentList(datasetId, req);
if (remotePage == null || remotePage.getList() == null || remotePage.getList().isEmpty()) {
break;
}
allRemoteDocs.addAll(remotePage.getList());
totalRemote = remotePage.getTotal();
pageNum++;
}
// 3. 获取本地已有文档
List<DocumentEntity> localDocs = documentDao.selectList(
new QueryWrapper<DocumentEntity>().eq("dataset_id", datasetId));
Set<String> localDocIds = localDocs.stream()
.map(DocumentEntity::getDocumentId)
.collect(Collectors.toSet());
// 4. 远端文档ID集合
Set<String> remoteDocIds = allRemoteDocs.stream()
.map(KnowledgeFilesDTO::getDocumentId)
.filter(id -> id != null)
.collect(Collectors.toSet());
// 5. 补充: 插入远端存在但本地缺失的文档
List<KnowledgeFilesDTO> newDocs = allRemoteDocs.stream()
.filter(doc -> doc.getDocumentId() != null && !localDocIds.contains(doc.getDocumentId()))
.collect(Collectors.toList());
int syncCount = 0;
if (!newDocs.isEmpty()) {
for (KnowledgeFilesDTO doc : newDocs) {
try {
self.saveDocumentShadow(datasetId, doc, doc.getName(), doc.getChunkMethod(), doc.getParserConfig());
// 同步远端已有的 token/chunk 统计
Long tokenCount = doc.getTokenCount() != null ? doc.getTokenCount() : 0L;
long chunkCount = doc.getChunkCount() != null ? doc.getChunkCount().longValue() : 0L;
if (tokenCount > 0 || chunkCount > 0) {
knowledgeBaseService.updateStatistics(datasetId, 0, chunkCount, tokenCount);
}
syncCount++;
} catch (Exception e) {
log.warn("同步单个文档影子记录失败: docId={}, error={}", doc.getDocumentId(), e.getMessage());
}
}
log.info("从RAGFlow新增同步 {} 个文档影子记录, datasetId={}", syncCount, datasetId);
}
// 6. 清理: 删除远端已不存在但本地仍保留的影子记录
List<DocumentEntity> deletedDocs = localDocs.stream()
.filter(entity -> !remoteDocIds.contains(entity.getDocumentId()))
.collect(Collectors.toList());
if (!deletedDocs.isEmpty()) {
List<String> deletedDocIds = new ArrayList<>();
long totalChunkDelta = 0;
long totalTokenDelta = 0;
for (DocumentEntity entity : deletedDocs) {
deletedDocIds.add(entity.getDocumentId());
totalChunkDelta += entity.getChunkCount() != null ? entity.getChunkCount() : 0L;
totalTokenDelta += entity.getTokenCount() != null ? entity.getTokenCount() : 0L;
}
try {
self.deleteDocumentShadows(deletedDocIds, datasetId, totalChunkDelta, totalTokenDelta);
log.info("清理远端已删除的影子记录: {} 个, datasetId={}", deletedDocs.size(), datasetId);
} catch (Exception e) {
log.warn("清理远端已删除的影子记录失败: datasetId={}, error={}", datasetId, e.getMessage());
}
}
// 7. 全量更新: 远端和本地都存在的文档,以远端为准同步所有字段
// 处理 RAGFlow 复用 documentId 重传、远端编辑后元数据变化等场景
Map<String, KnowledgeFilesDTO> remoteDocMap = allRemoteDocs.stream()
.filter(doc -> doc.getDocumentId() != null)
.collect(Collectors.toMap(KnowledgeFilesDTO::getDocumentId, doc -> doc, (a, b) -> b));
Map<String, DocumentEntity> localDocMap = localDocs.stream()
.collect(Collectors.toMap(DocumentEntity::getDocumentId, e -> e, (a, b) -> b));
int updateCount = 0;
for (Map.Entry<String, KnowledgeFilesDTO> entry : remoteDocMap.entrySet()) {
String docId = entry.getKey();
DocumentEntity local = localDocMap.get(docId);
if (local == null) {
continue; // 不在本地,由步骤5处理
}
KnowledgeFilesDTO remote = entry.getValue();
// 全量字段更新(以远端为准),确保本地与 RAGFlow 完全一致
UpdateWrapper<DocumentEntity> updateWrapper = new UpdateWrapper<DocumentEntity>()
.set("run", remote.getRun())
.set("status", remote.getStatus() != null ? remote.getStatus() : local.getStatus())
.set("progress", remote.getProgress())
.set("chunk_count", remote.getChunkCount())
.set("token_count", remote.getTokenCount())
.set("size", remote.getFileSize())
.set("error", remote.getError())
.set("process_duration", remote.getProcessDuration())
.set("updated_at", new Date())
.set("last_sync_at", new Date())
.eq("document_id", docId)
.eq("dataset_id", datasetId);
if (remote.getName() != null) {
updateWrapper.set("name", remote.getName());
}
if (remote.getThumbnail() != null) {
updateWrapper.set("thumbnail", remote.getThumbnail());
}
if (remote.getMetaFields() != null) {
try {
updateWrapper.set("meta_fields", objectMapper.writeValueAsString(remote.getMetaFields()));
} catch (Exception e) {
log.warn("同步更新元数据序列化失败: docId={}, error={}", docId, e.getMessage());
}
}
documentDao.update(null, updateWrapper);
// 同步统计差异(chunk/token 计数变化时修正父表)
Long remoteTokenCount = remote.getTokenCount() != null ? remote.getTokenCount() : 0L;
Long localTokenCount = local.getTokenCount() != null ? local.getTokenCount() : 0L;
long remoteChunkCount = remote.getChunkCount() != null ? remote.getChunkCount().longValue() : 0L;
long localChunkCount = local.getChunkCount() != null ? local.getChunkCount().longValue() : 0L;
long tokenDelta = remoteTokenCount - localTokenCount;
long chunkDelta = remoteChunkCount - localChunkCount;
if (tokenDelta != 0 || chunkDelta != 0) {
knowledgeBaseService.updateStatistics(datasetId, 0, chunkDelta, tokenDelta);
log.info("影子更新: 修正知识库统计, docId={}, chunkDelta={}, tokenDelta={}", docId, chunkDelta, tokenDelta);
}
updateCount++;
}
if (syncCount == 0 && deletedDocs.isEmpty() && updateCount == 0) {
log.info("本地影子表已与RAGFlow完全同步, datasetId={}", datasetId);
} else {
log.info("同步完成: 新增={}, 清理={}, 更新={}, datasetId={}", syncCount, deletedDocs.size(), updateCount, datasetId);
}
return syncCount;
}
@Override
public void syncRunningDocuments() {
// 1. 查询所有 RUNNING 状态的文档
@@ -755,7 +939,7 @@ public class KnowledgeFilesServiceImpl extends BaseServiceImpl<DocumentDao, Docu
// 2. 按 DatasetID 分组,复用 Adapter
Map<String, List<DocumentEntity>> groupedDocs = runningDocs.stream()
.collect(java.util.stream.Collectors.groupingBy(DocumentEntity::getDatasetId));
.collect(Collectors.groupingBy(DocumentEntity::getDatasetId));
groupedDocs.forEach((datasetId, docs) -> {
KnowledgeBaseAdapter adapter = null;
@@ -67,4 +67,13 @@ public interface LLMService {
* @return 是否可用
*/
boolean isAvailable(String modelId);
/**
* 生成会话标题
*
* @param conversation 对话内容
* @param modelId 模型ID
* @return 标题(约15字)
*/
String generateTitle(String conversation, String modelId);
}
@@ -1,6 +1,7 @@
package xiaozhi.modules.llm.service.impl;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -30,13 +31,38 @@ import xiaozhi.modules.model.service.ModelConfigService;
@Service
public class OpenAIStyleLLMServiceImpl implements LLMService {
// 需要禁用思考模式的平台域名及其对应参数
private static final Map<String, Map<String, Object>> THINKING_DISABLED_DOMAINS = new LinkedHashMap<>();
static {
THINKING_DISABLED_DOMAINS.put("aliyuncs.com", Map.of("enable_thinking", false));
Map<String, Object> thinkingDisabled = Map.of("thinking", Map.of("type", "disabled"));
THINKING_DISABLED_DOMAINS.put("bigmodel.cn", thinkingDisabled);
THINKING_DISABLED_DOMAINS.put("moonshot.cn", thinkingDisabled);
THINKING_DISABLED_DOMAINS.put("volces.com", thinkingDisabled);
}
@Autowired
private ModelConfigService modelConfigService;
private final RestTemplate restTemplate = new RestTemplate();
/**
* 根据域名自动禁用思考模式
*/
private void applyThinkingDisabled(String baseUrl, Map<String, Object> requestBody) {
for (Map.Entry<String, Map<String, Object>> entry : THINKING_DISABLED_DOMAINS.entrySet()) {
if (baseUrl.contains(entry.getKey())) {
requestBody.putAll(entry.getValue());
log.info("为域名 {} 禁用思考模式,参数: {}", baseUrl, entry.getValue());
break;
}
}
}
private static final String DEFAULT_SUMMARY_PROMPT = "你是一个经验丰富的记忆总结者,擅长将对话内容进行总结摘要,遵循以下规则:\n1、总结用户的重要信息,以便在未来的对话中提供更个性化的服务\n2、不要重复总结,不要遗忘之前记忆,除非原来的记忆超过了1800字,否则不要遗忘、不要压缩用户的历史记忆\n3、用户操控的设备音量、播放音乐、天气、退出、不想对话等和用户本身无关的内容,这些信息不需要加入到总结中\n4、聊天内容中的今天的日期时间、今天的天气情况与用户事件无关的数据,这些信息如果当成记忆存储会影响后续对话,这些信息不需要加入到总结中\n5、不要把设备操控的成果结果和失败结果加入到总结中,也不要把用户的一些废话加入到总结中\n6、不要为了总结而总结,如果用户的聊天没有意义,请返回原来的历史记录也是可以的\n7、只需要返回总结摘要,严格控制在1800字内\n8、不要包含代码、xml,不需要解释、注释和说明,保存记忆时仅从对话提取信息,不要混入示例内容\n9、如果提供了历史记忆,请将新对话内容与历史记忆进行智能合并,保留有价值的历史信息,同时添加新的重要信息\n\n历史记忆:\n{history_memory}\n\n新对话内容:\n{conversation}";
private static final String DEFAULT_TITLE_PROMPT = "请根据以下对话内容,生成一个简洁的会话标题(约15字以内),只返回标题,不要包含任何解释或标点符号:\n{conversation}";
@Override
public String generateSummary(String conversation) {
return generateSummary(conversation, null, null);
@@ -100,6 +126,9 @@ public class OpenAIStyleLLMServiceImpl implements LLMService {
requestBody.put("temperature", temperature != null ? temperature : 0.7);
requestBody.put("max_tokens", maxTokens != null ? maxTokens : 2000);
// 禁用思考模式
applyThinkingDisabled(baseUrl, requestBody);
// 发送HTTP请求
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
@@ -154,10 +183,8 @@ public class OpenAIStyleLLMServiceImpl implements LLMService {
// 从智控台获取LLM模型配置
ModelConfigEntity llmConfig;
if (modelId != null && !modelId.trim().isEmpty()) {
// 通过具体模型ID获取配置
llmConfig = modelConfigService.getModelByIdFromCache(modelId);
} else {
// 保持向后兼容,使用默认配置
llmConfig = getDefaultLLMConfig();
}
@@ -195,6 +222,9 @@ public class OpenAIStyleLLMServiceImpl implements LLMService {
requestBody.put("temperature", 0.2);
requestBody.put("max_tokens", 2000);
// 禁用思考模式
applyThinkingDisabled(baseUrl, requestBody);
// 发送HTTP请求
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
@@ -302,4 +332,94 @@ public class OpenAIStyleLLMServiceImpl implements LLMService {
return null;
}
}
@Override
public String generateTitle(String conversation, String modelId) {
if (!isAvailable()) {
log.warn("LLM服务不可用,无法生成标题");
return null;
}
try {
ModelConfigEntity llmConfig;
if (modelId != null && !modelId.trim().isEmpty()) {
llmConfig = modelConfigService.getModelByIdFromCache(modelId);
} else {
llmConfig = getDefaultLLMConfig();
}
if (llmConfig == null || llmConfig.getConfigJson() == null) {
log.error("未找到可用的LLM模型配置,modelId: {}", modelId);
return null;
}
JSONObject configJson = llmConfig.getConfigJson();
String baseUrl = configJson.getStr("base_url");
String model = configJson.getStr("model_name");
String apiKey = configJson.getStr("api_key");
if (StringUtils.isBlank(baseUrl) || StringUtils.isBlank(apiKey)) {
log.error("LLM配置不完整,baseUrl或apiKey为空");
return null;
}
String prompt = DEFAULT_TITLE_PROMPT.replace("{conversation}", conversation);
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("model", model != null ? model : "gpt-3.5-turbo");
Map<String, Object>[] messages = new Map[1];
Map<String, Object> message = new HashMap<>();
message.put("role", "user");
message.put("content", prompt);
messages[0] = message;
requestBody.put("messages", messages);
requestBody.put("temperature", 0.3);
requestBody.put("max_tokens", 50);
// 禁用思考模式
applyThinkingDisabled(baseUrl, requestBody);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer " + apiKey);
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);
String apiUrl = baseUrl;
if (!apiUrl.endsWith("/chat/completions")) {
if (!apiUrl.endsWith("/")) {
apiUrl += "/";
}
apiUrl += "chat/completions";
}
ResponseEntity<String> response = restTemplate.exchange(
apiUrl, HttpMethod.POST, entity, String.class);
if (response.getStatusCode().is2xxSuccessful()) {
JSONObject responseJson = JSONUtil.parseObj(response.getBody());
JSONArray choices = responseJson.getJSONArray("choices");
if (choices != null && choices.size() > 0) {
JSONObject choice = choices.getJSONObject(0);
JSONObject messageObj = choice.getJSONObject("message");
String title = messageObj.getStr("content");
if (StringUtils.isNotBlank(title)) {
title = title.trim().replaceAll("[,。!?、:;''\"\"【】()]", "");
if (title.length() > 15) {
title = title.substring(0, 15);
}
return title;
}
}
} else {
log.error("LLM API调用失败,状态码:{},响应:{}", response.getStatusCode(), response.getBody());
}
} catch (Exception e) {
log.error("调用LLM服务生成标题时发生异常,modelId: {}", modelId, e);
}
return null;
}
}
@@ -57,7 +57,8 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
.eq("model_type", modelType)
.eq("is_enabled", 1)
.like(StringUtils.isNotBlank(modelName), "model_name", modelName)
.select("id", "model_name"));
.select("id", "model_name")
.orderByAsc("sort"));
return ConvertUtils.sourceToTarget(entities, ModelBasicInfoDTO.class);
}
@@ -370,6 +371,13 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
}
}
// 删除在新JSON中不存在的非敏感字段
for (String oldKey : originalJson.keySet().toArray(new String[0])) {
if (!modelConfigBodyDTO.getConfigJson().containsKey(oldKey) && !SensitiveDataUtils.isSensitiveField(oldKey)) {
updatedJson.remove(oldKey);
}
}
modelConfigEntity.setConfigJson(updatedJson);
}
@@ -402,6 +410,13 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
}
}
}
// 删除在新JSON中不存在的非敏感子字段
for (String oldChildKey : originalChild.keySet().toArray(new String[0])) {
if (!updated.containsKey(oldChildKey) && !SensitiveDataUtils.isSensitiveField(oldChildKey)) {
originalChild.remove(oldChildKey);
}
}
}
/**
@@ -95,6 +95,7 @@ public class ModelProviderServiceImpl extends BaseServiceImpl<ModelProviderDao,
QueryWrapper<ModelProviderEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("model_type", StringUtils.isBlank(modelType) ? "" : modelType);
queryWrapper.orderByAsc("sort");
List<ModelProviderEntity> providerEntities = modelProviderDao.selectList(queryWrapper);
return ConvertUtils.sourceToTarget(providerEntities, ModelProviderDTO.class);
}
@@ -147,7 +148,8 @@ public class ModelProviderServiceImpl extends BaseServiceImpl<ModelProviderDao,
UserDetail user = SecurityUser.getUser();
modelProviderDTO.setUpdater(user.getId());
modelProviderDTO.setUpdateDate(new Date());
if (modelProviderDao.updateById(ConvertUtils.sourceToTarget(modelProviderDTO, ModelProviderEntity.class)) == 0) {
if (modelProviderDao
.updateById(ConvertUtils.sourceToTarget(modelProviderDTO, ModelProviderEntity.class)) == 0) {
throw new RenException(ErrorCode.UPDATE_DATA_FAILED);
}
return ConvertUtils.sourceToTarget(modelProviderDTO, ModelProviderDTO.class);
@@ -90,6 +90,7 @@ public class ShiroConfig {
filterMap.put("/agent/chat-history/report", "server");
filterMap.put("/agent/chat-history/download/**", "anon");
filterMap.put("/agent/chat-summary/**", "server");
filterMap.put("/agent/chat-title/**", "server");
filterMap.put("/agent/play/**", "anon");
filterMap.put("/voiceClone/play/**", "anon");
filterMap.put("/**", "oauth2");
@@ -0,0 +1,7 @@
-- 新增仅上报聊天记录记忆模型供应器
delete from `ai_model_provider` where `id` = 'SYSTEM_Memory_mem_report_only';
delete from `ai_model_config` where `id` = 'Memory_mem_report_only';
INSERT INTO `ai_model_provider` VALUES ('SYSTEM_Memory_mem_report_only', 'Memory', 'mem_report_only', '仅上报聊天记录', '[]', 4, 1, NOW(), 1, NOW());
INSERT INTO `ai_model_config` VALUES ('Memory_mem_report_only', 'Memory', 'mem_report_only', '仅上报聊天记录', 0, 1, '{"type": "mem_report_only"}', NULL, '仅上报聊天记录,不总结记忆', 3, NULL, NULL, NULL, NULL);
@@ -0,0 +1,2 @@
-- 修改聊天内容字段类型
ALTER TABLE ai_agent_chat_history MODIFY COLUMN content TEXT COMMENT '聊天内容';
@@ -0,0 +1,7 @@
-- 修改记忆模型名称
UPDATE `ai_model_config` SET `model_name` = '本地短期记忆(总结记忆)' WHERE `id` = 'Memory_mem_local_short';
UPDATE `ai_model_provider` SET `name` = '本地短期记忆(总结记忆)' WHERE `id` = 'SYSTEM_Memory_mem_local_short';
UPDATE `ai_model_config` SET `model_name` = '仅上报聊天记录(不总结记忆)' WHERE `id` = 'Memory_mem_report_only';
UPDATE `ai_model_provider` SET `name` = '仅上报聊天记录(不总结记忆)' WHERE `id` = 'SYSTEM_Memory_mem_report_only';
@@ -0,0 +1 @@
INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (312, 'tool_call_timeout', '30', 'number', 1, '工具调用超时时间(秒)');
@@ -0,0 +1,16 @@
-- 智能体表添加小模型ID字段
SET @col_exists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'ai_agent' AND COLUMN_NAME = 'slm_model_id');
SET @sql = IF(@col_exists = 0, 'ALTER TABLE `ai_agent` ADD COLUMN `slm_model_id` VARCHAR(255) NULL COMMENT ''小模型ID'' AFTER `llm_model_id`', 'SELECT ''Column slm_model_id already exists'' AS msg');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- 创建聊天标题表
DROP TABLE IF EXISTS `ai_agent_chat_title`;
CREATE TABLE `ai_agent_chat_title` (
`id` VARCHAR(32) NOT NULL COMMENT '主键ID',
`session_id` VARCHAR(255) NOT NULL COMMENT '会话ID',
`title` VARCHAR(255) DEFAULT NULL COMMENT '聊天标题',
`created_at` DATETIME DEFAULT NULL COMMENT '创建时间',
`updated_at` DATETIME DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_session_id` (`session_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='智能体聊天标题表';
@@ -0,0 +1,17 @@
-- 更新模型名称:qwen2.5-vl-3b-instruct 改为 qwen3.5-flash
UPDATE `ai_model_config`
SET `config_json` = JSON_SET(`config_json`, '$.model_name', 'qwen3.5-flash')
WHERE `id` = 'VLLM_QwenVLVLLM'
AND JSON_EXTRACT(`config_json`, '$.model_name') = 'qwen2.5-vl-3b-instruct';
-- 更新模型名称:qwen-turbo 改为 qwen-flash
UPDATE `ai_model_config`
SET `config_json` = JSON_SET(`config_json`, '$.model_name', 'qwen-flash')
WHERE `id` = 'LLM_AliLLM'
AND JSON_EXTRACT(`config_json`, '$.model_name') = 'qwen-turbo';
-- 更新备注:qwen-turbo 改为 qwen-flash
UPDATE `ai_model_config`
SET `remark` = REPLACE(`remark`, 'qwen-turbo', 'qwen-flash')
WHERE `id` = 'LLM_AliLLM'
AND `remark` LIKE '%qwen-turbo%';
@@ -0,0 +1,102 @@
-- 新增豆包语音合成模型2.0供应器(使用seed-tts-2.0资源ID
-- 与火山双流式TTS配置相同,但resource_id固定为seed-tts-2.0
-- 插入豆包语音合成模型2.0供应器
delete from `ai_model_provider` where id = 'SYSTEM_TTS_HSDSTTS_V2';
INSERT INTO `ai_model_provider` (`id`, `model_type`, `provider_code`, `name`, `fields`, `sort`, `creator`, `create_date`, `updater`, `update_date`) VALUES
('SYSTEM_TTS_HSDSTTS_V2', 'TTS', 'huoshan_double_stream', '豆包语音合成模型2.0', '[
{"key": "ws_url", "type": "string", "label": "WebSocket地址"},
{"key": "appid", "type": "string", "label": "应用ID"},
{"key": "access_token", "type": "string", "label": "访问令牌"},
{"key": "resource_id", "type": "string", "label": "资源ID"},
{"key": "speaker", "type": "string", "label": "默认音色"},
{"key": "enable_ws_reuse", "type": "boolean", "label": "是否开启链接复用", "default": true},
{"key": "audio_params", "type": "dict", "label": "音频输出配置"},
{"key": "additions", "type": "dict", "label": "高级文本处理配置"},
{"key": "mix_speaker", "type": "dict", "label": "混音控制配置"}
]', 14, 1, NOW(), 1, NOW());
-- 插入豆包语音合成模型2.0配置
delete from `ai_model_config` where id = 'TTS_HSDSTTS_V2';
INSERT INTO `ai_model_config` VALUES ('TTS_HSDSTTS_V2', 'TTS', 'HuoshanDoubleStreamTTSV2', '豆包语音合成模型2.0', 0, 1, '{
"type": "huoshan_double_stream",
"ws_url": "wss://openspeech.bytedance.com/api/v3/tts/bidirection",
"appid": "",
"access_token": "",
"resource_id": "seed-tts-2.0",
"speaker": "zh_female_xiaohe_uranus_bigtts",
"enable_ws_reuse": true,
"audio_params": {
"speech_rate": 0,
"loudness_rate": 0
},
"additions": {
"aigc_metadata": {},
"cache_config": {},
"post_process": {
"pitch": 0
}
},
"mix_speaker": {}
}', NULL, NULL, 17, NULL, NULL, NULL, NULL);
-- 豆包语音合成模型2.0配置说明文档
UPDATE `ai_model_config` SET
`doc_link` = 'https://www.volcengine.com/docs/6561/1329505',
`remark` = '豆包语音合成模型2.0配置说明(基于火山引擎seed-tts-2.0):
1. 访问 https://www.volcengine.com/ 注册并开通火山引擎账号
2. 访问 https://console.volcengine.com/speech/service/10035 开通语音合成大模型,购买音色
3. 在页面底部获取appid和access_token
4. 资源ID固定为:seed-tts-2.0(豆包语音合成模型2.0
5. 链接复用:开启WebSocket连接复用,默认true减少链接损耗(注意:复用后设备处于聆听状态时空闲链接会占并发数)
详细参数文档:https://www.volcengine.com/docs/6561/1329505
【audio_params】音频输出配置 - 用户可自定义添加火山引擎支持的任何音频参数
- speech_rate: 语速(-50~100),默认0
- loudness_rate: 音量(-50~100),默认0
示例:{"speech_rate": 10, "loudness_rate": 5}
【additions】高级文本处理配置 - 用户可自定义添加火山引擎支持的任何高级参数
- post_process.pitch: 音高(-12~12),默认0
- aigc_metadata: AIGC元数据配置
- cache_config: 缓存配置
示例:{"post_process": {"pitch": 2}, "aigc_metadata": {}, "cache_config": {}}
注意:
- 豆包语音合成模型2.0使用seed-tts-2.0资源ID,与火山双流式TTSvolc.service_type.10029)不同
- 相关音色列表:https://www.volcengine.com/docs/6561/1257544
- 用户可根据火山引擎API文档自行添加更多参数
' WHERE `id` = 'TTS_HSDSTTS_V2';
-- 添加豆包语音合成模型2.0音色(与火山双流式TTS音色相同)
delete from `ai_tts_voice` where tts_model_id = 'TTS_HSDSTTS_V2';
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0001', 'TTS_HSDSTTS_V2', 'Vivi', 'zh_female_vv_uranus_bigtts', '普通话、日语、印尼语、墨西哥西班牙语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_vv_uranus_bigtts.wav', NULL, NULL, NULL, 1, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0002', 'TTS_HSDSTTS_V2', '小何', 'zh_female_xiaohe_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_xiaohe_uranus_bigtts.mp3', NULL, NULL, NULL, 2, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0003', 'TTS_HSDSTTS_V2', '云舟', 'zh_male_m191_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_m191_uranus_bigtts.mp3', NULL, NULL, NULL, 3, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0004', 'TTS_HSDSTTS_V2', '小天', 'zh_male_taocheng_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_taocheng_uranus_bigtts.mp3', NULL, NULL, NULL, 4, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0005', 'TTS_HSDSTTS_V2', '刘飞', 'zh_male_liufei_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_liufei_uranus_bigtts.mp3', NULL, NULL, NULL, 5, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0006', 'TTS_HSDSTTS_V2', '魅力苏菲', 'zh_female_sophie_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_sophie_uranus_bigtts.mp3', NULL, NULL, NULL, 6, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0007', 'TTS_HSDSTTS_V2', '清新女声', 'zh_female_qingxinnvsheng_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_qingxinnvsheng_uranus_bigtts.mp3', NULL, NULL, NULL, 7, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0008', 'TTS_HSDSTTS_V2', '知性灿灿', 'zh_female_cancan_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_cancan_uranus_bigtts.mp3', NULL, NULL, NULL, 8, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0009', 'TTS_HSDSTTS_V2', '撒娇学妹', 'zh_female_sajiaoxuemei_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_sajiaoxuemei_uranus_bigtts.mp3', NULL, NULL, NULL, 9, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0010', 'TTS_HSDSTTS_V2', '甜美小源', 'zh_female_tianmeixiaoyuan_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_tianmeixiaoyuan_uranus_bigtts.mp3', NULL, NULL, NULL, 10, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0011', 'TTS_HSDSTTS_V2', '甜美桃子', 'zh_female_tianmeitaozi_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_tianmeitaozi_uranus_bigtts.mp3', NULL, NULL, NULL, 11, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0012', 'TTS_HSDSTTS_V2', '爽快思思', 'zh_female_shuangkuaisisi_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_shuangkuaisisi_uranus_bigtts.mp3', NULL, NULL, NULL, 12, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0013', 'TTS_HSDSTTS_V2', '佩奇猪', 'zh_female_peiqi_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_peiqi_uranus_bigtts.mp3', NULL, NULL, NULL, 13, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0014', 'TTS_HSDSTTS_V2', '邻家女孩', 'zh_female_linjianvhai_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_linjianvhai_uranus_bigtts.mp3', NULL, NULL, NULL, 14, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0015', 'TTS_HSDSTTS_V2', '少年梓辛/Brayan', 'zh_male_shaonianzixin_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_shaonianzixin_uranus_bigtts.mp3', NULL, NULL, NULL, 15, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0016', 'TTS_HSDSTTS_V2', '猴哥', 'zh_male_sunwukong_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_sunwukong_uranus_bigtts.mp3', NULL, NULL, NULL, 16, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0017', 'TTS_HSDSTTS_V2', '魅力女友', 'zh_female_meilinvyou_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_meilinvyou_uranus_bigtts.mp3', NULL, NULL, NULL, 17, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0018', 'TTS_HSDSTTS_V2', 'Tim', 'en_male_tim_uranus_bigtts', '英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/en_male_tim_uranus_bigtts.mp3', NULL, NULL, NULL, 18, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0019', 'TTS_HSDSTTS_V2', 'Dacey', 'en_female_dacey_uranus_bigtts', '英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/en_female_dacey_uranus_bigtts.mp3', NULL, NULL, NULL, 19, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0020', 'TTS_HSDSTTS_V2', 'Stokie', 'en_female_stokie_uranus_bigtts', '英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/en_female_stokie_uranus_bigtts.mp3', NULL, NULL, NULL, 20, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0021', 'TTS_HSDSTTS_V2', '温暖阿虎/Alvin', 'zh_male_wennuanahu_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_wennuanahu_uranus_bigtts.mp3', NULL, NULL, NULL, 21, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0022', 'TTS_HSDSTTS_V2', '奶气萌娃', 'zh_male_naiqimengwa_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_naiqimengwa_uranus_bigtts.mp3', NULL, NULL, NULL, 22, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0023', 'TTS_HSDSTTS_V2', '婆婆', 'zh_female_popo_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_popo_uranus_bigtts.mp3', NULL, NULL, NULL, 23, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0024', 'TTS_HSDSTTS_V2', '开朗姐姐', 'zh_female_kailangjiejie_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_kailangjiejie_uranus_bigtts.mp3', NULL, NULL, NULL, 24, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0025', 'TTS_HSDSTTS_V2', '轻盈朵朵', 'saturn_zh_female_qingyingduoduo_cs_tob', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/ICL_zh_female_qingyingduoduo_cs_tob.mp3', NULL, NULL, NULL, 25, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0026', 'TTS_HSDSTTS_V2', '温婉珊珊', 'saturn_zh_female_wenwanshanshan_cs_tob', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/ICL_zh_female_wenwanshanshan_cs_tob.mp3', NULL, NULL, NULL, 26, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0027', 'TTS_HSDSTTS_V2', '霸气青叔', 'zh_male_baqiqingshu_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_baqiqingshu_uranus_bigtts.mp3', NULL, NULL, NULL, 27, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0028', 'TTS_HSDSTTS_V2', '悬疑解说', 'zh_male_xuanyijieshuo_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_xuanyijieshuo_uranus_bigtts.mp3', NULL, NULL, NULL, 28, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0029', 'TTS_HSDSTTS_V2', '古风少御', 'zh_female_gufengshaoyu_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_gufengshaoyu_uranus_bigtts.mp3', NULL, NULL, NULL, 29, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0030', 'TTS_HSDSTTS_V2', '唐僧', 'zh_male_tangseng_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_tangseng_uranus_bigtts.mp3', NULL, NULL, NULL, 30, NULL, NULL, NULL, NULL);
@@ -0,0 +1,38 @@
-- 智能体替换词文件表
CREATE TABLE IF NOT EXISTS `ai_agent_correct_word_file` (
`id` VARCHAR(32) NOT NULL,
`file_name` VARCHAR(256) NOT NULL COMMENT '原始文件名',
`word_count` INT NOT NULL DEFAULT 0 COMMENT '替换词数量',
`content` TEXT COMMENT '文件原始内容',
`creator` BIGINT DEFAULT NULL,
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
`updater` BIGINT DEFAULT NULL,
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
INDEX `idx_creator` (`creator`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='替换词文件';
-- 替换词词条表
CREATE TABLE IF NOT EXISTS `ai_agent_correct_word_item` (
`id` VARCHAR(32) NOT NULL,
`file_id` VARCHAR(32) NOT NULL COMMENT '所属文件ID',
`source_word` VARCHAR(128) NOT NULL COMMENT '原词',
`target_word` VARCHAR(128) NOT NULL COMMENT '替换词',
PRIMARY KEY (`id`),
INDEX `idx_file_id` (`file_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='替换词词条';
-- 智能体替换词文件关联表
CREATE TABLE IF NOT EXISTS `ai_agent_correct_word_mapping` (
`id` VARCHAR(32) NOT NULL,
`agent_id` VARCHAR(32) NOT NULL,
`file_id` VARCHAR(32) NOT NULL,
`creator` BIGINT DEFAULT NULL,
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
`updater` BIGINT DEFAULT NULL,
`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE INDEX `uk_agent_file` (`agent_id`, `file_id`),
INDEX `idx_agent_id` (`agent_id`),
INDEX `idx_file_id` (`file_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='智能体替换词文件关联';
@@ -0,0 +1,45 @@
-- 修复豆包语音合成模型2.0 provider_code 重复问题,添加 ASR 2.0 支持
-- ==================== 豆包语音合成模型2.0 ====================
-- 删除 TTS 2.0 供应器(不再需要单独的供应器)
delete from `ai_model_provider` where id = 'SYSTEM_TTS_HSDSTTS_V2';
-- ==================== 豆包语音识别(流式) ====================
-- 修正原有豆包语音识别(流式)供应器,移除cluster字段,添加resource_id字段
UPDATE `ai_model_provider` SET `fields` = '[{"key":"appid","type":"string","label":"应用ID"},{"key":"access_token","type":"string","label":"访问令牌"},{"key":"boosting_table_name","type":"string","label":"热词文件名称"},{"key":"correct_table_name","type":"string","label":"替换词文件名称"},{"key":"output_dir","type":"string","label":"输出目录"},{"key":"end_window_size","type":"number","label":"静音判定时长(ms)"},{"key":"enable_multilingual","type":"boolean","label":"是否开启多语种识别模式"},{"key":"language","type":"string","label":"指定语言编码"},{"key":"resource_id","type":"string","label":"资源ID"}]' WHERE `id` = 'SYSTEM_ASR_DoubaoStreamASR';
-- 修正原有豆包语音识别(流式)配置,移除cluster字段,添加resource_id默认值
UPDATE `ai_model_config` SET `config_json` = JSON_REMOVE(JSON_SET(`config_json`, '$.resource_id', 'volc.bigasr.sauc.duration'), '$.cluster') WHERE `id` = 'ASR_DoubaoStreamASR';
-- ==================== 豆包语音识别模型2.0 ====================
-- 插入豆包语音识别模型2.0配置
delete from `ai_model_config` where id = 'ASR_DoubaoStreamASRV2';
INSERT INTO `ai_model_config` VALUES ('ASR_DoubaoStreamASRV2', 'ASR', 'DoubaoStreamASRV2', '豆包语音识别模型2.0', 0, 1, '{
"type": "doubao_stream",
"appid": "",
"access_token": "",
"resource_id": "volc.seedasr.sauc.duration",
"end_window_size": 200,
"enable_multilingual": false,
"language": "zh-CN",
"output_dir": "tmp/"
}', NULL, NULL, 6, NULL, NULL, NULL, NULL);
-- 豆包语音识别模型2.0配置说明文档
UPDATE `ai_model_config` SET
`doc_link` = 'https://www.volcengine.com/docs/6561/109979',
`remark` = '豆包语音识别模型2.0配置说明(基于火山引擎seed-asr):
1. 访问 https://www.volcengine.com/ 注册并开通火山引擎账号
2. 访问 https://console.volcengine.com/speech/service/10038 开通豆包流式语音识别模型2.0
3. 在页面底部获取appid和access_token
4. 资源ID有两种:小时版(volc.seedasr.sauc.duration)和并发版(volc.seedasr.sauc.concurrent
- 小时版:固定为:volc.seedasr.sauc.duration(豆包语音识别模型2.0
- 并发版:固定为:volc.seedasr.sauc.concurrent(豆包语音识别模型2.0
详细参数文档:https://www.volcengine.com/docs/6561/109979
注意:
- 豆包语音识别模型2.0使用volc.seedasr.sauc.duration资源ID,与豆包语音识别(流式)volc.bigasr.sauc.duration)不同
- 语音识别模型2.0价格更为便宜,建议在高并发场景下使用并发版资源ID
' WHERE `id` = 'ASR_DoubaoStreamASRV2';
@@ -0,0 +1,57 @@
-- 修改模型名称
UPDATE `ai_model_config` SET `model_name` = '豆包语音识别2.0(流式)' WHERE `id` = 'ASR_DoubaoStreamASRV2';
UPDATE `ai_model_config` SET `model_name` = '豆包语音合成2.0(流式)' WHERE `id` = 'TTS_HSDSTTS_V2';
-- 调整模型音色
delete from `ai_tts_voice` where tts_model_id = 'TTS_HSDSTTS_V2';
-- 通用场景 and 视频配音
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0001', 'TTS_HSDSTTS_V2', '湾湾小何', 'zh_female_xiaohe_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_xiaohe_uranus_bigtts.mp3', NULL, NULL, NULL, 1, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0002', 'TTS_HSDSTTS_V2', 'Vivi', 'zh_female_vv_uranus_bigtts', '普通话、日语、印尼语、墨西哥西班牙语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_vv_uranus_bigtts.wav', NULL, NULL, NULL, 2, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0003', 'TTS_HSDSTTS_V2', '可爱女生', 'saturn_zh_female_keainvsheng_tob', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/saturn_zh_female_keainvsheng_tob.wav', NULL, NULL, NULL, 3, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0004', 'TTS_HSDSTTS_V2', '调皮公主', 'saturn_zh_female_tiaopigongzhu_tob', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/saturn_zh_female_tiaopigongzhu_tob.wav', NULL, NULL, NULL, 4, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0005', 'TTS_HSDSTTS_V2', '阳光青年', 'zh_male_yangguangqingnian_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_yangguangqingnian_uranus_bigtts.mp3', NULL, NULL, NULL, 5, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0006', 'TTS_HSDSTTS_V2', '爽朗少年', 'saturn_zh_male_shuanglangshaonian_tob', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/saturn_zh_male_shuanglangshaonian_tob.wav', NULL, NULL, NULL, 6, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0007', 'TTS_HSDSTTS_V2', '爽朗少年', 'saturn_zh_male_tiancaitongzhuo_tob', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/saturn_zh_male_tiancaitongzhuo_tob.wav', NULL, NULL, NULL, 7, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0008', 'TTS_HSDSTTS_V2', '大壹', 'zh_male_dayi_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_dayi_saturn_bigtts.wav', NULL, NULL, NULL, 8, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0009', 'TTS_HSDSTTS_V2', '黑猫侦探社咪仔', 'zh_female_mizai_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_mizai_saturn_bigtts.wav', NULL, NULL, NULL, 9, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0010', 'TTS_HSDSTTS_V2', '鸡汤女', 'zh_female_jitangnv_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_jitangnv_saturn_bigtts.wav', NULL, NULL, NULL, 10, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0011', 'TTS_HSDSTTS_V2', '魅力女友', 'zh_female_meilinvyou_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_meilinvyou_saturn_bigtts.wav', NULL, NULL, NULL, 11, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0012', 'TTS_HSDSTTS_V2', '流畅女声', 'zh_female_liuchangnv_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/%E6%B5%81%E7%95%85%E5%A5%B3%E5%A3%B0.wav', NULL, NULL, NULL, 12, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0013', 'TTS_HSDSTTS_V2', '儒雅逸辰', 'zh_male_ruyayichen_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_ruyayichen_saturn_bigtts.wav', NULL, NULL, NULL, 13, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0014', 'TTS_HSDSTTS_V2', '云舟', 'zh_male_m191_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_m191_uranus_bigtts.mp3', NULL, NULL, NULL, 14, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0015', 'TTS_HSDSTTS_V2', '小天', 'zh_male_taocheng_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_taocheng_uranus_bigtts.mp3', NULL, NULL, NULL, 15, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0016', 'TTS_HSDSTTS_V2', '刘飞', 'zh_male_liufei_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_liufei_uranus_bigtts.mp3', NULL, NULL, NULL, 16, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0017', 'TTS_HSDSTTS_V2', '魅力苏菲', 'zh_female_sophie_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_sophie_uranus_bigtts.mp3', NULL, NULL, NULL, 17, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0018', 'TTS_HSDSTTS_V2', '清新女声', 'zh_female_qingxinnvsheng_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_qingxinnvsheng_uranus_bigtts.mp3', NULL, NULL, NULL, 18, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0019', 'TTS_HSDSTTS_V2', '知性灿灿', 'zh_female_cancan_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_cancan_uranus_bigtts.mp3', NULL, NULL, NULL, 19, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0020', 'TTS_HSDSTTS_V2', '撒娇学妹', 'zh_female_sajiaoxuemei_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_sajiaoxuemei_uranus_bigtts.mp3', NULL, NULL, NULL, 20, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0021', 'TTS_HSDSTTS_V2', '甜美小源', 'zh_female_tianmeixiaoyuan_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_tianmeixiaoyuan_uranus_bigtts.mp3', NULL, NULL, NULL, 21, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0022', 'TTS_HSDSTTS_V2', '甜美桃子', 'zh_female_tianmeitaozi_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_tianmeitaozi_uranus_bigtts.mp3', NULL, NULL, NULL, 22, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0023', 'TTS_HSDSTTS_V2', '邻家女孩', 'zh_female_linjianvhai_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_linjianvhai_uranus_bigtts.mp3', NULL, NULL, NULL, 23, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0024', 'TTS_HSDSTTS_V2', '爽快思思', 'zh_female_shuangkuaisisi_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_shuangkuaisisi_uranus_bigtts.mp3', NULL, NULL, NULL, 24, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0025', 'TTS_HSDSTTS_V2', '少年梓辛/Brayan', 'zh_male_shaonianzixin_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_shaonianzixin_uranus_bigtts.mp3', NULL, NULL, NULL, 25, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0026', 'TTS_HSDSTTS_V2', '温暖阿虎/Alvin', 'zh_male_wennuanahu_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_wennuanahu_uranus_bigtts.mp3', NULL, NULL, NULL, 26, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0027', 'TTS_HSDSTTS_V2', '奶气萌娃', 'zh_male_naiqimengwa_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_naiqimengwa_uranus_bigtts.mp3', NULL, NULL, NULL, 27, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0028', 'TTS_HSDSTTS_V2', '婆婆', 'zh_female_popo_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_popo_uranus_bigtts.mp3', NULL, NULL, NULL, 28, NULL, NULL, NULL, NULL);
-- 客服场景
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0029', 'TTS_HSDSTTS_V2', '轻盈朵朵', 'saturn_zh_female_qingyingduoduo_cs_tob', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/ICL_zh_female_qingyingduoduo_cs_tob.mp3', NULL, NULL, NULL, 29, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0030', 'TTS_HSDSTTS_V2', '温婉珊珊', 'saturn_zh_female_wenwanshanshan_cs_tob', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/ICL_zh_female_wenwanshanshan_cs_tob.mp3', NULL, NULL, NULL, 30, NULL, NULL, NULL, NULL);
-- 多语种
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0031', 'TTS_HSDSTTS_V2', 'Tim', 'en_male_tim_uranus_bigtts', '英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/en_male_tim_uranus_bigtts.mp3', NULL, NULL, NULL, 31, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0032', 'TTS_HSDSTTS_V2', 'Dacey', 'en_female_dacey_uranus_bigtts', '英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/en_female_dacey_uranus_bigtts.mp3', NULL, NULL, NULL, 32, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0033', 'TTS_HSDSTTS_V2', 'Stokie', 'en_female_stokie_uranus_bigtts', '英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/en_female_stokie_uranus_bigtts.mp3', NULL, NULL, NULL, 33, NULL, NULL, NULL, NULL);
-- 有声阅读
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0034', 'TTS_HSDSTTS_V2', '古风少御', 'zh_female_gufengshaoyu_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_gufengshaoyu_uranus_bigtts.mp3', NULL, NULL, NULL, 34, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0035', 'TTS_HSDSTTS_V2', '霸气青叔', 'zh_male_baqiqingshu_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_baqiqingshu_uranus_bigtts.mp3', NULL, NULL, NULL, 35, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0036', 'TTS_HSDSTTS_V2', '悬疑解说', 'zh_male_xuanyijieshuo_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_xuanyijieshuo_uranus_bigtts.mp3', NULL, NULL, NULL, 36, NULL, NULL, NULL, NULL);
-- 角色扮演
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0037', 'TTS_HSDSTTS_V2', '佩奇猪', 'zh_female_peiqi_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_female_peiqi_uranus_bigtts.mp3', NULL, NULL, NULL, 37, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0038', 'TTS_HSDSTTS_V2', '猴哥', 'zh_male_sunwukong_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_sunwukong_uranus_bigtts.mp3', NULL, NULL, NULL, 38, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0039', 'TTS_HSDSTTS_V2', '猪八戒', 'zh_male_zhubajie_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_zhubajie_uranus_bigtts.mp3', NULL, NULL, NULL, 39, NULL, NULL, NULL, NULL);
INSERT INTO `ai_tts_voice` VALUES ('TTS_HSDSTTS_V2_0040', 'TTS_HSDSTTS_V2', '唐僧', 'zh_male_tangseng_uranus_bigtts', '普通话、英语', 'https://lf3-static.bytednsdoc.com/obj/eden-cn/lm_hz_ihsph/ljhwZthlaukjlkulzlp/portal/bigtts/zh_male_tangseng_uranus_bigtts.mp3', NULL, NULL, NULL, 40, NULL, NULL, NULL, NULL);
@@ -0,0 +1,5 @@
-- 删除provider_code为ttson的供应器
DELETE FROM `ai_model_provider` WHERE `provider_code` = 'ttson';
-- 删除model_code为ACGNTTS的配置
DELETE FROM `ai_model_config` WHERE `model_code` = 'ACGNTTS';
@@ -0,0 +1,6 @@
-- 删除model_code为GizwitsTTS的配置
DELETE FROM `ai_model_config` WHERE `model_code` = 'GizwitsTTS';
-- 删除关联的TTS音色记录
DELETE FROM `ai_tts_voice` WHERE `tts_model_id` = 'TTS_GizwitsTTS';
DELETE FROM `ai_tts_voice` WHERE `tts_model_id` = 'TTS_ACGNTTS';
@@ -0,0 +1,8 @@
-- 删除provider_code为linkerai的供应商配置
DELETE FROM `ai_model_provider` WHERE `provider_code` = 'linkerai';
-- 删除model_code为LinkeraiTTS的模型配置
DELETE FROM `ai_model_config` WHERE `model_code` = 'LinkeraiTTS';
-- 删除LinkeraiTTS关联的TTS音色记录
DELETE FROM `ai_tts_voice` WHERE `tts_model_id` = 'TTS_LinkeraiTTS';
@@ -571,3 +571,94 @@ databaseChangeLog:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202603111131.sql
- changeSet:
id: 202603231037
author: rainv123
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202603231037.sql
- changeSet:
id: 202603311200
author: cgd
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202603311200.sql
- changeSet:
id: 202604010930
author: rainv123
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202604010930.sql
- changeSet:
id: 202604011035
author: RanChen
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202604011035.sql
- changeSet:
id: 202604011545
author: rainv123
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202604011545.sql
- changeSet:
id: 202604161357
author: hrz
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202604161357.sql
- changeSet:
id: 202604201719
author: RanChen
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202604201719.sql
- changeSet:
id: 202604211515
author: rainv123
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202604211515.sql
- changeSet:
id: 202604211700
author: RanChen
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202604211700.sql
- changeSet:
id: 202604300930
author: RanChen
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202604300930.sql
- changeSet:
id: 202605071037
author: fyb
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202605071037.sql
- changeSet:
id: 202605071120
author: fyb
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202605071120.sql
- changeSet:
id: 202605081008
author: fyb
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202605081008.sql
@@ -206,4 +206,8 @@
10197=\u6807\u7B7E\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
10198=\u6807\u7B7E\u4E0D\u5B58\u5728
10199=\u89E3\u6790\u4E2D\u6587\u4EF6\u6682\u4E0D\u652F\u6301\u6B64\u64CD\u4F5C
10200=\u6CA1\u6709\u6743\u9650\u67E5\u770B\u8BE5\u667A\u80FD\u4F53\u7684MCP\u63A5\u5165\u70B9\u5730\u5740
10201=\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u8FDB\u5165\u53C2\u6570\u7BA1\u7406\u914D\u7F6Emcp\u63A5\u5165\u70B9\u5730\u5740
10202=\u6CA1\u6709\u6743\u9650\u67E5\u770B\u8BE5\u667A\u80FD\u4F53\u7684MCP\u5DE5\u5177\u5217\u8868
10203=\u6587\u4EF6\u540D\u79F0\u5DF2\u5B58\u5728
10204=\u6587\u4EF6\u5927\u5C0F\u8D85\u8FC71MB\u9650\u5236
@@ -206,4 +206,8 @@
10197=Tag-Name darf nicht leer sein
10198=Tag nicht gefunden
10199=Dateianalyse l\u00E4uft, dieser Vorgang wird nicht unterst\u00FCtzt
10200=Keine Berechtigung, die MCP-Endpunktadresse dieses Agenten anzuzeigen
10201=Bitte kontaktieren Sie den Administrator, um die MCP-Endpunktadresse in der Parameterverwaltung zu konfigurieren
10202=Keine Berechtigung, die MCP-Tool-Liste dieses Agenten anzuzeigen
10203=Dateiname existiert bereits
10204=Dateigr\u00f6\u00dfe \u00fcberschreitet 1MB Limit
@@ -206,4 +206,9 @@
10197=Tag name cannot be empty
10198=Tag not found
10199=Parsing in progress, this operation is not supported
10200=No permission to view the MCP endpoint address of this agent
10201=Please contact the administrator to configure the MCP endpoint address in parameter management
10202=No permission to view the MCP tool list of this agent
10203=File name already exists
10204=File size exceeds 1MB limit
@@ -21,7 +21,7 @@
10015=Exclua primeiro os usu\u00E1rios do departamento
10016=Falha na implanta\u00E7\u00E3o, sem fluxo
10017=Diagrama incorreto, verifique
10018=Falha na exporta\u00E7\u00E3o, ID do modelo {0}
10018=Falha na exporta\u00E7\u00E3o, ID do modelo {0}
10019=Por favor, fa\u00E7a upload do arquivo
10020=Token n\u00E3o pode ser vazio
@@ -57,7 +57,7 @@
10046=Erro ao salvar impress\u00E3o vocal, entre em contato com o administrador
10047=Limite di\u00E1rio de envio atingido
10048=Erro na inser\u00E7\u00E3o da senha antiga
10049=LLM configurado n\u00E3o openai ou Collama
10049=LLM configurado n\u00E3o openai ou Collama
10050=Falha na gera\u00E7\u00E3o do token
10051=Recurso n\u00E3o existe
10052=Agente padr\u00E3o n\u00E3o encontrado
@@ -72,12 +72,12 @@
10061=C\u00F3digo de ativa\u00E7\u00E3o n\u00E3o pode ser vazio
10062=C\u00F3digo de ativa\u00E7\u00E3o incorreto
10063=Dispositivo j\u00E1 ativado
10064=Este modelo o modelo padr\u00E3o, configure primeiro outro modelo como padr\u00E3o
10064=Este modelo o modelo padr\u00E3o, configure primeiro outro modelo como padr\u00E3o
10065=Falha ao adicionar dados
10066=Falha ao modificar dados
10067=C\u00F3digo de verifica\u00E7\u00E3o gr\u00E1fico incorreto
10068=Registro por telefone n\u00E3o habilitado, fun\u00E7\u00E3o de c\u00F3digo de verifica\u00E7\u00E3o por SMS indispon\u00EDvel
10069=Nome de usu\u00E1rio n\u00E3o n\u00FAmero de telefone, por favor insira novamente
10069=Nome de usu\u00E1rio n\u00E3o n\u00FAmero de telefone, por favor insira novamente
10070=Este n\u00FAmero de telefone j\u00E1 est\u00E1 registrado
10071=N\u00FAmero de telefone inserido n\u00E3o est\u00E1 registrado
10072=Registro de usu\u00E1rio comum n\u00E3o permitido atualmente
@@ -90,7 +90,7 @@
10079=Par\u00E2metros de sele\u00E7\u00E3o incompat\u00EDveis entre LLM e reconhecimento de inten\u00E7\u00E3o Intent
10080=A pessoa ({0}) correspondente a esta impress\u00E3o vocal j\u00E1 est\u00E1 registrada, escolha outra voz para registrar
10081=Erro ao excluir impress\u00E3o vocal
10082=Esta modifica\u00E7\u00E3o n\u00E3o permitida, esta voz j\u00E1 est\u00E1 registrada como impress\u00E3o vocal ({0})
10082=Esta modifica\u00E7\u00E3o n\u00E3o permitida, esta voz j\u00E1 est\u00E1 registrada como impress\u00E3o vocal ({0})
10083=Erro ao modificar impress\u00E3o vocal, entre em contato com o administrador
10084=Erro no endere\u00E7o da interface de impress\u00E3o vocal, acesse o gerenciamento de par\u00E2metros para modificar o endere\u00E7o
10085=Dados de \u00E1udio n\u00E3o pertencem a este agente
@@ -205,4 +205,9 @@
10196=Nome da etiqueta j\u00E1 existe
10197=Nome da etiqueta n\u00E3o pode ser vazio
10198=Etiqueta n\u00E3o existe
10199=Esta opera\u00E7\u00E3o n\u00E3o suportada na an\u00E1lise de arquivos chineses
10199=Esta opera\u00E7\u00E3o n\u00E3o suportada na an\u00E1lise de arquivos chineses
10200=Sem permiss\u00e3o para visualizar o endere\u00e7o do endpoint MCP deste agente
10201=Por favor, contate o administrador para configurar o endere\u00e7o do endpoint MCP no gerenciamento de par\u00e2metros
10202=Sem permiss\u00e3o para visualizar a lista de ferramentas MCP deste agente
10203=Nome do arquivo j\u00E1 existe
10204=O tamanho do arquivo excede o limite de 1MB
@@ -206,4 +206,8 @@
10197=T\u00EAn th\u1EB9\uFFFD kh\u00F4ng th\u1EC3 \u0111\u1EC3 tr\u1ED1ng
10198=Kh\u00F4ng t\u00ECm th\u1EA5y th\u1EB9\uFFFD
10199=T\u1EC7p \u0111ang \u0111\u01B0\u1EE3c ph\u00E2n t\u00EDch, thao t\u00E1c n\u00E0y kh\u00F4ng \u0111\u01B0\u1EE3c h\u1ED7 tr\u1EE3
10200=Kh\u00f4ng c\u00f3 quy\u1ec1n xem \u0111\u1ecba ch\u1ec9 \u0111i\u1ec3m cu\u1ed1i MCP c\u1ee7a \u0111\u1ea1i l\u00fd n\u00e0y
10201=Vui l\u00f2ng li\u00ean h\u1ec7 qu\u1ea3n tr\u1ecb vi\u00ean \u0111\u1ec3 c\u1ea5u h\u00ecnh \u0111\u1ecba ch\u1ec9 \u0111i\u1ec3m cu\u1ed1i MCP trong qu\u1ea3n l\u00fd tham s\u1ed1
10202=Kh\u00f4ng c\u00f3 quy\u1ec1n xem danh s\u00e1ch c\u00f4ng c\u1ee5 MCP c\u1ee7a \u0111\u1ea1i l\u00fd n\u00e0y
10203=T\u00EAn t\u1EC7p \u0111\u00E3 t\u1ED3n t\u1EA1i
10204=K\u00EDch th\u01b0\u1edbc t\u1ec7p v\u01b0\u1ee3t qu\u00e1 1MB
@@ -206,3 +206,8 @@
10197=\u6807\u7B7E\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A
10198=\u6807\u7B7E\u4E0D\u5B58\u5728
10199=\u89E3\u6790\u4E2D\u6587\u4EF6\u6682\u4E0D\u652F\u6301\u6B64\u64CD\u4F5C
10200=\u6CA1\u6709\u6743\u9650\u67E5\u770B\u8BE5\u667A\u80FD\u4F53\u7684MCP\u63A5\u5165\u70B9\u5730\u5740
10201=\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u8FDB\u5165\u53C2\u6570\u7BA1\u7406\u914D\u7F6Emcp\u63A5\u5165\u70B9\u5730\u5740
10202=\u6CA1\u6709\u6743\u9650\u67E5\u770B\u8BE5\u667A\u80FD\u4F53\u7684MCP\u5DE5\u5177\u5217\u8868
10203=\u6587\u4EF6\u540D\u79F0\u5DF2\u5B58\u5728
10204=\u6587\u4EF6\u5927\u5C0F\u8D85\u8FC71MB\u9650\u5236
@@ -206,4 +206,8 @@
10197=\u6A19\u7C64\u540D\u7A31\u4E0D\u80FD\u4E3A\u7A7A
10198=\u6A19\u7C64\u4E0D\u5B58\u5728
10199=\u89E3\u6790\u4E2D\u6587\u4EF6\u66AB\u4E0D\u652F\u6301\u6B64\u64CD\u4F5C
10200=\u6C92\u6709\u6B0A\u9650\u67E5\u770B\u8A72\u667A\u80FD\u9AD4\u7684MCP\u63A5\u5165\u9EDE\u5730\u5740
10201=\u8ACB\u806F\u7E6B\u7BA1\u7406\u54E1\u9032\u5165\u53C3\u6578\u7BA1\u7406\u914D\u7F6Emcp\u63A5\u5165\u9EDE\u5730\u5740
10202=\u6C92\u6709\u6B0A\u9650\u67E5\u770B\u8A72\u667A\u80FD\u9AD4\u7684MCP\u5DE5\u5177\u5217\u8868
10203=\u6A94\u6848\u540D\u7A31\u5DF2\u5B58\u5728
10204=\u6A94\u6848\u5927\u5C0F\u8D85\u904E1MB\u9650\u5236
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="xiaozhi.modules.agent.dao.AgentCorrectWordMappingDao">
<delete id="deleteByAgentId">
DELETE FROM ai_agent_correct_word_mapping WHERE agent_id = #{agentId}
</delete>
<delete id="deleteByFileId">
DELETE FROM ai_agent_correct_word_mapping WHERE file_id = #{fileId}
</delete>
<insert id="batchInsertMapping" parameterType="java.util.List">
INSERT INTO ai_agent_correct_word_mapping (id, agent_id, file_id, creator, created_at, updater, updated_at)
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.id}, #{item.agentId}, #{item.fileId}, #{item.creator}, #{item.createdAt}, #{item.updater}, #{item.updatedAt})
</foreach>
</insert>
<select id="selectByAgentId" resultType="xiaozhi.modules.agent.entity.AgentCorrectWordMappingEntity">
SELECT id, agent_id, file_id, creator, created_at, updater, updated_at
FROM ai_agent_correct_word_mapping
WHERE agent_id = #{agentId}
</select>
</mapper>
@@ -14,6 +14,8 @@
<result column="asrModelId" property="asrModelId"/>
<result column="vadModelId" property="vadModelId"/>
<result column="llmModelId" property="llmModelId"/>
<result column="slmModelId" property="slmModelId"/>
<result column="vllmModelId" property="vllmModelId"/>
<result column="ttsModelId" property="ttsModelId"/>
<result column="ttsVoiceId" property="ttsVoiceId"/>
<result column="ttsLanguage" property="ttsLanguage"/>
@@ -46,6 +48,7 @@
a.asr_model_id AS asrModelId,
a.vad_model_id AS vadModelId,
a.llm_model_id AS llmModelId,
a.slm_model_id AS slmModelId,
a.vllm_model_id AS vllmModelId,
a.tts_model_id AS ttsModelId,
a.tts_voice_id AS ttsVoiceId,
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="xiaozhi.modules.correctword.dao.CorrectWordItemDao">
<insert id="batchInsert" parameterType="java.util.List">
INSERT INTO ai_agent_correct_word_item (id, file_id, source_word, target_word)
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.id}, #{item.fileId}, #{item.sourceWord}, #{item.targetWord})
</foreach>
</insert>
</mapper>
@@ -131,6 +131,7 @@ export function getMcpAddress(agentId: string) {
meta: {
ignoreAuth: false,
toast: false,
isExposeError: true,
},
})
}
@@ -28,6 +28,7 @@ export interface AgentDetail {
asrModelId: string
vadModelId: string
llmModelId: string
slmModelId: string
vllmModelId: string
ttsModelId: string
ttsVoiceId: string
@@ -34,6 +34,9 @@ export function getChatHistory(agentId: string, sessionId: string) {
ignoreAuth: false,
toast: false,
},
cacheFor: {
expire: -1,
},
})
}
@@ -3,6 +3,7 @@ export interface ChatSession {
sessionId: string
createdAt: string
chatCount: number
title: string
}
// 聊天会话列表响应
@@ -14,7 +15,7 @@ export interface ChatSessionsResponse {
// 聊天消息
export interface ChatMessage {
createdAt: string
chatType: 1 | 2 // 1是用户,2是AI
chatType: 1 | 2 | 3 // 1是用户,2是AI3是参数说明
content: string
audioId: string | null
macAddress: string
@@ -60,3 +60,13 @@ export function updateVoicePrint(data: VoicePrint) {
},
})
}
// 获取音频下载ID
export function getAudioDownloadId(audioId: string) {
return http.Post<string>(`/agent/audio/${audioId}`, {}, {
meta: {
ignoreAuth: false,
toast: false,
},
})
}
@@ -1,5 +1,6 @@
import type { uniappRequestAdapter } from '@alova/adapter-uniapp'
import type { IResponse } from './types'
import type { Language } from '@/store/lang'
import AdapterUniapp from '@alova/adapter-uniapp'
import { createAlova } from 'alova'
import { createServerTokenAuthentication } from 'alova/client'
@@ -8,6 +9,16 @@ import { getEnvBaseUrl } from '@/utils'
import { toast } from '@/utils/toast'
import { ContentTypeEnum, ResultEnum, ShowMessage } from './enum'
// 语言映射, 用于设置 Accept-language 头
const langMap: Record<Language, string> = {
zh_CN: 'zh-CN',
en: 'en-US',
zh_TW: 'zh-TW',
de: 'de',
vi: 'vi',
pt_BR: 'pt-BR',
}
/**
* 创建请求实例
*/
@@ -51,6 +62,7 @@ const alovaInstance = createAlova({
// 检查混合内容错误(HTTPS页面请求HTTP接口)
const currentProtocol = typeof window !== 'undefined' && window.location.protocol
const requestProtocol = method.baseURL?.split(':')[0]
const currentLang = langMap[uni.getStorageSync('app_language') as Language || 'zh_CN']
if (currentProtocol === 'https:' && requestProtocol === 'http') {
const errorMessage = '无法配置http协议地址,请检查接口地址'
throw new Error(errorMessage)
@@ -60,6 +72,7 @@ const alovaInstance = createAlova({
method.config.headers = {
'Content-Type': ContentTypeEnum.JSON,
'Accept': 'application/json, text/plain, */*',
'Accept-language': currentLang,
...method.config.headers,
}
+14 -6
View File
@@ -11,7 +11,7 @@ export default {
'login.navigationTitle': 'Anmelden',
'login.fetchConfigError': 'Konfiguration konnte nicht abgerufen werden:',
'login.selectLanguage': 'Sprache auswählen',
'login.selectLanguageTip': 'Vi',
'login.selectLanguageTip': 'De',
'login.welcomeBack': 'Willkommen zurück',
'login.pleaseLogin': 'Bitte melden Sie sich an',
'login.enterUsername': 'Bitte Benutzernamen eingeben',
@@ -69,7 +69,7 @@ export default {
'home.createFirstAgent': 'Klicken Sie auf die + Schaltfläche unten rechts, um Ihren ersten Agenten zu erstellen',
'home.dialogTitle': 'Agent erstellen',
'home.inputPlaceholder': 'z.B. Kundenservice-Assistent, Sprachassistent, Wissens-F&A',
'home.createError': 'Bitte Agenten-Namen eingeben',
'home.createError': 'Der Name muss zwischen 1 und 64 Zeichen lang sein.',
'home.createNow': 'Jetzt erstellen',
'home.justNow': 'Gerade eben',
'home.minutesAgo': 'Minuten her',
@@ -85,7 +85,7 @@ export default {
'agent.chatHistory': 'Chat-Verlauf',
'agent.voiceprintManagement': 'Stimmabdruckverwaltung',
'agent.editTitle': 'Agent bearbeiten',
'agent.toolsTitle': 'Funktionen bearbeiten',
'agent.toolsTitle': 'Bearbeiten',
'agent.voiceActivityDetection': 'Sprachaktivitätserkennung',
'agent.speechRecognition': 'Spracherkennung',
'agent.largeLanguageModel': 'Großes Sprachmodell',
@@ -108,7 +108,8 @@ export default {
'agent.modelConfig': 'Modellkonfiguration',
'agent.vad': 'Sprachaktivitätserkennung',
'agent.asr': 'Spracherkennung',
'agent.llm': 'Großes Sprachmodell',
'agent.llm': 'Hauptsprachenmodell',
'agent.slm': 'Kleine Parametermodelle',
'agent.vllm': 'Vision-Sprachmodell',
'agent.intent': 'Absichtserkennung',
'agent.memory': 'Speicher',
@@ -116,7 +117,7 @@ export default {
'agent.tts': 'Text-zu-Sprache',
'agent.voiceprint': 'Agenten-Stimme',
'agent.plugins': 'Plugins',
'agent.editFunctions': 'Funktionen bearbeiten',
'agent.editFunctions': 'Bearbeiten',
'agent.historyMemory': 'Verlaufsspeicher',
'agent.memoryContent': 'Speicherinhalt',
'agent.saving': 'Wird gespeichert...',
@@ -371,7 +372,7 @@ export default {
'agent.tools.mcpAccessPoint': 'MCP-Zugangspunkt',
'agent.tools.copy': 'Kopieren',
'agent.tools.noTools': 'Keine Werkzeuge verfügbar',
'agent.tools.parameterConfig': 'Parameterkonfiguration',
'agent.tools.parameterConfig': 'Konfig',
'agent.tools.noParamsNeeded': 'Keine Parameter benötigt',
'agent.tools.pleaseInput': 'Bitte eingeben',
'agent.tools.inputOneItemPerLine': 'Ein Element pro Zeile eingeben',
@@ -471,6 +472,7 @@ export default {
'deviceConfig.reCheck': 'Erneut prüfen',
'deviceConfig.connectedXiaozhiHotspot': 'Mit xiaozhi Hotspot verbunden',
'deviceConfig.wifiNetwork': 'WiFi-Netzwerk',
'deviceConfig.wifiPassword': 'WiFi-Passwort',
'deviceConfig.scanning': 'Wird gescannt',
'deviceConfig.cancel': 'Abbrechen',
'deviceConfig.clickRefreshScan': 'Bitte Scan aktualisieren klicken',
@@ -484,6 +486,7 @@ export default {
'deviceConfig.wifiConfigFailed': 'WiFi-Konfiguration fehlgeschlagen',
'deviceConfig.pleaseCheckNetworkConnection': 'Bitte Netzwerkverbindung prüfen',
'deviceConfig.startWifiConfigButton': 'Konfiguration starten',
'deviceConfig.configuring': 'Wird konfiguriert...',
'deviceConfig.wifiConfigInstructions': 'WiFi-Konfigurationsanleitung',
'deviceConfig.phoneConnectXiaozhiHotspot': 'Telefon mit xiaozhi Hotspot verbinden',
'deviceConfig.selectTargetWifiNetwork': 'Ziel-WiFi-Netzwerk auswählen',
@@ -492,4 +495,9 @@ export default {
'deviceConfig.afterConfigSuccessDeviceWillRestart': 'Nach erfolgreicher Konfiguration startet Gerät automatisch neu',
'deviceConfig.audioPlaybackError': 'Audio-Wiedergabe-Fehler',
'deviceConfig.playbackFailed': 'Wiedergabe fehlgeschlagen',
// Voiceprint page
'voiceprint.audioNotExist': 'Audio existiert nicht',
'voiceprint.getAudioFailed': 'Audio konnte nicht abgerufen werden',
'voiceprint.audioPlayFailed': 'Audio-Wiedergabe fehlgeschlagen',
}
+12 -4
View File
@@ -11,7 +11,7 @@ export default {
'login.navigationTitle': 'Login',
'login.fetchConfigError': 'Failed to fetch configuration:',
'login.selectLanguage': 'Select Language',
'login.selectLanguageTip': '中文',
'login.selectLanguageTip': 'En',
'login.welcomeBack': 'Welcome Back',
'login.pleaseLogin': 'Please log in to your account',
'login.enterUsername': 'Please enter username',
@@ -69,7 +69,7 @@ export default {
'home.createFirstAgent': 'Click the + button in the lower right corner to create your first agent',
'home.dialogTitle': 'Create Agent',
'home.inputPlaceholder': 'e.g. Customer Service Assistant, Voice Assistant, Knowledge Q&A',
'home.createError': 'Please input agent name',
'home.createError': 'The name length must be between 1 and 64 characters',
'home.createNow': 'Create Now',
'home.justNow': 'Just now',
'home.minutesAgo': 'minutes ago',
@@ -108,7 +108,8 @@ export default {
'agent.modelConfig': 'Model Configuration',
'agent.vad': 'Voice Activity Detection',
'agent.asr': 'Speech Recognition',
'agent.llm': 'Large Language Model',
'agent.llm': 'Main language model',
'agent.slm': 'Small parameter model',
'agent.vllm': 'Vision Language Model',
'agent.intent': 'Intent Recognition',
'agent.memory': 'Memory',
@@ -371,7 +372,7 @@ export default {
'agent.tools.mcpAccessPoint': 'MCP Access Point',
'agent.tools.copy': 'Copy',
'agent.tools.noTools': 'No tools available',
'agent.tools.parameterConfig': 'Parameter Configuration',
'agent.tools.parameterConfig': 'Param Config',
'agent.tools.noParamsNeeded': 'No parameters needed',
'agent.tools.pleaseInput': 'Please input',
'agent.tools.inputOneItemPerLine': 'Input one item per line',
@@ -471,6 +472,7 @@ export default {
'deviceConfig.reCheck': 'Re-check',
'deviceConfig.connectedXiaozhiHotspot': 'Connected to xiaozhi hotspot',
'deviceConfig.wifiNetwork': 'WiFi Network',
'deviceConfig.wifiPassword': 'WiFi Password',
'deviceConfig.scanning': 'Scanning',
'deviceConfig.cancel': 'Cancel',
'deviceConfig.clickRefreshScan': 'Please click Refresh Scan',
@@ -484,6 +486,7 @@ export default {
'deviceConfig.wifiConfigFailed': 'WiFi configuration failed',
'deviceConfig.pleaseCheckNetworkConnection': 'Please check network connection',
'deviceConfig.startWifiConfigButton': 'Start Configuration',
'deviceConfig.configuring': 'Configuring...',
'deviceConfig.wifiConfigInstructions': 'WiFi Configuration Instructions',
'deviceConfig.phoneConnectXiaozhiHotspot': 'Phone connect to xiaozhi hotspot',
'deviceConfig.selectTargetWifiNetwork': 'Select target WiFi network',
@@ -492,4 +495,9 @@ export default {
'deviceConfig.afterConfigSuccessDeviceWillRestart': 'After successful configuration, device will automatically restart',
'deviceConfig.audioPlaybackError': 'Audio playback error',
'deviceConfig.playbackFailed': 'Playback failed',
// Voiceprint page
'voiceprint.audioNotExist': 'Audio does not exist',
'voiceprint.getAudioFailed': 'Failed to get audio',
'voiceprint.audioPlayFailed': 'Audio playback failed',
}

Some files were not shown because too many files have changed in this diff Show More