1.修改ai返回格式为markdown2.新增冒险放弃指令

This commit is contained in:
2025-10-30 11:57:46 +08:00
parent 3d4f754a0a
commit 8ffd261bb0
5 changed files with 162 additions and 7 deletions

View File

@@ -107,6 +107,21 @@ WPSBotGame/
- 阻碍因素:无
- 状态:成功
## 2025-10-30_00:00:00
- 已修改:
- games/adventure.py新增放弃指令分支新增 `_abandon_adventure`,更新帮助)
- games/base.py在全局帮助中添加放弃用法
- 更改:
1. 新增冒险放弃功能:
- 支持指令:`.adventure abandon``.adventure 放弃`
- 结算规则:最低倍率 × 已冒险分钟向下取整至少1分钟
- 发放奖励后删除状态
2. 帮助信息更新:
- 本地帮助与全局帮助均加入放弃用法说明
- 原因:允许用户在冒险过程中主动放弃并按最低倍率获得奖励
- 阻碍因素:无
- 状态:成功
# 详细实施记录
## 文件修改清单
@@ -205,6 +220,7 @@ state_data = {
- ✅ 游戏互斥:冒险期间阻止炼金操作
- ✅ 指令注册:`.adventure` 和 `.冒险` 指令正常工作
- ✅ 帮助信息:显示在全局帮助中
- ✅ 冒险放弃:`.adventure abandon` / `.adventure 放弃` 按最低倍率结算已冒险分钟并清理状态
## 代码质量
- ✅ 所有语法检查通过

View File

@@ -364,3 +364,65 @@ llama-index-llms-ollama>=0.1.0
## 实施与计划匹配度
实施与计划完全匹配 ✅
## 补充分析Markdown 渲染与发送通道2025-10-30
### 现状观察
- `routers/callback.py` 中仅当 `response_text.startswith('#')` 时才通过 `send_markdown()` 发送,否则使用 `send_text()`。这意味着即使 AI 返回了合法的 Markdown但不以 `#` 开头例如代码块、列表、表格、普通段落等也会被按纯文本通道发送导致下游WPS 侧)不进行 Markdown 渲染。
- `games/ai_chat.py` 的 `_generate_response()` 直接返回 `str(response)`,未对内容类型进行标注或判定,上层仅依赖首字符为 `#` 的启发式判断来选择发送通道。
- `utils/message.py` 已具备 `send_markdown()` 与 `send_text()` 两种发送方式,对应 `{"msgtype":"markdown"}` 与 `{"msgtype":"text"}` 消息结构;当前缺少自动识别 Markdown 的逻辑。
### 影响
- 当 AI 返回包含 Markdown 元素但非标题(不以 `#` 开头)的内容时,用户端看到的是未渲染的原始 Markdown 文本,表现为“格式不能成功排版”。
### 待确认问题(不含解决方案,需产品/实现口径)
1. 目标平台WPS 机器人)对 Markdown 的要求是否仅需 `msgtype=markdown` 即可渲染?是否存在必须以标题开头的限制?
2. 期望策略:
- 是否希望“.ai 的所有回复”统一走 Markdown 通道?
- 还是需要基于 Markdown 特征进行判定(如代码块、列表、链接、表格、行内格式等)?
3. 兼容性:若统一改为 Markdown 通道,是否会影响既有纯文本展示(例如换行、转义、表情)?
4. 其他指令模块是否也可能返回 Markdown若有是否一并纳入同一策略
### 相关代码参照点(路径)
- `routers/callback.py`:回复通道选择逻辑(基于 `startswith('#')`
- `games/ai_chat.py`AI 回复内容生成与返回(直接返回字符串)
- `utils/message.py``send_markdown()` 与 `send_text()` 的消息结构
### 决策结论与范围2025-10-30
- 分支策略:不创建新分支,继续在当前任务上下文内推进。
- 发送策略:`.ai` 产生的回复统一按 Markdown 发送。
- 影响范围:仅限 AI 对话功能(`.ai`/`ai_chat`),不扩展到其他指令模块。
# 任务进度(补充)
## [2025-10-30_??:??:??] 标注 Markdown 渲染问题(记录现状与待确认项)
- 已修改:
- `.tasks/2025-10-29_3_ai_chat.md`补充“Markdown 渲染与发送通道”分析与待确认清单(仅问题陈述,无解决方案)。
- 更改:
- 明确当前仅以标题开头触发 Markdown 发送的启发式导致部分 Markdown 未被渲染。
- 原因:
- 用户反馈“AI 返回内容支持 Markdown但当前直接当作文本返回导致无法正确排版”。
- 阻碍因素:
- 目标平台的 Markdown 渲染细节与统一策略选择待确认。
- 状态:
- 未确认(等待策略口径与平台渲染规范确认)。
## [2025-10-30_11:40:31] 执行AI 回复统一按 Markdown 发送(仅限 AI
- 已修改:
- `routers/callback.py`:在 `callback_receive()` 的发送阶段,当 `game_type == 'ai_chat'` 且存在 `response_text` 时,无条件调用 `send_markdown(response_text)`;若发送异常,记录日志并回退到 `send_text(response_text)`;其他指令模块继续沿用 `startswith('#')` 的启发式逻辑。
- 更改:
- 使 `.ai` 产生的回复在 WPS 端稳定触发 Markdown 渲染,不再依赖以 `#` 开头。
- 原因:
- 对齐“统一按 Markdown 发送(仅限 AI”的决策解决 Markdown 文本被当作纯文本发送导致的排版问题。
- 阻碍因素:
- 暂无。
- 状态:
- 成功。

View File

@@ -56,6 +56,10 @@ class AdventureGame(BaseGame):
if args in ['help', '帮助', 'info']:
return self._get_adventure_help()
# 放弃当前冒险(按最低倍率结算已冒险时间)
if args in ['abandon', '放弃']:
return await self._abandon_adventure(chat_id, user_id)
# 默认冒险耗时1分钟
else:
# 解析消耗时间
@@ -170,6 +174,69 @@ class AdventureGame(BaseGame):
return text
async def _abandon_adventure(self, chat_id: int, user_id: int) -> str:
"""放弃当前冒险,按最低倍率结算已冒险时间
Args:
chat_id: 会话ID使用0作为用户级标识
user_id: 用户ID
Returns:
放弃结果消息
"""
try:
# 查询冒险状态
state = self.db.get_game_state(0, user_id, 'adventure')
if not state:
return "❌ 当前没有进行中的冒险,可使用 `.adventure` 开始新的冒险。"
state_data = state.get('state_data', {})
start_time = state_data.get('start_time')
cost_time = state_data.get('cost_time')
if start_time is None or cost_time is None:
# 状态异常,清理并提示
self.db.delete_game_state(0, user_id, 'adventure')
return "⚠️ 冒险状态异常已清理,请使用 `.adventure` 重新开始。"
current_time = int(time.time())
elapsed_seconds = max(0, current_time - int(start_time))
elapsed_minutes = elapsed_seconds // 60
if elapsed_minutes < 1:
elapsed_minutes = 1
# 计算最低倍率
try:
min_multiplier = min(m for _, m, _ in self.prize_pool)
except Exception:
# 兜底若奖池异常按0.5处理
min_multiplier = 0.5
reward_points = int(min_multiplier * elapsed_minutes)
if reward_points < 0:
reward_points = 0
# 发放奖励并清理状态
if reward_points > 0:
self.db.add_points(user_id, reward_points, "adventure", "冒险放弃奖励")
self.db.delete_game_state(0, user_id, 'adventure')
# 查询当前积分
updated_points = self.db.get_user_points(user_id)
# 输出
text = f"## ⚡️ 冒险放弃\n\n"
text += f"**已计入时间**: {elapsed_minutes} 分钟\n\n"
text += f"**最低倍率**: {min_multiplier}\n\n"
text += f"**获得积分**: {reward_points}\n\n"
text += f"**当前积分**: {updated_points['points']}\n\n"
text += "---\n\n"
text += "💡 提示:可随时使用 `.adventure` 再次踏上冒险之旅!"
return text
except Exception as e:
logger.error(f"放弃冒险时出错: {e}", exc_info=True)
# 失败时不影响原状态,返回提示
return f"❌ 放弃冒险失败:{str(e)}"
def _draw_prize(self, prize_pool: list) -> dict:
"""从奖品池中抽取奖品
@@ -209,6 +276,8 @@ class AdventureGame(BaseGame):
text += f"- `.adventure time` - 消耗time分钟进行冒险, 最少一分钟\n"
text += f"### 其他功能\n"
text += f"- `.adventure abandon` - 放弃当前冒险,按最低倍率结算已冒险时间\n"
text += f"- `.adventure 放弃` - 放弃当前冒险,按最低倍率结算已冒险时间\n"
text += f"- `.adventure help` - 查看帮助\n\n"
return text

View File

@@ -100,6 +100,8 @@ def get_help_message() -> str:
- `.adventure` - 消耗1分钟进行冒险
- `.冒险` - 消耗1分钟进行冒险
- `.adventure 5` - 消耗5分钟进行冒险
- `.adventure abandon` - 放弃当前冒险,按最低倍率结算已冒险时间
- `.adventure 放弃` - 放弃当前冒险,按最低倍率结算已冒险时间
- `.adventure help` - 查看冒险帮助
### 🎁 积分赠送系统

View File

@@ -77,13 +77,19 @@ async def callback_receive(request: Request):
if response_text:
sender = get_message_sender()
# 根据内容选择消息类型
if response_text.startswith('#'):
# Markdown格式
await sender.send_markdown(response_text)
# AI 对话:统一按 Markdown 发送(按任务决策)
if game_type == 'ai_chat':
try:
await sender.send_markdown(response_text)
except Exception as send_md_err:
logger.error(f"发送Markdown消息失败改用文本发送: {send_md_err}")
await sender.send_text(response_text)
else:
# 普通文本
await sender.send_text(response_text)
# 其他模块保持原有启发式:以 # 开头视为 Markdown否则文本
if response_text.startswith('#'):
await sender.send_markdown(response_text)
else:
await sender.send_text(response_text)
return JSONResponse({"result": "ok"})