refactor: 提取所有AI提示词到配置文件并实现统一管理

- 新增 config/prompts.yaml 提示词配置文件,包含10个核心提示词
- 新增 src/bidmaster/config/prompt_manager.py 提示词管理工具类
- 重构 src/bidmaster/tools/llm.py 使用配置化系统消息
- 重构 src/bidmaster/tools/parser.py 使用配置化解析提示词(3个)
- 重构 src/bidmaster/tools/rag.py 使用配置化RAG生成提示词
- 重构 src/bidmaster/nodes/toc/llm_helper.py 使用配置化TOC提示词(2个)
- 重构 src/bidmaster/nodes/toc/adjust_chapters.py 使用配置化章节调整提示词
- 重构 src/bidmaster/nodes/toc/optimize_with_feedback.py 使用配置化优化反馈提示词

优势:
- 集中管理: 所有提示词统一配置,易于维护
- 易于调优: 修改提示词无需改动代码
- 版本控制: 提示词变更可独立追踪
- A/B测试: 方便测试不同提示词效果
- 可扩展性: 支持未来多语言提示词

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
sladro 2025-10-10 10:12:25 +08:00
parent 18f85a1fa7
commit bf7eb9ca9d
8 changed files with 460 additions and 238 deletions

277
config/prompts.yaml Normal file
View File

