作为一名普通的开发者,我总觉得在 GitHub 上寻找问题答案的过程特别低效。每次遇到技术问题,要么在搜索引擎里翻找半天,要么在 GitHub Issues 里来回切换查看同类问题。最无奈的是,即使提了新 Issue,等待项目维护者回复的过程也异常煎熬 ------ 快则半天,慢则好几天。这种体验,想必很多开发者都深有体会。
2023 年是我的" AI 元年"。像很多开发者一样,我开始大量使用 ChatGPT 来解决日常开发问题。它确实很神奇------通过不断调教 prompt,基本能得到我想要的答案。但随着依赖度越来越高,ChatGPT 的局限性也逐渐显露:它经常会自信满满地"胡说八道",尤其在涉及最新开源项目或特定框架问题时。更让我困扰的是,在 GitHub上做代码审查或参与 Issue 讨论时,ChatGPT 完全帮不上忙 ------ 它对项目的具体上下文一无所知。
和你一样,我很自然地冒出一个想法:如果能给每个 GitHub 仓库配备一个" AI 管家"该多好。想象一下,当你遇到问题时,有一个了解仓库所有上下文的AI助手,随时待命解答你的问题。它不是那种通用型的 ChatGPT,而是专门为这个仓库"定制"的智能助手------了解代码库的每个角落,熟悉所有历史issue的来龙去脉。于是,我开始着手实现这个想法...
打造 GitHub Assistant 原型
研究现有方案时,我发现已经有不少人在探索扩展大模型服务的边界。例如 GPTs 技术能让大模型调用函数去获取某些私有的、实时性高的数据。Software Copilot 等产品实现了对话式人机交互的复杂系统,让**智能体(Agent)**的概念深入人心。通过了解背后的实现,其实打造 Agent 比想象的更加简单:将大模型服务作为核心,结合若干GitHub 相关工具,我们就能得到一个简单、可靠的 GitHub Agent 。
已经迫不及待想实现一个简单的原型机来验证这个事情的可行性。我先准备好了 ChatGPT 账号用来获取大模型服务,同时也准备好了 Python 开发环境。参考一些社区的实践,决定借助 LangChain 的一些工具方法进行 Agent 构建。
按照上图中的设计,首先我们需要有一个稳定的大模型中心,它可以直接提供大模型服务,也可以获取其挂载工具的能力。为了让大模型能根据用户问题,调用合适的工具,输出正确的答案,我们的系统 Prompt 非常重要。我先给大模型一个角色定位,让它扮演一名仓库助手的角色。
md
You are a skilled assistant dedicated to {repo\_name}, capable of delivering comprehensive insights and solutions pertaining to {repo\_name}.
You excel in fixing code issues correlated with {repo\_name}.
由于系统 Prompt 在大模型推理过程中占据着非常重要的位置,我可以通过提示词明确地赋予它技能,使 Agent 在每次处理用户输入信息时都能应用这些技能。
在实践过程中发现,这些技能点不能太细碎,不然会让大模型的能力受限。也不能太宽泛,否则无法使用合适的工具输出想要的结果。经过考虑,我先赋予它与用户互动的能力
md
### Skill 1: Engaging Interaction
Your primary role involves engaging with users, offering them in-depth responses to their {repo_name} inquiries in a conversational fashion.
然后希望它使用工具在互联网 、仓库内搜素仓库相关信息,拓宽回答的信息来源,提升信息的相关性、准确性。
vbnet
### Skill 2: Insightful Information Search
For queries that touch upon unfamiliar zones, you are equipped with two powerful knowledge lookup tools, used to gather necessary details:
* search_knowledge: This is your initial resource for queries concerning ambiguous topics about {repo_name}. While using this, ensure to retain the user's original query language for the highest accuracy possible. Therefore, a specific question like '{repo_name} 的特性是什么?' should be searched as '{repo_name} 的特性是什么?'.
* tavily_search_results_json: Should search_knowledge fail to accommodate the required facts, this tool would be the next step.
* search_repo: This tool is used to retrieve basic information about a GitHub repository, including star count, fork count, and commit count.
最后让 Agent 可以自行决定是输出合适答案或者引导用户提交 Issue 。
vbnet
### Skill 3: Expert Issue Solver
In case of specific issues reported by users, you are to aid them using a selection of bespoke tools, curated as per the issue nature and prescribed steps. The common instances cater to:
* Routine engagement with the user.
* Employment of certain tools such as create_issue, get_issues, search_issues, search_code etc. When the user is facing a specific hurdle.
如果直接询问大模型某个仓库的 Star 数量,大模型一般做不到直接输出数量。但为了让 GitHub Assistant 能准确回答该信息,我可以制作一个工具,让大模型能参考该工具的返回信息,输出正确的答案。说是工具,实际上是一段函数:
python
from github import Github
g = Github()
def (repo_name):
"""
Get basic information of a GitHub repository including star count, fork count, and commit count.
:param repo_name: Name of the repository in the format 'owner/repo'
:return: A object with basic repo information.
"""
repo_detail_info = g.get_repo(repo_name)
return json.dumps({**repo_detail_info})
在准备好提示词 、大模型服务 、GitHub 相关工具之后,借助 LangChain 中的 AgentExecutor 等方法将它们组合就得到了一个略显简单的 GitHub Assistant 原型 (关于大模型如何在代码中真正与工具函数交互的代码可移步 LangChain )。在使用 Agent 时,将询问 GitHub Assistant 的信息转换成符合 OpenAI 接口规范的消息格式,然后再交给 GitHub Assistant 执行,最后就可以得到对应的对话结果。对实现细节感兴趣的读者可以点击 链接 前往 GitHub 仓库内查看。
更实用的 GitHub Assistant
有了基础原型后,是时候让它在实际场景中发挥作用了。得益于 GitHub 平台提供的 webhook 能力,当开发者在仓库内的一些常见行为,例如提交代码、提交 Issue ,都会发送消息给注册的 webhook 地址。于是我仅需提供一个 HTTP 接口,就能让 GitHub 和 GitHub Assistant 原型建立连接。对于建立连接之后的 GitHub Assistant,我期望它能具备下面两个关键能力:
- 代码审核: 代码时,它会阅读我的代码,并尝试检查代码是否符合规范,甚至检查潜在的 BUG。
- 回复 Issue : 任意用户在仓库内提交 Issue 后,它能根据 Issue 的标题、内容尝试解答,也可以参与到 Issue 讨论去。
这两个能力相对独立,且都是难度不低的任务,我打算采取分工合作的模式,基于 GitHub Assistant 原型制作两个不同的 Agent,它们可以根据自己的专业知识和技能来承担相应的任务,从而提高工作质量。
Pull Request (PR) 评审功能
在用户向仓库提交代码时,GitHub 会通过 WebHook 携带相关信息通知 GitHub Assistant。此时 GitHub Assistant 可判断用户需要 PR 评审功能 Agent 。
PR 评审 Agent 在结构上和 GitHub Assistant 原型基本相同。首先我赋予它专业的代码审查员的身份,要求其在代码的功能性、逻辑错误、安全漏洞、主要的性能问题四个方向来进行审核。
sql
# Character Description
You are an experienced Code Reviewer, specializing in identifying critical functional issues, logical errors, vulnerabilities, and major performance problems in Pull Requests (PRs).
为了 PR 评审 Agent 能像真正的工程师一样审核代码,我希望它即可以对 PR 内容进行宏观总结,还能对代码进行细致评论。为此我先准备了两个输出内容到 GitHub 仓库的工具:create_pr_summary 工具让 Agent 在 PR 的评论区输出一段话;create_review_comment 工具让 Agent 在代码 Commit 的某一行提出评论意见。这两个工具分别对应了 PR 评审 Agent 应该对完整 PR 进行总结、对具体代码进行评审的两个具体任务。
less
You are an AI Assistant specialized in reviewing pull requests with a focus on critical issues.
You are equipped with two tools to leave a summary and code review comments:
* create_pr_summary: Used to create a summary of the PR.
* create_review_comment: Used to leave a review comment on specific files.
审核代码的工作需要严谨,认真。借鉴思维链的思想,Prompt 中可以制定具体的任务实现方法以达到更好的效果。
任务一:PR 总结
在 PR 总结的任务中,强调了总结内容要遵行具体的 markdown 格式,且需要较少的字数以求精简。总结内容使用 create_pr_summary
具输出到 GitHub PR 评论区。
vbnet
## Task 1: Summarize the Pull Request
Using `create_pr_summary` tool to create PR summary.
Provider your response in markdown with the following content. follow the user's language.
* **Walkthrough**: A high-level summary of the overall change instead of specific files within 80 words.
* **Changes**: A markdown table of files and their summaries. Group files with similar changes together into a single row to save space.
下面是它在真实仓库中的表现:
任务二:代码逐行审核
相比于 PR 总结,对用户代码的审核就显得非常有挑战。在正式让 Agent 进行审核前,首先我需要允许用户主动跳过代码评审任务,并从机制上忽略了对草稿版本代码的评审。
sql
Skip Task Whitelist
**SKIP\_KEYWORDS**: A list of keywords. If any of these keywords are present in the PR title or description, the corresponding task will be skipped.
* xamples: "skip", "ignore", "wip", "merge", "\[skip ci]"
* If the draft flag is set to true, the task should be skipped.
然后做出具体的指示,强调在关注在一些逻辑、功能性的变更上,忽略代码格式的变更。
csharp
Review the diff for significant errors in the updated files. Focus exclusively on logical, functional issues, or security vulnerabilities. Avoid comments on stylistic changes, minor refactors, or insignificant issues.
最后更重要的是让 Agent 的审核意见准确的评论在具体的代码位置上。
sql
### Specific instructions:
* Take into account that you don't have access to the full code but only the code diff.
* Only comment on code that introduces potential functional or security errors.
* If no critical issues are found in the changes, do not provide any comments.
不同于人类可以使用双眼看到 GitHub 提供的代码审核界面,为了能让 GitHub Assistant 能够进行代码评审,我需要将 PR 中代码的前后变化以更友好的形式,而不是 GitHub 提供的以文件形式存在的二进制码。
代码文件内容丰富多样,而一些文件的评审意义不大,而有的文件可读性较差超出了大模型的能力范围,我根据 diff 文件的路径忽略了一些特殊文件,例如构建产物文件夹里的内容、图片、项目配置文件等。
为了让大模型能够精确定位到具体到某一行,我会使用代码工具处理文件内每一行内容,为其标注行号,区分是删除代码、还是新增代码。
在完成这些操作后,我会将处理后的内容聚合起来以用户消息的形式交给 Agent,而不是放在系统提示词里。当然我会在提示词里声明我已经将代码处理成了以下格式,这是大模型能够正确理解代码变更,然后进行代码评审的前提。
css
### Input format
* The input format follows Github diff format with addition and subtraction of code.
* The + sign means that code has been added.
* The - sign means that code has been removed.
最终 PR 评审 Agent 将根据提示词的要求和信息,针对具体行数的代码给出评审意见,并调用 create_review_comment
在 PR 指定代码行数给出评论:
其它优化 Tips
让大模型输出内容很简单,但让大模型不说话却很困难,所以我允许用户通过 PR 的标题或者描述跳过代码审核任务。
sql
# Skip Task Whitelist
**SKIP_KEYWORDS**: A list of keywords. If any of these keywords are present in the PR title or description, the corresponding task will be skipped.
* Examples: "skip", "ignore", "wip", "merge", "\[skip ci]"
* If the draft flag is set to true, the task should be skipped.
对于一些特殊的边界情况,我在提示词的最后面通过约束和补充说明的形式添加了一些补丁。主要是让 Agent 能专注于新增和变动的代码部分,同时避免对微小的风格不一致、格式问题或不影响功能的更改发表评论。因为 GitHub 是一个国际化的平台,需要注意输出的语言和 PR title、comments 一致。
markdown
# Constraints
* Strictly avoid commenting on minor style inconsistencies, formatting issues, or changes that do not impact functionality.
* Do not review files outside of the modified changeset (i.e., if a file has no diffs, it should not be reviewed).
* Only flag code changes that introduce serious problems (logical errors, security vulnerabilities, typo or functionality-breaking bugs).
* Respect the language of the PR's title and description when providing summaries and comments (e.g., English or Chinese).
Issue 处理与讨论
与 PR 评审功能类似,GitHub Assistant 也能响应 GitHub 平台抛出的 issues、issue_comment 事件。在 Issue 处理 Agent 内与原型 Agent 实现类似,主要期望 Agent 能灵活使用提供的工具,例如开放互联网信息检索、仓库 Issue 信息检索工具。在一些工具的使用细节上进行了描述,强化工具使用的效果。
kotlin
* if the found issue_number is the same as this issue_number: {issue_number}, it means no similar issues were found, You don't need to mention the issue again.
* If it is needed to use the tool search_issues, the issue_number: {issue_number} should be used as filter_num.
相比 GitHub Assistant 原型,在处理 Issue Agent 内,再次强调了对事实性的尊重,要严肃回答用户的问题,不能不懂装懂。
vbnet
* If you don't have any useful conclusions, use your own knowledge to assist the user as much as possible, but do not fabricate facts.
* Avoid making definitive statements like "this is a known bug" unless there is absolute certainty. Such irresponsible assumptions can be misleading.
下面是在开源仓库内机器人对 Issue 处理的效果:
更可靠的 GitHub Assistant
经过努力,我已经让 GitHub Assistant 原型和 GitHub 无缝对接上了。但在实际使用中却发现它不太聪明:在回复 Issue 时,看起来只是换了个地方调用大模型的服务,这并没有解决大模型本身的缺点。我希望它能更加了解仓库的文档、代码内容,能掌握仓库最新的代码变更,能从历史的 Issue 讨论中总结解决方案。
经过调研,我发现解决这个问题有两种主流方案:一种是微调,还有就是通过 RAG(Retrieval-Augmented Generation)的思路。因为微调将数据与原来的大模型服务训练后结合在一起,需要不小的资源开销,并不适合代码仓库这类变动频繁的场景。而 RAG 方案看起来则轻量了许多。
RAG 核心流程图
为了接入 RAG 的能力,我需要做两步操作。第一步:将某个仓库内的有用内容,包括代码、历史 Issue 进行向量化,然后存储到一个向量数据库内。第二步:提供知识检索工具,该工具能够使用向量化检索的方式,召回与问题相关的内容。召回结果将传递给大模型 ,让 GitHub Assistant 输出更准确、时效性更高的内容。
第一步:仓库向量化
因为代码仓库内文件可能会很多,而且存在不同的文件类型,所以不适合简单的将仓库内容交给向量化模型直接进行向量化转换。我们以文件为最小粒度,以递归的方式遍历仓库内每个文件,为他们建立一个向量化任务。为了让这个过程不堵塞机器的创建,我引入了 AWS Lambda Function 将向量化任务拆解成一个个异步任务。
仓库内容向量化
Github 文件下载
开发者可以使用 Github 提供的开放 API 获取某个仓库指定路径下的文件内容。PeterCat 通过对应 API 下载了仓库内指定路径的文件。
python
repo = github.get_repo(repoinfo)
file_content = repo.get_contents(path, ref=commit_id)
file_sha = file_content.sha
return base64.b64decode(file_content.content).decode("utf-8")
在执行文本向量化之前,我根据文件的 sha 与向量数据库中的数据做了重复检查,如果数据库中已经有相同文件的 sha 值,将不在进行向量化操作。同时因为代码仓库中,除了代码文件之外,大部分有用的信息都以 ** Markdown** 的格式存在,例如 README.md 。为了减少无效信息的感染,忽略了 Markdown 格式以外的文件。
此外仓库内**历史 Issue **信息也存在极大的价值,而这部分正好是 GPT 等大模型会忽略的地方。并非所有 Issue 都值得入库,低质量的内容会降低 RAG 召回内容的效果 。因此我筛选了 Issue 讨论度高且处于关闭状态的 Issue 进行入库。
文本向量化
在获取到仓库内的 Markdown 和优质的 Issue 内容后,需要将其进行向量化。向量化过程中,因为输入长度的限制,需要将长文本按照 CHUNK_SIZE 的长度进行切分。当文本被分割成单独的块时,单独处理每个块可能会导致失去在块之间传递的重要上下文信息。通过在块之间添加重叠区域(CHUNK_OVERLAP),确保一些重要的上下文信息能够在不同块中共享,从而减小了块之间的割裂感。通过引入重叠,可以减小这种边界效应的影响,使 RAG 算法对临界信息的捕捉更为准确。
python
from langchain_text_splitters import CharacterTextSplitter
text_splitter = CharacterTextSplitter(
chunk_size=CHUNK_SIZE, chunk_overlap=CHUNK_OVERLAP
)
docs = text_splitter.split_documents(documents)
可以将切分后的文本使用 OpenAI 提供的向量模型进行向量化操作。然后使用 Supabase 数据库存储向量化之后的结果。
第二步:内容召回
当用户与 GitHub Assistant 对话时,可以根据用户的输入进行检索,找出仓库内与输入相关的内容。在检索时我们仍然需要将用户的输入内容向量化,然后与向量数据库进行匹配。为此我们需要在 Supabase 中创建一个根据 embedding 检索的 Function,类似如下:
plain
begin
return query
select
id,
content,
1 - (rag_docs.embedding <=> query_embedding
) as similarity
from rag_docs
order by rag_docs.embedding <=> query_embedding;
end;
向量检索出的内容并不总和用户输入相关,因此检索出的文本内容需要交给大模型理解,最后输出更符合用户需要的内容。正因为我们独一无二的聚焦性,相比大模型直接输出,GitHub Assistant 能输出更专业的回答。
升级为 GitHub Assistant 工厂
随着原型逐渐成熟,我开始思考如何让更多开源项目受益。为了实现每个 GitHub 仓库都有专属智能机器人的梦想,我需要打造一个 GitHub Assistant 工厂。用户将仓库信息提供给该工厂,工厂就能生产出仓库专属的 GItHub Assistant。
建设机器人工厂需要更多的支持,幸好我身处的组织蚂蚁集团非常鼓励开源活动,我所处的部门 AFX 有许多优秀的开源作品,例如 Ant Design、UMI、Mako... 于是我很快就找来了一些优秀的小伙伴一起来打造这个机器人工厂并计划开源,我们为这个项目申请了专属的名字 PeterCat 。
PeterCat 是一个全新的项目,可以自由的选择技术栈选型。考虑到我们将具备 GitHub APP、第三方门户网站、PeterCat 官网等多种产品形态,所以我们采用了前后端服务分离的架构,通过 HTTP 建立服务与产品之间的联系。
自由的创建机器人
创建 AI 助手的过程我们力求简单直观。用户仅需输入仓库地址,PeterCat 就会自动生成一个配置合理的助手 ------ 包括它的头像、名字,甚至是性格特点(也就是系统 prompt )。大多数情况下,这些默认设置已经够用了。
不过,我们还想让这个过程更有趣一点。于是开发了一个"助手的助手" ------ 一个专门帮你定制 GitHub Assistant 的向导。你可以直接跟它对话,告诉它你想要什么样的助手。比如"我想要一个专注于性能优化的助手",或者"帮我设置一个擅长新手指导的助手"。而且最棒的是,你说一句,右边的预览窗口就会实时显示变化,让你即时看到自己"创造"的成果。
它背后的实现原理与上文提及的 Agent 们类似,事实上这种简单、可组合的模式进行构建能起到很好的效果。
一键部署机器人到仓库
最后一步是帮助用户把 AI 助手部署到实际仓库。早期原型中,我们用 webhook 实现助手与仓库的连接。但要让这个过程对用户来说足够顺畅,还需要更完整的解决方案。
最终我们选择了开发一个 GitHub App 作为连接的桥梁。用户只需要像安装普通应用一样完成 GitHub App 的授权,PeterCat 就能自动获取到用户的仓库列表。接下来,用户只需要在 PeterCat 网站上船创建完成 GitHub Assistant 的部署环节从仓库列表中选择一个仓库,AI 助手就会正式入驻到仓库中。从此以后,无论是在 Issue 讨论区还是 PR 评审中,它都会积极参与其中。
总结与展望
从想法到落地,PeterCat 走过了一段有趣的旅程。2024年9月,我们在上海外滩大会上正式宣布项目开源。短短三个月,就获得了 700 多个star,有 178 个开源项目开始使用我们的 AI 助手。看到这些数字,最让我欣慰的不是增长速度,而是每一个被解决的实际问题。
比如在这个案例中,AI 助手在多轮对话中帮助用户解决了 Ant Design table 组件的使用问题。用户的反馈让我感觉这一切都是值得的 ------ 这正是我们最初想要达到的效果:让开发者能更轻松地使用和贡献开源项目。
回顾这个项目,最大的感触是开源社区的力量。正是站在了众多优秀开源项目的肩膀上,我们才能在短时间内将想法变成现实。LangChain、FastAPI、Supabase...这些开源技术不仅为项目提供了强大的支持,更让我看到了技术平民化的魅力 ------ 任何怀揣想法的开发者,都能通过开源社区接触到最前沿的技术实践。
这或许就是开源精神最打动人的地方:技术不该是少数人的特权,而是所有人共同进步的阶梯。如果你也有这样的体会,不妨加入社区,用代码写下自己的故事。
目前的PeterCat还很年轻,但已经展现出了令人期待的潜力。作为开发者,我已经规划好了下一阶段的成长路线:
- 首先是打破仓库间的隔阂:目前每个AI助手都是独立工作的,这多少有些"井底之蛙"。通过实现 multi-agent架构,让多个助手能够协同工作,互相参考对方的知识库,从而给出更全面的解答。
- 其次是提升代码理解能力:特别是在处理复杂的业务逻辑,或需要跨文件上下文的场景时,还有很大的提升空间。这需要在代码语义理解上下更多功夫。
- 第三是无缝集成到开发工具中:计划开发 VS Code 插件,让开发者不用切换窗口就能随时获得帮助,实现更自然的开发体验。
- 最后是赋予用户更多控制权:RAG 技术帮我们解决了不少问题,但用户才是领域专家。我们希望让用户能方便地管理和优化知识库,毕竟他们最了解项Ï目的需求。
欢迎与猫猫畅谈🐾
如果你也对这个项目感兴趣,欢迎来一起撸猫(不对,是一起写代码)!无论是贡献代码,还是仅仅给项目点个star,都是对这只 AI 猫咪最好的鼓励。
👉 在线体验:petercat.ai
🐱 项目地址:GitHub.com/petercat-ai...