01.背景
在当今快速发展的软件开发领域,人工智能(AI)扮演着越来越重要的角色。尽管AI底层科学有所进展,但仍未有颠覆性突破。当前的大语言模型,如GPT-3.5和LLaMA,主要通过规模扩展和工程优化提升性能,包括提示工程、指令微调和人类反馈强化学习(RLHF)等技术。
提升大模型应用能力和效率可通过模型本身的进化和工程方法的优化两种途径实现,但它们在实现成本和长期可扩展性上存在差异。基础模型的调优和训练需要大量算力和专业人员投入。同时,这些模型在实际应用中仍面临幻觉、不可靠和不可扩展等问题,制约了其广泛应用。比如难以完全避免的幻觉问题就凸显了当前AI技术的固有局限性。因此,智能体(Agent)系统作为AI的一个重要分支,在AI软件开发体系中展现出巨大的潜力,有望解决或缓解这些问题。
本文将探讨如何构建一个可扩展的智能体系统,并以代码审查(Code Review)任务为例,展示从概念到实践的全过程。我们将分享工程化方法与最佳实践,但不涉及模型微调(fine-tuning)技术。
在此之前我们先了解一些常听到的名词:
1. 大语言模型:是一种基于深度学习的自然语言处理模型,能够理解、生成和翻译人类语言。它们通过大量文本数据训练,可以执行各种语言任务,常常听到的GPT-4,clude,llama等都属于大语言模型。
2. 智能体:是一种能够感知环境、做出决策并采取行动以实现特定目标的AI系统。智能体可以更灵活地处理复杂任务,并且通常具有一定的自主性。
3. 大模型幻觉:指大语言模型生成的看似真实但实际上不准确或虚构的信息。这种现象对于基座模型而言本身是难以完全避免的,因为模型是基于概率生成响应,而非检索确定的事实。
02. 智能体(Agent)系统现状
为什么agent系统能解决(缓解)
这些问题呢?
智能体系统通过整合大语言模型的能力,并增加额外的控制和决策机制,有望提高 AI 应用的可靠性、稳定性和可扩展性。它具有以下几个特点:
1. 模块化和可控性:智能体系统允许将复杂任务分解为更小、更易管理的模块,每个模块都可以单独优化和控制,从而提高整体系统的可靠性和可维护性。
2. 自主决策能力:智能体可以根据预定义的目标和规则自主做出决策,这有助于矫正和减少不正确的结果(过滤、拦截等)。
3. 可编程约束:开发者可以为智能体设定明确的行为约束和验证规则,这有助于确保输出结果符合预期标准,提高可靠性。
4. 自适应学习:许多智能体系统具有学习和适应能力,可以从经验中改进其表现,这有助于提高系统在不同环境和任务中的稳定性和可靠性。
目前主流的开发方式和框架有:
基于大语言模型的链式调用:代表框架:LangChain
特点:提供模块化组件,用于构建复杂的语言模型应用流程。
自主任务规划与执行:代表框架:AutoGPT
特点:能够根据高级目标自主规划和执行任务步骤。
多智能体协作系统:代表框架:MetaGPT
特点:模拟软件开发团队,多个专门化智能体协同完成复杂任务。
函数调用增强:代表方法:OpenAI's GPT Function Calling
特点:允许语言模型调用预定义的函数,扩展模型能力。
与传统编程语言集成:代表框架:Semantic Kernel(微软)
特点:将大型语言模型无缝集成到传统编程环境中。
尽管智能体系统在解决一些基础问题上取得了进展,但在实际应用中,我们仍面临着一些根本性的挑战:
1. 可靠性与幻觉问题:虽然多重验证机制能够减少错误,但在面对开放域问题时,完全消除"幻觉"仍然困难。如何在保持智能体创造力的同时确保输出的可靠性,是一个需要权衡的问题。
2. 上下文理解与一致性:智能体在长对话或复杂任务中可能会"遗忘"之前的上下文,导致前后矛盾。如何增强智能体的长期记忆和上下文理解能力,是提高一致性的关键。
3. 可扩展性与性能平衡:随着任务复杂度增加,系统的响应时间和资源消耗也会相应增加。如何在扩展功能的同时保持高效性,是实际部署中的重要考量。
4. 人机协作的界限:虽然智能体可以自主决策,但在关键决策点上如何恰当地引入人类判断,以及如何设计直观的交互界面,仍然是一个开放性问题。
这些挑战不仅是技术问题,更涉及到系统设计、人机交互、伦理等多个层面。它们之间又相互关联,例如,提高可靠性可能会牺牲一定的效率,增强上下文理解可能会带来更高的资源消耗。
为了更好地理解这些挑战及其解决方案,我们将通过一个实际的code-review agent开发案例,展示如何在项目中应对这些问题。通过这个案例,我们将看到不同阶段面临的具体困难,以及如何逐步优化和改进智能体系统。
03. Code Review Agent 开发实践
3.1 第一阶段:朴实无华
当我们想要利用AI进行code review时,最直接、最"朴素"的方法往往是将代码直接复制粘贴到对话框中,并在最后附加一个简单的指令:"请帮我review一下这段代码"。这种方法简单快捷,但效果往往不尽如人意。
如上图左边所示,AI的回复通常过于笼统,缺乏针对性。它可能会指出一些明显的语法错误或编码风格问题,但难以提供深入的代码结构或逻辑方面的建议。这种方法存在以下几个主要问题:
1. 缺乏上下文:AI无法了解代码的背景、目的和更大的项目结构。
2. 指令不明确:简单的"review"指令没有提供具体的审查重点或期望。
3. 无法捕捉复杂逻辑:对于跨多个文件或模块的复杂逻辑,这种方法难以进行全面分析。
4. 缺乏个性化:没有考虑到特定项目的编码规范或最佳实践。
3.2 第二阶段:Prompt Engineering
认识到”朴素”方法的局限性后,我们可以尝试使用prompt engineering技术来优化AI的输出质量。这个阶段的重点是设计更加详细和结构化的prompt,以引导AI提供更有价值的code review。
但作为一个新兴领域,它的实践思路也是各不相同。
比如有比较简单的基于角色的:"假如你是xxx,我需要你xxx"。也有明确规则和目标的结构化prompt等等。也出现一些基于经验总结出来的框架和论文:
https://github.com/langgptai/LangGPT?tab=readme-ov-file
https://github.com/mattnigh/ChatGPT3-Free-Prompt-List?tab=readme-ov-file#crispe-prompt-framework
"Prompt Programming for Large Language Models: Beyond the Few-Shot Paradigm" (2021)
"A Systematic Survey of Prompt Engineering in Large Language Models: Techniques and Applications" (2024)
…
结合上文,一个优化后的prompt可能包含以下元素:
代码的背景和目的
特定的review重点(如性能、安全性、可读性等)
项目的编码规范和最佳实践
期望输出的格式和详细程度
例如,一个改进的prompt可能是这
角色: 你是一位经验丰富的高级软件工程师,专门从事代码审查和质量保证。
情境: 我正在开发一个重要的软件项目,需要对代码进行全面审查以确保其质量、可维护性和性能。
指令: 请对我提供的代码段进行彻底的代码审查。关注以下方面:
1. 代码质量和可读性
2. 潜在的错误和漏洞
3. 性能优化机会
4. 遵循编码最佳实践和设计模式
5. 代码文档和注释的完整性
具体步骤:
1. 仔细阅读并分析代码
2. 指出代码中的优点
3. 标识任何问题或改进机会
4. 为每个问题提供具体的修改建议
5. 总结审查结果,给出整体评价
人物: 作为一名严谨且富有建设性的代码审查者,请提供专业、客观且有见地的反馈。
评估: 你的代码审查应该全面、深入且有实际价值。请确保你的反馈既有助于提高代码质量,又能帮助开发者成长。
基于下面的代码,你给我一个返回结果。
[这里粘贴代码]
使用这种经过优化的prompt,我们得到的结果有了明显的改善:
如上图右边所示,AI的回复变得更加结构化和针对性强。它不仅指出了具体的问题,还提供了改进建议,甚至可能指出一些潜在的安全隐患或性能瓶颈。
然而,这种方法仍然存在一些局限性:
需要人工编写详细的prompt,这本身就是一项耗时的工作。
没法和现有系统及数据打通,实际使用场景和能力有限
对于大型项目或需要持续review的场景,每次都需要定制prompt可能不够高效。
虽然结果有所改善,但AI仍可能会遗漏一些重要的问题,特别是那些需要更广泛上下文的问题。
3.3 第三阶段 - 利用主流agent
框架构建Code Review Agent
尽管在前两个阶段,我们通过简单的代码复制粘贴和prompt工程化方法已经取得了一定的进展,但随着需求不断增加,这些基础方法开始暴露出一些局限性。例如,它们难以集成到现有系统,缺乏可扩展性,无法充分利用项目背景知识等。
在这个阶段,我们决定采用主流的agent框架来开发Code Review Agent,以期构建一个更加强大和可靠的系统。
选择LangChain框架
在评估了当前市面上的agent系统开发方案后,我们最终选择了利用LangChain来构建Code Review Agent。这个选择并非一蹴而就,而是经过了深思熟虑:
首先,Code Review Agent的功能需求相对集中,不需要太复杂的多角色协作等特性。相比之下,设计更复杂的agent框架如MetaGPT可能会带来过度设计和复杂化的风险。LangChain恰好能够符合我们当前项目的技术需求。
其次,LangChain提供了丰富的可复用组件,这种模块化设计有利于快速搭建所需的系统架构,并为将来的功能扩展奠定良好的基础。
此外,LangChain拥有一个广泛活跃的使用者群体和开发者社区。并且原生支持与OpenAI、Anthropic等主流AI平台的集成,使我们能够更便捷地利用先进的语言模型能力。即便是私有部署模型,只要对齐OpenAI 的API接口,就能无缝使用。
综合以上诸多因素,我们认为LangChain是一个非常适合用于构建Code Review Agent的主流agent框架。当然,随着项目的深入,我们期望构建更复杂的上下文(比如prd等),我们也会考虑使用其他框架开发。
Code Review Agent 系统设计
基于LangChain框架,我们设计了Code Review Agent的整体架构:
该架构主要由以下核心组件组成:
1. 输入处理模块:
a. 负责接收用户提交的待审查代码,并将其转换为模型可处理的格式。
b. 通过调用 getReviewFiles 模块,获取需要审查的代码文件。
2. 上下文构建模块:
a. 根据代码的来源、项目背景等信息,构建出丰富的上下文数据。
b. 利用 GitlabRESTClient 获取仓库信息,以及 getRemoteMergeRequestFiles 和 getMergeRequestCommits 获取提交历史的commit信息,用于构建更丰富的上下文
3. 审查执行模块:
a. 集成了基于Langchain的组件,使用对齐OpenAI API的模型接口,通过 performCodeReview 模块调用AI模型进行分析。
b. processFeedbacks 处理AI模型返回的审查意见,createSummary 生成总结报告,便于用户简单快速的了解结果。
4. 结果输出模块:
a. 将审查结果按照规范的格式返回给用户,包括问题描述、改进建议等。
b. 通过 commentOnPRGitlab 和 commentPreFile 将结果直接推送到Gitlab。
5. Prompt模块:
a. 通过维护一套独立的、任务特定的prompts,系统能够灵活应对不同类型的代码审查需求,不仅能做复杂的逻辑分析,也能根据不同团队侧重点定制需求。
6. 评估模块(独立系统):
a. 能够从历史审查记录中抽样审查模型的code review结果,生成评估报告和指标,作为第三方评估Code Review 的质量,也为后续优化提供方向
这样的架构不仅能够清晰地展现各个模块的职责,也充分利用了Gitlab的协作功能,将审查结果直接反馈到代码库中。同时,它还保留了之前描述中的一些核心组件,如上下文构建,不同系统交互等,确保了系统的完整性和可扩展性。
关键交互流程
以下时序图展示了Code Review Agent的核心工作流程,清晰呈现了从用户发起请求到获得审查结果的整个过程,以及系统各模块之间的关键交互:
Code Review Agent的工作流程涉及几个核心技术点:
1. 增量分析策略:在我们的实践中,全文审查确实能优化上下文理解,并可以附带发现一些问题,但在实际开发中也会带来额外问题:未变更代码不属于当前迭代,贸然优化会引入额外风险。但只有变更行,又会降低AI对代码的理解度。所以我们采取保留git dif 上下文N行的策略。
1.上下文构建:在代码审查过程中,仅仅理解当前的代码片段是远远不够的。要进行高质量的审查,我们需要全面把握代码的依赖关系、变更目的,以及背后的需求动机。
目前,我们已经在上下文中整合了当前文件的代码、JavaScript的依赖函数声明,以及Git commit信息。这为我们提供了一个基础的审查环境。不仅如此,我们计划将产品需求文档(PRD)通过AI解析成结构化数据,纳入各个团队的定制审查重点,并利用检索增强生成(RAG)技术来整合公司现有的知识库。这些举措将大大丰富我们的上下文理解。
但是,这个过程的推进速度比我们预期的要慢。主要原因在于实际情况比我们想象的要复杂得多。我们发现很多commit信息并不能准确反映代码的变更内容,而PRD的质量参差不齐,需要额外的AI预处理。这让我们想起了20年前企业推动无纸化(信息化)的过程 —— 看似简单的转变,实际上需要整个组织的方方面面相互配合,互相适应。
尽管如此,我们认为在DevOps流程中更多地引入AI技术可能是一个突破口。比如,我们可以尝试使用AI来协助编写commit信息。这不仅能提高效率,还能帮助我们积累更准确、更有价值的代码变更记录。通过这种方式,我们希望能逐步改善各类文档和信息的质量,为更全面的上下文构建铺平道路。
2.多阶段 AI 推理:下个阶段会将审查过程拆分为多个阶段,包括代码依赖分析、代码审查,上下文构建等。每个阶段都由AI处理,甚至微调,最后整合成最终结果。
3.自适应反馈循环:系统会记录每次审查的结果及其在实际开发中的采纳情况,并用这些数据来不断优化 AI 模型和审查策略。
4.事件驱动架构:系统采用事件驱动模型,通过简单的开发,就能无缝接入gitlab,CLI,git hooks等。最终使用起来非常简单,以下是一个 GitLab Webhooks 服务的示例:
可以看出这个系统能结合commit信息得到一个不错的review结果。同时可以做到和普通服务一样,无缝集成到现有系统中,提供系统的易用性。
04. 挑战与解决方案
在开发的过程中,我们也遇到了很多的问题:
1. 可靠性与"幻觉"问题
正如前文所述,大语言模型在面对开放域问题时,很难完全消除"幻觉"的风险,这对于代码审查这样需要高度可靠性的任务来说,无疑是一大挑战。为了解决这个问题,我们在审查执行模块中加入了多重机制。
首先,我们通过Prompt去规范和约束返回结果。这里面有不少重点和技巧,比如引入Self-reflection,Few-shot learning等等,后续会单独文章介绍
### **风险评估标准**:
- **低风险**: 仅格式或样式问题,无需改动。
- **中等风险**: 性能问题或未采用最佳实践。
- **高风险**: 安全漏洞、逻辑错误或敏感信息泄露。
### **审查重点**:
1. 专注于代码中的潜在问题
2. **问题与建议**:
- 从四个角度评估:["性能问题", "安全漏洞", "逻辑错误", "代码依赖"]。仅在发现问题时提供反馈,否则返回空。
- 用中文列出具体问题,并提供详细的改进建议。
- 详细说明问题的原因、潜在影响,以及改进方法。
3. 如果代码更改涉及多行或非简单内容,请进行深入分析,以确保没有潜在问题被忽略。若有问题,请按以下格式返回数组,不要在反馈中提及文件名或风险等级。只有在经过全面分析后未发现任何问题时,才应返回空数组。
其次,我们在代码层面进行数据结构约束。理论上,返回格式也可以通过微调(fine-tuning)实现并获得稳定的输出,但本次不涉及微调,故不做讨论。
接着,我们引入另一个模型来审核这个模型生成的报告,从第三方角度评估报告的合理性
最后,我们结合这份抽样报告及原始的模型输入输出,进行人工评估,以排查潜在问题并确定后续优化方向。
2. 可扩展性与性能平衡
为了解决这个问题,我们采用了模块化和分层的架构设计。每个核心组件都有明确的职责边界,彼此之间通过标准化的接口进行交互。这不仅有利于未来的功能扩展,也能更好地控制系统的整体复杂度。
目前,我们利用异步和交互设计解决了响应等待的问题。随着系统复杂度的提升,我们计划引入向量数据库和缓存等技术,以应对可能出现的瓶颈。
3. 人机协作的界限
尽管Code Review Agent拥有一定的自主决策能力,但在某些关键决策点上,人类专家的判断仍然不可或缺。如何设计恰当的人机交互流程,有效融合人类和机器的优势,在现有条件下发挥最大效能,是我们一直在探索的方向。
以下是两个典型案例:
1. 我们在结果输出模块中加入了人工审核环节。如果所有结果都需要人工参与,显然无法达到AI提高效率的目的。因此,我们需要不断探索并制定策略,包括抽样审核、月度报告、季度报告以及报告指标的选取等。
2. "没有银弹"这句话也适用于现阶段的AI实践。因此,我们将产品定位从"替你code review"调整为"提前帮你提炼重点,提升code review效率"。这种调整从一开始就改变了用户的预期和使用流程,使得在使用率上有不小提升
4. 上下文理解与一致性
目前阶段,我们初步涉及了代码上下文。然而,实际应用中还可能涉及其他领域的上下文。例如,某段代码的实现可能与特定功能相关,看似不优雅的方案可能是因为特定功能(或历史原因)而做出的妥协。又或者,根据功能或业务需求,我们可能需要重点关注核心功能的review。
随着上下文的增多和领域的扩展,这一方面需要深入研究和探索。由于篇幅限制,我们将在后续文章中详细讨论这个话题。
总的来说,在大模型达不到“给你一个眼神你就懂了”的阶段,以上的挑战和实践将会持续并不断发展。
05. 总结
在开发Code Review Agent的过程中,我们获得了许多宝贵的经验和洞察。以下是我们的主要发现和未来展望:
TDD(测试驱动开发)的应用
由于大语言模型输出的高度不确定性,开发和测试面临着独特的挑战。在这种情况下,测试驱动开发(TDD)展现出了显著优势。通过为每个预期行为预先定义测试,我们能够建立清晰的模块输出标准(包括AI输出)。这种方法不仅提供了客观评估系统表现的基准,更重要的是,它为每次迭代后的系统稳定性评估提供了可靠机制。
promptfoo的引入
引入promptfoo工具是我们开发过程中的一个重要转折点。这个工具显著降低了Prompt Engineering的开发复杂度。它允许我们系统地测试、评估和优化各种prompt,大大提高了我们迭代和改进AI模型行为的效率。promptfoo不仅帮助我们更快地找到最优的prompt,还为我们提供了一种结构化的方法来管理和版本控制我们的prompts。上文提到的关于模型输出的UT也有promptfoo的参与。如有兴趣也可单独开一篇实践文章
下图是我们用于评估各模型之间区别的截图:
模块化设计的重要性
AI系统的复杂性和模型输出的不确定性为开发和调试带来了巨大挑战。在我们的项目中,最终结果的不准确可能源于输入处理、模型处理过程或输出解析等多个环节。这种多源不确定性大大增加了系统调试的难度。
采用了模块化和分层思想,我们将系统划分为独立的模块(如输入处理、上下文管理、代码分析等),使我们能够隔离和定位问题。这种设计使我们能够独立测试和优化各个组件,从而显著提高了问题定位的效率和准确性。
在实践中,模块化设计不仅提升了代码的可维护性,更重要的是,它为我们提供了一种系统化的方法来管理AI带来的不确定性。这种方法显著降低了整体系统的调试难度和复杂度。
对langchain的评价:复杂度高,谨慎使用
虽然langchain为我们提供了许多有用的工具和抽象,但我们也发现它带来了相当的复杂性。在某些情况下,langchain的抽象层反而增加了理解和调试的难度。我们建议在使用langchain时要谨慎,特别是在项目初期。可能的策略是先使用简单的自定义解决方案,只在确实需要langchain的特性时才引入它。
06. 未来展望和下一步计划
展望未来,我们计划进一步优化Code Review Agent的性能和准确性。具体计划包括:
1. 引入检索增强生成(RAG)技术,充分利用现有知识库,提高review结果的接受率。
2. 完善上下文管理系统,建立稳定的思维链和处理方案,以便将经验迁移到DevOps的其他流程中。
3. 为不同角色(团队)提供量身定制的Code Review服务和报告,满足各自的关注点。
4. 加强与现有开发工具链的整合,打造更流畅的用户体验。
-End-
作者丨深白
开发者问答
在AI应用开发中,你是否遇到过这种情况:一个demo半天就能跑通,但实际工程开发却需要半年时间?
欢迎在留言区分享你的见解~
转发本文至朋友圈并留言,即可参与下方抽奖⬇️
小编将抽取1位幸运的小伙伴获取扭扭龙+b站pu定制包
抽奖截止时间:02月14日12:00
如果喜欢本期内容的话,欢迎点个“在看”吧!
往期精彩指路
丨丨
丨丨
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……
还没有评论,来说两句吧...