fix(feishu): fetch and prepend quoted message content for reply context

修复飞书回复消息时引用内容丢失的问题。当用户回复某条消息时,事件中携带的
root_id/parent_id 此前被忽略,导致 LLM 看不到被引用的原始消息。

Fix: when a Feishu message is a reply, the event's root_id/parent_id
were previously ignored, so the LLM had no context about which message
was being replied to.

Changes / 改动:
- Store root_id and parent_id in message metadata
  将 root_id 和 parent_id 存入消息 metadata
- Fetch parent message content via im/v1/message.Get API
  通过飞书 API 获取父消息内容
- Prepend quoted text (truncated to 200 chars) before current content
  将引用文本(截断至 200 字符)前置到当前消息

Example / 示例:
  Before (修复前):
    User replies "这个方案可以" to message "明天下午3点开会讨论新需求"
    LLM sees:  "这个方案可以"
    (引用上下文完全丢失 / quoted context entirely lost)

  After (修复后):
    LLM sees:  "[quoted message]: 明天下午3点开会讨论新需求\n\n这个方案可以"
    (LLM 能看到被回复的原始消息 / LLM sees the original message being replied to)
This commit is contained in:
青柠 2026-03-11 14:41:14 +08:00
parent 110fc71349
commit f3be98ed87

View file

@ -391,6 +391,14 @@ func (c *FeishuChannel) handleMessageReceive(ctx context.Context, event *larkim.
if chatType != "" {
metadata["chat_type"] = chatType
}
rootID := stringValue(message.RootId)
parentID := stringValue(message.ParentId)
if rootID != "" {
metadata["root_id"] = rootID
}
if parentID != "" {
metadata["parent_id"] = parentID
}
if sender != nil && sender.TenantKey != nil {
metadata["tenant_key"] = *sender.TenantKey
}
@ -417,6 +425,13 @@ func (c *FeishuChannel) handleMessageReceive(ctx context.Context, event *larkim.
content = cleaned
}
// Prepend quoted (parent) message content so the LLM sees the reply context.
if parentID != "" {
if quoted := c.fetchQuotedContent(ctx, parentID); quoted != "" {
content = fmt.Sprintf("[quoted message]: %s\n\n%s", quoted, content)
}
}
logger.InfoCF("feishu", "Feishu message received", map[string]any{
"sender_id": senderID,
"chat_id": chatID,
@ -430,6 +445,45 @@ func (c *FeishuChannel) handleMessageReceive(ctx context.Context, event *larkim.
// --- Internal helpers ---
// fetchQuotedContent retrieves the text content of the parent (quoted) message via API.
// Returns empty string on any failure so the caller can silently skip.
func (c *FeishuChannel) fetchQuotedContent(ctx context.Context, parentMsgID string) string {
req := larkim.NewGetMessageReqBuilder().
MessageId(parentMsgID).
Build()
resp, err := c.client.Im.V1.Message.Get(ctx, req)
if err != nil {
logger.DebugCF("feishu", "Failed to fetch quoted message", map[string]any{
"parent_id": parentMsgID,
"error": err.Error(),
})
return ""
}
if !resp.Success() {
logger.DebugCF("feishu", "Quoted message API error", map[string]any{
"parent_id": parentMsgID,
"code": resp.Code,
"msg": resp.Msg,
})
return ""
}
if resp.Data == nil || len(resp.Data.Items) == 0 {
return ""
}
msg := resp.Data.Items[0]
msgType := stringValue(msg.MsgType)
bodyContent := ""
if msg.Body != nil {
bodyContent = stringValue(msg.Body.Content)
}
text := extractContent(msgType, bodyContent)
return utils.Truncate(text, 200)
}
// fetchBotOpenID calls the Feishu bot info API to retrieve and store the bot's open_id.
func (c *FeishuChannel) fetchBotOpenID(ctx context.Context) error {
resp, err := c.client.Do(ctx, &larkcore.ApiReq{