当前位置:   article > 正文

使用 Amazon Bedrock + Claude 3 打造个性化智能编程助手

使用 Amazon Bedrock + Claude 3 打造个性化智能编程助手

最近,随着人工智能技术的迅速发展,代码助手已经成为软件开发领域备受关注的工具。像 Amazon CodeWhisperer 和 Github Copilot 这样的工具可以在集成开发环境中帮助用户自动生成代码,极大地提高了开发效率。然而,这些助手通常缺乏直接执行代码的能力,需要额外集成开发环境来执行代码。为了解决这一问题,OpenAI 在 ChatGPT 中推出了code interpreter,微软也开发了开源项目 AutoGen 来实现代码执行能力,类似的工具还包括开源项目 Open Interpreter。这些工具允许我们直接执行大型语言模型生成的代码,但是它们提供的的代码解释器主要以 Python 为主,对其他开发语言比如 Nodejs,Golang,Rust,PHP 支持得并不好。

亚马逊云科技开发者社区为开发者们提供全球的开发技术资源。这里有技术文档、开发案例、技术专栏、培训视频、活动与竞赛等。帮助中国开发者对接世界最前沿技术,观点,和项目,并将中国优秀开发者或技术推荐给全球云社区。如果你还没有关注/收藏,看到这里请一定不要匆匆划过,点这里让它成为你的技术宝库!

