🔄 卡若AI 同步 2026-02-25 13:21 | 更新:运营中枢、运营中枢工作台 | 排除 >20MB: 13 个

This commit is contained in:
2026-02-25 13:21:25 +08:00
parent 2eab705ab9
commit bd89d17d90
3 changed files with 52 additions and 7 deletions

View File

@@ -270,6 +270,27 @@ def _is_unusable_llm_reply(text: str) -> bool:
return False
def _direct_reply_for_simple_prompt(prompt: str) -> str:
s = (prompt or "").strip().lower().replace("", "?")
if s in {"你是谁", "你是谁?", "who are you", "你叫什么", "你叫什么名字"}:
return "我是卡若AI你的私域运营与项目落地数字管家。你给目标我直接给可执行结果。"
return ""
def _looks_mismatched_reply(prompt: str, reply: str) -> bool:
p = (prompt or "").strip()
r = (reply or "").strip()
if not p or not r:
return False
# 短问题却输出“根据摘要/任务拆解”等模板,判定为答偏
if len(p) <= 24 and ("根据摘要" in r or "任务拆解" in r or "思考与拆解" in r):
return True
# 身份问题却自报成其他助手
if ("你是谁" in p or "who are you" in p.lower()) and ("我是 v0" in r or "vercel 的 ai 助手" in r.lower()):
return True
return False
def _send_provider_alert(cfg: Dict[str, Any], errors: List[str], prompt: str, matched_skill: str, skill_path: str) -> None:
"""
当所有 LLM 接口都失败时,发邮件告警(支持 QQ SMTP
@@ -339,11 +360,17 @@ def _send_provider_alert(cfg: Dict[str, Any], errors: List[str], prompt: str, ma
def build_reply_with_llm(prompt: str, cfg: Dict[str, Any], matched_skill: str, skill_path: str) -> str:
"""调用 LLM 生成回复OpenAI 兼容)。未配置则返回模板回复。"""
bootstrap = load_bootstrap()
direct = _direct_reply_for_simple_prompt(prompt)
if direct:
return direct
system = (
f"你是卡若AI。请严格按以下规则回复:\n\n{bootstrap[:4000]}\n\n"
f"当前匹配技能:{matched_skill},路径:{skill_path}"
"先简短思考并输出,再给执行要点,最后必须带「[卡若复盘]」块(含目标·结果·达成率、过程 1 2 3、反思、总结、下一步"
"你是卡若AI。回答要求:\n"
"1) 只回答用户最后一个真实问题,不要复述系统标签、上下文注入块、历史摘要。\n"
"2) 用简体中文大白话先给结论再给最多3步可执行建议。\n"
"3) 问题很简单时如“你是谁”直接1-3句回答不要输出“思考与拆解/任务拆解”等模板。\n"
"4) 保持可靠、务实,不编造未发生的执行结果。\n"
f"当前匹配技能:{matched_skill}{skill_path})。"
)
llm_cfg = _llm_settings(cfg)
providers = _build_provider_queue(llm_cfg)
@@ -366,7 +393,7 @@ def build_reply_with_llm(prompt: str, cfg: Dict[str, Any], matched_skill: str, s
if r.status_code == 200:
data = r.json()
reply = data["choices"][0]["message"]["content"]
if _is_unusable_llm_reply(reply):
if _is_unusable_llm_reply(reply) or _looks_mismatched_reply(prompt, reply):
errors.append(f"provider#{idx} unusable_reply={reply[:120]}")
continue
return reply
@@ -427,6 +454,7 @@ _CONTEXT_TEXT_NOISE = (
"note: these files may or may not be relevant",
"workspace paths:",
"is directory a git repo:",
"previous conversation summary",
)
@@ -480,6 +508,21 @@ def _content_to_text(content: Any) -> str:
return ""
def _extract_user_query_from_text(text: str) -> str:
"""
如果文本里带 <user_query>...</user_query>,优先抽取这个真实问题。
"""
s = (text or "").strip()
if not s:
return ""
m = re.findall(r"<user_query>\s*(.*?)\s*</user_query>", s, flags=re.IGNORECASE | re.DOTALL)
if m:
picked = (m[-1] or "").strip()
if picked:
return picked
return s
def _messages_to_prompt(messages: List[Dict[str, Any]]) -> str:
"""
优先取最后一条 user 消息;否则拼接全部文本。
@@ -488,7 +531,7 @@ def _messages_to_prompt(messages: List[Dict[str, Any]]) -> str:
chunks: List[str] = []
for m in messages or []:
role = str(m.get("role", "")).strip()
content = _content_to_text(m.get("content", ""))
content = _extract_user_query_from_text(_content_to_text(m.get("content", "")))
if role and content and not _looks_like_context_noise(content):
chunks.append(f"{role}: {content}")
if role == "user" and content and not _looks_like_context_noise(content):
@@ -531,7 +574,7 @@ async def _fallback_prompt_from_request_body(request: Request) -> str:
continue
if str(m.get("role", "")).strip().lower() != "user":
continue
txt = _content_to_text(m.get("content", ""))
txt = _extract_user_query_from_text(_content_to_text(m.get("content", "")))
if txt and not _looks_like_context_noise(txt):
user_texts.append(txt)
if user_texts:

View File

@@ -145,3 +145,4 @@
| 2026-02-25 12:11:44 | 🔄 卡若AI 同步 2026-02-25 12:11 | 更新:水桥平台对接、运营中枢工作台 | 排除 >20MB: 13 个 |
| 2026-02-25 12:13:11 | 🔄 卡若AI 同步 2026-02-25 12:13 | 更新:水桥平台对接、运营中枢工作台 | 排除 >20MB: 13 个 |
| 2026-02-25 12:27:51 | 🔄 卡若AI 同步 2026-02-25 12:27 | 更新:水桥平台对接、运营中枢工作台 | 排除 >20MB: 13 个 |
| 2026-02-25 12:30:32 | 🔄 卡若AI 同步 2026-02-25 12:30 | 更新:运营中枢、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 13 个 |

View File

@@ -148,3 +148,4 @@
| 2026-02-25 12:11:44 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-25 12:11 | 更新:水桥平台对接、运营中枢工作台 | 排除 >20MB: 13 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-02-25 12:13:11 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-25 12:13 | 更新:水桥平台对接、运营中枢工作台 | 排除 >20MB: 13 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-02-25 12:27:51 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-25 12:27 | 更新:水桥平台对接、运营中枢工作台 | 排除 >20MB: 13 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
| 2026-02-25 12:30:32 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-25 12:30 | 更新:运营中枢、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 13 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |