ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

科学智能体基础模型:架构原理与Python最小实现

2026/9/4 15:28:52 拓冰建站 浏览量
科学智能体基础模型:架构原理与Python最小实现 最近在梳理科学人工智能方向的项目时“Intern-S2-Preview: Scientific Agentic Foundation Model”这个命名很容易让人多想一层它不像传统模型那样只强调“参数量大、对话能力强”而是把关键词落在了Scientific、Agentic、Foundation Model三个方向上。这说明一个大背景正在发生AI 在科研领域的应用正在从“帮你检索论文、帮你润色摘要”转向“帮你完成一条多步骤科研流水线”。本文会围绕这个模型方向展开讲清楚什么是科学智能体基础模型、它和普通大模型有什么区别、主流架构如何设计并给出一套可以照着改的 Python 最小实现。文章面向有 Python 基础、想了解 Agent 工程化落地方式的开发者读完你能掌握一套可复用的 Agent 骨架也能理解评估、安全和工程化中的坑点。需要先说明一点模型本身还在 Preview 阶段公开资料、评测结果和开放接口随时可能变化。所以本文不会把某个版本的功能细节写死重点是把“科学智能体”这一类系统的设计方法论拆给你看方便后续对齐真实模型能力。1. 先建立共识Agentic、Foundation Model 与 Scientific1.1 Agentic 模型与传统大模型的差别传统的对话式大模型本质是一个“输入文本 → 输出文本”的映射器。你问一个问题它给你一个答案整个过程通常只有一轮交互。这样的模式在开放闲聊、内容总结、单次翻译上是够用的但在真实科研任务里远远不够。真实科研任务往往是多步骤的先理解用户提的科学问题。去文献库或知识库检索相关信息。根据信息设计计算或实验步骤。执行代码得到数值结果。判断结果是否合理。不合理时修正方法重新计算。最后把过程和结论整理成可复查的报告。如果你把这 7 步全部塞给一个普通大模型它大概率会在早期的某一步“想当然”——比如直接根据常识编一个公式结果而不去真实计算。这就是所谓“幻觉”在科学任务里格外危险的原因科学结论要求可复核不能接受“看起来合理但实际没有依据”的输出。Agentic 模型要解决的正是这个问题。它不再要求模型一步到位输出最终答案而是让模型具备一个“行动循环”观察当前任务状态 → 决定下一步动作 → 调用工具执行 → 观察工具返回结果 → 再决定下一步模型变成系统的“决策大脑”真正执行数值计算、代码运行、数据读取的是外部工具。模型负责规划、拆解、判断和总结。这种设计牺牲了一部分“直接回答”的速度但换来了可追溯性和更高的任务成功率。1.2 Scientific Agentic Foundation Model 要解决什么问题把“Scientific”和“Agentic”叠加在一起含义会更具体。它不是通用 Agent 的一个简单分支而是面向科学发现全流程设计的基础模型。科学任务有几个特点对错误容忍度低一个单位换算错误、一个符号错误可能导致整个结论失效。步骤复杂度高实验设计、数据分析、理论推导经常需要多轮迭代。强依赖外部知识文献、数据库、仿真软件、计算库都是模型不具备的“外部世界”。要求过程可复现科研人员不仅要结果还需要看到每一步怎么来的。因此Scientific Agentic Foundation Model 的定位不是“一个会调工具的聊天机器人”而是在底层模型能力上围绕科学任务做了强化训练、工具对齐和推理增强的 Agent 系统底座。以 Intern-S2-Preview 这个标题为例它属于典型的基础模型系列迭代产物。带 Preview 后缀通常意味着面向早期用户开放能力预览帮助团队收集真实场景反馈同时模型在知识、工具调用、推理链路上还在快速演进。所以在工程上越是接触 Preview 模型越应该做好“模型可替换”的设计避免把代码耦死在某个特定的模型输出格式上。1.3 典型科学任务拆解为了理解这类模型的能力边界我们不妨把科研中的高频任务做一次抽象。通常能落到 Agent 系统上的任务大概有这么几类任务类型具体例子Agent 需要的核心能力文献调研找出“2020 年后钙钛矿电池稳定性”相关论文并做对比检索、摘要、溯源实验方案生成根据反应物设计一组温度梯度实验知识推理、方案生成数据清洗去掉异常点并按公式归一化代码执行、数据理解数值验证检查论文中的公式推导是否一致符号计算、逐步推导结果解释分析模拟输出并给出物理解释领域知识、结构化输出复现实验将论文方法转成可运行脚本代码生成、调试执行单看这些任务花几个小时人工也能完成。但科学智能体追求的是“把低层重复劳动自动化让人把时间留给假设提出和结果判断”。这也是 Foundation Model 存在的价值模型先提供通用的科学知识底座再通过 Agent 框架解决执行和反馈问题。2. 从 Intern-S2-Preview 看科学智能体的设计架构2.1 名称信息先说个实话关于 S2 的准确缩写全称、训练数据和评测基准目前没有足够公开材料支撑我给出一个权威解释。按照命名惯例这类带系列前缀的模型通常意味着同一技术底座下的特定方向版本而 Preview 强调它是一个早期可用版本。在没有官方完整文档之前不建议开发者对模型能力做过度外推。正确做法是提前盯着这几个官方信息源去核对。模型卡里的知识截止时间和擅长领域。官方提供的 API 兼容协议是否支持 OpenAI 兼容接口。工具调用格式是走原生 function calling还是走统一的 JSON action。评测集是否覆盖了你所在学科的数据类型。许可证与数据使用边界尤其是科研数据是否允许商用。这些信息比模型名称本身更能决定你的架构设计。2.2 标准 Agent 架构里的五个层从架构上看一个科学智能体可以拆成五层第一层环境层环境是 Agent 能感知和操作的“外部世界”包括数据文件、数据库、科学计算库、仿真软件、论文数据库、实验设备接口等。第二层感知层感知层负责把环境中的信息转成模型能理解的结构化内容。例如读取 CSV 后生成字段摘要通过 API 查询后返回 JSON而不是直接丢给模型一坨原始日志。第三层决策层决策层就是基础模型本身负责理解任务、拆解子目标、选择下一个动作、判断当前结果是否满足要求。这一层是 Foundation Model 的主场。第四层行动层行动层是把决策转成真实动作的模块包括函数调用、代码执行、SQL 查询、文件写入、Web 请求等。行动层必须做权限控制和异常捕获。第五层记忆层记忆层保存任务上下文、中间结果、历史失败经验。科学任务通常有很长的中间推导因此记忆层设计不好Agent 很容易“做了后面忘了前面”。五层之间的关系可以用一个简单流程表示用户任务进入 ↓ 决策层规划 ↓ 感知层读取当前状态 ↓ 行动层调用工具 ↓ 环境返回结果 ↓ 结果回到记忆层 ↓ 决策层判断是否完成未完成则进入下一轮循环这五层在代码里不一定是独立服务但逻辑上必须清晰分开。很多 Agent 项目失败不是因为模型不够强而是因为感知、行动、记忆混在一个函数里导致无法定位问题。2.3 Agentic RAG 与传统 RAG 的差异科学智能体经常和 RAG 一起出现。但现在的热词已经从 RAG 升级到了 Agentic RAG两者的设计思想差别很大。传统 RAG 的流程是“用户问题 → 向量检索 → Top-K 文档拼接 → 一次生成回答”。它把检索当成一个固定前置步骤适合回答“某篇论文的结论是什么”“某材料的性质是什么”这类事实性提问。Agentic RAG 则把检索变成了 Agent 内部的一个“可调用工具”。模型可以自己决定当前问题是否真的需要检索。是要做单次检索还是需要拆成多个子问题分别检索。第一轮结果不够时是否要换关键词重新检索。检索到多篇文献时是做对比还是做汇总。最终回答是否需要引用证据。举个例子。用户问“比较 Method A 和 Method B 在高温条件下的稳定性差异并给出可能原因”。传统 RAG 会一次性把相关论文片段拼出来很容易让模型“强行总结”出前后矛盾的答案。Agentic RAG 则可能这样工作第一步检索 Method A 高温稳定性相关论文 第二步检索 Method B 高温稳定性相关论文 第三步对比两者的实验条件和退化机制 第四步检索是否有更早的关键机理文献 第五步汇总并输出引用列表每一步都是独立动作每步结果都可以被检查。这种设计更接近科研人员检索文献的真实习惯。3. 核心机制工具、记忆、反思与上下文管理3.1 Observation-Action 循环科学 Agent 的最小工作单元是“观察—行动”循环。Observation 是当前系统状态可以是工具结果、报错信息、数据表也可以是模型自己产出的中间分析。Action 是模型决定要执行的工具调用。循环终止条件可以是“得到最终答案”或“达到最大步数”。这个循环和强化学习里的环境交互很相似所以业界会用 Agentic RL 来描述这类系统。区别在于传统 RL 的奖励来自环境而语言 Agent 的“策略”往往来自模型自身的推理能力。在实际代码里Observation-Action 并不复杂核心是一个while循环像下面这个伪代码while not finished and step max_step: observation collect_state() action llm.decide(observation) if action.is_final_answer(): finished True else: result execute_tool(action) update_memory(result) step 1设计这个循环时最容易被忽略的是“终止条件”。如果只允许成功终止、不允许超时终止Agent 会在一个错误方向上空转很久。因此工程实现里一定要有最大步数限制、单次调用超时、结果长度上限和失败降级策略。3.2 记忆设计暂存、长期与任务上下文科学任务的长上下文特点决定了记忆不能只靠“把聊天记录全部堆进模型”。记忆至少要分三层短期工作记忆当前任务正在处理的数据、上一步工具返回结果、临时变量。这部分通常保存在 Agent 运行时的状态对象里。长期工作记忆跨任务复用的知识包括个人知识库、历史实验记录、常用代码片段。长期记忆应通过检索按需注入而不是全部塞进对话。外部结构化存储适合管理中间结果的文件目录、数据库表和日志。模型只保存“哪里可以找到数据”不需要自己记住大数据内容。在科学场景里我建议把“数值中间结果”和“推理文本”分开存储。数值去重、精度比较、单位校验用程序完成比让模型读文本更可靠。3.3 反思与失败恢复Agent 与普通 LLM 应用最大的区别之一是具备反思能力。反思在代码上通常表现为“一次额外调用”模型在得到最终答案前先自己检查答案是否合理。比如单位是否一致数量级是否正常是否引用了不存在的变量代码是否有异常但被忽略了这被称为 Reflection 或 Self-Correction。OpenAI 等公司在 Agent 体系中加入的 Critic、Self-Critique 机制本质上都是让模型在最终输出前增加一次校验步骤。但反思不是越多越好。过度反思不仅增加成本还会把原本正确的答案改成错误的。实际工程中应该在“高风险数值计算”和“长链路工具调用”之后启用反思而不是每步都反思。一个比较稳妥的做法是把反思做成一个可插拔模块只在特定信号出现时触发比如工具返回 error、结果超出物理合理范围、多个来源数据冲突。3.4 上下文压缩每个 Agent 循环都会把上一次的完整输出和工具结果拼接到历史里。如果每个工具结果都不做裁剪几轮之后上下文就会失控。科学 Agent 里常用下面几种压缩策略截断只保留工具结果的前 N 个字符。摘要让模型对长日志做摘要再把摘要放回历史。结构化抽象把大段数据抽象成“最大值、最小值、均值、异常点数量”等指标。检索覆盖把历史记录向量化下一次只检索与当前动作最相关的一部分。上下文压缩不是简单的“截断聊天记录”它要保证压缩后不丢失结论可复现所需的关键中间值。因此生产系统里建议把“给模型看的历史”和“给审计看的全量日志”分开存储。4. 最小实战搭建一个轻量科学智能体下面进入可以动手的部分。这一节的目标不是写一个完整产品而是实现一个结构清晰、可以扩展的轻量科学智能体骨架。它会包含 LLM 接入层、工具注册层和 ReAct 主循环三部分。4.1 实战目标我们要实现一个能处理“单位转换 相对误差计算”这类简单科学问题的小 Agent。它演示以下能力模型接收任务后自主判断该调用哪个工具。工具返回真实计算结果而不是由模型硬猜。多步任务按顺序执行。最后输出一个可追溯的结论。为了让示例不绑定某个特定云厂商本节以 OpenAI 兼容的/chat/completions协议作为接入抽象。实际项目中如果 Intern-S2-Preview 提供了官方 SDK你只需要替换llm_client.py这个文件。4.2 项目结构与依赖项目结构如下sci-agent-demo/ |-- README.md |-- requirements.txt |-- llm_client.py |-- tools_builtin.py |-- agent_core.py -- run_scientific_agent.py依赖非常简单只需要requests# requirements.txt requests2.31.0安装命令pip install -r requirements.txt4.3 LLM 接入层llm_client.py的作用是屏蔽底层模型差异。只要你的模型服务提供 OpenAI 兼容接口都可以通过环境变量注入地址和模型名。# llm_client.py import os import requests class OpenAICompatClient: 使用 OpenAI 兼容协议调用底层模型。 如果你的模型有官方 SDK可以重写 chat 方法 让上层 Agent 代码不需要改动。 def __init__(self, base_urlNone, api_keyNone, modelNone, timeout120): self.base_url ( base_url or os.getenv(AGENT_LLM_BASE_URL, http://127.0.0.1:8000/v1) ).rstrip(/) self.api_key api_key or os.getenv(AGENT_LLM_API_KEY, EMPTY) self.model model or os.getenv(AGENT_LLM_MODEL, your-model-name) self.timeout timeout def chat(self, messages, temperature0.2): url f{self.base_url}/chat/completions headers {Authorization: fBearer {self.api_key}} payload { model: self.model, messages: messages, temperature: temperature, } resp requests.post(url, headersheaders, jsonpayload, timeoutself.timeout) resp.raise_for_status() data resp.json() return data[choices][0][message][content].strip()这段代码值得注意的地方base_url指向/v1最后会拼接成/chat/completions。默认模型名是your-model-name你要替换成实际部署的服务名。temperature设置较低目的是让 Agent 在工具调用场景下尽量稳定减少随机输出。如果你的环境没有可用的远程服务也可以用本地推理工具启动一个兼容服务。只要服务商提供标准 Chat Completions 接口上面代码几乎不用改。4.4 内置科学工具工具层放在tools_builtin.py。这里用注册表模式管理工具好处是新增工具时不需要改主循环代码。# tools_builtin.py import json import os import subprocess import tempfile TOOL_REGISTRY {} def register(func): 把函数注册到全局工具表。 TOOL_REGISTRY[func.__name__] func return func def tool_call(tool_name: str, args: dict): 统一的工具调用入口负责捕获异常并返回结构化的工具结果。 if tool_name not in TOOL_REGISTRY: return {result: None, error: ftool not found: {tool_name}} try: data TOOL_REGISTRY[tool_name](**(args or {})) return {result: data, error: None} except Exception as exc: return {result: None, error: f{type(exc).__name__}: {exc}} register def convert_temperature(value: float, from_unit: str, to_unit: str) - dict: 温度单位转换支持 C(摄氏度)、F(华氏度)、K(开尔文)。 value float(value) from_unit from_unit.strip().lower() to_unit to_unit.strip().lower() if from_unit not in {c, f, k} or to_unit not in {c, f, k}: return {error: unit must be one of C/F/K} # 先统一转到开尔文 if from_unit c: kelvin value 273.15 elif from_unit f: kelvin (value - 32) * 5 / 9 273.15 else: kelvin value # 再转到目标单位 if to_unit c: out kelvin - 273.15 elif to_unit f: out (kelvin - 273.15) * 9 / 5 32 else: out kelvin return {value: round(out, 4), unit: to_unit.upper()} register def calculate_relative_error(expected: float, measured: float) - dict: 计算相对误差返回百分比。 expected float(expected) measured float(measured) if expected 0: return {error: expected cannot be zero} rel_error abs(measured - expected) / abs(expected) * 100 return {relative_error_percent: round(rel_error, 4)} register def run_python_code(code: str, timeout_seconds: int 30) - dict: 在临时进程中执行 Python 代码。 安全提醒本函数只适合在本地可信环境中做演示。 生产环境必须改为容器、沙箱或远程执行服务 禁止直接在宿主环境执行模型生成的任意代码。 tmp_file tempfile.NamedTemporaryFile( w, suffix.py, deleteFalse, encodingutf-8 ) try: tmp_file.write(code) tmp_file.flush() tmp_file.close() try: completed subprocess.run( [python, tmp_file.name], capture_outputTrue, textTrue, timeouttimeout_seconds, ) return { returncode: completed.returncode, stdout: completed.stdout[-4000:], stderr: completed.stderr[-4000:], } except subprocess.TimeoutExpired: return {returncode: -1, stdout: , stderr: timeout} finally: try: os.unlink(tmp_file.name) except Exception: pass这里故意加入了代码执行工具因为科学 Agent 最常见的动作就是“生成一段 Python 做数据分析”。但它也是风险最高的工具。所以代码注释里写得很清楚生产环境必须换成沙箱容器绝不能因为图方便让模型在宿主机上任意执行代码。4.5 可扩展提示词与 ReAct 主循环主循环放在agent_core.py。为了让模型输出稳定我们规定模型要么输出一个 JSON 格式的工具调用要么输出final.../final格式的最终结论。# agent_core.py import json from llm_client import OpenAICompatClient from tools_builtin import TOOL_REGISTRY, tool_call SYSTEM_PROMPT 你是一个面向科学研究场景的智能体。你必须严格按下面的格式输出 1. 如果需要调用工具只输出一个 JSON 对象不能带任何解释。JSON 格式 {tool: 工具名, args: {参数名: 参数值}} 2. 如果已经拿到工具结果并能给出最终答案输出 final最终结论/final 可用工具 - convert_temperature: 温度单位转换 - calculate_relative_error: 计算相对误差 - run_python_code: 执行一段 Python 代码 原则 - 涉及数值时必须先调用工具真实计算不能靠模型记忆硬答。 - 工具报错时阅读错误信息并修正后重试。 .strip() MAX_STEPS 6 def _strip_code_fence(text: str) - str: 去掉模型输出里可能带上的 json 代码围栏。 text text.strip() if text.startswith(): lines text.split(\n) if lines and lines[0].startswith(): lines lines[1:] if lines and lines[-1].strip() : lines lines[:-1] text \n.join(lines).strip() return text def parse_model_output(text: str): 解析模型输出区分是工具调用还是最终答案。 text _strip_code_fence(text) if text.startswith(final): end text.find(/final) if end -1: end len(text) content text[len(final):end].strip() return {type: final, content: content} try: obj json.loads(text) except json.JSONDecodeError: # 模型没有按格式输出时把它当作最终文本保证流程不卡死。 return {type: final, content: text} if isinstance(obj, dict) and tool in obj: return { type: tool, tool: str(obj[tool]), args: obj.get(args) or {}, } return {type: final, content: text} class ScientificAgent: def __init__(self, llm_client, max_stepsMAX_STEPS, verboseTrue): self.llm llm_client self.max_steps max_steps self.verbose verbose self.history [{role: system, content: SYSTEM_PROMPT}] def run(self, task: str) - str: self.history.append({role: user, content: f任务{task}}) for step in range(1, self.max_steps 1): raw self.llm.chat(self.history, temperature0.2) parsed parse_model_output(raw) if self.verbose: print(f\n[step {step}] 模型输出{raw[:300]}) if parsed[type] final: return parsed[content] tool_name parsed[tool] args parsed[args] if self.verbose: print(f[step {step}] 调用工具{tool_name}参数{args}) tool_result tool_call(tool_name, args) if self.verbose: print( [step {}] 工具结果{}.format( step, json.dumps(tool_result, ensure_asciiFalse)[:500] ) ) # 把工具结果作为新的用户消息放回上下文 self.history.append({role: assistant, content: raw}) self.history.append( { role: user, content: ( 工具执行结果 json.dumps(tool_result, ensure_asciiFalse) ), } ) return 已达到最大步数未得到最终答案。这段代码的关键设计点如下parse_model_output保证了模型即使输出少量非 JSON 内容Agent 也不会直接崩溃。每次工具调用结果都以 JSON 文本追加回对话历史模型可以看到上一步执行情况。最大步数限制避免了死循环。TOOL_REGISTRY是全局注册表新增工具只需要写一个注册函数主循环无需改动。4.6 运行与验证最后是入口文件# run_scientific_agent.py from agent_core import ScientificAgent from llm_client import OpenAICompatClient def main(): client OpenAICompatClient() agent ScientificAgent(client, max_steps6) task ( 某实验在 25 摄氏度下进行。 请先把该温度转换为华氏度 再计算理论活化能 45.2 kJ/mol测量活化能 47.8 kJ/mol 相对误差是多少 ) answer agent.run(task) print(\n) print(最终结论) print(answer) if __name__ __main__: main()运行前先设置环境变量export AGENT_LLM_BASE_URLhttp://127.0.0.1:8000/v1 export AGENT_LLM_API_KEYEMPTY export AGENT_LLM_MODELyour-model-name python run_scientific_agent.py如果模型服务正常一个合格的执行轨迹可能类似下面这段具体内容取决于模型输出[step 1] 模型输出{tool: convert_temperature, args: {value: 25, from_unit: C, to_unit: F}} [step 1] 工具结果{result: {value: 77.0, unit: F}, error: null} [step 2] 模型输出{tool: calculate_relative_error, args: {expected: 45.2, measured: 47.8}} [step 2] 工具结果{result: {relative_error_percent: 5.7522}, error: null} [step 3] 模型输出final25 摄氏度等于 77 华氏度理论值 45.2 kJ/mol 与测量值 47.8 kJ/mol 的相对误差为 5.75%。/final注意这里展示的是