1.修改ai返回格式为markdown2.新增冒险放弃指令
This commit is contained in:
@@ -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 放弃` 按最低倍率结算已冒险分钟并清理状态
|
||||
|
||||
## 代码质量
|
||||
- ✅ 所有语法检查通过
|
||||
|
||||
@@ -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 文本被当作纯文本发送导致的排版问题。
|
||||
- 阻碍因素:
|
||||
- 暂无。
|
||||
- 状态:
|
||||
- 成功。
|
||||
@@ -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
|
||||
|
||||
@@ -100,6 +100,8 @@ def get_help_message() -> str:
|
||||
- `.adventure` - 消耗1分钟进行冒险
|
||||
- `.冒险` - 消耗1分钟进行冒险
|
||||
- `.adventure 5` - 消耗5分钟进行冒险
|
||||
- `.adventure abandon` - 放弃当前冒险,按最低倍率结算已冒险时间
|
||||
- `.adventure 放弃` - 放弃当前冒险,按最低倍率结算已冒险时间
|
||||
- `.adventure help` - 查看冒险帮助
|
||||
|
||||
### 🎁 积分赠送系统
|
||||
|
||||
@@ -76,14 +76,20 @@ 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"})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user