Skip to content
fanjk 的技术博客
Go back

实习第一个月总结

实习第一个月总结

姓名:已隐去
岗位:AI Agent 运维开发实习生
入职日期:已隐去 汇报日期:已隐去 导师:已隐去


一、自我介绍

我目前就读于某高校数学与应用数学专业,已推免至某高校软件工程方向。

实习期间,我的工作主要围绕两类问题展开:

  • 完善 AI 学习场景中的基础体验,包括 Markdown 渲染、统一 Toast 和阅读式对话流式输出。
  • 推进首页学习 Agent,重点解决多阶段学习业务中的 State 建模和复杂 Agent 的可扩展架构问题。

二、实习期间的工作内容

2.1 项目一:AI 对话 Markdown 渲染增强与代码主题个性化

项目背景

AI 对话中经常出现代码块、表格、公式和 Mermaid 图表。基础 Markdown 渲染虽然能够展示文本,但代码阅读、复制、图表加载和主题切换体验不够完整;如果不同页面分别处理这些能力,也会形成重复实现。

主要设计与实现

  • 将 Mermaid、代码行号和代码主题能力收敛到共享 Markdown 渲染模块,供多个 AI 对话和学习场景复用。
  • Mermaid 采用按需动态加载,避免无图表内容也引入额外资源;渲染失败时回退展示源码,保证内容仍然可读。
  • 为 fenced code block 增加行号,同时让复制操作只复制原始代码,不把行号混入复制结果。
  • 代码主题采用白名单管理和动态 CSS 加载,避免任意主题值直接影响页面资源。
  • 将用户的代码主题偏好保存到用户扩展配置中的代码主题字段,后端使用 JSON path 原子更新,避免修改主题时覆盖其他用户配置。

项目产出

  • 建立了统一、可复用的 AI 内容渲染入口,减少不同页面重复维护 Markdown 扩展逻辑。
  • 提升了代码阅读、复制和图表展示体验,并为后续阅读式对话流式交互和 Agent 对话复用打下基础。
  • 新增代码主题偏好的持久化能力,用户选择的主题可以跨会话保留,保证使用体验的一致性。

2.2 项目二:前端统一 Toast / 弹出提示

项目背景

平台不同页面对成功、失败、警告和普通信息的展示方式不统一。部分页面各自维护显示状态、定时器和定位样式,不仅用户体验不一致,后续修改也需要重复处理。

主要设计与实现

  • 新增统一 Toast 基础模块,对外提供 successerrorwarninginfo 等一致的调用入口。
  • 使用 Zustand 管理全局 Toast 队列,集中处理新增、关闭、自动消失和队列上限。
  • 支持操作按钮等扩展能力,使 Toast 不只能够展示文本,也能够承载简单的后续操作。
  • 将 个人设置、头像、接口密钥和部分管理场景中的原有提示逐步迁移到统一模块。

项目产出

  • 统一了平台轻提示的视觉和交互方式。
  • 业务页面不再重复维护局部 Toast 状态、计时器和固定定位样式,降低了后续接入与维护成本。
  • 为后续新增业务场景提供了稳定、统一的反馈基础设施。

2.3 项目三:阅读式对话流式输出

项目背景

原有阅读式对话需要等待 AI 完整生成回复后再一次性展示。回复较长时,学生只能持续等待,无法提前阅读已经生成的内容,影响反馈速度和学习过程的连贯性。

主要设计与实现

  • 后端使用 FastAPI StreamingResponse 和 SSE 持续返回生成事件。
  • 前端使用 fetch + ReadableStream 处理 POST 请求的流式响应,并将增量内容实时更新到当前 assistant 消息。
  • 前端设置自适应显示缓冲,以固定节拍释放内容,并根据缓冲区积压的字符数动态调整每次展示量:积压越多,单次展示越多,在保持打字机观感的同时避免内容滞后。
  • 补充请求幂等、同一 Session 并发、客户端断连和 Session 切换等异常场景处理。
  • Markdown 使用累计内容渲染;当代码块或 Mermaid 围栏尚未闭合时,暂缓复杂渲染,避免流式生成过程中反复报错或闪烁。

项目产出

  • 学生可以边生成边阅读,缩短长回复场景中的感知等待时间。
  • 在引入流式交互的同时,保持了原有判题、进度和消息持久化规则稳定。

2.4 项目四:首页学习 Agent V2

2.4.1 项目背景与目标

平台现有学习路径是”课程 → 知识点 → 任务”的自驱动模式,学生需要自行在知识图谱中寻找下一步;阅读式学习又主要依靠固定流程推进,既缺少能够根据学生情况持续带学的入口,也不利于后续扩展新的教学阶段和教学策略。

首页学习 Agent 希望把首页从学习看板升级为对话式学习入口:学生可以用自然语言发现课程和任务;进入任务后,Agent 结合任务内容、前序对话和学生记忆生成可修改、可确认的个性化学习计划,并按计划教学、推进任务进度。

2.4.2 我的职责与总体产出

我负责首页学习 Agent 的 PRD、技术方案、核心架构设计、功能实现和测试验证,并根据与团队讨论和 review 反馈持续调整方案。

围绕这个项目,我重点完成了两项设计:

  1. 将 Agent 当前所处的学习阶段建模为后端可持久化的业务 State。
  2. 围绕一次 Agent Turn 重新划分职责,将状态、上下文、模型循环、工具执行、消息和持久化放到明确的架构边界中。

两项设计之间的关系可以概括为:

State 设计解决 Agent 在业务上”当前是什么、应该做什么、允许做什么”;架构设计保证这些规则在一次复杂执行中被稳定贯彻。


2.4.3 核心设计一:用确定性的业务 State 约束不确定的模型行为

为什么不能只依赖 Prompt 表示阶段

如果只依赖 Prompt 或对话历史推断阶段,业务状态和工具权限会变得不稳定。因此,我将当前阶段显式保存为后端 State,使它成为可恢复、可检查的业务事实。

State 如何描述学习业务
stateDiagram-v2
    [*] --> home
    home --> planning: enter_planning
    planning --> teaching: confirm_learning_plan
    planning --> home: cancel_planning
    teaching --> planning: revise_learning_plan
    teaching --> home: exit_teaching
State业务职责代表性能力退出条件
home发现和选择学习目标浏览课程、查询进度、推荐并选择任务选定任务并进入规划
planning围绕任务制定学习计划获取任务内容、生成和修改计划、确认计划确认计划进入教学,或取消返回首页
teaching按已确认计划执行教学基于任务内容教学、推进进度、判断是否需要调整修订计划返回 planning,或退出任务返回 home

这里最重要的不是当前有三个状态,而是每个 State 都有清晰的业务职责、可用能力和退出方向。完整链路也不是单向流程:教学过程中发现计划不合适时,可以从 teaching 回到 planning 调整,再继续教学。

状态策略对象将阶段策略集中管理

每个 State 对应一个 状态策略对象,当前集中声明三个维度:

状态策略对象
  ├── system_prompts:当前阶段加载的 system prompt / skill 文件
  ├── tool_visibility:当前阶段向模型暴露的工具集合
  └── can_transition_to:当前阶段声明允许到达的目标状态

这使原本可能散落在 Prompt、工具和主流程中的阶段判断,收敛为一份可以集中阅读和检查的业务策略:

  • homeplanningteaching 不再共享一份承担所有职责的 Prompt。
  • 模型只看到当前阶段需要的工具,降低错误选择工具的概率。
  • 合法迁移关系可以从状态策略中直接读取,而不需要阅读整条 Agent 循环。
  • 新增 State,或者调整某个 State 的 Prompt、Skill 和工具时,不需要修改 Agent 循环,只需要扩展对应的状态策略和迁移工具。
工具权限与状态迁移

工具权限采用两层检查:工具注册中心 根据 State 只向模型暴露允许的工具,工具执行器 在实际执行前再次读取当前状态并校验权限。前者负责引导模型,后者负责保证执行边界。

状态迁移通过 enter_planningconfirm_learning_planrevise_learning_plan 等有明确业务含义的工具完成。工具对应的后端处理函数会同步更新 State 和相关业务事实,而不是根据模型回复中是否出现”开始教学”等文本判断状态。

State 设计带来的结果
  • Agent 当前处于什么阶段,从隐含在 Prompt 和聊天文本中的信息,变成后端可恢复、可检查的业务事实。
  • Prompt、工具视野和迁移方向按照 State 集中组织,模型能力与当前业务场景保持一致。
  • 学习计划具有草稿、确认和修订过程,不再只是一段难以追踪的自然语言回复。
  • 新增学习场景或调整阶段能力时,变化主要落在状态策略对象和迁移工具中,不需要不断扩大 Agent 循环。
  • 状态策略对象未来可以进一步让教研、产品等非研发人员参与 State 和教学流程设计。

我的核心判断是:不让模型自己猜”现在进行到哪一步”,而是让后端 State 决定模型当前应该看到什么、能够调用什么,以及业务可以进入哪个下一阶段。


2.4.4 核心设计二:从可运行原型到可扩展的 Agent 架构

V1 的价值与复杂度变化

V1 以较低成本跑通了模型调用、工具调用和流式对话链路,验证了首页学习 Agent 的基本可行性。随着 State、学习计划、上下文治理、消息持久化和刷新恢复逐步加入,原有结构开始不再适配新的复杂度。

最有代表性的三个问题是:

V1 中的问题具体影响V2 的设计方向
执行入口同时承担模型、工具、恢复、预算、流式和持久化修改一种能力可能影响整条执行链拆分执行协调器、Agent 循环、恢复模块和回调模块等职责
实时消息列表结果消息列表 两套消息管道并存正式回复、进度和观察结果需要反复清洗、合并和去重按用途分离正式 message、transient progress 和 tool 观察结果
工具定义集中硬编码,输入输出缺少统一契约新增工具需要修改多个中央分支,边界不易验证注册中心自动发现,工具独立声明 Pydantic Input/Output 和处理函数

这些问题的共同点不是文件太大,而是不同变化原因集中在同一条主流程里。修改模型协议、增加工具、调整 State、增加压缩策略或修复刷新恢复,本应是不同类型的变化,却会相互影响。

因此,V2 的目标不是简单地把大文件拆成小文件,而是把同类变化归到同一层,让每个模块只负责一类变化。

V2 整体架构
%%{init: {"themeVariables": {"fontSize": "11px"}, "flowchart": {"nodeSpacing": 20, "rankSpacing": 30}}}%%
flowchart TB
    UI[首页 Agent]

    subgraph Backend[后端应用内部]
        API[Chat / SSE 接口] --> APP[应用服务]
        APP --> ORCH[执行协调器]

        ORCH --> STATE[State / 状态策略对象]
        ORCH --> CONTEXT[上下文处理]
        ORCH --> LOOP[Agent 循环]
        ORCH --> STORE[会话存储]

        LOOP --> PROVIDER[模型服务]
        LOOP --> GUARD[边界检查与回调]
        LOOP --> HUB[工具调度中心<br/>后端工具调度]
        HUB --> HANDLERS[受控业务处理函数<br/>调用后端业务函数]
    end

    UI --> API

图中的工具调度中心和受控业务处理函数都位于后端应用内部。模型只能请求调用已经注册的工具,最终执行的是受控业务函数,并不是让模型通过终端或 shell 操作系统环境。

各层主要回答不同的问题:

模块核心职责
应用服务一次外部请求如何可靠开始和结束,包括幂等、活动流状态 和流式响应
执行协调器按固定生命周期协调一次 Agent Turn
State / 状态策略对象当前业务阶段是什么,以及这一阶段使用哪些 Prompt、工具和迁移规则
上下文处理本轮模型真正需要看到哪些历史和业务事实
Agent 循环只处理模型调用、工具请求、观察结果和继续推理的循环
工具调度中心在后端内部发现、校验并调度业务工具
边界检查与回调处理输入输出边界,以及增量内容、进度、工具执行前刷新等执行事件
会话存储持久化消息、State、学习计划和其他会话事实
围绕一次 Turn 组织生命周期

执行协调器 将一次执行稳定地组织为:

RESTORE -> BUILD -> RUN -> SAVE
  • RESTORE:从 会话存储 恢复消息、State、任务和其他会话事实。
  • BUILD:根据当前 State 组装 Prompt、业务事实和可见工具。
  • RUN:将模型与工具迭代交给 Agent 循环。
  • SAVE:合并本轮消息和业务状态,完成最终持久化。

