1. 项目概述一个面向开发者的AI代码生成与协作平台最近在GitHub上看到一个挺有意思的项目叫opencode-ai/opencode。光看这个名字你可能会联想到各种AI代码生成工具比如GitHub Copilot或者一些开源的代码补全模型。但深入了解一下你会发现这个项目定位有点不一样。它不是一个单纯的代码补全插件而更像是一个试图构建“AI原生”开发工作流的平台或工具集。简单来说它的目标不是在你写代码时给你几个建议而是希望重新定义开发者与AI协作编写、理解和维护代码的方式。我自己作为写了十几年代码的人对这类工具一直抱着既期待又审慎的态度。期待的是AI确实能帮我们处理很多繁琐的、模式化的编码任务比如写单元测试、生成API文档、或者把一段复杂的业务逻辑用更清晰的方式重构出来。审慎的是很多工具要么集成度不够需要你在不同窗口间来回切换要么生成的代码质量不稳定需要花大量时间去检查和修改反而降低了效率。opencode项目吸引我的地方在于它似乎想解决这个“最后一公里”的问题。它不是提供一个孤立的模型而是围绕“代码”这个核心资产设计了一整套交互、验证和迭代的机制。你可以把它想象成一个智能的“开发副驾驶”但这个副驾驶不仅坐在你旁边提示你还能帮你操作方向盘、查看地图甚至在你需要的时候直接接管一段平直的道路。对于个人开发者、小团队或者那些希望探索AI增强开发流程的工程师来说这个项目提供了一个非常值得研究的范本。它涉及到的技术栈、设计理念和实际应用中的挑战都是当前“AI开发”领域的热点。2. 核心架构与设计理念拆解要理解opencode我们不能只看它表面的功能得先拆解它的设计思路。这决定了它和Copilot、Codeium这类工具的根本区别。2.1 从“辅助补全”到“协同创作”的范式转变传统的AI编码助手其交互模式本质上是“我写一点你建议一点”。这是一个被动的、响应式的模型。AI的上下文通常局限于当前编辑的文件和光标前后的一小段代码。这种模式对于补全一个函数名、一行调用语句非常高效但对于更复杂的任务比如“为这个类添加一个完整的CRUD接口”或者“重构这个模块以降低耦合度”就显得力不从心了。因为这类任务需要AI理解更广泛的代码库上下文、项目结构甚至设计模式。opencode的设计理念我认为是朝着“协同创作”的方向发展的。它试图将AI提升为一个可以主动理解任务、规划步骤、并生成连贯代码片段的合作伙伴。这意味着任务驱动的交互你可能会以一个自然语言描述的任务开始比如“添加用户登录功能需要邮箱密码验证并返回JWT令牌”。opencode需要解析这个需求将其分解为一系列子任务创建用户模型、设计认证服务、实现控制器、编写路由等。全库上下文感知为了完成上述任务AI需要“看到”整个项目。它需要知道现有的数据模型结构、已有的工具函数、项目依赖的框架和库。opencode很可能通过建立项目的代码索引比如抽象语法树AST分析或向量化嵌入来让AI模型具备这种全局视野。多轮迭代与验证生成的代码不可能一次就完美。opencode需要支持一种交互式的修订流程。比如AI生成了代码你运行测试发现了一个边界情况bug你可以直接对AI说“这里当用户名为空时会抛异常请修复”。AI需要能理解这个反馈定位到具体代码并进行修正。这构成了一个“生成-反馈-修正”的闭环。2.2 技术栈猜想与核心组件虽然无法看到opencode的全部源码细节但根据其项目定位和当前AI编码领域的最佳实践我们可以推断其核心技术栈可能包含以下几个层面1. 后端AI服务层这是大脑。它很可能不是从头训练一个巨型的代码模型而是基于某个强大的开源或闭源基础模型进行精调Fine-tuning或采用提示工程Prompt Engineering来优化其代码生成能力。基础模型候选可能是像CodeLlama、StarCoder这类在代码上预训练的开源模型也可能是通过API调用如GPT-4、Claude等通用大模型。选择开源模型的好处是可控、可私有化部署选择顶级闭源API则能获得当前最强的代码理解与生成能力。上下文管理这是关键。如何将庞大的项目代码库可能数十万行有效地塞进模型有限的上下文窗口如128K tokens技术方案可能包括代码分块与检索将代码库按文件、类或函数切割成块向量化后存入向量数据库。当处理一个任务时根据任务描述检索最相关的代码块作为上下文提供给模型。抽象语法树AST分析通过解析AST可以更精准地理解代码结构、依赖关系和调用链路从而智能地选取关键上下文而不是简单按文本邻近度检索。摘要与大纲为大型文件或模块生成自然语言摘要或结构大纲在需要概览时提供给模型节省token。2. 代码分析与工程化层这是手和眼。AI生成的不只是文本必须是可运行、符合项目规范的代码。静态分析工具集成集成如tree-sitter用于快速解析多种语言的AST、ESLint、Pylint、Black代码格式化等工具。在AI生成代码后自动进行语法检查、风格校验甚至运行简单的静态分析来发现潜在问题。安全扫描集成基础的安全代码扫描检查生成的代码中是否有明显的漏洞模式如SQL注入、命令注入的潜在风险。依赖与导入管理智能处理import/require语句确保生成的代码引用了正确的库和内部模块并自动添加或更新依赖声明文件如package.json,requirements.txt。3. 用户交互与前端层这是界面。如何让开发者以最自然、最低摩擦的方式与AI协作IDE插件可能是VS Code、JetBrains全家桶的扩展。提供聊天界面、内联代码建议、右键菜单触发代码生成任务等。Web Dashboard一个独立的Web界面用于管理项目、查看AI生成历史、配置模型参数、管理上下文检索策略等。自然语言指令解析将开发者模糊的指令“让这个函数跑得快一点”转化为具体的代码优化任务“将此处的循环算法复杂度从O(n²)优化至O(n log n)”。注意以上是基于领域常识的合理推测。一个真实的opencode项目可能会选择其中几个核心方向进行突破而不是面面俱到。其创新点往往就在于在这些分层中某个环节的独特设计。2.3 与主流工具的核心差异点分析为了更清楚定位我们可以将其与常见工具做个对比特性/工具GitHub Copilot / CursorCodeium / Tabnineopencode (推测定位)核心模式行级/块级自动补全聊天辅助行级自动补全任务级代码生成与协同编辑上下文范围当前文件为主有限多文件当前文件为主全项目代码库检索与理解交互方式注释触发、聊天框按键触发补全自然语言任务描述、交互式修订输出产物代码片段、单文件修改建议代码片段可能涉及多文件联动修改、测试生成、文档更新定制化程度较低模型通用低可能支持针对项目代码库的精调或高度定制化提示适用场景日常编码提速、解释代码日常编码提速功能模块开发、代码重构、遗留系统理解、测试覆盖可以看出opencode的野心在于处理更宏观、更复杂的开发任务试图成为项目级别的AI协作者。3. 潜在应用场景与价值深度挖掘一个工具的价值最终体现在它能解决什么实际问题上。对于opencode这类平台我认为其应用场景可以深入到软件开发的多个核心环节。3.1 场景一快速启动新项目或功能模块“从零到一”这是最直观的场景。当你拿到一个新需求需要新建一个服务或模块时传统的步骤是设计数据结构 - 搭建框架脚手架 - 实现核心逻辑 - 编写API层 - 补充测试和文档。这个过程里有大量重复性、模式化的代码。opencode如何介入你可以直接给出需求文档或产品原型的描述。例如“创建一个待办事项Todo后端API支持用户注册登录每个用户有自己的待办列表包含标题、描述、完成状态、创建时间字段需要完整的CRUD接口。”预期工作流AI解析需求建议技术栈例如Node.js Express MongoDB JWT。生成项目基础结构package.json、基础文件夹、数据库连接配置。生成用户User和待办事项Todo的Mongoose Schema或Sequelize Model。生成对应的控制器Controller文件包含注册、登录、获取/创建/更新/删除待办项的逻辑。生成Express路由文件将API端点与控制器方法绑定。生成基础的单元测试文件例如用Jest包含对关键逻辑的测试用例。甚至生成简单的API文档如OpenAPI/Swagger规范片段。价值将数小时甚至一天的初始化工作压缩到几分钟的交互和审查中。开发者可以将精力集中在业务逻辑的特殊性、架构设计和代码审查上而不是敲打样板代码。3.2 场景二遗留代码库的理解与现代化重构“从旧到新”接手一个老旧项目是很多开发者的噩梦。文档缺失代码风格混杂设计模式陈旧。理解代码和重构的风险都很高。opencode如何介入将整个代码库索引给AI。预期工作流代码理解与问答你可以直接向AI提问“这个PaymentProcessor类的主要职责是什么它和OrderService的依赖关系是怎样的” AI可以基于代码分析给出准确回答甚至画出简单的依赖图。生成重构建议你可以指令“这个模块里用了很多回调函数导致嵌套很深请将其重构为使用async/await的版本。” AI可以分析出所有相关函数生成重构后的代码并提示你注意错误处理方式的改变。技术栈迁移辅助例如“将这个使用Express 4和回调风格的API迁移到使用Express 5并支持async/await中间件。” AI可以系统地找出需要修改的模式并生成更改建议。生成更新后的文档基于最新的代码自动为关键模块和API生成或更新注释文档。价值极大降低理解大型、复杂代码库的门槛让重构工作变得更有信心和条理减少因理解偏差引入的bug。3.3 场景三自动化测试生成与漏洞辅助排查“质量守护”编写测试用例枯燥但重要。同样排查一些偶发性的bug或安全漏洞也需要花费大量时间。opencode如何介入结合代码静态分析和AI的逻辑推理能力。预期工作流单元测试生成选中一个函数或类指令“为这个calculateDiscount函数生成覆盖边界条件的单元测试。” AI会分析函数输入输出生成包含正常值、边界值如0、负数、极大值、异常输入的测试用例。集成测试场景生成描述一个用户场景“模拟用户从添加商品到支付完成的完整流程生成集成测试。” AI可以串联起多个API调用生成测试脚本并模拟各种数据状态。漏洞模式识别AI在生成代码或审查代码时可以集成安全规则。例如当它看到字符串拼接后直接传入数据库查询时可以提示“检测到可能的SQL注入风险建议使用参数化查询或ORM方法。” 并直接给出修复后的代码示例。Bug根因分析辅助当你提供一个错误日志和堆栈跟踪时AI可以结合代码库推测最可能出错的代码段并解释原因。价值提升代码测试覆盖率将开发者从重复的测试编写中解放出来在编码阶段提前发现潜在问题提升软件安全性和健壮性。3.4 场景四团队知识沉淀与新人 onboarding“知识传承”每个团队都有自己独特的代码规范、工具链和业务逻辑处理方式。新成员融入需要时间。opencode如何介入将团队的代码规范、最佳实践案例、架构设计文档作为知识库喂给AI。预期工作流新成员在编写代码时AI给出的建议会自动符合团队的代码风格如命名规范、目录结构、特定的工具函数使用方式。新成员可以提问“我们团队如何处理分页查询” AI不仅给出通用方案还能结合团队项目中的具体实现例子来回答。新成员接到一个关于“支付回调处理”的任务时AI可以引导他参考项目中已有的类似模块如订单状态回调并生成一个符合现有模式的代码框架。价值加速新人上手速度降低沟通成本促进团队编码风格统一让项目知识从隐性的、碎片化的状态转化为一个随时可查询的“AI伙伴”。4. 实操推演如何构建一个简易的“opencode”核心原型理解了理念和场景我们不妨动手推演一下如果要构建一个opencode最核心的“任务驱动代码生成”功能我们需要怎么做。这不是复制项目而是理解其技术实现的关键路径。4.1 第一步搭建基础环境与选择模型我们假设一个Python技术栈的演示原型。1. 项目初始化mkdir opencode-prototype cd opencode-prototype python -m venv venv source venv/bin/activate # Linux/Mac # venv\Scripts\activate # Windows pip install openai langchain chromadb tiktoken python-dotenvopenai用于调用GPT系列模型API或其他兼容API的模型。langchain一个强大的框架用于连接LLM、工具、数据并编排复杂的工作流。它简化了检索增强生成RAG等模式的实现。chromadb一个轻量级的向量数据库用于存储和检索代码块的向量表示。tiktoken用于精确计算文本的token数量管理上下文长度。python-dotenv管理环境变量如API密钥。2. 模型选择与配置对于原型使用OpenAI的GPT-4 Turbo或GPT-3.5 Turbo是快速验证想法的方式。在项目根目录创建.env文件OPENAI_API_KEYyour_api_key_here在代码中加载import os from dotenv import load_dotenv from langchain_openai import ChatOpenAI load_dotenv() llm ChatOpenAI(modelgpt-4-turbo-preview, temperature0.1, api_keyos.getenv(OPENAI_API_KEY))temperature0.1设置较低的“温度”使模型输出更确定、更专注于代码减少随机性。实操心得在原型阶段闭源API快速便捷。但考虑生产环境的成本、数据隐私和定制化需求最终很可能会转向开源模型如CodeLlama 70B。这时需要解决模型本地部署、推理加速使用vLLM、TGI等框架和提示工程优化等问题复杂度会显著增加。4.2 第二步实现代码库的索引与检索RAG核心这是让AI“理解”你项目代码的关键。我们不能把整个项目代码都塞进提示词必须建立检索机制。1. 代码分块与加载import os from pathlib import Path from langchain_community.document_loaders import TextLoader from langchain.text_splitter import RecursiveCharacterTextSplitter def load_and_split_code_files(repo_path, allowed_extensions[.py, .js, .java, .md]): 加载并分割代码文件 docs [] for ext in allowed_extensions: for file_path in Path(repo_path).rglob(f*{ext}): try: loader TextLoader(str(file_path), encodingutf-8) raw_docs loader.load() # 为每个文档添加元数据记录文件路径 for doc in raw_docs: doc.metadata[source] str(file_path.relative_to(repo_path)) docs.extend(raw_docs) except Exception as e: print(fError loading {file_path}: {e}) continue # 使用更适合代码的分割器 text_splitter RecursiveCharacterTextSplitter( chunk_size1000, # 每个块的大小 chunk_overlap200, # 块之间的重叠保持上下文连贯 separators[\n\n, \n, , ] # 分割符 ) split_docs text_splitter.split_documents(docs) print(fLoaded {len(split_docs)} code chunks from {repo_path}) return split_docs2. 向量化与存储from langchain_openai import OpenAIEmbeddings from langchain_community.vectorstores import Chroma def create_vector_store(code_chunks, persist_directory./chroma_db): 创建代码块的向量存储 embeddings OpenAIEmbeddings(modeltext-embedding-3-small) vectorstore Chroma.from_documents( documentscode_chunks, embeddingembeddings, persist_directorypersist_directory ) vectorstore.persist() return vectorstore # 使用示例 repo_path /path/to/your/project code_chunks load_and_split_code_files(repo_path) vectorstore create_vector_store(code_chunks)这个过程将代码文本转换为高维向量并存入ChromaDB。当用户提出问题时系统会将问题也转换为向量并在数据库中查找“语义”上最接近的代码块。3. 构建检索链from langchain.chains import RetrievalQA qa_chain RetrievalQA.from_chain_type( llmllm, chain_typestuff, # 将检索到的文档“塞”进提示词 retrievervectorstore.as_retriever(search_kwargs{k: 4}), # 检索最相关的4个块 return_source_documentsTrue, # 返回来源便于验证 chain_type_kwargs{ prompt: YOUR_CUSTOM_PROMPT # 可以定义更专业的提示词模板 } )现在你可以通过qa_chain.run(项目里用户认证是怎么实现的)来提问了。AI会先检索相关代码再结合这些上下文生成回答。注意事项简单的文本分割会破坏代码的结构如函数被腰斩。更优的方案是使用tree-sitter等库进行基于AST的代码解析按函数、类等逻辑单元进行分块这样检索的准确性和上下文完整性会高得多。但这会显著增加实现复杂度。4.3 第三步设计任务驱动的代码生成提示工程简单的问答还不够我们需要让AI执行具体的代码生成任务。这需要精心设计提示词Prompt。1. 定义系统角色和任务格式from langchain.prompts import ChatPromptTemplate, HumanMessagePromptTemplate, SystemMessagePromptTemplate system_template 你是一个资深的{language}软件开发专家。你的任务是根据用户的需求和提供的项目上下文生成高质量、可运行、符合项目现有风格的代码。 ## 项目上下文 {context} ## 你的能力 1. 严格遵循项目已有的代码风格、命名规范和设计模式。 2. 只生成必要的代码避免冗余。 3. 如果需求模糊你会提出澄清问题。 4. 生成的代码应包含必要的注释解释关键逻辑。 5. 如果任务涉及多个文件请清晰地说明每个文件的路径和内容。 ## 输出格式 请以以下格式输出 [语言如python] // 生成的代码[如果需要在这里添加简要的解释] system_message_prompt SystemMessagePromptTemplate.from_template(system_template)human_template 请完成以下任务 {task}当前工作目录或相关文件{current_file} human_message_prompt HumanMessagePromptTemplate.from_template(human_template)chat_prompt ChatPromptTemplate.from_messages([system_message_prompt, human_message_prompt])**2. 构建任务执行链** 我们需要将检索到的上下文context和用户任务task填充到提示词中然后发给LLM。 python from langchain.chains import LLMChain def generate_code_for_task(task_description, current_file_path, vector_storeNone, k6): 为特定任务生成代码 # 1. 检索相关上下文 if vector_store: relevant_docs vector_store.similarity_search(task_description, kk) context \n\n.join([f文件{doc.metadata[source]}\n内容{doc.page_content} for doc in relevant_docs]) else: context 暂无项目上下文。 # 2. 构建完整提示词并调用LLM chain LLMChain(llmllm, promptchat_prompt) full_prompt_input { language: Python, # 可以根据项目动态判断 context: context, task: task_description, current_file: current_file_path } result chain.run(full_prompt_input) return result # 使用示例 task 在现有项目中为User模型添加一个‘last_login_time’字段并更新对应的序列化器。 generated_code generate_code_for_task(task, current_file_pathmodels/user.py, vector_storevectorstore) print(generated_code)4.4 第四步集成代码分析与简单验证生成代码后不能直接信任需要基础验证。1. 语法检查import ast import subprocess def validate_python_syntax(code_block): 验证Python代码语法 try: ast.parse(code_block) return True, 语法正确 except SyntaxError as e: return False, f语法错误{e} def validate_with_tool(code_block, language): 使用外部工具验证如black格式化检查flake8 lint if language python: # 尝试用black格式化如果格式差异大可能代码结构有问题 try: result subprocess.run([black, --check, --diff, -], inputcode_block.encode(), capture_outputTrue, timeout5) if result.returncode 0: return True, 代码格式良好 else: # black建议了修改代码可能风格不一致但语法正确 return True, f代码格式可优化建议调整\n{result.stdout.decode()} except FileNotFoundError: return None, 未安装black工具跳过格式检查 return None, f暂无{language}的验证工具2. 集成到生成流程在generate_code_for_task函数返回结果前可以添加验证步骤# ... 生成代码 result ... # 假设我们从结果中提取出了代码块 code_str 和语言 lang is_valid, msg validate_python_syntax(code_str) if not is_valid: result f\n\n⚠️ **语法检查警告**{msg}\n请仔细审查生成的代码。 else: tool_valid, tool_msg validate_with_tool(code_str, lang) if tool_msg: result f\n\n **工具建议**{tool_msg}至此一个最基础的原型就完成了。它可以1. 读取你的代码库并建立索引2. 理解自然语言任务3. 检索相关代码作为参考4. 生成新的代码5. 进行基础的语法和风格检查。5. 深入挑战与进阶优化方向上面实现的原型仅仅是个开始。要让它真正达到“可用”甚至“好用”还需要攻克大量难题。5.1 挑战一代码上下文的精准检索与表示问题简单的文本向量检索很容易检索到不相关或碎片化的代码。比如任务是要“修改登录逻辑”可能检索到的是包含“登录”字符串的配置文件注释而不是核心的认证服务类。进阶方案分层检索先检索文件/模块级别的概述通过为每个文件生成摘要获得定位到相关模块再深入检索该模块内的具体函数和类。图增强检索利用代码的图结构调用图、继承图、导入图。当检索到函数A时可以自动将其直接调用的函数B、C也纳入上下文即使B、C的文本语义与任务描述不直接相关。混合检索结合基于关键词的稀疏检索如BM25和基于向量的稠密检索兼顾精确匹配和语义相似度。元数据过滤检索时加入过滤器比如只检索.py文件、只检索类定义、只检索最近修改的文件等。5.2 挑战二复杂任务的规划与分解问题用户指令可能是复杂的、多步骤的如“重构这个单体应用为微服务”。模型需要自己规划子任务和步骤。进阶方案引入ReAct (Reasoning Acting)模式或LLM Agents智能体。思路让LLM不仅生成最终答案还生成“思考过程”和“下一步行动”。例如Thought: 用户想添加一个缓存层。我需要先检查项目用了什么缓存客户端如Redis然后找到需要缓存的Service修改其代码最后更新配置。Action: Search[项目中的缓存配置]Observation: 在config目录下找到了redis配置但尚未在业务代码中使用。Thought: 现在我需要为ProductService的getProductById方法添加缓存。先找到这个文件。Action: Search[ProductService getProductById]...如此循环直到完成任务。实现可以使用LangChain的Agent和Tool框架。为AI定义一系列“工具”如search_code,edit_file,run_tests,check_syntax。AI根据任务决定调用哪个工具并处理工具返回的结果。5.3 挑战三生成代码的可靠性与安全性问题AI可能生成存在bug、安全漏洞或性能问题的代码。盲目信任会导致生产事故。进阶方案沙箱执行验证对于生成的不太复杂的函数或脚本可以在一个安全的隔离环境如Docker容器中自动运行用预设的测试用例验证其基本功能。强化静态分析集成更强大的linter和静态分析工具如SonarQube,Semgrep对生成的代码进行安全、漏洞、坏味道的深度扫描。测试用例共生要求AI在生成代码的同时必须生成对应的单元测试。这既是对生成逻辑的验证也直接产生了测试资产。人工审核流程集成生成的代码不应直接写入主分支。系统应生成一个Pull Request (PR)并自动请求相关人员审查。AI可以在PR描述中详细说明更改内容和原因。5.4 挑战四与现有开发工具链的深度集成问题原型是一个独立脚本但开发者工作在主流的IDEVS Code, JetBrains和协作平台GitHub, GitLab中。进阶方案开发IDE插件这是必须的。插件可以提供侧边栏聊天、代码选中右键菜单、内联建议不仅是补全也可以是“解释这段代码”、“为这段代码生成测试”等。集成版本控制AI的每次修改都应该对应一个清晰的commitcommit message由AI根据更改内容自动生成。这保证了历史可追溯。对接CI/CD生成的代码在提交后能自动触发项目的CI/CD流水线运行完整的测试套件。如果测试失败可以将错误日志反馈给AI让它尝试修复。6. 未来展望与个人思考玩转这个原型并思考上述挑战后我对opencode这类项目的未来有几点个人判断1. 短期看是“强辅助”而非“替代”。它最擅长的还是处理那些模式清晰、上下文明确的开发任务比如写样板代码、生成测试、进行不复杂的重构。对于需要深度业务理解、创造性架构设计或处理极端边界情况的工作人类开发者依然不可替代。它的角色是处理“繁琐”放大开发者的“创造力”。2. 核心价值在于“工作流”而非“单点模型”。未来各家模型的代码生成能力可能会逐渐趋同。真正的差异化竞争力在于如何将模型能力无缝、智能地嵌入到整个软件开发生命周期SDLC中——从需求分析、到设计、编码、测试、调试、部署、运维。谁能打造出最流畅、最智能的AI原生开发工作流谁就能赢得开发者。3. 开源与可定制化是关键。企业级用户对代码安全、数据隐私、模型定制有极高要求。一个开放核心Open Core的模式可能更受欢迎基础平台开源允许企业私有化部署并可以用自己的代码数据对模型进行微调使其更符合内部编码规范和业务逻辑。4. 对开发者技能树的重构。这并不意味着程序员失业而是意味着技能重心转移。未来的开发者可能需要更强的能力在于 *问题分解与精准描述如何将模糊的需求转化为AI能精确理解的指令。 *代码审查与验证如何高效地审查AI生成的代码识别其逻辑缺陷和潜在风险。 *提示工程与AI工作流设计如何为团队设计高效的、与AI协作的标准化流程。 *系统设计与架构这部分创造性工作的重要性会进一步提升。回过头看opencode-ai/opencode这个项目它更像一个探索方向的旗帜。它提出的愿景——让AI成为深度融入开发流程的协作者——正是当前开发者工具演进的最前沿。无论这个具体项目最终成果如何它所代表的趋势已不可逆。对于每一位开发者而言现在开始了解、尝试并思考如何与这些AI工具共舞或许就是为未来几年职业发展所做的最有价值的投资之一。