在日常工作中,我们发现许多客户经常需要使用 Amazon SDK 进行开发,通常涉及多种编程语言如 Go、Node.js 和 Rust,为此我们创建了”Bedrock-claude-codecoach”开源项目(项目链接:https://github.com/aws-samples/bedrock-claude-codecoach?trk=cndc-detail ),它是使用 Amazon Bedrock、Piston、LangChainJS 和 NextJS 开发的编程助手和代码解释器,支持 Anthropic Claude 2/2.1,Claude 3 和开源 Mistral 7B、Mixtral 8x7B 等模型。通过这个开源项目,我们旨在提供一个开箱即用的 Bedrock 编程助手,可以快速帮助用户进行 Amazon SDK 代码开发和调试。另外,我们还内置了一个提示词编辑工具,方便那些初次接触 Amazon Bedrock 的提示词工程师进行提示词调试。

1. 架构介绍

CodeCoach 架构图如下:

1.png

本项目最大的价值是提供方便快捷的部署方案,以较高的准确度生成代码并提供执行代码的各种运行环境。因此在项目中我们使用了 Amazon Bedrock 服务进行代码的生成。

Amazon Bedrock 是一个 MaaS 的平台,提供了各种业界翘楚的商业化模型(如:Athropic 的 Claude)和开源模型(如:Llama/Mistral),最大程度地减少了客户选择和使用基础模型的试错成本和时间周期,并且 Bedrock 上的基础模型会不断迭代更新,确保客户能始终 enable 最新最强的模型的功能,如最近新上架的 Athropic Claude v3 的模型,在各项指标评测中赶超 GPT-4,并且增加了多模态 VQA,Function Calling 等最新功能。

在本项目中,我们采用了 Bedrock 上的 Claude 3 和 Mistral 7B 模型进行代码生成,经测试其在各项代码生成中稳定性和功能均表现突出;用户验证信息和提示词模版等数据存储在 DynamoDB 中,支持多人访问; 此外项目还集成了 Piston 多语言代码执行引擎,通过 Piston 我们可以打造自己开源的 code interpreter,目前 Piston 支持 30 多种执行环境,在 CodeCoach 里面我们选择了常见的几种开发语言:Python,Golang,JavaScript/TypeScript,PHP 和 Rust,并且已经预集成了Amazon SDK 可以直接使用;同时为了简化部署过程,我们提供了 CloudFormation 一键部署模版(cf-template.yaml) 。

2. 部署项目

CodeCoach 已经提供了 CloudFormation 模版(https://github.com/aws-samples/bedrock-claude-codecoach/blob/main/cf-template.yaml?trk=cndc-detail ),可以直接下载到本地后进行部署。

方法一、通过 CloudFormation 服务控制界面进行部署

首先请登陆 亚马逊云科技(海外)控制台,进入 CloudFormation 界面,点击创建 Stack

2.png

选择上传模版文件, 上传 cf-template.yaml 模版

3.png

设置 EC2 密钥对的名字,这样就可以直接登陆到 EC2 进行调试

4.png

5.png

方法二、使用命令行工具部署 CodeCoach
  1. #请替换<你的 KeyPair 名字> 为实际使用的 Key Pair 名字
  2. aws cloudformation create-stack --region us-west-2 \
  3. --stack-name codecoach \
  4. --template-body file://cf-template.yaml \
  5. --parameters ParameterKey=SSHKeyName,ParameterValue=<你的 KeyPair 名字> \
  6. --capabilities CAPABILITY_IAM

CloudFormation Stack 创建完成后会输出 Cloudfront 地址(下图红框提示处即访问地址),直接访问即可。

6.png

访问该网页点击 Get Start 即可登录,初始用户密码( admin@demo.com/123456!@# ),首次登录后请点击右上角设置(齿轮图标),立即修改用户密码。

7.png

8.png

选择助手,点击模型下拉列表,选择合适的模型然后点击测试,测试通过后点击保存,项目默认是 Claude2,推荐使用最新的 Claude3 Sonnet。

9.png

3. CodeCoach 功能介绍

3.1 使用 Amazon SDK 进行程序开发

我们在日常工作中可能会需要使用各种语言的 Amazon SDK 代码,比如 Python(boto3),Golang,JavaScript/TypeScript,PHP,Rust 等。下面我们就以 Python(boto3)/Golang 为例来演示如何使用 CodeCoach 进行 Amazon SDK 开发。

3.1.1 使用 boto3 进行开发,打印某个区域的 EC2 实例列表,并可以返回运行状态

首先我们输入“使用 boto3 列出 us-east-1 的 ec2 实例”,然后等待 CodeCoach 返回,点击执行。

10.png

UI 就会调出代码编辑界面,在这个界面点击执行即可,在 output 显示框我们可以看见 Amazon SDK 的返回结果,如果代码有问题,会出现“fix”按钮,我们可以要求 CodeCoach 修复错误,并进行说明。

11.png

3.1.2 使用亚马逊 Golang SDK 打印 S3 桶列表

输入“使用 Amazon Golang SDK 编写一个 list s3 bucket 代码”,点击执行,因为 Golang 需要编译,所以执行的时候需要比 Python 更久的时间,点击后请耐心等待。

12.png

13.png

3.2 错误修复

如果生成的代码出现了错误,我们可以直接点击“修复助手”,CodeCoach 会调用大语言模型给出修复方法和说明。

14.png

15.png

可以看到 Claude3 不仅帮助我们纠正了错误,而且还给出了关于错误的解释:返回值“没有 Instances”这个 Key,需要使用 “Reservations”。

3.3 提示词模版工具

CodeCoach 支持自定义提示词,直接打开提示词,可以编写加载自己的提示词,我们分别使用 Claude2,Claude3 来编写一个翻译助手。

3.3.1 编写 Claude3 提示词

Claude3 不再需要 Human,Assistant 限定格式,可以直接进行编写,可以参考 Anthropic Claude Message API 文档, 下面我们以 Claude3 为例编写一个翻译助手。

1)不使用 System Role,直接在用户提示词里面编写任务

  1. 你是一个中英文翻译专家,你先要判断我的问题是中文还是英文
  2. 1.如果是英文请转换成中文
  3. 2.如果是中文请转换成英文
  4. 这里是我的问题:
  5. {query}
  6. 请返回 {"input": "question", "output":"你的答案“}

16.png

点击提交,Claude3 会返回{“input”: “我想买一辆车.”, “output”: “I want to buy a car.”} ,完全符合我们的预期。

后台查看 Message API 格式如下:

18.png

2)使用 Claude3 的 System Role,我们将原始提示词里面任务的定义填写到 Claude3 System Role 里面

18.png

注意,这里整个 payload 就多了一个新 key “system”,这个就是 Claude3 最新的 Message API 定义 system role 的方法。

19.png

3.3.2 编写 Claude2 提示词,Human,Assistant 分别代表了输入和模型的输出
  1. Human: 你是一个中英文翻译专家,你先要判断我的问题是中文还是英文
  2. 1.如果是英文请转换成中文
  3. 2.如果是中文请转换成英文
  4. 这里是我的问题:
  5. {query}
  6. 请返回 {"input": "question", "output":"你的答案“}
  7. Assistant:

20.png

Mistral 7B 使用方式类似,在这里我们就不再赘述了。

其次在提示词调试工具里面我们可以定义任意的{变量}, 变量会自动生成文本输入框,通过 LangChainJS Prompt Template 进行加载,点击提交即可测试你的提示词, 点击保存就可以将模版保存在 Amazon DynamoDB 中方便下次使用。

4. 如何自定义 Piston 执行环境

Piston 的开源地址为 https://github.com/engineer-man/piston?trk=cndc-detail ,目前已经集成了 112 种运行环境,参考 https://github.com/engineer-man/piston/releases/download/pkgs/index?trk=cndc-detail 。官方的 repo 中仅提供每种语言基础的运行环境,但是在实际的使用过程中,我们需要丰富运行环境,安装额外的依赖,例如我们需要为 python 环境增加 boto3 的 SDK,或者为 Bash 环境增加 Amazon CLI 等,因此我们需要在官方 repo 的基础上自定义 Piston 环境。

4.1 复制全量执行环境(可选)

为了拥有全量的执行环境,我们先将官方的 repo 中的所有运行环境迁移到自己的 github 仓库中,我们可以 Fork 官方 git 仓库之后通过以下脚本批量的迁移 Release 中发布的所有环境。

  1. #!/bin/bash
  2. repo="piston"
  3. # GitHub Token
  4. token="xxxxxxx"
  5. # github owner code
  6. owner="xxx"
  7. # Release
  8. release_name="Packages"
  9. # 本地附件目录
  10. attach_dir="/opt/pkgs"
  11. # ReleaseId
  12. release_id=""
  13. upload_assets(){
  14. release_id=$1
  15. file=$2
  16. echo "-----"
  17. echo $release_id $file
  18. # 上传文件
  19. upload_url=$(curl -H "Authorization: token $token" \
  20. -H "Content-Type: application/gzip" \
  21. https://uploads.github.com/repos/$owner/$repo/releases/$release_id/assets?name=$(basename $file) \
  22. --data-binary @$file > /dev/null)
  23. # 添加为Release附件
  24. curl -X POST -s -H "Authorization: token $token" \
  25. -H "Content-Type: application/json" \
  26. -d '{"name":"'$(basename $file)'","url":"'$upload_url'"}' \
  27. https://api.github.com/repos/$owner/$repo/releases/$release_id/assets > /dev/null
  28. }
  29. release_to_github(){
  30. # 获取 release_id
  31. if [ "x"${release_id} == "x" ]; then
  32. # 判断release是否存在
  33. release_exist=$(curl -s -H "Authorization: token $token" https://api.github.com/repos/$owner/$repo/releases/tags/$release_name | jq '.id')
  34. if [ -z "$release_exist" ]; then
  35. # 不存在则创建
  36. release_id=$(curl -s -X POST -H "Authorization: token $token" \
  37. -d '{"tag_name":"'"$release_name"'","name":"'"$release_name"'"}' \
  38. https://api.github.com/repos/$owner/$repo/releases | jq '.id')
  39. else
  40. # 存在则直接使用
  41. release_id=$release_exist
  42. fi
  43. fi
  44. # 遍历附件目录上传文件
  45. for file in $attach_dir/*.tar.gz; do
  46. echo "release: $file"
  47. upload_assets $release_id $file
  48. done
  49. }
  50. download_from_source(){
  51. cd $attach_dir
  52. rm -fr index && curl -s -L https://github.com/engineer-man/piston/releases/download/pkgs/index -o index
  53. count=1
  54. while read line; do
  55. if [ "x"$line == "x" ];then
  56. continue
  57. fi
  58. # 分割CSV字段
  59. IFS=',' read -ra fields <<< "$line"
  60. # 下载文件
  61. url="${fields[3]}"
  62. echo "${count}:${url}"
  63. filename=$(basename $url)
  64. curl -s -L -o "$filename" "$url"
  65. release_to_github
  66. rm -fr $filename
  67. ((count++))
  68. done < ./index
  69. sed -i 's!https://github.com/engineer-man/piston/releases/download/pkgs/!https://github.com/yanjun-ios/piston/releases/download/Packages/!g' index
  70. # 合并index文件
  71. mv index index_1 && curl -s -L https://github.com/$owner/piston/releases/download/Packages/index -o index_2
  72. cat index_1 index_2 | sort | uniq > index
  73. echo "upload the index file !"
  74. # 上传 index 文件
  75. upload_assets $release_id $attach_dir/index
  76. }
  77. download_from_source
4.2 修改执行环境,重新构建执行环境的安装包

在 piston 的工程中的 piston/packages/ 目录下存放了所有执行环境的构建代码,每个执行环境主要包括 build.sh,environment,metadata.json,run,test 五个文件,其中 build.sh 中定义了执行环境的安装过程,environment 中定义了我们要暴露的环境变量,在自定义执行环境的时候我们需要修改这两个文件。

21.png

我们以 bash 执行环境中添加 awscli 为例,将 iston/packages/bash/5.2.0/build.sh 中内容改成下代码:

  1. #!/usr/bin/env bash
  2. # Put instructions to build your package in here
  3. PREFIX=$(realpath $(dirname $0))
  4. mkdir -p build
  5. cd build
  6. curl "https://ftp.gnu.org/gnu/bash/bash-5.2.tar.gz" -o bash.tar.gz
  7. tar xzf bash.tar.gz --strip-components=1
  8. # === autoconf based ===
  9. ./configure --prefix "$PREFIX"
  10. make -j$(nproc)
  11. make install -j$(nproc)
  12. cd ../
  13. rm -rf build
  14. # install aws cli
  15. PREFIX=$PWD
  16. curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64-2.0.30.zip" -o "awscliv2.zip"
  17. unzip awscliv2.zip
  18. ./aws/install -i $PREFIX/aws-cli -b $PREFIX/bin
  19. rm -fr awscliv2.zip
  20. rm -fr ./aws

在 environment 中添加 awscli 的环境变量

  1. #!/usr/bin/env bash
  2. # Put 'export' statements here for environment variables
  3. export PATH=$PWD/bin:$PATH
  4. export PATH=$PWD/aws-cli:$PATH

修改完执行环境,我们修改 piston/docker-compose.yaml 文件,从本地代码构建 docker 镜像,并启动容器

  1. services:
  2. api:
  3. build: api
  4. container_name: piston_api
  5. cap_add:
  6. - CAP_SYS_ADMIN
  7. restart: always
  8. ports:
  9. - 2000:2000
  10. volumes:
  11. - ./data/piston/packages:/piston/packages
  12. environment:
  13. - PISTON_REPO_URL=https://github.com/@owner/piston/releases/download/Packages/index
  14. - PISTON_DISABLE_NETWORKING=false
  15. - PISTON_RUN_TIMEOUT=300000
  16. - PISTON_OUTPUT_MAX_SIZE=102400
  17. tmpfs:
  18. - /piston/jobs:exec,uid=1000,gid=1000,mode=711

启动并进入容器中,构建自定义执行环境

  1. docker compose up -d
  2. docker exec -it piston_repo bash
  3. cd piston/repo/
  4. # 执行构建命令
  5. sh build_package.sh bash=5.2.0
  6. # 将构建好的安装包发布到github
  7. sh build_package.sh release

其中 build_package.sh 代码如下:

  1. #!/bin/bash
  2. #set -x
  3. # usage: sh build_package.sh python or sh build_package.sh python=2.7.18
  4. repo="piston"
  5. # GitHub Token
  6. token="xxxxx"
  7. # github owner code
  8. owner="xxxx"
  9. # Release
  10. release_name="Packages"
  11. # 本地附件目录
  12. attach_dir="/piston/repo/"
  13. # ReleaseId
  14. release_id=""
  15. all_assets=()
  16. upload_assets(){
  17. release_id=$1
  18. file=$2
  19. echo "-----"
  20. echo "Upload File, Release Id: $release_id file name : $file"
  21. # 上传文件
  22. upload_url=$(curl -H "Authorization: token $token" \
  23. -H "Content-Type: application/gzip" \
  24. https://uploads.github.com/repos/$owner/$repo/releases/$release_id/assets?name=$(basename $file) \
  25. --data-binary @$file > /dev/null)
  26. # 添加为Release附件
  27. curl -X POST -s -H "Authorization: token $token" \
  28. -H "Content-Type: application/json" \
  29. -d '{"name":"'$(basename $file)'","url":"'$upload_url'"}' \
  30. https://api.github.com/repos/$owner/$repo/releases/$release_id/assets > /dev/null
  31. }
  32. # 获取所有的asset
  33. get_all_assets() {
  34. page=1
  35. #release_id=128663575
  36. assets_url="https://api.github.com/repos/$owner/$repo/releases/${release_id}/assets?per_page=100"
  37. while :
  38. do
  39. local assets
  40. assets=$(curl -fsS -H "Authorization: token ${token}" "${assets_url}&page=${page}" | jq -r '.[] | "\(.name)-\(.id)"')
  41. name=$(echo ${assets[@]}| grep "pkg.tar.gz")
  42. # result=$(echo $name | grep "=")
  43. if [ $? -ne 0 ];then
  44. break;
  45. fi
  46. # 将当前页面的assets添加到数组中
  47. #mapfile -t assets < <(echo "$assets")
  48. for i in ${assets[*]}
  49. do
  50. # echo "this is i: "$i
  51. all_assets[${#all_assets[*]}]=${i}
  52. done
  53. ((page++))
  54. done
  55. echo "全量数组长度:${#all_assets[@]}"
  56. }
  57. # 根据文件名删除附件
  58. delete_release_asset(){
  59. release_id=$1
  60. file_name=$2
  61. if [ ${#all_assets[@]} -eq 0 ]; then
  62. echo "assets 为空,请求全量assets"
  63. get_all_assets
  64. fi
  65. asset_id=""
  66. for element in "${all_assets[@]}"; do
  67. if [[ $element == *"$file_name"* ]]; then
  68. asset_id=$(echo $element | awk -F '-' '{print $NF}')
  69. break
  70. fi
  71. done
  72. echo "Delete File, file Name : $file_name asset_id :$asset_id"
  73. if [ "$asset_id" == "" ]; then
  74. echo "Delete failed, No asset found with filename: $filename"
  75. return 1
  76. fi
  77. asset_url="https://api.github.com/repos/$owner/${repo}/releases/assets/${asset_id}"
  78. response=$(curl -s -X DELETE -H "Authorization: token ${token}" ${asset_url})
  79. if echo "$response" | grep -q '204 No Content'; then
  80. echo "Failed to delete asset: $response"
  81. else
  82. echo "Asset deleted successfully"
  83. fi
  84. }
  85. # 发布到github release
  86. release_to_github(){
  87. cd $attach_dir
  88. if [ ! -f *.tar.gz ];then
  89. echo "there is no packages to be released , exit 1"
  90. exit 1
  91. fi
  92. # 获取 release_id
  93. if [ "x"${release_id} == "x" ]; then
  94. # 判断release是否存在
  95. release_exist=$(curl -s -H "Authorization: token $token" https://api.github.com/repos/$owner/$repo/releases/tags/$release_name | jq '.id')
  96. if [ -z "$release_exist" ]; then
  97. # 不存在则创建
  98. release_id=$(curl -s -X POST -H "Authorization: token $token" \
  99. -d '{"tag_name":"'"$release_name"'","name":"'"$release_name"'"}' \
  100. https://api.github.com/repos/$owner/$repo/releases | jq '.id')
  101. else
  102. # 存在则直接使用
  103. release_id=$release_exist
  104. fi
  105. fi
  106. # 遍历 tar.gz 附件目录上传文件
  107. for file in *.tar.gz; do
  108. echo "release: $file"
  109. delete_release_asset $release_id $file
  110. upload_assets $release_id $file
  111. done
  112. # 合并index文件
  113. mv index index_1 && curl -s -L https://github.com/@owner/piston/releases/download/Packages/index -o index_2
  114. if [ $? -ne 0 ];then
  115. echo "download index file failed,exit 1"
  116. exit 1
  117. fi
  118. for file in *.tar.gz; do
  119. sed -i "/${file}$/d" index_2
  120. done
  121. cat index_1 index_2 | sort | uniq > index
  122. echo "upload the index file !"
  123. # 上传 index 文件
  124. delete_release_asset $release_id index
  125. upload_assets $release_id index
  126. }
  127. # 构建安装包
  128. build_package(){
  129. cd /piston/packages
  130. echo "build packages from args..."
  131. for pkg in "$@"
  132. do
  133. shift
  134. if [ ! -d `echo $pkg | awk -F'=' '{print $1}'` ];then
  135. echo "Packages not found for $pkg"
  136. continue
  137. fi
  138. result=$(echo $pkg | grep "=")
  139. if [ $? -eq 0 ];then
  140. echo "install $pkg"
  141. pkgname=$(echo ${pkg/=/-})
  142. echo $pkgname
  143. make -j16 $pkgname.pkg.tar.gz PLATFORM=docker-debian
  144. else
  145. if [ -d "$pkg" ];then
  146. echo "install all version for $pkg"
  147. for version in $pkg/*;do
  148. version=$(echo $version | awk -F '/' '{print $2}')
  149. pkgname=${pkg}"-"${version}
  150. echo $pkgname
  151. make -j16 $pkgname.pkg.tar.gz PLATFORM=docker-debian
  152. done
  153. fi
  154. fi
  155. done
  156. if [ ! -f *.tar.gz ];then
  157. echo "there is no packages to be released , exit 1"
  158. exit 1
  159. fi
  160. cd /piston/repo
  161. echo "Creating index"
  162. ./mkindex.sh
  163. echo "Index created"
  164. }
  165. if [ $1 == "release" ];then
  166. release_to_github
  167. else
  168. build_package $@
  169. # release_to_github
  170. fi

至此,我们已经在自己的 github 中拥有一个完全独立的 piston 的 repo,在使用时,我们只需要进入 CloudFormation 启动的 EC2,修改/root/bedrock-claude-codecoach/docker-compose.yaml 文件,通过环境变量的方式指定我们自定义 piston repo 中的 index 文件,重新执行 init.sh 即可。docker-compose 示例如下图:

image.png

5. 总结

我们可以通过 Amazon Bedrock 和 Claude 3,Mistral 7B 打造自己的代码助手,同时通过扩展 Piston 提供自定义安全可靠的执行环境,并且始终保持整个数据访问限定在企业内部,满足数据合规要求。项目后续考虑加入 Agent 和文档检索增强,利用 Amazon SDK 文档进一步提高 CodeCoach 的代码正确率,通过 Agent 来实现自动化测试。

附录

  1. Anthropic’s Claude on Amazon Bedrock

  2. Mistral AI on Amazon Bedrock

  3. Piston

文章来源:使用 Amazon Bedrock + Claude 3 打造个性化智能编程助手

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/339614
推荐阅读
相关标签
  

闽ICP备14008679号