@ -0,0 +1,277 @@
# BidMaster-CLI 提示词配置文件
# 所有AI相关的提示词统一在此管理
# ============================================================================
# 系统消息配置
# ============================================================================
system_messages:
# LLM通用系统消息
default: "你是一个专业的招投标文档分析助手。"
# RAG内容生成系统消息
rag_generator: "你是一个专业的标书撰写助手。"
# ============================================================================
# 文档解析相关提示词
# ============================================================================
parser_prompts:
# AI解析评分表格
parse_scoring_table: |
请从复杂的评分表格中提取评分项和分值,并智能分类,返回JSON。
表格内容:
{table_text}
要求:
1. 仔细分析表格结构,即使有合并单元格也要正确提取
2. 从"评审内容"、"评分因素"、"评分标准"等列中提取评分项名称
3. 从"分值"列或评分标准描述中提取具体分值(如3分、5分、40分)
4. 忽略总分构成等汇总信息,只提取具体评分项
5. 智能分类各评分项:
**技术类别:**
- technical_solution: 技术方案、技术条款、技术完整性、技术先进性
- equipment_spec: 设备规格、产品参数、设备可靠性、技术指标
- implementation: 项目实施、施工方案、进度计划、实施能力
- quality_safety: 质量管理、安全管理、环境管理、质量体系
- after_sales: 售后服务、维保服务、培训服务、技术支持、服务部分
- compliance: 技术资质、认证证书、技术合规性、技术条款应答
**商务类别:**
- commercial: 价格评分、报价、商务条件、企业资质、财务状况、业绩、商务条款应答、响应文件制作质量、同类项目业绩、商务部分
示例输入分析:
- "响应文件制作质量 3分" → 商务类别
- "同类项目业绩 5分" → 商务类别
- "技术条款应答 2分" → 合规类别
- "商务条款应答 2分" → 商务类别
格式:
{{
"scoring_criteria": [
{{"item_name": "响应文件制作质量", "max_score": 3, "description": "文件格式内容要求", "category": "commercial"}},
{{"item_name": "同类项目业绩", "max_score": 5, "description": "项目经验证明", "category": "commercial"}},
{{"item_name": "技术条款应答", "max_score": 2, "description": "技术要求符合性", "category": "compliance"}}
]
}}
只返回JSON,无其他文字:
# AI解析偏离表格
parse_deviation_table: |
请提取表格中的偏离项,返回JSON。
表格内容:
{table_text}
要求:
1. 提取技术要求和响应类型
2. 响应类型如:正偏离、负偏离、无偏离等
3. 忽略序号和表头
格式:
{{
"deviation_items": [
{{"requirement": "设备需符合国标", "response_type": "正偏离"}},
{{"requirement": "技术指标要求", "response_type": "无偏离"}}
]
}}
只返回JSON,无其他文字:
# AI识别表格类型
identify_table_type: |
分析表格内容,判断这是什么类型的表格。
表格内容:
{table_text}
请判断这个表格属于以下哪种类型:
1. scoring - 评分表:包含评分项、分值、评分标准等内容,例如:
- 有"分值"、"评分标准"、"得分"等列
- 有具体的分值数字(如3分、5分、40分等)
- 有"商务部分"、"技术部分"、"服务部分"等分类
- 有评审内容和评分因素
2. deviation - 偏离表:包含技术要求、响应类型、偏离说明等
3. other - 其他表格:不是评分表也不是偏离表
注意:即使表格结构复杂、有合并单元格,只要包含评分标准和分值信息就是评分表。
只返回一个单词:scoring 或 deviation 或 other
# ============================================================================
# 目录生成相关提示词
# ============================================================================
toc_prompts:
# 生成子章节
generate_sub_chapters: |
为以下大类别生成专业的标书子标题:
【大类别】: {parent_title}
【评分项】:
{criteria_info}
生成要求:
1. 为每个评分项生成对应的子标题名称(不要包含编号)
2. 重要评分项可添加三级子标题(不要包含编号)
3. 只返回标题文本,编号由Word自动管理
返回JSON格式:
{{
"sub_chapters": [
{{"title": "技术架构设计", "level": 2, "score": 5, "children": []}}
]
}}
只返回JSON:
# 审查目录结构
review_structure: |
请审查这个标书目录结构的合理性和完整性。
【技术评分项分布】:
{criteria_summary}
【当前生成的章节结构】:
{chapters_summary}
【审查要求】:
1. 是否缺少重要的标准章节?
2. 章节顺序是否合理?
3. 每个评分项是否都有对应章节?
返回JSON格式:
{{
"overall_assessment": "总体评价",
"suggestions": [
{{"type": "add/modify/reorder", "description": "建议内容", "priority": "high/medium/low"}}
],
"optimization_score": 85
}}
只返回JSON:
# AI调整章节结构
adjust_chapters: |
请根据用户选择的建议调整以下标书章节结构。
【当前章节结构】:
{chapters_text}
【用户选择应用的建议】:
{suggestions_text}
【调整要求】:
1. 根据用户选择的建议对章节结构进行合理调整
2. 保持章节的逻辑性和完整性
3. 每个章节都要有明确的标题和层级
4. 保持专业的标书格式
返回JSON格式:
{{
"adjusted_chapters": [
{{
"id": "chapter_1_technical_solution",
"title": "技术方案",
"level": 1,
"score": 0,
"children": [
{{
"id": "chapter_2_1_architecture",
"title": "系统架构设计",
"level": 2,
"score": 0,
"children": []
}}
]
}}
]
}}
只返回JSON:
# 根据用户反馈优化目录
optimize_with_feedback: |
你是一个专业的标书目录结构优化助手。
**⚠️ 警告:如果你不按照用户的要求修改目录结构,将被视为任务失败!⚠️**
**🔥 强制要求:你必须完全执行用户的修改要求,不能返回未修改的结构!🔥**
当前目录结构:
{current_toc}
用户反馈意见:
{feedback}
**执行步骤(必须严格遵守):**
1. **理解要求**:分析用户具体要求什么修改
2. **执行修改**:必须按要求修改目录结构,不能保持原样
3. **确认修改**:检查确保已经按用户要求进行了修改
**重要修改规则:**
- 如果用户说"内容太少,多补充"或"多增加",必须增加新的同级别子章节
- 如果用户指定某个章节,只修改该章节
- 如果用户要求"调整顺序",必须重新排列
- 如果用户要求"修改标题",必须更新标题
**具体示例:**
示例1:用户说"售后服务章节多增加一个子标题"
- 在"售后服务"下新增子章节(如"技术支持服务"、"培训服务"等)
示例2:用户说"合规响应的内容太少,多补充一些"
- 在"合规响应"下除了"技术实力",还要增加:
* "资质认证展示"
* "成功案例介绍"
* "行业认可度说明"
* "合规承诺声明"
**最终检查:**
修改完成后,你必须确认已经按用户要求进行了实际修改,不能返回相同的结构!
请返回优化后的目录结构JSON,格式如下:
{{
"chapters": [
{{
"id": "chapter_1",
"title": "章节标题",
"level": 1,
"score": 0.0,
"children": [
{{
"id": "chapter_1_1",
"title": "子章节标题",
"level": 2,
"score": 0.0,
"children": []
}}
]
}}
]
}}
只返回JSON,不要其他内容。
# ============================================================================
# 内容生成相关提示词
# ============================================================================
content_prompts:
# RAG内容生成
generate_with_rag: |
你是一个专业的标书撰写助手。请根据以下信息生成标书章节内容:
章节标题:{title}
评分要求:{requirements}{emphasis_part}{rag_part}
要求:
1. 内容专业、详实,符合招标文件要求
2. 突出技术优势和实施能力
3. 语言正式、逻辑清晰
4. 字数控制在500-800字
请直接输出章节内容,不要包含章节标题。

