refactor(agents): 抽取阶段常量与对话上下文;快档 LLM;图片 prompt 可禁止回退
访谈与阶段 - 新增 app/agents/stage_constants.py:集中 CHAT_STAGES、章节分类/顺序、阶段到默认 memoir 类别等,与 MemoirState 默认槽位顺序对齐;减少散落在 prompts 内的重复常量。 - 新增 app/agents/chat/prompt_context.py:以 ChatPromptContext 汇总 guided 系统提示所需字段(阶段、槽位、轮次、人设、记忆证据、回复长度模式、背景声线、职业等),统一走 get_guided_conversation_prompt。 - 大幅收敛 app/agents/chat/prompts_conversation.py;调整 prompts.py、stage_prompts.py、stage_detection.py;同步 interview_agent、profile_agent、helpers 与 state_schema,使对话侧构造提示的方式一致、可测。 回忆录流水线 - memoir/prompts.py 删除已迁至 stage_constants / 独立模板的大段常量与图片占位相关逻辑;classification / extraction / fidelity / narrative agents 与 orchest(全量历史仍可用于计数,注入模型时按轮次与字符上限截断)、image_prompt_fallback_disabled。 - dependencies 增加 get_llm_provider_fast(LRU 缓存,可与默认共用密钥与 base_url)。 任务与编排 - memoir_tasks:prepare_batches 注入 llm_fast;开启独立快档模型时打结构化日志。 - chapter_cover_tasks、story_image_tasks:与图片 prompt / JSON 工具路径或策略变更对齐(import 与行为一致)。 - story_pipeline_sync 等小处同步。 其它核心 - langchain_llm、text_normalize 随上述调用链微调。 开发者体验 - .cursor/settings.json:启用 redis-development、postman 插件。 测试 - 新增 test_image_prompt_policy:覆盖「禁止回退」等图片 prompt 策略。 - 更新 test_interview_prompts、test_interview_reply_length、test_experience_regressions、test_json_and_memory_utils,匹配新常量位置、json_utils 与对话/长度行为。
This commit is contained in:
@@ -13,13 +13,12 @@ import re
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from app.agents.memoir.prompts import (
|
||||
CHAPTER_CATEGORIES,
|
||||
get_chapter_classification_json_prompt,
|
||||
)
|
||||
from app.agents.memoir.prompts import get_chapter_classification_json_prompt
|
||||
from app.agents.stage_constants import CHAPTER_CATEGORIES
|
||||
from app.agents.stage_constants import STAGE_TO_DEFAULT_CATEGORY
|
||||
from app.core.json_utils import extract_json_payload
|
||||
from app.core.langchain_llm import invoke_json_object
|
||||
from app.core.logging import get_logger
|
||||
from app.features.memoir.memoir_images.json_payload import extract_json_payload
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
@@ -50,15 +49,6 @@ STAGE_KEYWORDS = {
|
||||
"belief": ["信念", "价值观", "座右铭", "坚持", "原则"],
|
||||
}
|
||||
|
||||
# 5-stage → 默认 8-category 映射(LLM 分类失败时的兜底)
|
||||
_STAGE_TO_DEFAULT_CATEGORY = {
|
||||
"childhood": "childhood",
|
||||
"education": "education",
|
||||
"career": "career_early",
|
||||
"family": "family",
|
||||
"belief": "beliefs",
|
||||
}
|
||||
|
||||
|
||||
def _detect_stage(text: str, fallback_stage: str) -> str:
|
||||
"""根据关键词检测消息所属的 5-stage 阶段"""
|
||||
@@ -176,8 +166,8 @@ class ClassificationAgent:
|
||||
logger.warning("ClassificationAgent LLM 章节分类失败: {}", e)
|
||||
|
||||
stage = _detect_stage(text, fallback_stage)
|
||||
cat = _STAGE_TO_DEFAULT_CATEGORY.get(
|
||||
cat = STAGE_TO_DEFAULT_CATEGORY.get(
|
||||
stage,
|
||||
_STAGE_TO_DEFAULT_CATEGORY.get(fallback_stage, "childhood"),
|
||||
STAGE_TO_DEFAULT_CATEGORY.get(fallback_stage, "childhood"),
|
||||
)
|
||||
return ChapterClassifyResult(category=cat, llm_said_none=False)
|
||||
|
||||
@@ -12,7 +12,7 @@ from typing import Any, Dict
|
||||
from app.agents.memoir.prompts import get_state_extraction_prompt
|
||||
from app.core.langchain_llm import invoke_json_object
|
||||
from app.core.logging import get_logger
|
||||
from app.features.memoir.memoir_images.json_payload import extract_json_payload
|
||||
from app.core.json_utils import extract_json_payload
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ from typing import Any
|
||||
from app.core.config import settings
|
||||
from app.core.langchain_llm import invoke_json_object
|
||||
from app.core.logging import get_logger
|
||||
from app.features.memoir.memoir_images.json_payload import extract_json_payload
|
||||
from app.core.json_utils import extract_json_payload
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ from app.agents.memoir.prompts import (
|
||||
)
|
||||
from app.core.langchain_llm import invoke_json_object
|
||||
from app.core.logging import get_logger
|
||||
from app.features.memoir.memoir_images.json_payload import extract_json_payload
|
||||
from app.core.json_utils import extract_json_payload
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
@@ -53,14 +53,18 @@ class MemoirOrchestrator:
|
||||
llm: Any,
|
||||
get_or_create_state: Callable[[], MemoirStateSchema],
|
||||
update_slot: Callable[[str, str, str, List[str]], MemoirStateSchema],
|
||||
llm_fast: Any | None = None,
|
||||
) -> PreparedMemoirBatches:
|
||||
"""
|
||||
遍历 segments:Extraction → slot 更新 → Classification → 按 category 分桶。
|
||||
不含锁与写章节/故事(由调用方显式执行)。
|
||||
|
||||
``llm_fast``:分类与抽取专用;未传时与 ``llm`` 相同(叙事/路由仍用 ``llm``)。
|
||||
"""
|
||||
state = get_or_create_state()
|
||||
category_to_segments: Dict[str, List[Segment]] = {}
|
||||
segment_skip_story_ids: Set[str] = set()
|
||||
classify_extract_llm = llm_fast if llm_fast is not None else llm
|
||||
|
||||
for segment in segments:
|
||||
text = segment.user_input_text or ""
|
||||
@@ -79,7 +83,7 @@ class MemoirOrchestrator:
|
||||
user_message=text,
|
||||
current_stage=state.current_stage or "childhood",
|
||||
stage_slots=stage_slots_raw,
|
||||
llm=llm,
|
||||
llm=classify_extract_llm,
|
||||
)
|
||||
detected_stage = result.detected_stage
|
||||
for slot_name, snippet in result.slots.items():
|
||||
@@ -93,7 +97,7 @@ class MemoirOrchestrator:
|
||||
classify_result = self.classification_agent.classify(
|
||||
text=text,
|
||||
fallback_stage=detected_stage,
|
||||
llm=llm,
|
||||
llm=classify_extract_llm,
|
||||
segment_id=segment.id,
|
||||
)
|
||||
chapter_category = classify_result.category
|
||||
@@ -147,6 +151,7 @@ class MemoirOrchestrator:
|
||||
Tuple[Any, bool],
|
||||
],
|
||||
raise_retry: Callable[[], None],
|
||||
llm_fast: Any | None = None,
|
||||
) -> Tuple[Set[str], int]:
|
||||
"""
|
||||
执行回忆录流水线。
|
||||
@@ -158,6 +163,7 @@ class MemoirOrchestrator:
|
||||
prepared = self.prepare_batches(
|
||||
segments=segments,
|
||||
llm=llm,
|
||||
llm_fast=llm_fast,
|
||||
get_or_create_state=get_or_create_state,
|
||||
update_slot=update_slot,
|
||||
)
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"""
|
||||
|
||||
import json
|
||||
import re
|
||||
from typing import Optional
|
||||
|
||||
from app.agents.chat.background_voice import get_background_voice_narrative_block
|
||||
@@ -13,129 +12,6 @@ from app.features.memory.evidence_format import (
|
||||
format_evidence_chunks_for_prompt,
|
||||
)
|
||||
|
||||
CHAPTER_CATEGORIES = {
|
||||
"childhood": "童年与成长背景",
|
||||
"education": "教育经历与青年时期",
|
||||
"career_early": "崭露头角",
|
||||
"career_achievement": "主要成就与巅峰时刻",
|
||||
"career_challenge": "挫折、挑战与重大转折",
|
||||
"family": "家庭与情感",
|
||||
"beliefs": "信念与价值观",
|
||||
"summary": "人生总结",
|
||||
}
|
||||
|
||||
CHAPTER_ORDER = [
|
||||
"childhood",
|
||||
"education",
|
||||
"career_early",
|
||||
"career_achievement",
|
||||
"career_challenge",
|
||||
"family",
|
||||
"beliefs",
|
||||
"summary",
|
||||
]
|
||||
|
||||
STAGE_TO_ORDER = {
|
||||
"childhood": 0,
|
||||
"education": 1,
|
||||
"career": 2,
|
||||
"career_early": 2,
|
||||
"career_achievement": 3,
|
||||
"career_challenge": 4,
|
||||
"family": 5,
|
||||
"belief": 6,
|
||||
"beliefs": 6,
|
||||
"summary": 7,
|
||||
}
|
||||
|
||||
IMAGE_PLACEHOLDER_TEMPLATE = (
|
||||
"温暖怀旧风格,年代感复古色调,柔和光影,朴素温馨氛围,安静治愈,低饱和度,"
|
||||
"质感柔和细腻,简约构图,充满岁月沉淀感与故事感,高清唯美插画封面,不要包含文字,"
|
||||
"要适合老年人审美,画面要真实可信、让老年人产生共鸣与代入感,"
|
||||
"场景环境、建筑风格、服饰器物必须严格符合所述时代背景和地域特色,"
|
||||
"有朦胧怀旧的年代感。"
|
||||
)
|
||||
|
||||
_IMAGE_PLACEHOLDER_ANY_BRACES_RE = re.compile(
|
||||
r"(\{\{)+IMAGE:\s*([^}]+)(\}\})+",
|
||||
re.DOTALL,
|
||||
)
|
||||
|
||||
|
||||
def inject_image_placeholder_template(content: str) -> str:
|
||||
"""
|
||||
对正文中的 IMAGE 占位符拼上固定风格模板(四层花括号)。
|
||||
**线上写路径已不使用**;保留供离线迁移脚本处理历史数据。
|
||||
"""
|
||||
if not content or not content.strip():
|
||||
return content
|
||||
|
||||
def replace_one(match: re.Match) -> str:
|
||||
inner = (match.group(2) or "").strip()
|
||||
if not inner:
|
||||
return match.group(0)
|
||||
if inner.startswith(IMAGE_PLACEHOLDER_TEMPLATE):
|
||||
desc = inner[len(IMAGE_PLACEHOLDER_TEMPLATE) :].lstrip("。").strip()
|
||||
return (
|
||||
"{{{{IMAGE:"
|
||||
+ IMAGE_PLACEHOLDER_TEMPLATE
|
||||
+ ("。" + desc if desc else "")
|
||||
+ "}}}}"
|
||||
)
|
||||
return "{{{{IMAGE:" + IMAGE_PLACEHOLDER_TEMPLATE + "。" + inner + "}}}}"
|
||||
|
||||
content = _IMAGE_PLACEHOLDER_ANY_BRACES_RE.sub(replace_one, content)
|
||||
return content
|
||||
|
||||
|
||||
def get_memoir_editor_system_prompt() -> str:
|
||||
"""传记整理 Agent 的系统提示词(口语转书面、章节归类;与访谈对话用的 system prompt 不同)。"""
|
||||
return """你是一位专业的传记作家和文字编辑,擅长将口语化的对话内容整理成优雅的书面语回忆录章节。
|
||||
|
||||
你的任务:
|
||||
1. 接收对话段落文本(口语化,可能来自语音转写)
|
||||
2. **先提炼对话中与人生经历相关的核心内容**,过滤掉无关信息
|
||||
3. 识别内容主题,归类到对应章节(童年/教育/事业/家庭/信念/总结)
|
||||
4. 将口语化表达改写为书面语,保持原意和情感
|
||||
5. 生成合适的章节标题和段落结构
|
||||
6. 提取关键信息,形成连贯的叙述
|
||||
|
||||
## 内容筛选原则(最重要)
|
||||
对话中往往夹杂大量与回忆录无关的噪音,你必须严格筛选,只保留有价值的内容:
|
||||
|
||||
应该保留的内容:
|
||||
- 具体的人生事件、经历、故事
|
||||
- 提到的人物及其关系(家人、朋友、同事、恩师等)
|
||||
- 地点、时间、场景描写
|
||||
- 用户的情感表达、内心感受
|
||||
- 人生感悟、价值观、信念
|
||||
- 具体的细节(食物、声音、画面等)
|
||||
|
||||
应该过滤掉的内容:
|
||||
- 语气词、填充词(嗯、啊、那个、就是说、对对对、然后呢等)
|
||||
- 对话中的寒暄、问候(你好、谢谢、好的等)
|
||||
- 用户与AI助手之间的交互指令(你帮我、我想问、你说得对等)
|
||||
- 重复、冗余的表述(取核心含义即可)
|
||||
- 与个人经历完全无关的闲聊内容
|
||||
|
||||
## 改写原则
|
||||
- 保持用户的真实情感
|
||||
- 使用优雅但不失亲切的书面语,不要直接引用对话原话
|
||||
- 适当添加过渡句,使段落连贯
|
||||
- 保留生动的细节,但将口语表达改写为书面叙述
|
||||
- 去除口语中的填充词和无意义重复
|
||||
- 保持时间顺序和逻辑清晰
|
||||
|
||||
## 章节分类规则
|
||||
- 童年相关 → "童年与成长背景"
|
||||
- 学校、老师、同学 → "教育经历与青年时期"
|
||||
- 工作、职业、成就 → "主要成就与巅峰时刻" 或 "崭露头角"
|
||||
- 困难、挫折 → "挫折、挑战与重大转折"
|
||||
- 伴侣、孩子、家庭生活 → "家庭与情感"
|
||||
- 价值观、信念、座右铭 → "信念与价值观"
|
||||
- 总结、感悟、展望 → "人生总结"
|
||||
"""
|
||||
|
||||
|
||||
def _memoir_fidelity_core_rules() -> str:
|
||||
"""事实边界 1–4 条(与文体第 5 条拆分,供 story 叙事与标题等复用)。"""
|
||||
@@ -159,7 +35,7 @@ def _memoir_fidelity_user_profile_rules() -> str:
|
||||
|
||||
|
||||
def get_memoir_fidelity_system_prompt() -> str:
|
||||
"""叙事/标题生成专用:准确性优先,禁止编造事实(与 get_memoir_editor_system_prompt 分离)。"""
|
||||
"""叙事/标题生成专用:准确性优先,禁止编造事实。"""
|
||||
return f"""你是回忆录编辑助手,任务是把用户口述整理为第一人称书面叙述。
|
||||
|
||||
{_memoir_fidelity_core_rules()}
|
||||
@@ -179,7 +55,7 @@ def get_memoir_fidelity_facts_only_prompt() -> str:
|
||||
|
||||
|
||||
def _memoir_editor_narrative_style_block() -> str:
|
||||
"""与 `get_memoir_editor_system_prompt` 对齐的传记作家改写要点(用于写入 chapter 的 story 正文)。"""
|
||||
"""传记作家改写要点(用于写入 chapter 的 story 正文)。"""
|
||||
return """## 传记作家文体(须同时遵守上文「事实边界」)
|
||||
你是一位专业的传记作家和文字编辑,擅长将口语化的对话内容整理成优雅、有温度的书面语回忆录章节。
|
||||
|
||||
@@ -193,7 +69,7 @@ def _memoir_editor_narrative_style_block() -> str:
|
||||
- 保留生动的细节,将口语表达改写为有画面感的书面叙述
|
||||
- 去除口语中的填充词和无意义重复
|
||||
- 保持时间顺序和逻辑清晰
|
||||
- **在事实边界内,鼓励使用有温度的传记笔法**,让读者感受到讲述者当时的心情;可有文学性的表达与恰当的情感渲染,但不得虚构新的事实来增色
|
||||
- **在事实边界内,鼓励使用有温度的传记笔法**,让读者感受到讲述者当时的心情;可有文学性的表达与恰当的情感渲染;**须同时遵守上文「事实边界」规则 1–4**
|
||||
|
||||
### 示例(仅供参考允许的改写程度;只改语气、不加新事实)
|
||||
- 原文:「那时候穷啊,一家人挤一间房。」
|
||||
@@ -224,45 +100,11 @@ def get_narrative_editor_system_prompt(
|
||||
|
||||
|
||||
def _short_classification_edit_prefix() -> str:
|
||||
"""章节分类专用短系统前缀(不重复整段 get_memoir_editor_system_prompt)。"""
|
||||
"""章节分类专用短系统前缀。"""
|
||||
return """你是回忆录编辑。先忽略语气词与寒暄,只根据**与人生经历有关的实质内容**判断归类。
|
||||
保留:事件、人物关系、地点时间、情感与信念。过滤:纯寒暄、与 AI 的交互、无关闲聊。"""
|
||||
|
||||
|
||||
def get_chapter_classification_prompt(segments_text: str) -> str:
|
||||
"""获取章节分类的提示词(短系统段 + 规则;供纯文本输出路径或兼容)。"""
|
||||
return f"""{_short_classification_edit_prefix()}
|
||||
|
||||
请分析以下对话内容,判断应归类到哪个章节类别,或是否不足以写入回忆录正文。
|
||||
|
||||
## 章节类别
|
||||
- childhood: 童年与成长背景
|
||||
- education: 教育经历与青年时期
|
||||
- career_early: 崭露头角(早期事业)
|
||||
- career_achievement: 主要成就与巅峰时刻
|
||||
- career_challenge: 挫折、挑战与重大转折
|
||||
- family: 家庭与情感
|
||||
- beliefs: 信念与价值观
|
||||
- summary: 人生总结
|
||||
|
||||
## 何时必须返回 none(与「零散档案点」区分)
|
||||
若去掉寒暄后,内容仅为**档案式点状信息**,**没有可讲述的叙事骨架**(无事件、场景、过程、互动或情绪展开),则必须返回 **none**,例如:
|
||||
- 仅出生年份、籍贯一笔、职业名词、姓名等单句事实;
|
||||
- 仅罗列事实、无画面与过程的短答。
|
||||
|
||||
以下情况**不是** none:篇幅短但已构成**微型故事**(有画面、动作、对话、转折、感受),应归入最贴合的章节类别。
|
||||
|
||||
## 示例(仅作判断参考)
|
||||
- 应返回 none:「我1999年出生的。」「籍贯上海。」「工程师。」
|
||||
- 应返回 childhood(或其它合适类别):「小学时有次下大雨,爷爷背我过河,鞋全湿了,他一直笑。」
|
||||
|
||||
对话内容:
|
||||
{segments_text}
|
||||
|
||||
请只返回章节类别英文 key(如:childhood),不要返回其它说明。
|
||||
若内容不足以独立成篇、仅为零散信息,返回 none。"""
|
||||
|
||||
|
||||
def get_chapter_classification_json_prompt(segments_text: str) -> str:
|
||||
"""章节分类:JSON 输出(与 invoke_json_object 配合)。"""
|
||||
return f"""{_short_classification_edit_prefix()}
|
||||
@@ -270,7 +112,7 @@ def get_chapter_classification_json_prompt(segments_text: str) -> str:
|
||||
## 章节 key(英文)
|
||||
childhood, education, career_early, career_achievement, career_challenge, family, beliefs, summary;不足以成篇则 **none**。
|
||||
|
||||
规则与「何时必须返回 none」同 `get_chapter_classification_prompt`(档案点、无叙事骨架 → none)。
|
||||
当去掉寒暄后仅为档案式点状信息、无可讲述叙事骨架(无事件/场景/过程/互动/情绪展开)→ **none**;短但有画面的微型故事应归入最贴类别。
|
||||
|
||||
对话内容:
|
||||
{segments_text}
|
||||
@@ -295,9 +137,10 @@ def get_state_extraction_prompt(
|
||||
"belief": ["value", "regret", "pride", "lesson"],
|
||||
}
|
||||
|
||||
return f"""{get_memoir_fidelity_system_prompt()}
|
||||
return f"""你是回忆录访谈信息抽取助手。从用户话语中提取结构化信息,判断用户实际在谈论哪个人生阶段。
|
||||
只提取口述中确有依据的片段,不得编造或推测。
|
||||
|
||||
你需要从用户话语中**先提炼与人生经历相关的核心内容**,然后抽取结构化信息,并判断用户实际在谈论哪个人生阶段(slots 仅填口述中确有依据的片段)。
|
||||
你需要从用户话语中**先提炼与人生经历相关的核心内容**,然后抽取结构化信息(slots 仅填口述中确有依据的片段)。
|
||||
|
||||
**JSON 输出**:接口已启用 `response_format=json_object`,你必须只输出一个合法 JSON 对象,不要 markdown 代码块或其它文字。
|
||||
|
||||
@@ -406,61 +249,6 @@ def get_creative_title_json_prompt(
|
||||
)
|
||||
|
||||
|
||||
def get_narrative_prompt(
|
||||
stage: str,
|
||||
slots: dict,
|
||||
new_content: str,
|
||||
existing_content: str = "",
|
||||
user_profile: str = "",
|
||||
birth_year: Optional[int] = None,
|
||||
archived_summaries: str = "",
|
||||
background_voice: str = "default",
|
||||
occupation: str = "",
|
||||
) -> str:
|
||||
"""将新对话改写为叙述(只输出新内容的改写,不重复已有内容)"""
|
||||
context_tail = ""
|
||||
if existing_content:
|
||||
context_tail = (
|
||||
existing_content[-300:] if len(existing_content) > 300 else existing_content
|
||||
)
|
||||
context_section = (
|
||||
f"\n\n【衔接上下文(已有内容的末尾,仅供参考衔接,不要重复)】:\n{context_tail}"
|
||||
if context_tail
|
||||
else ""
|
||||
)
|
||||
archived_section = (
|
||||
f"\n\n【已删除的该类别历史章节(仅供参考,请勿直接使用或重复)】:\n{archived_summaries}"
|
||||
if archived_summaries
|
||||
else ""
|
||||
)
|
||||
|
||||
profile_section = f"\n\n用户基本信息:\n{user_profile}" if user_profile else ""
|
||||
age_hint = _build_age_hint(stage, birth_year)
|
||||
time_section = f"\n时间参考:{age_hint}" if age_hint else ""
|
||||
|
||||
return f"""{get_narrative_editor_system_prompt(background_voice=background_voice, occupation=occupation)}
|
||||
|
||||
阶段:{stage}
|
||||
可用信息(slots,仅可复述其中已出现事实):{slots}{profile_section}{time_section}
|
||||
|
||||
输入材料(请严格区分「本段口述」与参考区,规则见系统说明):
|
||||
{new_content}
|
||||
{context_section}
|
||||
{archived_section}
|
||||
|
||||
## 步骤
|
||||
1. 从「本段用户口述」提炼可写事实;丢弃语气词、寒暄、与 AI 的交互。
|
||||
2. 改写为第一人称书面叙述(优雅、连贯,可适当过渡;可调整语序与用词),**不得**新增事实。
|
||||
3. 若材料中无值得记录的人生经历内容,输出空字符串。
|
||||
|
||||
## 格式
|
||||
- 不要插入章节标题或 `#`、`##`;不要用 Markdown 表格。
|
||||
- 不要写入与「本段用户口述」无关的交互套话。
|
||||
|
||||
只输出改写后的正文。无内容则输出空字符串。
|
||||
"""
|
||||
|
||||
|
||||
def get_narrative_json_prompt(
|
||||
stage: str,
|
||||
slots: dict,
|
||||
@@ -499,11 +287,9 @@ def get_narrative_json_prompt(
|
||||
{context_section}
|
||||
|
||||
## 要求
|
||||
1. **只展开「本段用户口述」**;若有参考摘录区,不得把摘录中的具体事实写成本轮亲历经历(见系统说明)。
|
||||
2. 过滤语气词、寒暄、与 AI 的交互;不重复已有故事全文;本批只写同一主题/事件链。
|
||||
3. 段落数量与每段长度**随材料而定**,禁止为凑字数编造。
|
||||
4. 使用第一人称、**优雅书面语**,改写须符合系统说明中的「传记作家文体」与「改写原则」(含示例):在事实边界内可做书面化、过渡与情感渲染,**须基于口述事实**;不要直接引用原话;不要用 `#`、`##`、表格。
|
||||
5. **不推断结局**:若用户未明确说结果(是否录取、是否被选中等),不要凭常识补全为确定结论;只复述已说清楚的内容。
|
||||
1. **格式与输出**:只输出 JSON;第一人称;不使用 `#`、`##`、表格;`content` 仅含正文。
|
||||
2. **事实与取材**:(须遵守系统说明中的事实边界规则 1–4)。只展开「本段用户口述」;若有参考摘录区,不得把摘录中的具体事实写成本轮亲历;过滤语气词与寒暄;不重复已有故事全文;本批同一主题/事件链;段落数量与长度随材料,禁止为凑字数编造。
|
||||
3. **不推断结局**:用户未明确说结果(是否录取、是否被选中等)时,不要凭常识补全为确定结论。
|
||||
|
||||
## 输出格式(严格 JSON)
|
||||
{{
|
||||
@@ -578,11 +364,10 @@ def get_narrative_merge_json_prompt(
|
||||
{existing_section}
|
||||
|
||||
## 要求
|
||||
1. 输出为**完整故事正文**(不是仅写本段):`paragraphs` 须包含重组后的**全文**。
|
||||
2. **禁止编造**:不得新增用户未在「已有」或「本段」中出现的人名、地点、时间、对话、数字。
|
||||
3. 若本段与旧文完全重复或无新信息,可仅输出与旧文等价重组后的正文(不得无故缩短到明显少于旧文)。
|
||||
4. 使用第一人称、**优雅书面语**,改写须符合系统说明中的「传记作家文体」与「改写原则」(含示例):在事实边界内可做书面化、过渡与情感渲染,**须基于口述与旧文已有事实**;不要用 `#`、`##`、表格。
|
||||
5. **不推断结局**:本段口述未明确结果时,不要用常识补全落选/未通过等确定说法,除非旧文中已有同一事实。
|
||||
1. **全文输出**:`paragraphs` 须为重组后的**完整故事正文**(非仅本段)。
|
||||
2. **事实边界**:(须遵守系统说明中的事实边界规则 1–4)。不得新增「已有」或「本段」未出现的人名、地点、时间、对话、数字;第一人称、优雅书面语须符合上文传记作家文体说明;不用 `#`、`##`、表格。
|
||||
3. 若本段与旧文完全重复或无新信息,可输出与旧文等价重组的正文(不得无故缩短到明显少于旧文)。
|
||||
4. **不推断结局**:本段未明确结果时,不要补全落选/未通过等确定说法,除非旧文中已有同一事实。
|
||||
|
||||
## 输出格式(严格 JSON)
|
||||
{{
|
||||
@@ -714,7 +499,3 @@ def format_narrative_user_content(oral_text: str, evidence_text: str = "") -> st
|
||||
|
||||
|
||||
# dedupe_evidence_chunk_rows / format_evidence_chunks_for_prompt 见 app.features.memory.evidence_format
|
||||
|
||||
|
||||
# 向后兼容:旧代码中的 get_system_prompt 指「回忆录编辑」系统提示,勿与访谈模块的 get_system_prompt 混淆
|
||||
get_system_prompt = get_memoir_editor_system_prompt
|
||||
|
||||
Reference in New Issue
Block a user