diff --git a/astrbot/core/config/default.py b/astrbot/core/config/default.py index ff6f86c22..70c9ffa43 100644 --- a/astrbot/core/config/default.py +++ b/astrbot/core/config/default.py @@ -593,6 +593,11 @@ CONFIG_METADATA_2 = { "dashscope_app_type": "agent", "dashscope_api_key": "", "dashscope_app_id": "", + "rag_options": { + "pipeline_ids": [], + "file_ids": [], + "output_reference": False, + }, "variables": {}, "timeout": 60, }, @@ -665,6 +670,30 @@ CONFIG_METADATA_2 = { }, }, "items": { + "rag_options": { + "description": "RAG 选项", + "type": "object", + "hint": "检索知识库设置, 非必填。仅 Agent 应用类型支持(智能体应用, 包括 RAG 应用)", + "items": { + "pipeline_ids": { + "description": "知识库 ID 列表", + "type": "list", + "items": {"type": "string"}, + "hint": "对指定知识库内所有文档进行检索, 前往 https://bailian.console.aliyun.com/ 数据应用->知识索引创建和获取 ID。", + }, + "file_ids": { + "description": "非结构化文档 ID, 传入该参数将对指定非结构化文档进行检索。", + "type": "list", + "items": {"type": "string"}, + "hint": "对指定非结构化文档进行检索。前往 https://bailian.console.aliyun.com/ 数据管理创建和获取 ID。", + }, + "output_reference": { + "description": "是否输出知识库/文档的引用", + "type": "bool", + "hint": "在每次回答尾部加上引用源。默认为 False。", + }, + }, + }, "sensevoice_hint": { "description": "部署SenseVoice", "type": "string", @@ -681,12 +710,14 @@ CONFIG_METADATA_2 = { "type": "string", "hint": "modelscope 上的模型名称。默认:iic/SenseVoiceSmall。", }, - # "variables": { - # "description": "工作流固定输入变量", - # "type": "object", - # "obvious_hint": True, - # "hint": "可选。工作流固定输入变量,将会作为工作流的输入。也可以在对话时使用 /set 指令动态设置变量。如果变量名冲突,优先使用动态设置的变量。", - # }, + "variables": { + "description": "工作流固定输入变量", + "type": "object", + "obvious_hint": True, + "items": {}, + "hint": "可选。工作流固定输入变量,将会作为工作流的输入。也可以在对话时使用 /set 指令动态设置变量。如果变量名冲突,优先使用动态设置的变量。", + "invisible": True, + }, # "fastgpt_app_type": { # "description": "应用类型", # "type": "string", @@ -697,7 +728,7 @@ CONFIG_METADATA_2 = { "dashscope_app_type": { "description": "应用类型", "type": "string", - "hint": "阿里云百炼应用的应用类型。", + "hint": "百炼应用的应用类型。", "options": [ "agent", "agent-arrange", diff --git a/astrbot/core/provider/sources/dashscope_source.py b/astrbot/core/provider/sources/dashscope_source.py index 9647b41c0..7158d57b9 100644 --- a/astrbot/core/provider/sources/dashscope_source.py +++ b/astrbot/core/provider/sources/dashscope_source.py @@ -1,3 +1,4 @@ +import re import asyncio import functools from typing import List @@ -40,11 +41,24 @@ class ProviderDashscope(ProviderOpenAIOfficial): raise Exception("阿里云百炼 APP 类型不能为空。") self.model_name = "dashscope" self.variables: dict = provider_config.get("variables", {}) + self.rag_options: dict = provider_config.get("rag_options", {}) + self.output_reference = self.rag_options.get("output_reference", False) + self.rag_options = self.rag_options.copy() + self.rag_options.pop("output_reference", None) self.timeout = provider_config.get("timeout", 120) if isinstance(self.timeout, str): self.timeout = int(self.timeout) + def has_rag_options(self): + if ( + self.rag_options + and self.rag_options.get("pipeline_ids", None) + and self.rag_options.get("file_ids", None) + ): + return True + return False + async def text_chat( self, prompt: str, @@ -62,7 +76,10 @@ class ProviderDashscope(ProviderOpenAIOfficial): session_var = session_vars.get(session_id, {}) payload_vars.update(session_var) - if self.dashscope_app_type in ["agent", "dialog-workflow"]: + if ( + self.dashscope_app_type in ["agent", "dialog-workflow"] + and self.has_rag_options() + ): # 支持多轮对话的 new_record = {"role": "user", "content": prompt} if image_urls: @@ -86,12 +103,17 @@ class ProviderDashscope(ProviderOpenAIOfficial): else: # 不支持多轮对话的 # 调用阿里云百炼 API + payload = { + "app_id": self.app_id, + "prompt": prompt, + "api_key": self.api_key, + "biz_params": payload_vars or None, + } + if self.rag_options: + payload["rag_options"] = self.rag_options partial = functools.partial( Application.call, - app_id=self.app_id, - promtp=prompt, - api_key=self.api_key, - biz_params=payload_vars or None, + **payload, ) response = await asyncio.get_event_loop().run_in_executor(None, partial) @@ -107,6 +129,14 @@ class ProviderDashscope(ProviderOpenAIOfficial): ) output_text = response.output.get("text", "") + # RAG 引用脚标格式化 + output_text = re.sub(r"\[(\d+)\]", r"[\1]", output_text) + if self.output_reference and response.output.get("doc_references", None): + ref_str = "" + for ref in response.output.get("doc_references", []): + ref_str += f"{ref['index_id']}. {ref['title']}\n" + output_text += f"\n\n回答来源:\n{ref_str}" + return LLMResponse(role="assistant", completion_text=output_text) async def forget(self, session_id):