View File

@ -0,0 +1,129 @@
"""提示词管理工具
统一管理项目中所有AI提示词,支持从配置文件加载和模板变量替换
"""
import logging
from pathlib import Path
from typing import Any, Dict, Optional
import yaml
logger = logging.getLogger(__name__)
class PromptManager:
"""提示词管理器
单例模式,统一管理所有提示词配置
"""
_instance: Optional['PromptManager'] = None
_prompts: Dict[str, Any] = {}
_config_loaded: bool = False
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
if not self._config_loaded:
self._load_prompts()
self._config_loaded = True
def _load_prompts(self) -> None:
"""加载提示词配置文件"""
try:
# 查找config/prompts.yaml
config_path = Path(__file__).parent.parent.parent.parent / "config" / "prompts.yaml"
if not config_path.exists():
logger.warning(f"提示词配置文件不存在: {config_path}")
self._prompts = self._get_default_prompts()
return
with open(config_path, 'r', encoding='utf-8') as f:
self._prompts = yaml.safe_load(f)
logger.info(f"成功加载提示词配置: {config_path}")
except Exception as e:
logger.error(f"加载提示词配置失败: {e}")
self._prompts = self._get_default_prompts()
def _get_default_prompts(self) -> Dict[str, Any]:
"""获取默认提示词配置"""
return {
"system_messages": {
"default": "你是一个专业的招投标文档分析助手。",
"rag_generator": "你是一个专业的标书撰写助手。"
}
}
def get_system_message(self, key: str = "default") -> str:
"""获取系统消息
Args:
key: 系统消息键名
Returns:
系统消息内容
"""
return self._prompts.get("system_messages", {}).get(key, "你是一个专业的助手。")
def get_parser_prompt(self, key: str, **kwargs) -> str:
"""获取文档解析提示词
Args:
key: 提示词键名
**kwargs: 模板变量
Returns:
格式化后的提示词
"""
template = self._prompts.get("parser_prompts", {}).get(key, "")
return template.format(**kwargs) if template else ""
def get_toc_prompt(self, key: str, **kwargs) -> str:
"""获取目录生成提示词
Args:
key: 提示词键名
**kwargs: 模板变量
Returns:
格式化后的提示词
"""
template = self._prompts.get("toc_prompts", {}).get(key, "")
return template.format(**kwargs) if template else ""
def get_content_prompt(self, key: str, **kwargs) -> str:
"""获取内容生成提示词
Args:
key: 提示词键名
**kwargs: 模板变量
Returns:
格式化后的提示词
"""
template = self._prompts.get("content_prompts", {}).get(key, "")
return template.format(**kwargs) if template else ""
def reload(self) -> None:
"""重新加载配置文件"""
self._config_loaded = False
self._load_prompts()
self._config_loaded = True
logger.info("提示词配置已重新加载")
# 模块级便捷函数
def get_prompt_manager() -> PromptManager:
"""获取提示词管理器实例
Returns:
PromptManager实例
"""
return PromptManager()

View File

