You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Aevatar 后端 / 架构入门测验
如果你是前端同事,只需要掌握基础架构边界,不需要做这 8 道全量题。请做:
为什么这个测验值得做
允许用 AI 答题,恰恰是因为这套题不奖励"AI 顺手生成的标准答案"。Aevatar 的设计规范、模块边界、与 Discussion #568 之间的差距,只有把 CLAUDE.md / ADR / 关键源文件读穿才答得对。AI 帮你检索和组织语言,但代码引用、文件路径、行号、变量名、规则原文必须由你确认到本仓库当前状态。
如果你能在 90 分钟内交出一份证据扎实的答卷,说明你已经具备独立改 aevatar 的最低门槛。
必读材料
仓库内:
CLAUDE.md表述不同,以当前分支实际生效的仓库约束为准,答题时写清楚引用的是哪一份。仓库外:
答题方式
后端 / 架构全量题每道题一个 markdown 文件。请在该文件末尾的
## 答题区之下作答,不要修改题面。提交时把整个tests/目录连同改动一起 commit 到你自己的分支。如果交卷时已经过了 2026-05-11,请把日期改成你 push 当天的日期,仍然遵循
YYYY-MM-DD定长格式。题目 8 道,建议时间分配:
评分维度
每题 10 分,总分 80。评分会同时看:
如果一道题你认为题面有错或前提不成立,可以直接在答题区写 "我认为题面有问题,理由是...",逻辑自洽且能引到证据,照样给分。
出题人友情提示
题 01 — 硬规则甄别(PR 评审模式)
题面
下面是 5 个伪 PR diff 片段,由不同新同事提交。每段 diff 都至少违反 CLAUDE.md 中的一条强制约束(标记为"强制"或"硬约束"的条目)。
请对每段 diff:
tools/ci/下),写出脚本文件名;不是每条规则都有专门的 CI 脚本——如果你grep一圈没找到,直接写 "无对应 CI 脚本,规则仅在 CLAUDE.md 文字层" 即可,不扣分。任一小步缺失或答错扣 0.5 分。
A. 在
Aevatar.CQRS.Projection.Core中新增B. 在
RoleGAgent内的工具回调里C. 在新写的集成测试里
D. 在 Application 层新增
E. 在新增的领域事件契约里
答题区
题 02 — 一次 Lark 群消息回复的模块依赖图
题面
一条用户消息从飞书侧发出,经 NyxID Relay 进入 aevatar,再产生一次 LLM 回复推回飞书。请回答下列问题。
2.1(4 分)画依赖链
按调用 / 投递顺序列出从 "NyxID Relay 调到 aevatar HTTP 端点" 起,到 "回执 chunk 走出 aevatar 进程" 为止,消息穿过的每一个有名字的角色。每一行写:
要求:
EventEnvelope字眼或与之等价的投递载体说明。[event]标注。2.2(3 分)边界
回答以下三个问题,每问 ≤ 30 字,必须点到具体类型/方法名:
ConversationGAgent拒绝承担哪一类执行职责?把那个职责现在落到哪个类承担?AgentRunGAgent用什么作为它的actorId?这个actorId提供的是寻址/幂等还是stale 守门?stale 请求由另一个什么机制丢弃?请给出两处源码 file::line——一处指向 actorId 构造,一处指向 stale 实际判定。NeedsLlmReplyEvent从ConversationGAgent投到AgentRunGAgent有没有经过 Projection Pipeline?为什么?2.3(3 分)演进状态识别
下列5 个名字中:
ChannelLlmReplyInboxRuntimeIChannelLlmReplyRunDispatcherIChannelLlmReplyInboxAgentRunGAgentToolCallLoop请回答:
grep或rg命令。当前主链路:一次 Lark 回复现在真的会经过它。保留的边界 / 适配点:还在为当前链路服务,但不是执行主体(典型如 IO worker / 适配 port)。历史 / 已下线:当前仓库不存在,或只剩文档、历史引用、测试迁移痕迹。待后续收敛:当前仍在用,但 Issue 将 ChatRuntime / ChannelLlmReplyInboxRuntime 收敛为 actor-owned Agent Continuation #596 / Discussion [Aevatar Harness] 核心能力边界讨论 #568 已把它标成历史包袱或后续拆解对象。gh issue view输出。答题区
题 03 — Continuation 当前实现 vs Discussion #568 §1 理想形态
题面
Discussion #568 §1 给出一个核心判断:"Agent 在 Aevatar 里的推进,不是 loop,是 actor 内的 event choreography。" 同时承认当前代码里
ChatRuntime/ToolCallLoop仍然是 in-stack loop,是历史包袱。loning 在 §1 reply 中进一步主张 "我的理解, 不应该有 loop. 即便看起来是 loop, 也是基于事件驱动的",并质疑ChatRuntime这个抽象应当消失。3.1(3 分)找到 loop 的"案发现场"
到
src/Aevatar.AI.Core/Chat/ChatRuntime.cs中:for或while循环的起始行号和 1 行 diff-style 引用,证明这就是 §1 描述的 in-stack loop。async/await,仍然违反 CLAUDE.md §"Actor 执行模型 — 跨 actor 等待 continuation 化"?3.2(4 分)对照 AgentRunGAgent 是不是 §1 的最小闭环
AgentRunGAgent(agents/Aevatar.GAgents.NyxidChat/AgentRunGAgent.cs)已经是 Issue #596 Phase A 的产物。但它不等于 Discussion #568 §1 描述的最终形态。请用以下表格作答(每行 ≤ 25 字):
最后一行加一句结论(≤ 30 字):当前
AgentRunGAgent把多拍折叠成几拍?3.3(3 分)loning 的 "ChatRuntime 不该存在"
loning 在 §1 第二轮回复中说:"ChatRuntime 感觉也不该存在. 实际上, 软件工程就是最好的 Harness ..." 对这个观点:
ChatRuntime的 loop 性质再削弱一档,你会改哪三个文件?每个写一行该改什么。答题区
题 04 — Trivial 抽象判定
题面
Discussion #568 反复用一个判断方式:"如果一个抽象只是给已有结构起新名字,没产生新约束 / 新能力,它就是 trivial(平凡)抽象,不值得引入。" loning 把它和数学里的"平凡解"做了类比。
下面是 4 个新同事提案。每个提案请回答:
判断错(a 给反)直接 0 分。其他每小问 0.5~1 分。
提案 1:
AgentGovernanceProfile新同事提议在 Application 层引入:
理由:"这样我们就能集中治理。"
提案 2:
ICapabilityRegistry新同事提议把所有
IAgentToolSource的工具枚举包装成一个统一的ICapabilityRegistry:CapabilityDescriptor内部字段就是IAgentTool已有的 name / args schema /IsReadOnly/RequiresApproval,加上一个source字段标记是 NyxID-backed 还是 Aevatar-native。理由:"前端要画一个统一的'能力中心'页面。"
提案 3:
ITurnContextStore新同事在
Aevatar.AI.Core加:实现是
InMemoryTurnContextStore : ConcurrentDictionary<string, TurnContext>。理由:"工具回调里需要恢复 turn 上下文。"
提案 4:
AgentProtocolDescriptor新同事提议:每个 GAgent 用
[AgentProtocol(...)]attribute 声明自己接受的入站事件类型、超时、是否要求 approval、能否被跨 scope 寻址;编译时生成一份 manifest,运行时由 dispatch 层在投递前校验。理由:"让 schema-level 治理对每个 GAgent 都成立。"
答题区
题 05 — Governance 四层落点
题面
Discussion #568 §4(重写版)把 Governance 分为 四层:Schema / Boundary / Actor / Topology。每一层分别"让某种违规根本无法构造 / 无法发出 / 无法到达 / 无法越权"。loning 在 reply 里强调:"hooks 不是 hard gate"。
5.1(4 分)四层映射 — 现实证据
请用下表作答。每一层给出仓库里一个真实的、当前正在运行的机制,要求:
<相对路径>::<类型/接口/方法>至少一个;禁止用
IToolCallMiddleware当 Schema 层或 Actor 层的答案——那是 hooks 类(详见 5.2)。5.2(3 分)IToolCallMiddleware 是 hard gate 还是 hook?
下列 4 个判断里至少有 2 个是错的,至多有 3 个是对的。请逐条标
T / F,并对每条 ≤ 25 字 解释你的依据(必要时引 §4 原文或仓库源码):IToolCallMiddleware在调用栈上同步阻断违规调用,所以是 hard gate。ToolApprovalMiddleware+YieldApprovalHandler+PendingToolApprovalState把 approval 事件化进入 actor inbox,这部分语义已经落到 Actor-level,而不仅仅是 hook。IToolCallMiddleware写成"返回 Deny → 直接 throw",它就成 hard gate 了。5.3(3 分)loning 的 "Hooks 跟 GAgent 基类绑定"
loning 的 reply 原文:"所有的 agents 在定义过程中会定义自己的 harness, 只需要提供继承关系或者 tools 就可以, 不需要有包含语义的 Hooks. 即便是有, 那也是对于某一个 GAgent 基类, 有一些专门的 Hooks、Gates. 也就是说, 这些都应该跟某些有语义的 GAgent 基类绑定."
请回答:
RoleGAgent的工具 / 中间件 / Hook 注册路径违反或部分违反了 loning 这条主张吗?给出一处具体证据(一行 file::line 或一句源码引用)。如果你认为没有违反,也要给出反证。答题区
题 06 — 读写分离 + Memory 模块去留
题面
6.1(5 分)读写分离的应用题
新同事 A 在 review 中收到这样的需求:"前端要展示当前会话的最近 10 条消息,给我一个接口。" 他写出下面三个候选:
候选 1
候选 2
候选 3
请回答:
run_current_statereadmodel 上的is_sampling布尔)。这样合规吗?如果合规,对前端"瞬时流式态"的需求是不是合适?6.2(5 分)UserMemoryGAgent 是不是 #568 §3 反对的"Memory 模块"?
打开
agents/Aevatar.GAgents.UserMemory/UserMemoryGAgent.cs。Discussion #568 §3 的最终结论是 "Memory 作为独立 Harness 模块作废",理由摘录:但是仓库里真实存在
UserMemoryGAgent、UserMemoryState、MemoryEntryAddedEvent等命名包含 "Memory" 的对象。这是不是与 §3 矛盾?请按下列结构作答(每段 ≤ 60 字):
UserMemoryGAgent放入这张表对应的某一行;说明它对应的是哪一行的"需求"和哪一行的"已有结构"。UserMemoryGAgent的状态类型(UserMemoryState)是 protobuf message,事件通过PersistDomainEventAsync走 ES。从这两点判断它是不是 §3 反对的"旁挂式 sidecar memory(vector DB / chat history / sidecar)"。UserMemoryGAgent这个命名有没有犯这个错?如果有,建议改成什么名字、或者改成什么继承关系来淡化"Memory 是独立模块"的暗示?UserMemoryGAgent一个整数评分并说明理由。答题区
题 07 — Multi-Agent 组织拓扑现状
题面
Discussion #568 §5(重写版)的核心结论:
TeamManagerGAgent/TaskBoardGAgent不再是平台 primitive,应"退回 application-level example pattern"——具体到代码上 §5 写过一行明确建议:"TaskBoardGAgent→ 同上,作为'分发部门'的具体实现示例 ... 移到examples/或samples/,命名改为业务名"。但如果你
find . -name "TaskBoardGAgent.cs",会发现它当前还住在src/Aevatar.Foundation.Core/MultiAgent/——这是平台基础层,不是 examples。7.1(4 分)现状盘点
find或rg命令证明TaskBoardGAgent当前的位置。把命令和输出第一行贴在答题区。Aevatar.Foundation.Core这个项目在 CLAUDE.md /docs/canon/architecture-vocabulary.md的语境里属于哪一层?请引一句原文。TaskBoardGAgent留在Foundation.Core而不挪到examples/,与 §5 重写后的结论之间存在几条矛盾?逐条列出(不少于 2 条),每条 ≤ 25 字。7.2(3 分)平台 primitive 三件套是否当前已足够
§5 主张:"GAgent + parent/child link + 消息" 已经够用,多 worker / reviewer / 任务分发都是这三件套的运行时编排。
路径::方法名)。如果给出 2 个,请说明它们是抽象 + 实现(interface ↔ impl)的一对,还是两个独立入口。7.3(3 分)StreamProxy 的命名歧义
Issue #560 提示了一个命名警告:
请回答:
StreamingProxyGAgent的真实职责是什么?用一句 ≤ 30 字 的话概括(先打开它的 State 类型看字段)。SessionStreamGAgent真正想抽象的是什么?用一句 ≤ 30 字 的话概括。RoleGAgent的输入 / 输出契约面向多模态需要补什么?给一个具体改造点(一句话即可,可以是新 proto field、新 message、新 sub-state)。答题区
题 08 — 综合论述(≤ 300 字)
题面
请用≤ 300 字的中文,向一位刚入职、有通用 agent harness(如 LangChain / OpenAI Assistants / Claude Agent SDK)经验的工程师,回答:
要求作答时:
EventEnvelope和 stream 的 message planeCommittedStateEventPublished→ projection / readmodel 的观察链路while-loop / 旁挂 vector memory / 中心 policy config)在 Aevatar 这个底座上是结构性多余而非 "也能做"。答题区
All reactions