执行协调器负责协调生命周期,但不实现具体工具业务、模型协议或历史压缩算法。这样,修改某个局部能力时,不需要让主流程了解更多细节。

消息管道按用途分离

消息系统是这次架构调整中最有代表性的落地案例。V2 不再按照消息来自哪个临时列表处理,而是先区分不同数据的用途:

数据流学生是否可见是否进入正式历史用途
正式 message / delta最终 message 落库学生最终收到的 assistant 回复
transient progress”正在读取任务”等临时过程提示
tool 观察结果作为模型上下文使用让模型读取工具结果并继续推理

正式 assistant 回复使用稳定的 message ID。工具执行前通过 工具执行前刷新 保存已经生成的内容,工具完成后继续使用同一 ID 更新消息;活动流状态 记录当前生成状态,使页面刷新后仍然能够恢复执行结果。

这样,正式回复、界面进度和模型观察结果不再共享同一条保存链路,新增一种展示事件也不需要修改正式消息的持久化规则。

工具由中央分支变成独立契约

每个内置工具独立声明 Pydantic InputOutput、描述和异步 execute 处理函数;注册中心负责发现工具,执行器统一处理 State 权限、数据结构校验、超时和安全检查。

新增工具时,主要工作是实现统一契约和业务处理函数,不需要继续在 Agent 循环或执行协调器中增加工具特例。工具的具体业务变化与 Agent 核心循环因此被隔离开。

架构设计带来的结果
  • 调整某个 State 的 Prompt、Skill 和工具策略,不需要修改 Agent 循环。
  • 新增工具主要扩展工具契约和处理函数,不需要修改执行协调器。
  • 新增上下文治理策略,可以在上下文处理内扩展,而不是继续扩大执行入口。
  • 清晰的模块边界为代码 review、问题定位和单元测试提供了明确落点。

这次演进的核心不是把一个大文件拆成多个小文件,而是把状态、模型循环、工具执行、上下文、持久化和流式输出从同一个变化单元中分离出来。

2.4.5 项目复盘与关键取舍

**思考:

  • 在实现中,由于第一版没有设计transcript,后来才加上去,导致消息管道混乱,要做三重清洗,此外脱敏逻辑也有这个问题。从中学到在使用AI改动代码时,要先定边界,再写代码;先定契约,再让 AI 实现;不要用补丁式叠加代替架构设计
  • 由于之前只参考了某开源项目,导致 loop.py 和 runner.py 有上千行,可读性和可维护性差。从中学到复杂逻辑不能长期集中在单个大文件中,需要在设计阶段先拆清模块职责,例如模型调用、工具执行、上下文构建、状态流转和持久化分别由独立组件负责。
  • 核心引擎层应保持独立且要薄,其他功能实现都要写成可插拔的组件。
  • 使用 AI 辅助复杂任务时,需要及时用文档记录架构决策、模块边界和待讨论问题,为 Agent 提供稳定上下文,减少跑偏,同时在有开源项目可以借鉴时,克隆到工作区给AI借鉴。

2.5 日常运维与协作

  • 处理流式输出结束后交互按钮恢复过慢的问题。
  • 修复 Markdown 渲染中的行内代码背景不明显、引用内容多出引号等展示问题。
  • 对需求和技术方案先形成文档,再与团队讨论关键边界,根据反馈迭代方案和代码。
  • 根据 code review 和测试反馈完成修改、回归与闭环。
  • 参与导师文档和代码的评审

三、成长与收获

刚开始做需求时,我对怎么样的代码是好的什么是不好的没有清晰认知,指挥AI改代码容易一层一层往上叠,导致一个函数知道太多细节,可读性差且后续改动难、可维护性差。之后在agent实现过程中,在指导下,知道了我写代码时出现的问题是:

  • 把具体业务写进核心流程
  • 一个函数知道太多细节
  • 没有把”同类变化”提成接口
  • 数据一开始没分清类型
  • 配置和逻辑混在一起 然后关于架构上,知道了文件之间互相不要互相调用,依赖关系要明确,如果有一个东西被两个地方共用,就要抽取一个公共组件。 代码结构在 AI 时代要高内聚、低耦合、分层分块。

Share this post:

Next Post
实习三个月后,我开始写博客