Skip to content

feat(channel-runtime): Phase 1 — Lark bot end-to-end with direct webhook#172

Merged
eanzhao merged 4 commits intodevfrom
feature/bot
Apr 10, 2026
Merged

feat(channel-runtime): Phase 1 — Lark bot end-to-end with direct webhook#172
eanzhao merged 4 commits intodevfrom
feature/bot

Conversation

@eanzhao
Copy link
Copy Markdown
Contributor

@eanzhao eanzhao commented Apr 10, 2026

Summary

Aevatar Channel Runtime Phase 1: 让 Lark bot 端到端跑通。

架构背景

按照 NyxID #191 + aevatar #113 的架构决策:

  • NyxID 只做 credential broker + outbound bot API proxy(channel mode 已废弃)
  • Aevatar 拥有完整的 channel runtime(inbound webhook + chat session + reply orchestration)
  • 接收链路: Lark → Aevatar(直接 callback,不经过 NyxID)
  • 发送链路: Aevatar → NyxID proxy(api-lark-bot)→ Lark

CEO 决策(#204): "NyxID 零修改。身份映射由 Aevatar Channel Runtime 在业务层处理。"

本 PR 改动

Bug 修复:

  • 修复 ChannelCallbackEndpoints.cs:229 webhook URL 插值 /{null} bug
  • Callback 响应从 200 OK 改为 202 Accepted(Lark 有 5s webhook 超时)

可靠性增强:

  • Task.Run 加 120s CancellationToken + 异常捕获(Phase 1 妥协,Phase 2 迁移到 actor self-message)
  • 两阶段 webhook 去重:先设 10s 短 TTL 挡并发重复,dispatch 成功后延长到 5 分钟。Dispatch 失败时短 TTL 自然过期,Lark 重试可正常处理

NyxId Chat 工具:

  • 新增 channel_registrations tool(list/register/delete),NyxId chat agent 可以自主完成 channel 配置
  • register 会轮询 projection 返回真实 registration ID(对比 dispatch 前快照,避免误匹配旧记录)
  • delete 需要 confirm=true,首次调用返回详情供用户确认

代码质量:

  • 提取 ChannelMetadataKeys 常量类,替换 hardcoded string keys
  • DI 注册 AddMemoryCache() + ChannelRegistrationToolSource

设计文档

完整设计文档: ~/.gstack/projects/aevatarAI-aevatar/zhaoyiqi-feature-bot-design-20260410-220049.md

三阶段渐进式交付:

  • Phase 1(本 PR): Lark 端到端 — bug fix + 异步模型 + 去重 + tool
  • Phase 2: 安全增强 — 签名验证 + 事件加密 + 身份映射 + durable dedup
  • Phase 3: 统一 handler — ChannelIngestionPipeline + IChannelWebhookParser + 多平台

相关 Issues

Issue 描述
#113 integrate bot callbacks/channel runtime in Aevatar
#125 Lark adapter 安全增强(Phase 2)
#124 per-message 身份解析(Phase 2)
#156 统一 Channel Event Handler(Phase 3)
NyxID #191 NyxID 不拥有 channel runtime
NyxID #204 NyxID 零修改,身份映射在 Aevatar 本地

改动文件

文件 改动
ChannelCallbackEndpoints.cs bug fix + 202 + timeout + 两阶段 dedup
ChannelUserGAgent.cs metadata keys 用常量
ServiceCollectionExtensions.cs AddMemoryCache + ToolSource 注册
ChannelMetadataKeys.cs 新建 — 6 个 typed 常量
ChannelRegistrationTool.cs 新建 — list/register/delete
ChannelRegistrationToolSource.cs 新建 — DI tool source

Test plan

  • 构建 0 错误
  • 78 ChannelRuntime 测试通过
  • 3326 全量测试通过(1 个 Redis 环境失败,非本 PR)
  • CI architecture guards 通过(playground asset drift 是已知问题)
  • 部署后:注册 Lark bot → 配置 webhook URL → 发消息 → 收到 AI 回复

🤖 Generated with Claude Code

eanzhao added 4 commits April 10, 2026 17:14
- 新增 ChannelRegistrationToolSource 和 ChannelRegistrationTool,使 NyxIdChatGAgent 能够通过 channel_registrations 工具管理频道机器人注册
- 引入 ChannelMetadataKeys 类,统一频道运行时上下文的元数据键,用于在 ChatRequestEvent.Metadata 中传递频道特定信息
- 更新 NyxIdApiClient.ListConversationRoutesAsync 以支持按 botId 筛选对话路由
- 扩展 NyxIdChannelBotsTool,增加 update_route 操作和更多平台(Lark、Discord)的注册字段支持,并支持群聊中的按发送者路由
- 改进 ChannelCallbackEndpoints,添加基于内存缓存的 webhook 去重机制,防止平台重试导致的重复处理
- 更新系统提示文档,提供多平台(Telegram、Lark、Discord)的频道机器人设置指南
- 注册后轮询新生成的ID,减少用户手动查询步骤
- 删除操作需二次确认,防止误删
- 更新返回消息以提供更明确的指引
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 03a45051a3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +109 to +110
if (cache.TryGetValue(dedupeKey, out _))
{
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Make webhook dedup reservation atomic

The dedup logic performs TryGetValue and Set as separate steps, so two identical callbacks arriving at nearly the same time can both observe a cache miss and both dispatch to the actor, leading to duplicate processing/replies. This violates the intended “block concurrent duplicates” behavior; reserve the dedup key atomically (or via a per-key lock) before starting dispatch.

Useful? React with 👍 / 👎.

Comment on lines +167 to +169
var all = await _queryPort.QueryAllAsync(ct);
var newEntry = all.FirstOrDefault(e => !existingIds.Contains(e.Id));
if (newEntry != null)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Correlate register polling to the submitted command

RegisterAsync infers the new registration by selecting the first ID absent from a pre-dispatch snapshot, which is unsafe under concurrent registrations: two callers can both detect the same "first new" row and one user gets another user's registration_id/URLs. This can misconfigure webhooks and break follow-up actions; the lookup needs a command-level correlation key (or another unique match) rather than global set-difference.

Useful? React with 👍 / 👎.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 10, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.68%. Comparing base (fc43d97) to head (5a91378).
⚠️ Report is 5 commits behind head on dev.

@@           Coverage Diff           @@
##              dev     #172   +/-   ##
=======================================
  Coverage   81.67%   81.68%           
=======================================
  Files         739      739           
  Lines       46986    46986           
  Branches     6233     6233           
=======================================
+ Hits        38378    38382    +4     
+ Misses       5916     5914    -2     
+ Partials     2692     2690    -2     
Flag Coverage Δ
ci 81.68% <ø> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.
see 5 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@eanzhao eanzhao merged commit 15cf1b4 into dev Apr 10, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant