Files
wehub-resource-sync 1443d3fdf9
Ruff Format Check / Ruff Format & Lint (push) Failing after 7m39s
Deploy VitePress site to Pages / build (push) Failing after 9m11s
Deploy VitePress site to Pages / Deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:32:26 +08:00

143 lines
5.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""知识库示例问题生成工具。"""
import json
import textwrap
from typing import Any
from fastapi import HTTPException
from yuxi import config, knowledge_base
from yuxi.knowledge.factory import KnowledgeBaseFactory
from yuxi.models import select_model
from yuxi.repositories.knowledge_base_repository import KnowledgeBaseRepository
from yuxi.utils import logger
SAMPLE_QUESTIONS_SYSTEM_PROMPT = """你是一个专业的知识库问答测试专家。
你的任务是根据知识库中的文件列表,生成有价值的测试问题。
要求:
1. 问题要具体、有针对性,基于文件名称和类型推测可能的内容
2. 问题要涵盖不同方面和难度
3. 问题要简洁明了,适合用于检索测试
4. 问题要多样化,包括事实查询、概念解释、操作指导等
5. 问题长度控制在10-30字之间
6. 直接返回JSON数组格式,不要其他说明
返回格式:
```json
{
"questions": [
"问题1",
"问题2",
"问题3"
]
}
```
"""
def build_sample_question_file_list(files: dict[str, dict[str, Any]]) -> list[dict[str, str]]:
return [
{
"filename": file_info.get("filename", ""),
"type": file_info.get("type") or file_info.get("file_type", ""),
}
for file_info in files.values()
]
def build_sample_questions_user_message(db_name: str, files_info: list[dict[str, str]], count: int) -> str:
files_text = "\n".join([f"- {file_info['filename']} ({file_info['type']})" for file_info in files_info[:20]])
file_count_text = f"(共{len(files_info)}个文件)" if len(files_info) > 20 else ""
return textwrap.dedent(f"""请为知识库\"{db_name}\"生成{count}个测试问题。
知识库文件列表{file_count_text}
{files_text}
请根据这些文件的名称和类型,生成{count}个有价值的测试问题。""")
def parse_sample_questions_content(content: str) -> list[str]:
if "```json" in content:
json_start = content.find("```json") + 7
json_end = content.find("```", json_start)
if json_end == -1:
raise ValueError("AI返回的JSON代码块不完整")
content = content[json_start:json_end].strip()
elif "```" in content:
json_start = content.find("```") + 3
json_end = content.find("```", json_start)
if json_end == -1:
raise ValueError("AI返回的代码块不完整")
content = content[json_start:json_end].strip()
questions_data = json.loads(content)
questions = questions_data.get("questions", []) if isinstance(questions_data, dict) else []
if not questions or not isinstance(questions, list):
raise ValueError("AI返回的问题格式不正确")
return questions
async def generate_database_sample_questions(kb_id: str, count: int = 10) -> dict[str, Any]:
db_info = await knowledge_base.get_database_info(kb_id, include_files=True)
if not db_info:
raise HTTPException(status_code=404, detail=f"知识库 {kb_id} 不存在")
kb_type = (db_info.get("kb_type") or "").lower()
if not KnowledgeBaseFactory.get_kb_class(kb_type).supports_documents:
raise HTTPException(status_code=400, detail=f"{db_info.get('name') or kb_type} 不支持基于文件生成测试问题")
db_name = db_info.get("name", "")
all_files = db_info.get("files", {})
if not all_files:
raise HTTPException(status_code=400, detail="知识库中没有文件")
files_info = build_sample_question_file_list(all_files)
logger.info(f"开始生成知识库问题,知识库: {db_name}, 文件数量: {len(files_info)}, 问题数量: {count}")
model = select_model(model_spec=config.default_model)
messages = [
{"role": "system", "content": SAMPLE_QUESTIONS_SYSTEM_PROMPT},
{"role": "user", "content": build_sample_questions_user_message(db_name, files_info, count)},
]
response = await model.call(messages, stream=False)
content = response.content if hasattr(response, "content") else str(response)
try:
questions = parse_sample_questions_content(content)
except (json.JSONDecodeError, ValueError) as e:
logger.error(f"AI返回的JSON解析失败: {e}, 原始内容: {content}")
raise HTTPException(status_code=500, detail=f"AI返回格式错误: {str(e)}") from e
logger.info(f"成功生成{len(questions)}个问题")
try:
await KnowledgeBaseRepository().update(kb_id, {"sample_questions": questions})
logger.info(f"成功保存 {len(questions)} 个问题到知识库 {kb_id}")
except Exception as save_error:
logger.error(f"保存问题失败: {save_error}")
return {
"message": "success",
"questions": questions,
"count": len(questions),
"kb_id": kb_id,
"db_name": db_name,
}
async def get_database_sample_questions(kb_id: str) -> dict[str, Any]:
kb = await KnowledgeBaseRepository().get_by_kb_id(kb_id)
if kb is None:
raise HTTPException(status_code=404, detail=f"知识库 {kb_id} 不存在")
questions = kb.sample_questions or []
return {
"message": "success",
"questions": questions,
"count": len(questions),
"kb_id": kb_id,
}