经过了几节课的产品体验和源码学习后,终于要来到实践环节了。在本节课以及下一节课,我会带你参考 Dify Agent 的思路,用 Go 语言做一个零代码可配置的 API Agent 产品出来。
之前有同学问过我,这个课程为什么要用 Go 语言来做,做 AI 开发的主流语言不应该是 Python 吗?在这里,我做一下统一回答。
我们知道云原生应用开发的“母语”是 Go,而学习这个课程的同学大多数是做云原生开发,希望额外学习 AI 相关的知识提升自己的。因此,使用 Go 语言来做课程,会让大多数同学上手更加方便,理解起来也会更加容易。试想,如果原理真的理解了,能用自己最熟悉的语言零框架手撸出来了,那需要用到 Python 或者一些第三方框架,比如 LangChain 时,上手也会非常的快。
第二点是在云原生的某些场景下,无法使用 Python 语言进行开发,比如下一章节要给你讲解的云原生网关以及 wasm 技术,就暂时不支持 Python 语言。所以我在综合考虑后,决定全程用 Go 语言来做课程。在这里也希望你能够摆脱语言的束缚,重点学习原理以及套路,将来不管用到什么工具时都能够从容应对。
ENGLISH_REACT_COMPLETION_PROMPT_TEMPLATES = """Respond to the human as helpfully and accurately as possible. {{instruction}} You have access to the following tools: {{tools}} """
这里我只是截取了模板中的几行,你理解我要表达的意思即可,完整模板可以去上节课查看。在这个模板中, 和 作为占位符,让我们明确知道在模板中的哪个部分需要插入特定的信息。那么,使用 Go 语言能否实现类似的效果呢?答案是肯定的。
Go 语言本身提供了强大的模板引擎,text/template 包可以帮助我们动态填充模板内容。这使得我们可以在 Go 中轻松实现与 Python 类似的功能,动态生成所需的字符串输出。Go 的模板引擎不仅支持基础的字符串替换,还能处理复杂的逻辑和条件判断,从而让模板更加灵活和智能。
在 Go 中,我们可以这样定义一个类似的模板:
1 2 3 4 5 6 7 8 9
const EN_Template = ` Respond to the human as helpfully and accurately as possible.
// 定义模板数据 data := PromptData{ Instruction: "Provide a detailed explanation of how AI can improve cloud-native architectures.", Tools: "AI tools, Cloud-Native tools", }
requestBody: required: true content: application/json: schema: type: object required: - text - target_lang properties: text: $ref: '#/components/schemas/TranslationText' ... components: schemas: TranslationText: description: | Text to be translated. Only UTF-8-encoded plain text is supported. The parameter may be specified up to 50 times in a single request. Translations are returned in the same order as they are requested. type: array maxItems: 50 items: type: string example: Hello, World!
if requestBody, ok := operation["requestBody"].(map[string]interface{}); ok { if content, ok := requestBody["content"].(map[string]interface{}); ok { for _, contentType := range content { if bodySchema, ok := contentType.(map[string]interface{})["schema"].(map[string]interface{}); ok { required := bodySchema["required"].([]interface{}) properties := bodySchema["properties"].(map[string]interface{}) for name, prop := range properties { propMap := prop.(map[string]interface{}) // 处理引用,如果有 if ref, ok := propMap["$ref"].(string); ok { root := openAPI.Components["schemas"] segments := strings.Split(ref, "/")[1:]
// 如果参数包含 enum,则添加枚举值 if enum, ok := propMap["enum"].([]interface{}); ok { var enumValues []string for _, e := range enum { enumValues = append(enumValues, e.(string)) } toolParam.Enum = enumValues }
// 类型处理 if typ := getParameterType(propMap); typ != "" { toolParam.Type = typ }
... properties: text: TranslationText: description: | Text to be translated. Only UTF-8-encoded plain text is supported. The parameter may be specified up to 50 times in a single request. Translations are returned in the same order as they are requested. type: array ...
instruction: 你是一个精通多国语言的翻译专家,可以翻译任何文本。 apis: - apiProvider: apiKey: name: DeepL-Auth-Key value: xxxxxxxxxxxxxxxxxxx in: header api: | openapi: 3.1.0 info: title: DeepL API Documentation description: The DeepL API provides programmatic access to DeepL’s machine translation technology. version: v1.0.0 servers: - url: https://api-free.deepl.com/v2 paths: /translate: post: description: Request Translation operationId: translateText requestBody: required: true content: application/json: schema: type: object required: - text - target_lang properties: text: $ref: '#/components/schemas/TranslationText' target_lang: $ref: '#/components/schemas/LanguageCode' responses: '200': description: Successful response components: schemas: TranslationText: description: | Text to be translated. Only UTF-8-encoded plain text is supported. The parameter may be specified up to 50 times in a single request. Translations are returned in the same order as they are requested. type: array maxItems: 50 items: type: string example: Hello, World! LanguageCode: description: The language into which the text should be translated. type: string enum: - BG ... - ZH-HANS example: DE
解析并注入 ReAct 模板后的效果是这样的:
可以看到解析得没有问题。
总结
本节课,我们开始用 Go 语言仿照 Dify Agent 的思路手撸可定制 API Agent。由于代码量以及细节比较多,因此我拆分成了两个课时。