@ -9,6 +9,7 @@ from typing import Dict, List, Any
from ..base import BaseNode, NodeContext
from ...tools.parser import DocumentChapter
from .base_mixins import TocNodeBase
from ...config.prompt_manager import get_prompt_manager
logger = logging.getLogger(__name__)
@ -109,43 +110,13 @@ class AdjustChaptersNode(BaseNode, TocNodeBase):
# 格式化用户选择的建议
suggestions_text = self._format_suggestions_for_prompt(suggestions)
prompt = f"""
请根据用户选择的建议调整以下标书章节结构
当前章节结构:
{chapters_text}
用户选择应用的建议:
{suggestions_text}
调整要求:
1. 根据用户选择的建议对章节结构进行合理调整
2. 保持章节的逻辑性和完整性
3. 每个章节都要有明确的标题和层级
4. 保持专业的标书格式
返回JSON格式
{{
"adjusted_chapters": [
{{
"id": "chapter_1_technical_solution",
"title": "技术方案",
"level": 1,
"score": 0,
"children": [
{{
"id": "chapter_2_1_architecture",
"title": "系统架构设计",
"level": 2,
"score": 0,
"children": []
}}
]
}}
]
}}
只返回JSON"""
# 从配置获取提示词
prompt_manager = get_prompt_manager()
prompt = prompt_manager.get_toc_prompt(
"adjust_chapters",
chapters_text=chapters_text,
suggestions_text=suggestions_text
)
return prompt

View File

@ -9,6 +9,7 @@ from typing import Dict, List, Any, Optional
from ...tools.llm import LLMService
from ...tools.parser import ScoringCriteria, DocumentChapter
from ...config.prompt_manager import get_prompt_manager
logger = logging.getLogger(__name__)
@ -94,26 +95,13 @@ class LLMHelper:
for criteria in criteria_list:
criteria_info.append(f"- {criteria.item_name} ({criteria.max_score}分)")
prompt = f"""
为以下大类别生成专业的标书子标题
大类别: {parent_chapter.title}
评分项:
{chr(10).join(criteria_info)}
生成要求
1. 为每个评分项生成对应的子标题名称不要包含编号
2. 重要评分项可添加三级子标题不要包含编号
3. 只返回标题文本编号由Word自动管理
返回JSON格式
{{
"sub_chapters": [
{{"title": "技术架构设计", "level": 2, "score": 5, "children": []}}
]
}}
只返回JSON"""
# 从配置获取提示词
prompt_manager = get_prompt_manager()
prompt = prompt_manager.get_toc_prompt(
"generate_sub_chapters",
parent_title=parent_chapter.title,
criteria_info=chr(10).join(criteria_info)
)
try:
response = LLMHelper.call_llm_with_retry(prompt)
@ -144,30 +132,13 @@ class LLMHelper:
criteria_summary = CategoryManager.format_criteria_summary(technical_criteria)
chapters_summary = LLMHelper._format_chapters_summary(preliminary_chapters)
review_prompt = f"""
请审查这个标书目录结构的合理性和完整性
技术评分项分布:
{criteria_summary}
当前生成的章节结构:
{chapters_summary}
审查要求:
1. 是否缺少重要的标准章节
2. 章节顺序是否合理
3. 每个评分项是否都有对应章节
返回JSON格式
{{
"overall_assessment": "总体评价",
"suggestions": [
{{"type": "add/modify/reorder", "description": "建议内容", "priority": "high/medium/low"}}
],
"optimization_score": 85
}}
只返回JSON"""
# 从配置获取提示词
prompt_manager = get_prompt_manager()
review_prompt = prompt_manager.get_toc_prompt(
"review_structure",
criteria_summary=criteria_summary,
chapters_summary=chapters_summary
)
try:
response = LLMHelper.call_llm_with_retry(review_prompt)

View File

@ -12,6 +12,7 @@ from ...tools.parser import DocumentChapter
from .base_mixins import TocNodeBase
from .llm_helper import LLMHelper
from .factories import ChapterFactory
from ...config.prompt_manager import get_prompt_manager
logger = logging.getLogger(__name__)
@ -84,68 +85,13 @@ class OptimizeWithFeedbackNode(BaseNode, TocNodeBase):
# 格式化当前目录
current_toc = self._format_chapters_for_prompt(chapters)
prompt = f"""
你是一个专业的标书目录结构优化助手
** 警告如果你不按照用户的要求修改目录结构将被视为任务失败**
**🔥 强制要求你必须完全执行用户的修改要求不能返回未修改的结构🔥**
当前目录结构
{current_toc}
用户反馈意见
{feedback}
**执行步骤必须严格遵守**
1. **理解要求**分析用户具体要求什么修改
2. **执行修改**必须按要求修改目录结构不能保持原样
3. **确认修改**检查确保已经按用户要求进行了修改
**重要修改规则**
- 如果用户说"内容太少,多补充""多增加"必须增加新的同级别子章节
- 如果用户指定某个章节只修改该章节
- 如果用户要求"调整顺序"必须重新排列
- 如果用户要求"修改标题"必须更新标题
**具体示例**
示例1用户说"售后服务章节多增加一个子标题"
- "售后服务"下新增子章节"技术支持服务""培训服务"
示例2用户说"合规响应的内容太少,多补充一些"
- "合规响应"下除了"技术实力"还要增加
* "资质认证展示"
* "成功案例介绍"
* "行业认可度说明"
* "合规承诺声明"
**最终检查**
修改完成后你必须确认已经按用户要求进行了实际修改不能返回相同的结构
请返回优化后的目录结构JSON格式如下
{{
"chapters": [
{{
"id": "chapter_1",
"title": "章节标题",
"level": 1,
"score": 0.0,
"children": [
{{
"id": "chapter_1_1",
"title": "子章节标题",
"level": 2,
"score": 0.0,
"children": []
}}
]
}}
]
}}
只返回JSON不要其他内容
"""
# 从配置获取提示词
prompt_manager = get_prompt_manager()
prompt = prompt_manager.get_toc_prompt(
"optimize_with_feedback",
current_toc=current_toc,
feedback=feedback
)
# 调用AI优化
logger.info("开始调用AI进行目录优化")

View File

@ -9,6 +9,7 @@ from typing import Optional
from openai import OpenAI
from ..config import get_settings
from ..config.prompt_manager import get_prompt_manager
logger = logging.getLogger(__name__)
@ -44,11 +45,12 @@ class LLMService:
"""
try:
settings = get_settings()
prompt_manager = get_prompt_manager()
response = self._client.chat.completions.create(
model=settings.model_name,
messages=[
{"role": "system", "content": "你是一个专业的招投标文档分析助手。"},
{"role": "system", "content": prompt_manager.get_system_message("default")},
{"role": "user", "content": prompt}
],
temperature=temperature,

View File

@ -15,6 +15,7 @@ from openai import OpenAI
from pydantic import BaseModel, Field
from ..config import get_settings
from ..config.prompt_manager import get_prompt_manager
logger = logging.getLogger(__name__)
@ -266,46 +267,8 @@ class BidParser:
def _ai_parse_scoring_table(self, table_text: str) -> List[ScoringCriteria]:
"""使用AI解析评分表格"""
try:
prompt = f"""
请从复杂的评分表格中提取评分项和分值并智能分类返回JSON
表格内容
{table_text}
要求
1. 仔细分析表格结构即使有合并单元格也要正确提取
2. "评审内容""评分因素""评分标准"等列中提取评分项名称
3. "分值"列或评分标准描述中提取具体分值如3分540
4. 忽略总分构成等汇总信息只提取具体评分项
5. 智能分类各评分项
**技术类别**
- technical_solution: 技术方案技术条款技术完整性技术先进性
- equipment_spec: 设备规格产品参数设备可靠性技术指标
- implementation: 项目实施施工方案进度计划实施能力
- quality_safety: 质量管理安全管理环境管理质量体系
- after_sales: 售后服务维保服务培训服务技术支持服务部分
- compliance: 技术资质认证证书技术合规性技术条款应答
**商务类别**
- commercial: 价格评分报价商务条件企业资质财务状况业绩商务条款应答响应文件制作质量同类项目业绩商务部分
示例输入分析
- "响应文件制作质量 3分" 商务类别
- "同类项目业绩 5分" 商务类别
- "技术条款应答 2分" 合规类别
- "商务条款应答 2分" 商务类别
格式
{{
"scoring_criteria": [
{{"item_name": "响应文件制作质量", "max_score": 3, "description": "文件格式内容要求", "category": "commercial"}},
{{"item_name": "同类项目业绩", "max_score": 5, "description": "项目经验证明", "category": "commercial"}},
{{"item_name": "技术条款应答", "max_score": 2, "description": "技术要求符合性", "category": "compliance"}}
]
}}
只返回JSON无其他文字"""
prompt_manager = get_prompt_manager()
prompt = prompt_manager.get_parser_prompt("parse_scoring_table", table_text=table_text)
# 调用LLM API
response = self.call_llm(prompt)
@ -370,25 +333,8 @@ class BidParser:
def _identify_table_type(self, table_text: str) -> str:
"""使用AI识别表格类型"""
prompt = f"""
分析表格内容判断这是什么类型的表格
表格内容
{table_text}
请判断这个表格属于以下哪种类型
1. scoring - 评分表包含评分项分值评分标准等内容例如
- "分值""评分标准""得分"等列
- 有具体的分值数字如3分540分等
- "商务部分""技术部分""服务部分"等分类
- 有评审内容和评分因素
2. deviation - 偏离表包含技术要求响应类型偏离说明等
3. other - 其他表格不是评分表也不是偏离表
注意即使表格结构复杂有合并单元格只要包含评分标准和分值信息就是评分表
只返回一个单词scoring deviation other"""
prompt_manager = get_prompt_manager()
prompt = prompt_manager.get_parser_prompt("identify_table_type", table_text=table_text)
response = self.call_llm(prompt)
if not response:
@ -483,26 +429,8 @@ class BidParser:
def _ai_parse_deviation_table(self, table_text: str) -> List[DeviationItem]:
"""使用AI解析偏离表格"""
try:
prompt = f"""
请提取表格中的偏离项返回JSON
表格内容
{table_text}
要求
1. 提取技术要求和响应类型
2. 响应类型如正偏离负偏离无偏离等
3. 忽略序号和表头
格式
{{
"deviation_items": [
{{"requirement": "设备需符合国标", "response_type": "正偏离"}},
{{"requirement": "技术指标要求", "response_type": "无偏离"}}
]
}}
只返回JSON无其他文字"""
prompt_manager = get_prompt_manager()
prompt = prompt_manager.get_parser_prompt("parse_deviation_table", table_text=table_text)
response = self.call_llm(prompt)
if not response:

View File

@ -21,6 +21,7 @@ from langchain_community.document_loaders import (
from langchain_core.documents import Document
from ..config import get_settings
from ..config.prompt_manager import get_prompt_manager
logger = logging.getLogger(__name__)
@ -212,24 +213,21 @@ class RAGTool:
emphasis = context.get('emphasis', '')
rag_context = context.get('rag_context', '')
# 构建提示词
# 构建提示词变量
emphasis_part = f'\n特别强调:{emphasis}' if emphasis else ''
rag_part = f'\n\n参考资料:\n{rag_context}' if rag_context else ''
prompt = f"""你是一个专业的标书撰写助手。请根据以下信息生成标书章节内容:
# 从配置获取提示词
prompt_manager = get_prompt_manager()
prompt = prompt_manager.get_content_prompt(
"generate_with_rag",
title=task_title,
requirements=task_requirements,
emphasis_part=emphasis_part,
rag_part=rag_part
)
章节标题{task_title}
评分要求{task_requirements}{emphasis_part}{rag_part}
要求
1. 内容专业详实符合招标文件要求
2. 突出技术优势和实施能力
3. 语言正式逻辑清晰
4. 字数控制在500-800
请直接输出章节内容不要包含章节标题"""
# 调用DeepSeek生成
# 调用LLM生成
client = OpenAI(
api_key=self.settings.api_key,
base_url=self.settings.base_url
@ -238,7 +236,7 @@ class RAGTool:
response = client.chat.completions.create(
model=self.settings.model_name,
messages=[
{"role": "system", "content": "你是一个专业的标书撰写助手。"},
{"role": "system", "content": prompt_manager.get_system_message("rag_generator")},
{"role": "user", "content": prompt}
],
temperature=0.7,